Merged revisions 72806 via svnmerge from
[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 = 1;
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_remove_include(int fd, int argc, char *argv[])
118 {
119         if (argc != 6)
120                 return RESULT_SHOWUSAGE;
121
122         if (strcmp(argv[4], "into"))
123                 return RESULT_SHOWUSAGE;
124
125         if (!ast_context_remove_include(argv[5], argv[3], registrar)) {
126                 ast_cli(fd, "We are not including '%s' into '%s' now\n",
127                         argv[3], argv[5]);
128                 return RESULT_SUCCESS;
129         }
130
131         ast_cli(fd, "Failed to remove '%s' include from '%s' context\n",
132                 argv[3], argv[5]);
133         return RESULT_FAILURE;
134 }
135
136 /*! \brief return true if 'name' is included by context c */
137 static int lookup_ci(struct ast_context *c, const char *name)
138 {
139         struct ast_include *i = NULL;
140
141         if (ast_rdlock_context(c))      /* error, skip */
142                 return 0;
143         while ( (i = ast_walk_context_includes(c, i)) )
144                 if (!strcmp(name, ast_get_include_name(i)))
145                         break;
146         ast_unlock_context(c);
147         return i ? -1 /* success */ : 0;
148 }
149
150 /*! \brief return true if 'name' is in the ignorepats for context c */
151 static int lookup_c_ip(struct ast_context *c, const char *name)
152 {
153         struct ast_ignorepat *ip = NULL;
154
155         if (ast_rdlock_context(c))      /* error, skip */
156                 return 0;
157         while ( (ip = ast_walk_context_ignorepats(c, ip)) )
158                 if (!strcmp(name, ast_get_ignorepat_name(ip)))
159                         break;
160         ast_unlock_context(c);
161         return ip ? -1 /* success */ : 0;
162 }
163
164 /*! \brief moves to the n-th word in the string, or empty string if none */
165 static const char *skip_words(const char *p, int n)
166 {
167         int in_blank = 0;
168         for (;n && *p; p++) {
169                 if (isblank(*p) /* XXX order is important */ && !in_blank) {
170                         n--;    /* one word is gone */
171                         in_blank = 1;
172                 } else if (/* !is_blank(*p), we know already, && */ in_blank) {
173                         in_blank = 0;
174                 }
175         }
176         return p;
177 }
178
179 /*! \brief match the first 'len' chars of word. len==0 always succeeds */
180 static int partial_match(const char *s, const char *word, int len)
181 {
182         return (len == 0 || !strncmp(s, word, len));
183 }
184
185 /*! \brief split extension\@context in two parts, return -1 on error.
186  * The return string is malloc'ed and pointed by *ext
187  */
188 static int split_ec(const char *src, char **ext, char ** const ctx)
189 {
190         char *c, *e = ast_strdup(src); /* now src is not used anymore */
191
192         if (e == NULL)
193                 return -1;      /* malloc error */
194         /* now, parse values from 'exten@context' */
195         *ext = e;
196         c = strchr(e, '@');
197         if (c == NULL)  /* no context part */
198                 *ctx = "";      /* it is not overwritten, anyways */
199         else {  /* found context, check for duplicity ... */
200                 *c++ = '\0';
201                 *ctx = c;
202                 if (strchr(c, '@')) { /* two @, not allowed */
203                         free(e);
204                         return -1;
205                 }
206         } 
207         return 0;
208 }
209
210 /* _X_ is the string we need to complete */
211 static char *complete_context_remove_include(const char *line, const char *word,
212         int pos, int state)
213 {
214         int which = 0;
215         char *res = NULL;
216         int len = strlen(word); /* how many bytes to match */
217         struct ast_context *c = NULL;
218
219         if (pos == 3) {         /* "dialplan remove include _X_" */
220                 if (ast_wrlock_contexts()) {
221                         ast_log(LOG_ERROR, "Failed to lock context list\n");
222                         return NULL;
223                 }
224                 /* walk contexts and their includes, return the n-th match */
225                 while (!res && (c = ast_walk_contexts(c))) {
226                         struct ast_include *i = NULL;
227
228                         if (ast_rdlock_context(c))      /* error ? skip this one */
229                                 continue;
230
231                         while ( !res && (i = ast_walk_context_includes(c, i)) ) {
232                                 const char *i_name = ast_get_include_name(i);
233                                 struct ast_context *nc = NULL;
234                                 int already_served = 0;
235
236                                 if (!partial_match(i_name, word, len))
237                                         continue;       /* not matched */
238
239                                 /* check if this include is already served or not */
240
241                                 /* go through all contexts again till we reach actual
242                                  * context or already_served = 1
243                                  */
244                                 while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served)
245                                         already_served = lookup_ci(nc, i_name);
246
247                                 if (!already_served && ++which > state)
248                                         res = strdup(i_name);
249                         }
250                         ast_unlock_context(c);
251                 }
252
253                 ast_unlock_contexts();
254                 return res;
255         } else if (pos == 4) { /* "dialplan remove include CTX _X_" */
256                 /*
257                  * complete as 'from', but only if previous context is really
258                  * included somewhere
259                  */
260                 char *context, *dupline;
261                 const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'include' */
262
263                 if (state > 0)
264                         return NULL;
265                 context = dupline = strdup(s);
266                 if (!dupline) {
267                         ast_log(LOG_ERROR, "Out of free memory\n");
268                         return NULL;
269                 }
270                 strsep(&dupline, " ");
271
272                 if (ast_rdlock_contexts()) {
273                         ast_log(LOG_ERROR, "Failed to lock contexts list\n");
274                         free(context);
275                         return NULL;
276                 }
277
278                 /* go through all contexts and check if is included ... */
279                 while (!res && (c = ast_walk_contexts(c)))
280                         if (lookup_ci(c, context)) /* context is really included, complete "from" command */
281                                 res = strdup("from");
282                 ast_unlock_contexts();
283                 if (!res)
284                         ast_log(LOG_WARNING, "%s not included anywhere\n", context);
285                 free(context);
286                 return res;
287         } else if (pos == 5) { /* "dialplan remove include CTX from _X_" */
288                 /*
289                  * Context from which we removing include ... 
290                  */
291                 char *context, *dupline, *from;
292                 const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'include' */
293                 context = dupline = strdup(s);
294                 if (!dupline) {
295                         ast_log(LOG_ERROR, "Out of free memory\n");
296                         return NULL;
297                 }
298
299                 strsep(&dupline, " "); /* skip context */
300
301                 /* fourth word must be 'from' */
302                 from = strsep(&dupline, " ");
303                 if (!from || strcmp(from, "from")) {
304                         free(context);
305                         return NULL;
306                 }
307
308                 if (ast_rdlock_contexts()) {
309                         ast_log(LOG_ERROR, "Failed to lock context list\n");
310                         free(context);
311                         return NULL;
312                 }
313
314                 /* walk through all contexts ... */
315                 c = NULL;
316                 while ( !res && (c = ast_walk_contexts(c))) {
317                         const char *c_name = ast_get_context_name(c);
318                         if (!partial_match(c_name, word, len))  /* not a good target */
319                                 continue;
320                         /* walk through all includes and check if it is our context */  
321                         if (lookup_ci(c, context) && ++which > state)
322                                 res = strdup(c_name);
323                 }
324                 ast_unlock_contexts();
325                 free(context);
326                 return res;
327         }
328
329         return NULL;
330 }
331
332 /*!
333  * REMOVE EXTENSION command stuff
334  */
335 static int handle_context_remove_extension(int fd, int argc, char *argv[])
336 {
337         int removing_priority = 0;
338         char *exten, *context;
339         int ret = RESULT_FAILURE;
340
341         if (argc != 5 && argc != 4) return RESULT_SHOWUSAGE;
342
343         /*
344          * Priority input checking ...
345          */
346         if (argc == 5) {
347                 char *c = argv[4];
348
349                 /* check for digits in whole parameter for right priority ...
350                  * why? because atoi (strtol) returns 0 if any characters in
351                  * string and whole extension will be removed, it's not good
352                  */
353                 if (!strcmp("hint", c))
354                         removing_priority = PRIORITY_HINT;
355                 else {
356                         while (*c && isdigit(*c))
357                                 c++;
358                         if (*c) { /* non-digit in string */
359                                 ast_cli(fd, "Invalid priority '%s'\n", argv[4]);
360                                 return RESULT_FAILURE;
361                         }
362                         removing_priority = atoi(argv[4]);
363                 }
364
365                 if (removing_priority == 0) {
366                         ast_cli(fd, "If you want to remove whole extension, please " \
367                                 "omit priority argument\n");
368                         return RESULT_FAILURE;
369                 }
370         }
371
372         /* XXX original overwrote argv[3] */
373         /*
374          * Format exten@context checking ...
375          */
376         if (split_ec(argv[3], &exten, &context))
377                 return RESULT_FAILURE; /* XXX malloc failure */
378         if ((!strlen(exten)) || (!(strlen(context)))) {
379                 ast_cli(fd, "Missing extension or context name in third argument '%s'\n",
380                         argv[3]);
381                 free(exten);
382                 return RESULT_FAILURE;
383         }
384
385         if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) {
386                 if (!removing_priority)
387                         ast_cli(fd, "Whole extension %s@%s removed\n",
388                                 exten, context);
389                 else
390                         ast_cli(fd, "Extension %s@%s with priority %d removed\n",
391                                 exten, context, removing_priority);
392                         
393                 ret = RESULT_SUCCESS;
394         } else {
395                 ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context);
396                 ret = RESULT_FAILURE;
397         }
398         free(exten);
399         return ret;
400 }
401
402 #define BROKEN_READLINE 1
403
404 #ifdef BROKEN_READLINE
405 /*
406  * There is one funny thing, when you have word like 300@ and you hit
407  * <tab>, you arguments will like as your word is '300 ', so it '@'
408  * characters acts sometimes as word delimiter and sometimes as a part
409  * of word
410  *
411  * This fix function, allocates new word variable and store here every
412  * time xxx@yyy always as one word and correct pos is set too
413  *
414  * It's ugly, I know, but I'm waiting for Mark suggestion if upper is
415  * bug or feature ...
416  */
417 static int fix_complete_args(const char *line, char **word, int *pos)
418 {
419         char *_line, *_strsep_line, *_previous_word = NULL, *_word = NULL;
420         int words = 0;
421
422         _line = strdup(line);
423
424         _strsep_line = _line;
425         while (_strsep_line) {
426                 _previous_word = _word;
427                 _word = strsep(&_strsep_line, " ");
428
429                 if (_word && strlen(_word)) words++;
430         }
431
432
433         if (_word || _previous_word) {
434                 if (_word) {
435                         if (!strlen(_word)) words++;
436                         *word = strdup(_word);
437                 } else
438                         *word = strdup(_previous_word);
439                 *pos = words - 1;
440                 free(_line);
441                 return 0;
442         }
443
444         free(_line);
445         return -1;
446 }
447 #endif /* BROKEN_READLINE */
448
449 static char *complete_context_remove_extension(const char *line, const char *word, int pos,
450         int state)
451 {
452         char *ret = NULL;
453         int which = 0;
454
455 #ifdef BROKEN_READLINE
456         char *word2;
457         /*
458          * Fix arguments, *word is a new allocated structure, REMEMBER to
459          * free *word when you want to return from this function ...
460          */
461         if (fix_complete_args(line, &word2, &pos)) {
462                 ast_log(LOG_ERROR, "Out of free memory\n");
463                 return NULL;
464         }
465         word = word2;
466 #endif
467
468         if (pos == 3) { /* 'dialplan remove extension _X_' (exten@context ... */
469                 struct ast_context *c = NULL;
470                 char *context = NULL, *exten = NULL;
471                 int le = 0;     /* length of extension */
472                 int lc = 0;     /* length of context */
473
474                 lc = split_ec(word, &exten, &context);
475 #ifdef BROKEN_READLINE
476                 free(word2);
477 #endif
478                 if (lc) /* error */
479                         return NULL;
480                 le = strlen(exten);
481                 lc = strlen(context);
482
483                 if (ast_rdlock_contexts()) {
484                         ast_log(LOG_ERROR, "Failed to lock context list\n");
485                         goto error2;
486                 }
487
488                 /* find our context ... */
489                 while ( (c = ast_walk_contexts(c)) ) {  /* match our context if any */
490                         struct ast_exten *e = NULL;
491                         /* XXX locking ? */
492                         if (!partial_match(ast_get_context_name(c), context, lc))
493                                 continue;       /* context not matched */
494                         while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */
495                                 if ( partial_match(ast_get_extension_name(e), exten, le) && ++which > state) { /* n-th match */
496                                         /* If there is an extension then return exten@context. XXX otherwise ? */
497                                         if (exten)
498                                                 asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c));
499                                         break;
500                                 }
501                         }
502                         if (e)  /* got a match */
503                                 break;
504                 }
505
506                 ast_unlock_contexts();
507         error2:
508                 if (exten)
509                         free(exten);
510         } else if (pos == 4) { /* 'dialplan remove extension EXT _X_' (priority) */
511                 char *exten = NULL, *context, *p;
512                 struct ast_context *c;
513                 int le, lc, len;
514                 const char *s = skip_words(line, 3); /* skip 'dialplan' 'remove' 'extension' */
515                 int i = split_ec(s, &exten, &context);  /* parse ext@context */
516
517                 if (i)  /* error */
518                         goto error3;
519                 if ( (p = strchr(exten, ' ')) ) /* remove space after extension */
520                         *p = '\0';
521                 if ( (p = strchr(context, ' ')) ) /* remove space after context */
522                         *p = '\0';
523                 le = strlen(exten);
524                 lc = strlen(context);
525                 len = strlen(word);
526                 if (le == 0 || lc == 0)
527                         goto error3;
528
529                 if (ast_rdlock_contexts()) {
530                         ast_log(LOG_ERROR, "Failed to lock context list\n");
531                         goto error3;
532                 }
533
534                 /* walk contexts */
535                 c = NULL;
536                 while ( (c = ast_walk_contexts(c)) ) {
537                         /* XXX locking on c ? */
538                         struct ast_exten *e;
539                         if (strcmp(ast_get_context_name(c), context) != 0)
540                                 continue;
541                         /* got it, we must match here */
542                         e = NULL;
543                         while ( (e = ast_walk_context_extensions(c, e)) ) {
544                                 struct ast_exten *priority;
545                                 char buffer[10];
546
547                                 if (strcmp(ast_get_extension_name(e), exten) != 0)
548                                         continue;
549                                 /* XXX lock e ? */
550                                 priority = NULL;
551                                 while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) {
552                                         snprintf(buffer, sizeof(buffer), "%u", ast_get_extension_priority(priority));
553                                         if (partial_match(buffer, word, len) && ++which > state) /* n-th match */
554                                                 ret = strdup(buffer);
555                                 }
556                                 break;
557                         }
558                         break;
559                 }
560                 ast_unlock_contexts();
561         error3:
562                 if (exten)
563                         free(exten);
564 #ifdef BROKEN_READLINE
565                 free(word2);
566 #endif
567         }
568         return ret; 
569 }
570
571 /*!
572  * Include context ...
573  */
574 static int handle_context_add_include(int fd, int argc, char *argv[])
575 {
576         if (argc != 6) /* dialplan add include CTX in CTX */
577                 return RESULT_SHOWUSAGE;
578
579         /* fifth arg must be 'into' ... */
580         if (strcmp(argv[4], "into"))
581                 return RESULT_SHOWUSAGE;
582
583         if (ast_context_add_include(argv[5], argv[3], registrar)) {
584                 switch (errno) {
585                 case ENOMEM:
586                         ast_cli(fd, "Out of memory for context addition\n");
587                         break;
588
589                 case EBUSY:
590                         ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
591                         break;
592
593                 case EEXIST:
594                         ast_cli(fd, "Context '%s' already included in '%s' context\n",
595                                 argv[3], argv[5]);
596                         break;
597
598                 case ENOENT:
599                 case EINVAL:
600                         ast_cli(fd, "There is no existence of context '%s'\n",
601                                 errno == ENOENT ? argv[5] : argv[3]);
602                         break;
603
604                 default:
605                         ast_cli(fd, "Failed to include '%s' in '%s' context\n",
606                                 argv[3], argv[5]);
607                         break;
608                 }
609                 return RESULT_FAILURE;
610         }
611
612         /* show some info ... */
613         ast_cli(fd, "Context '%s' included in '%s' context\n",
614                 argv[3], argv[5]);
615
616         return RESULT_SUCCESS;
617 }
618
619 static char *complete_context_add_include(const char *line, const char *word, int pos,
620     int state)
621 {
622         struct ast_context *c;
623         int which = 0;
624         char *ret = NULL;
625         int len = strlen(word);
626
627         if (pos == 3) {         /* 'dialplan add include _X_' (context) ... */
628                 if (ast_rdlock_contexts()) {
629                         ast_log(LOG_ERROR, "Failed to lock context list\n");
630                         return NULL;
631                 }
632                 for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
633                         if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
634                                 ret = strdup(ast_get_context_name(c));
635                 ast_unlock_contexts();
636                 return ret;
637         } else if (pos == 4) { /* dialplan add include CTX _X_ */
638                 /* complete  as 'into' if context exists or we are unable to check */
639                 char *context, *dupline;
640                 struct ast_context *c;
641                 const char *s = skip_words(line, 3); /* should not fail */
642
643                 if (state != 0) /* only once */
644                         return NULL;
645
646                 /* parse context from line ... */
647                 context = dupline = strdup(s);
648                 if (!context) {
649                         ast_log(LOG_ERROR, "Out of free memory\n");
650                         return strdup("into");
651                 }
652                 strsep(&dupline, " ");
653
654                 /* check for context existence ... */
655                 if (ast_rdlock_contexts()) {
656                         ast_log(LOG_ERROR, "Failed to lock context list\n");
657                         /* our fault, we can't check, so complete 'into' ... */
658                         ret = strdup("into");
659                 } else {
660                         for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
661                                 if (!strcmp(context, ast_get_context_name(c)))
662                                         ret = strdup("into"); /* found */
663                         ast_unlock_contexts();
664                 }
665                 free(context);
666                 return ret;
667         } else if (pos == 5) { /* 'dialplan add include CTX into _X_' (dst context) */
668                 char *context, *dupline, *into;
669                 const char *s = skip_words(line, 3); /* should not fail */
670                 context = dupline = strdup(s);
671                 if (!dupline) {
672                         ast_log(LOG_ERROR, "Out of free memory\n");
673                         return NULL;
674                 }
675                 strsep(&dupline, " "); /* skip context */
676                 into = strsep(&dupline, " ");
677                 /* error if missing context or fifth word is not 'into' */
678                 if (!strlen(context) || strcmp(into, "into")) {
679                         ast_log(LOG_ERROR, "bad context %s or missing into %s\n",
680                                 context, into);
681                         goto error3;
682                 }
683
684                 if (ast_rdlock_contexts()) {
685                         ast_log(LOG_ERROR, "Failed to lock context list\n");
686                         goto error3;
687                 }
688
689                 for (c = NULL; (c = ast_walk_contexts(c)); )
690                         if (!strcmp(context, ast_get_context_name(c)))
691                                 break;
692                 if (c) { /* first context exists, go on... */
693                         /* go through all contexts ... */
694                         for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
695                                 if (!strcmp(context, ast_get_context_name(c)))
696                                         continue; /* skip ourselves */
697                                 if (partial_match(ast_get_context_name(c), word, len) &&
698                                                 !lookup_ci(c, context) /* not included yet */ &&
699                                                 ++which > state)
700                                         ret = strdup(ast_get_context_name(c));
701                         }
702                 } else {
703                         ast_log(LOG_ERROR, "context %s not found\n", context);
704                 }
705                 ast_unlock_contexts();
706         error3:
707                 free(context);
708                 return ret;
709         }
710
711         return NULL;
712 }
713
714 /*!
715  * \brief 'save dialplan' CLI command implementation functions ...
716  */
717 static int handle_save_dialplan(int fd, int argc, char *argv[])
718 {
719         char filename[256];
720         struct ast_context *c;
721         struct ast_config *cfg;
722         struct ast_variable *v;
723         int incomplete = 0; /* incomplete config write? */
724         FILE *output;
725
726         const char *base, *slash, *file;
727
728         if (! (static_config && !write_protect_config)) {
729                 ast_cli(fd,
730                         "I can't save dialplan now, see '%s' example file.\n",
731                         config);
732                 return RESULT_FAILURE;
733         }
734
735         if (argc != 2 && argc != 3)
736                 return RESULT_SHOWUSAGE;
737
738         if (ast_mutex_lock(&save_dialplan_lock)) {
739                 ast_cli(fd,
740                         "Failed to lock dialplan saving (another proccess saving?)\n");
741                 return RESULT_FAILURE;
742         }
743         /* XXX the code here is quite loose, a pathname with .conf in it
744          * is assumed to be a complete pathname
745          */
746         if (argc == 3) {        /* have config path. Look for *.conf */
747                 base = argv[2];
748                 if (!strstr(argv[2], ".conf")) { /*no, this is assumed to be a pathname */
749                         /* if filename ends with '/', do not add one */
750                         slash = (*(argv[2] + strlen(argv[2]) -1) == '/') ? "/" : "";
751                         file = config;  /* default: 'extensions.conf' */
752                 } else {        /* yes, complete file name */
753                         slash = "";
754                         file = "";
755                 }
756         } else {
757                 /* no config file, default one */
758                 base = ast_config_AST_CONFIG_DIR;
759                 slash = "/";
760                 file = config;
761         }
762         snprintf(filename, sizeof(filename), "%s%s%s", base, slash, config);
763
764         cfg = ast_config_load("extensions.conf");
765
766         /* try to lock contexts list */
767         if (ast_rdlock_contexts()) {
768                 ast_cli(fd, "Failed to lock contexts list\n");
769                 ast_mutex_unlock(&save_dialplan_lock);
770                 ast_config_destroy(cfg);
771                 return RESULT_FAILURE;
772         }
773
774         /* create new file ... */
775         if (!(output = fopen(filename, "wt"))) {
776                 ast_cli(fd, "Failed to create file '%s'\n",
777                         filename);
778                 ast_unlock_contexts();
779                 ast_mutex_unlock(&save_dialplan_lock);
780                 ast_config_destroy(cfg);
781                 return RESULT_FAILURE;
782         }
783
784         /* fireout general info */
785         fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\nautofallthrough=%s\nclearglobalvars=%s\n\n",
786                 static_config ? "yes" : "no",
787                 write_protect_config ? "yes" : "no",
788                 autofallthrough_config ? "yes" : "no",
789                 clearglobalvars_config ? "yes" : "no");
790
791         if ((v = ast_variable_browse(cfg, "globals"))) {
792                 fprintf(output, "[globals]\n");
793                 while(v) {
794                         fprintf(output, "%s => %s\n", v->name, v->value);
795                         v = v->next;
796                 }
797                 fprintf(output, "\n");
798         }
799
800         ast_config_destroy(cfg);
801         
802 #define PUT_CTX_HDR     do { \
803         if (!context_header_written) {  \
804                 fprintf(output, "[%s]\n", ast_get_context_name(c));     \
805                 context_header_written = 1;     \
806         }       \
807         } while (0)
808
809         /* walk all contexts */
810         for (c = NULL; (c = ast_walk_contexts(c)); ) {
811                 int context_header_written = 0;
812                 struct ast_exten *e, *last_written_e = NULL;
813                 struct ast_include *i;
814                 struct ast_ignorepat *ip;
815                 struct ast_sw *sw;
816
817                 /* try to lock context and fireout all info */  
818                 if (ast_rdlock_context(c)) { /* lock failure */
819                         incomplete = 1;
820                         continue;
821                 }
822                 /* registered by this module? */
823                 /* XXX do we need this ? */
824                 if (!strcmp(ast_get_context_registrar(c), registrar)) {
825                         fprintf(output, "[%s]\n", ast_get_context_name(c));
826                         context_header_written = 1;
827                 }
828
829                 /* walk extensions ... */
830                 for (e = NULL; (e = ast_walk_context_extensions(c, e)); ) {
831                         struct ast_exten *p = NULL;
832
833                         /* fireout priorities */
834                         while ( (p = ast_walk_extension_priorities(e, p)) ) {
835                                 if (strcmp(ast_get_extension_registrar(p), registrar) != 0) /* not this source */
836                                         continue;
837                 
838                                 /* make empty line between different extensions */      
839                                 if (last_written_e != NULL &&
840                                             strcmp(ast_get_extension_name(last_written_e),
841                                                     ast_get_extension_name(p)))
842                                         fprintf(output, "\n");
843                                 last_written_e = p;
844                         
845                                 PUT_CTX_HDR;
846
847                                 if (ast_get_extension_priority(p)==PRIORITY_HINT) { /* easy */
848                                         fprintf(output, "exten => %s,hint,%s\n",
849                                                     ast_get_extension_name(p),
850                                                     ast_get_extension_app(p));
851                                 } else { /* copy and replace '|' with ',' */
852                                         const char *sep, *cid;
853                                         char *tempdata = "";
854                                         char *s;
855                                         const char *el = ast_get_extension_label(p);
856                                         char label[128] = "";
857  
858                                         s = ast_get_extension_app_data(p);
859                                         if (s) {
860                                                 char *t;
861                                                 tempdata = alloca(strlen(tempdata) * 2 + 1);
862
863                                                 for (t = tempdata; *s; s++, t++) {
864                                                         if (*s == '|')
865                                                                 *t = ',';
866                                                         else {
867                                                                 if (*s == ',')
868                                                                         *t++ = '\\';
869                                                                 *t = *s;
870                                                         }
871                                                 }
872                                                 /* Terminating NULL */
873                                                 *t = *s;
874                                         }
875
876                                         if (ast_get_extension_matchcid(p)) {
877                                                 sep = "/";
878                                                 cid = ast_get_extension_cidmatch(p);
879                                         } else
880                                                 sep = cid = "";
881                                 
882                                         if (el && (snprintf(label, sizeof(label), "(%s)", el) != (strlen(el) + 2)))
883                                                 incomplete = 1; /* error encountered or label > 125 chars */
884                                         
885                                         fprintf(output, "exten => %s%s%s,%d%s,%s(%s)\n",
886                                             ast_get_extension_name(p), sep, cid,
887                                             ast_get_extension_priority(p), label,
888                                             ast_get_extension_app(p), tempdata);
889                                 }
890                         }
891                 }
892
893                 /* written any extensions? ok, write space between exten & inc */
894                 if (last_written_e)
895                         fprintf(output, "\n");
896
897                 /* walk through includes */
898                 for (i = NULL; (i = ast_walk_context_includes(c, i)) ; ) {
899                         if (strcmp(ast_get_include_registrar(i), registrar) != 0)
900                                 continue; /* not mine */
901                         PUT_CTX_HDR;
902                         fprintf(output, "include => %s\n", ast_get_include_name(i));
903                 }
904                 if (ast_walk_context_includes(c, NULL))
905                         fprintf(output, "\n");
906
907                 /* walk through switches */
908                 for (sw = NULL; (sw = ast_walk_context_switches(c, sw)) ; ) {
909                         if (strcmp(ast_get_switch_registrar(sw), registrar) != 0)
910                                 continue; /* not mine */
911                         PUT_CTX_HDR;
912                         fprintf(output, "switch => %s/%s\n",
913                                     ast_get_switch_name(sw), ast_get_switch_data(sw));
914                 }
915
916                 if (ast_walk_context_switches(c, NULL))
917                         fprintf(output, "\n");
918
919                 /* fireout ignorepats ... */
920                 for (ip = NULL; (ip = ast_walk_context_ignorepats(c, ip)); ) {
921                         if (strcmp(ast_get_ignorepat_registrar(ip), registrar) != 0)
922                                 continue; /* not mine */
923                         PUT_CTX_HDR;
924                         fprintf(output, "ignorepat => %s\n",
925                                                 ast_get_ignorepat_name(ip));
926                 }
927
928                 ast_unlock_context(c);
929         }       
930
931         ast_unlock_contexts();
932         ast_mutex_unlock(&save_dialplan_lock);
933         fclose(output);
934
935         if (incomplete) {
936                 ast_cli(fd, "Saved dialplan is incomplete\n");
937                 return RESULT_FAILURE;
938         }
939
940         ast_cli(fd, "Dialplan successfully saved into '%s'\n",
941                 filename);
942         return RESULT_SUCCESS;
943 }
944
945 /*!
946  * \brief ADD EXTENSION command stuff
947  */
948 static int handle_context_add_extension(int fd, int argc, char *argv[])
949 {
950         char *whole_exten;
951         char *exten, *prior;
952         int iprior = -2;
953         char *cidmatch, *app, *app_data;
954         char *start, *end;
955
956         /* check for arguments at first */
957         if (argc != 6 && argc != 7)
958                 return RESULT_SHOWUSAGE;
959         if (strcmp(argv[4], "into"))
960                 return RESULT_SHOWUSAGE;
961         if (argc == 7) if (strcmp(argv[6], "replace")) return RESULT_SHOWUSAGE;
962
963         /* XXX overwrite argv[3] */
964         whole_exten = argv[3];
965         exten   = strsep(&whole_exten,",");
966         if (strchr(exten, '/')) {
967                 cidmatch = exten;
968                 strsep(&cidmatch,"/");
969         } else {
970                 cidmatch = NULL;
971         }
972         prior = strsep(&whole_exten,",");
973         if (prior) {
974                 if (!strcmp(prior, "hint")) {
975                         iprior = PRIORITY_HINT;
976                 } else {
977                         if (sscanf(prior, "%d", &iprior) != 1) {
978                                 ast_cli(fd, "'%s' is not a valid priority\n", prior);
979                                 prior = NULL;
980                         }
981                 }
982         }
983         app = whole_exten;
984         if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
985                 *start = *end = '\0';
986                 app_data = start + 1;
987                 ast_process_quotes_and_slashes(app_data, ',', '|');
988         } else {
989                 if (app) {
990                         app_data = strchr(app, ',');
991                         if (app_data) {
992                                 *app_data = '\0';
993                                 app_data++;
994                         }
995                 } else  
996                         app_data = NULL;
997         }
998
999         if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT))
1000                 return RESULT_SHOWUSAGE;
1001
1002         if (!app_data)
1003                 app_data="";
1004         if (ast_add_extension(argv[5], argc == 7 ? 1 : 0, exten, iprior, NULL, cidmatch, app,
1005                 (void *)strdup(app_data), free, registrar)) {
1006                 switch (errno) {
1007                 case ENOMEM:
1008                         ast_cli(fd, "Out of free memory\n");
1009                         break;
1010
1011                 case EBUSY:
1012                         ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1013                         break;
1014
1015                 case ENOENT:
1016                         ast_cli(fd, "No existence of '%s' context\n", argv[5]);
1017                         break;
1018
1019                 case EEXIST:
1020                         ast_cli(fd, "Extension %s@%s with priority %s already exists\n",
1021                                 exten, argv[5], prior);
1022                         break;
1023
1024                 default:
1025                         ast_cli(fd, "Failed to add '%s,%s,%s,%s' extension into '%s' context\n",
1026                                         exten, prior, app, app_data, argv[5]);
1027                         break;
1028                 }
1029                 return RESULT_FAILURE;
1030         }
1031
1032         if (argc == 7)
1033                 ast_cli(fd, "Extension %s@%s (%s) replace by '%s,%s,%s,%s'\n",
1034                         exten, argv[5], prior, exten, prior, app, app_data);
1035         else
1036                 ast_cli(fd, "Extension '%s,%s,%s,%s' added into '%s' context\n",
1037                         exten, prior, app, app_data, argv[5]);
1038
1039         return RESULT_SUCCESS;
1040 }
1041
1042 /*! dialplan add extension 6123,1,Dial,IAX/212.71.138.13/6123 into local */
1043 static char *complete_context_add_extension(const char *line, const char *word, int pos, int state)
1044 {
1045         int which = 0;
1046
1047         if (pos == 4) {         /* complete 'into' word ... */
1048                 return (state == 0) ? strdup("into") : NULL;
1049         } else if (pos == 5) { /* complete context */
1050                 struct ast_context *c = NULL;
1051                 int len = strlen(word);
1052                 char *res = NULL;
1053
1054                 /* try to lock contexts list ... */
1055                 if (ast_rdlock_contexts()) {
1056                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1057                         return NULL;
1058                 }
1059
1060                 /* walk through all contexts */
1061                 while ( !res && (c = ast_walk_contexts(c)) )
1062                         if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
1063                                 res = strdup(ast_get_context_name(c));
1064                 ast_unlock_contexts();
1065                 return res;
1066         } else if (pos == 6) {
1067                 return state == 0 ? strdup("replace") : NULL;
1068         }
1069         return NULL;
1070 }
1071
1072 /*!
1073  * IGNOREPAT CLI stuff
1074  */
1075 static int handle_context_add_ignorepat(int fd, int argc, char *argv[])
1076 {
1077         if (argc != 6)
1078                 return RESULT_SHOWUSAGE;
1079         if (strcmp(argv[4], "into"))
1080                 return RESULT_SHOWUSAGE;
1081
1082         if (ast_context_add_ignorepat(argv[5], argv[3], registrar)) {
1083                 switch (errno) {
1084                 case ENOMEM:
1085                         ast_cli(fd, "Out of free memory\n");
1086                         break;
1087
1088                 case ENOENT:
1089                         ast_cli(fd, "There is no existence of '%s' context\n", argv[5]);
1090                         break;
1091
1092                 case EEXIST:
1093                         ast_cli(fd, "Ignore pattern '%s' already included in '%s' context\n",
1094                                 argv[3], argv[5]);
1095                         break;
1096
1097                 case EBUSY:
1098                         ast_cli(fd, "Failed to lock context(s) list, please, try again later\n");
1099                         break;
1100
1101                 default:
1102                         ast_cli(fd, "Failed to add ingore pattern '%s' into '%s' context\n",
1103                                 argv[3], argv[5]);
1104                         break;
1105                 }
1106                 return RESULT_FAILURE;
1107         }
1108
1109         ast_cli(fd, "Ignore pattern '%s' added into '%s' context\n",
1110                 argv[3], argv[5]);
1111         return RESULT_SUCCESS;
1112 }
1113
1114 static char *complete_context_add_ignorepat(const char *line, const char *word,
1115         int pos, int state)
1116 {
1117         if (pos == 4)
1118                 return state == 0 ? strdup("into") : NULL;
1119         else if (pos == 5) {
1120                 struct ast_context *c;
1121                 int which = 0;
1122                 char *dupline, *ignorepat = NULL;
1123                 const char *s;
1124                 char *ret = NULL;
1125                 int len = strlen(word);
1126
1127                 /* XXX skip first three words 'dialplan' 'add' 'ignorepat' */
1128                 s = skip_words(line, 3);
1129                 if (s == NULL)
1130                         return NULL;
1131                 dupline = strdup(s);
1132                 if (!dupline) {
1133                         ast_log(LOG_ERROR, "Malloc failure\n");
1134                         return NULL;
1135                 }
1136                 ignorepat = strsep(&dupline, " ");
1137
1138                 if (ast_rdlock_contexts()) {
1139                         ast_log(LOG_ERROR, "Failed to lock contexts list\n");
1140                         return NULL;
1141                 }
1142
1143                 for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1144                         int found = 0;
1145
1146                         if (!partial_match(ast_get_context_name(c), word, len))
1147                                 continue; /* not mine */
1148                         if (ignorepat) /* there must be one, right ? */
1149                                 found = lookup_c_ip(c, ignorepat);
1150                         if (!found && ++which > state)
1151                                 ret = strdup(ast_get_context_name(c));
1152                 }
1153
1154                 if (ignorepat)
1155                         free(ignorepat);
1156                 ast_unlock_contexts();
1157                 return ret;
1158         }
1159
1160         return NULL;
1161 }
1162
1163 static int handle_context_remove_ignorepat(int fd, int argc, char *argv[])
1164 {
1165         if (argc != 6)
1166                 return RESULT_SHOWUSAGE;
1167         if (strcmp(argv[4], "from"))
1168                 return RESULT_SHOWUSAGE;
1169
1170         if (ast_context_remove_ignorepat(argv[5], argv[3], registrar)) {
1171                 switch (errno) {
1172                 case EBUSY:
1173                         ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
1174                         break;
1175
1176                 case ENOENT:
1177                         ast_cli(fd, "There is no existence of '%s' context\n", argv[5]);
1178                         break;
1179
1180                 case EINVAL:
1181                         ast_cli(fd, "There is no existence of '%s' ignore pattern in '%s' context\n",
1182                                         argv[3], argv[5]);
1183                         break;
1184
1185                 default:
1186                         ast_cli(fd, "Failed to remove ignore pattern '%s' from '%s' context\n", argv[3], argv[5]);
1187                         break;
1188                 }
1189                 return RESULT_FAILURE;
1190         }
1191
1192         ast_cli(fd, "Ignore pattern '%s' removed from '%s' context\n",
1193                 argv[3], argv[5]);
1194         return RESULT_SUCCESS;
1195 }
1196
1197 static char *complete_context_remove_ignorepat(const char *line, const char *word,
1198         int pos, int state)
1199 {
1200         struct ast_context *c;
1201         int which = 0;
1202         char *ret = NULL;
1203
1204         if (pos == 3) {
1205                 int len = strlen(word);
1206                 if (ast_rdlock_contexts()) {
1207                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1208                         return NULL;
1209                 }
1210
1211                 for (c = NULL; !ret && (c = ast_walk_contexts(c));) {
1212                         struct ast_ignorepat *ip;
1213
1214                         if (ast_rdlock_context(c))      /* error, skip it */
1215                                 continue;
1216                         
1217                         for (ip = NULL; !ret && (ip = ast_walk_context_ignorepats(c, ip));) {
1218                                 if (partial_match(ast_get_ignorepat_name(ip), word, len) && ++which > state) {
1219                                         /* n-th match */
1220                                         struct ast_context *cw = NULL;
1221                                         int found = 0;
1222                                         while ( (cw = ast_walk_contexts(cw)) && cw != c && !found) {
1223                                                 /* XXX do i stop on c, or skip it ? */
1224                                                 found = lookup_c_ip(cw, ast_get_ignorepat_name(ip));
1225                                         }
1226                                         if (!found)
1227                                                 ret = strdup(ast_get_ignorepat_name(ip));
1228                                 }
1229                         }
1230                         ast_unlock_context(c);
1231                 }
1232                 ast_unlock_contexts();
1233                 return ret;
1234         } else if (pos == 4) {
1235                  return state == 0 ? strdup("from") : NULL;
1236         } else if (pos == 5) { /* XXX check this */
1237                 char *dupline, *duplinet, *ignorepat;
1238                 int len = strlen(word);
1239
1240                 dupline = strdup(line);
1241                 if (!dupline) {
1242                         ast_log(LOG_WARNING, "Out of free memory\n");
1243                         return NULL;
1244                 }
1245
1246                 duplinet = dupline;
1247                 strsep(&duplinet, " ");
1248                 strsep(&duplinet, " ");
1249                 ignorepat = strsep(&duplinet, " ");
1250
1251                 if (!ignorepat) {
1252                         free(dupline);
1253                         return NULL;
1254                 }
1255
1256                 if (ast_rdlock_contexts()) {
1257                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
1258                         free(dupline);
1259                         return NULL;
1260                 }
1261
1262                 for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
1263                         if (ast_rdlock_context(c))      /* fail, skip it */
1264                                 continue;
1265                         if (!partial_match(ast_get_context_name(c), word, len))
1266                                 continue;
1267                         if (lookup_c_ip(c, ignorepat) && ++which > state)
1268                                 ret = strdup(ast_get_context_name(c));
1269                         ast_unlock_context(c);
1270                 }
1271                 ast_unlock_contexts();
1272                 free(dupline);
1273                 return NULL;
1274         }
1275
1276         return NULL;
1277 }
1278
1279 static int pbx_load_module(void);
1280
1281 static int handle_reload_extensions(int fd, int argc, char *argv[])
1282 {
1283         if (argc != 2)
1284                 return RESULT_SHOWUSAGE;
1285         if (clearglobalvars_config)
1286                 pbx_builtin_clear_globals();
1287         pbx_load_module();
1288         return RESULT_SUCCESS;
1289 }
1290
1291 /*!
1292  * CLI entries for commands provided by this module
1293  */
1294 static struct ast_cli_entry cli_pbx_config[] = {
1295         { { "dialplan", "add", "extension", NULL },
1296         handle_context_add_extension, "Add new extension into context",
1297         context_add_extension_help, complete_context_add_extension },
1298
1299         { { "dialplan", "remove", "extension", NULL },
1300         handle_context_remove_extension, "Remove a specified extension",
1301         context_remove_extension_help, complete_context_remove_extension },
1302
1303         { { "dialplan", "add", "ignorepat", NULL },
1304         handle_context_add_ignorepat, "Add new ignore pattern",
1305         context_add_ignorepat_help, complete_context_add_ignorepat },
1306
1307         { { "dialplan", "remove", "ignorepat", NULL },
1308         handle_context_remove_ignorepat, "Remove ignore pattern from context",
1309         context_remove_ignorepat_help, complete_context_remove_ignorepat },
1310
1311         { { "dialplan", "add", "include", NULL },
1312         handle_context_add_include, "Include context in other context",
1313         context_add_include_help, complete_context_add_include },
1314
1315         { { "dialplan", "remove", "include", NULL },
1316         handle_context_remove_include, "Remove a specified include from context",
1317         context_remove_include_help, complete_context_remove_include },
1318
1319         { { "dialplan", "reload", NULL },
1320         handle_reload_extensions, "Reload extensions and *only* extensions",
1321         reload_extensions_help },
1322 };
1323
1324
1325 static struct ast_cli_entry cli_dialplan_save = {
1326         { "dialplan", "save", NULL },
1327         handle_save_dialplan, "Save dialplan",
1328         save_dialplan_help };
1329
1330 /*!
1331  * Standard module functions ...
1332  */
1333 static int unload_module(void)
1334 {
1335         if (static_config && !write_protect_config)
1336                 ast_cli_unregister(&cli_dialplan_save);
1337         ast_cli_unregister_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry));
1338         ast_context_destroy(NULL, registrar);
1339         return 0;
1340 }
1341
1342 static int pbx_load_config(const char *config_file)
1343 {
1344         struct ast_config *cfg;
1345         char *end;
1346         char *label;
1347         char realvalue[256];
1348         int lastpri = -2;
1349         struct ast_context *con;
1350         struct ast_variable *v;
1351         const char *cxt;
1352         const char *aft;
1353
1354         cfg = ast_config_load(config_file);
1355         if (!cfg)
1356                 return 0;
1357
1358         /* Use existing config to populate the PBX table */
1359         static_config = ast_true(ast_variable_retrieve(cfg, "general", "static"));
1360         write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect"));
1361         if ((aft = ast_variable_retrieve(cfg, "general", "autofallthrough")))
1362                 autofallthrough_config = ast_true(aft);
1363         clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars"));
1364
1365         if ((cxt = ast_variable_retrieve(cfg, "general", "userscontext"))) 
1366                 ast_copy_string(userscontext, cxt, sizeof(userscontext));
1367         else
1368                 ast_copy_string(userscontext, "default", sizeof(userscontext));
1369                                                                     
1370         for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) {
1371                 memset(realvalue, 0, sizeof(realvalue));
1372                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1373                 pbx_builtin_setvar_helper(NULL, v->name, realvalue);
1374         }
1375         for (cxt = NULL; (cxt = ast_category_browse(cfg, cxt)); ) {
1376                 /* All categories but "general" or "globals" are considered contexts */
1377                 if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals"))
1378                         continue;
1379                 con=ast_context_find_or_create(&local_contexts,cxt, registrar);
1380                 if (con == NULL)
1381                         continue;
1382
1383                 for (v = ast_variable_browse(cfg, cxt); v; v = v->next) {
1384                         if (!strcasecmp(v->name, "exten")) {
1385                                 char *tc = ast_strdup(v->value);
1386                                 if (tc) {
1387                                         int ipri = -2;
1388                                         char realext[256]="";
1389                                         char *plus, *firstp, *firstc;
1390                                         char *pri, *appl, *data, *cidmatch;
1391                                         char *stringp = tc;
1392                                         char *ext = strsep(&stringp, ",");
1393                                         if (!ext)
1394                                                 ext="";
1395                                         pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
1396                                         cidmatch = strchr(realext, '/');
1397                                         if (cidmatch) {
1398                                                 *cidmatch++ = '\0';
1399                                                 ast_shrink_phone_number(cidmatch);
1400                                         }
1401                                         pri = strsep(&stringp, ",");
1402                                         if (!pri)
1403                                                 pri="";
1404                                         pri = ast_skip_blanks(pri);
1405                                         pri = ast_trim_blanks(pri);
1406                                         label = strchr(pri, '(');
1407                                         if (label) {
1408                                                 *label++ = '\0';
1409                                                 end = strchr(label, ')');
1410                                                 if (end)
1411                                                         *end = '\0';
1412                                                 else
1413                                                         ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno);
1414                                         }
1415                                         plus = strchr(pri, '+');
1416                                         if (plus)
1417                                                 *plus++ = '\0';
1418                                         if (!strcmp(pri,"hint"))
1419                                                 ipri=PRIORITY_HINT;
1420                                         else if (!strcmp(pri, "next") || !strcmp(pri, "n")) {
1421                                                 if (lastpri > -2)
1422                                                         ipri = lastpri + 1;
1423                                                 else
1424                                                         ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n");
1425                                         } else if (!strcmp(pri, "same") || !strcmp(pri, "s")) {
1426                                                 if (lastpri > -2)
1427                                                         ipri = lastpri;
1428                                                 else
1429                                                         ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n");
1430                                         } else if (sscanf(pri, "%d", &ipri) != 1 &&
1431                                             (ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
1432                                                 ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno);
1433                                                 ipri = 0;
1434                                         }
1435                                         appl = S_OR(stringp, "");
1436                                         /* Find the first occurrence of either '(' or ',' */
1437                                         firstc = strchr(appl, ',');
1438                                         firstp = strchr(appl, '(');
1439                                         if (firstc && (!firstp || firstc < firstp)) {
1440                                                 /* comma found, no parenthesis */
1441                                                 /* or both found, but comma found first */
1442                                                 appl = strsep(&stringp, ",");
1443                                                 data = stringp;
1444                                         } else if (!firstc && !firstp) {
1445                                                 /* Neither found */
1446                                                 data = "";
1447                                         } else {
1448                                                 /* Final remaining case is parenthesis found first */
1449                                                 appl = strsep(&stringp, "(");
1450                                                 data = stringp;
1451                                                 end = strrchr(data, ')');
1452                                                 if ((end = strrchr(data, ')'))) {
1453                                                         *end = '\0';
1454                                                 } else {
1455                                                         ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data);
1456                                                 }
1457                                                 ast_process_quotes_and_slashes(data, ',', '|');
1458                                         }
1459
1460                                         if (!data)
1461                                                 data="";
1462                                         appl = ast_skip_blanks(appl);
1463                                         if (ipri) {
1464                                                 if (plus)
1465                                                         ipri += atoi(plus);
1466                                                 lastpri = ipri;
1467                                                 if (!ast_opt_dont_warn && !strcmp(realext, "_."))
1468                                                         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);
1469                                                 if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), ast_free, registrar)) {
1470                                                         ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
1471                                                 }
1472                                         }
1473                                         free(tc);
1474                                 }
1475                         } else if (!strcasecmp(v->name, "include")) {
1476                                 memset(realvalue, 0, sizeof(realvalue));
1477                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1478                                 if (ast_context_add_include2(con, realvalue, registrar))
1479                                         ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
1480                         } else if (!strcasecmp(v->name, "ignorepat")) {
1481                                 memset(realvalue, 0, sizeof(realvalue));
1482                                 pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1483                                 if (ast_context_add_ignorepat2(con, realvalue, registrar))
1484                                         ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
1485                         } else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
1486                                 char *stringp= realvalue;
1487                                 char *appl, *data;
1488
1489                                 memset(realvalue, 0, sizeof(realvalue));
1490                                 if (!strcasecmp(v->name, "switch"))
1491                                         pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
1492                                 else
1493                                         ast_copy_string(realvalue, v->value, sizeof(realvalue));
1494                                 appl = strsep(&stringp, "/");
1495                                 data = strsep(&stringp, ""); /* XXX what for ? */
1496                                 if (!data)
1497                                         data = "";
1498                                 if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar))
1499                                         ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
1500                         } else {
1501                                 ast_log(LOG_WARNING, "==!!== Unknown directive: %s at line %d -- IGNORING!!!\n", v->name, v->lineno);
1502                         }
1503                 }
1504         }
1505         ast_config_destroy(cfg);
1506         return 1;
1507 }
1508
1509 static void append_interface(char *iface, int maxlen, char *add)
1510 {
1511         int len = strlen(iface);
1512         if (strlen(add) + len < maxlen - 2) {
1513                 if (strlen(iface)) {
1514                         iface[len] = '&';
1515                         strcpy(iface + len + 1, add);
1516                 } else
1517                         strcpy(iface, add);
1518         }
1519 }
1520
1521 static void pbx_load_users(void)
1522 {
1523         struct ast_config *cfg;
1524         char *cat, *chan;
1525         const char *zapchan;
1526         const char *hasexten;
1527         char tmp[256];
1528         char iface[256];
1529         char zapcopy[256];
1530         char *c;
1531         int len;
1532         int hasvoicemail;
1533         int start, finish, x;
1534         struct ast_context *con;
1535         
1536         cfg = ast_config_load("users.conf");
1537         if (!cfg)
1538                 return;
1539         con = ast_context_find_or_create(&local_contexts, userscontext, registrar);
1540         if (!con)
1541                 return;
1542
1543         for (cat = ast_category_browse(cfg, NULL); cat ; cat = ast_category_browse(cfg, cat)) {
1544                 if (!strcasecmp(cat, "general"))
1545                         continue;
1546                 iface[0] = '\0';
1547                 len = sizeof(iface);
1548                 if (ast_true(ast_config_option(cfg, cat, "hassip"))) {
1549                         snprintf(tmp, sizeof(tmp), "SIP/%s", cat);
1550                         append_interface(iface, sizeof(iface), tmp);
1551                 }
1552                 if (ast_true(ast_config_option(cfg, cat, "hasiax"))) {
1553                         snprintf(tmp, sizeof(tmp), "IAX2/%s", cat);
1554                         append_interface(iface, sizeof(iface), tmp);
1555                 }
1556                 if (ast_true(ast_config_option(cfg, cat, "hash323"))) {
1557                         snprintf(tmp, sizeof(tmp), "H323/%s", cat);
1558                         append_interface(iface, sizeof(iface), tmp);
1559                 }
1560                 hasexten = ast_config_option(cfg, cat, "hasexten");
1561                 if (hasexten && !ast_true(hasexten))
1562                         continue;
1563                 hasvoicemail = ast_true(ast_config_option(cfg, cat, "hasvoicemail"));
1564                 zapchan = ast_variable_retrieve(cfg, cat, "zapchan");
1565                 if (!zapchan)
1566                         zapchan = ast_variable_retrieve(cfg, "general", "zapchan");
1567                 if (!ast_strlen_zero(zapchan)) {
1568                         ast_copy_string(zapcopy, zapchan, sizeof(zapcopy));
1569                         c = zapcopy;
1570                         chan = strsep(&c, ",");
1571                         while (chan) {
1572                                 if (sscanf(chan, "%d-%d", &start, &finish) == 2) {
1573                                         /* Range */
1574                                 } else if (sscanf(chan, "%d", &start)) {
1575                                         /* Just one */
1576                                         finish = start;
1577                                 } else {
1578                                         start = 0; finish = 0;
1579                                 }
1580                                 if (finish < start) {
1581                                         x = finish;
1582                                         finish = start;
1583                                         start = x;
1584                                 }
1585                                 for (x = start; x <= finish; x++) {
1586                                         snprintf(tmp, sizeof(tmp), "Zap/%d", x);
1587                                         append_interface(iface, sizeof(iface), tmp);
1588                                 }
1589                                 chan = strsep(&c, ",");
1590                         }
1591                 }
1592                 if (!ast_strlen_zero(iface)) {
1593                         /* Add hint */
1594                         ast_add_extension2(con, 0, cat, -1, NULL, NULL, iface, NULL, NULL, registrar);
1595                         /* If voicemail, use "stdexten" else use plain old dial */
1596                         if (hasvoicemail) {
1597                                 snprintf(tmp, sizeof(tmp), "stdexten|%s|${HINT}", cat);
1598                                 ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Macro", strdup(tmp), ast_free, registrar);
1599                         } else {
1600                                 ast_add_extension2(con, 0, cat, 1, NULL, NULL, "Dial", strdup("${HINT}"), ast_free, registrar);
1601                         }
1602                 }
1603         }
1604         ast_config_destroy(cfg);
1605 }
1606
1607 static int pbx_load_module(void)
1608 {
1609         struct ast_context *con;
1610
1611         if(!pbx_load_config(config))
1612                 return AST_MODULE_LOAD_DECLINE;
1613         
1614         pbx_load_users();
1615
1616         ast_merge_contexts_and_delete(&local_contexts, registrar);
1617
1618         for (con = NULL; (con = ast_walk_contexts(con));)
1619                 ast_context_verify_includes(con);
1620
1621         pbx_set_autofallthrough(autofallthrough_config);
1622
1623         return 0;
1624 }
1625
1626 static int load_module(void)
1627 {
1628         if (pbx_load_module())
1629                 return AST_MODULE_LOAD_DECLINE;
1630  
1631         if (static_config && !write_protect_config)
1632                 ast_cli_register(&cli_dialplan_save);
1633         ast_cli_register_multiple(cli_pbx_config, sizeof(cli_pbx_config) / sizeof(struct ast_cli_entry));
1634
1635         return 0;
1636 }
1637
1638 static int reload(void)
1639 {
1640         if (clearglobalvars_config)
1641                 pbx_builtin_clear_globals();
1642         pbx_load_module();
1643         return 0;
1644 }
1645
1646 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Text Extension Configuration",
1647                 .load = load_module,
1648                 .unload = unload_module,
1649                 .reload = reload,
1650                );