Merged revisions 241015 via svnmerge from
[asterisk/asterisk.git] / main / config.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, 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 Configuration File Parser
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * Includes the Asterisk Realtime API - ARA
26  * See doc/realtime.txt and doc/extconfig.txt
27  */
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include "asterisk/paths.h"     /* use ast_config_AST_CONFIG_DIR */
34 #include "asterisk/network.h"   /* we do some sockaddr manipulation here */
35 #include <time.h>
36 #include <sys/stat.h>
37
38 #include <math.h>       /* HUGE_VAL */
39
40 #define AST_INCLUDE_GLOB 1
41
42 #include "asterisk/config.h"
43 #include "asterisk/cli.h"
44 #include "asterisk/lock.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/channel.h"
47 #include "asterisk/app.h"
48 #include "asterisk/astobj2.h"
49 #include "asterisk/strings.h"   /* for the ast_str_*() API */
50
51 #define MAX_NESTED_COMMENTS 128
52 #define COMMENT_START ";--"
53 #define COMMENT_END "--;"
54 #define COMMENT_META ';'
55 #define COMMENT_TAG '-'
56
57 static char *extconfig_conf = "extconfig.conf";
58
59
60 /*! \brief Structure to keep comments for rewriting configuration files */
61 struct ast_comment {
62         struct ast_comment *next;
63         char cmt[0];
64 };
65
66 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
67 struct cache_file_include {
68         AST_LIST_ENTRY(cache_file_include) list;
69         char include[0];
70 };
71
72 struct cache_file_mtime {
73         AST_LIST_ENTRY(cache_file_mtime) list;
74         AST_LIST_HEAD(includes, cache_file_include) includes;
75         unsigned int has_exec:1;
76         time_t mtime;
77         char *who_asked;
78         char filename[0];
79 };
80
81 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
82
83 static int init_appendbuf(void *data)
84 {
85         struct ast_str **str = data;
86         *str = ast_str_create(16);
87         return *str ? 0 : -1;
88 }
89
90 AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
91
92 /* comment buffers are better implemented using the ast_str_*() API */
93 #define CB_SIZE 250     /* initial size of comment buffers */
94
95 static void  CB_ADD(struct ast_str **cb, const char *str)
96 {
97         ast_str_append(cb, 0, "%s", str);
98 }
99
100 static void  CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
101 {
102         char *s = alloca(len + 1);
103         ast_copy_string(s, str, len);
104         ast_str_append(cb, 0, "%s", str);
105 }
106
107 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)  
108
109         if (cb) {
110                 ast_str_reset(cb);
111         }
112         if (llb) {
113                 ast_str_reset(llb);
114         }
115 }
116
117 static struct ast_comment *ALLOC_COMMENT(struct ast_str *buffer)
118
119         struct ast_comment *x = NULL;
120         if (!buffer || !ast_str_strlen(buffer)) {
121                 return NULL;
122         }
123         if ((x = ast_calloc(1, sizeof(*x) + ast_str_strlen(buffer) + 1))) {
124                 strcpy(x->cmt, ast_str_buffer(buffer)); /* SAFE */
125         }
126         return x;
127 }
128
129 /* I need to keep track of each config file, and all its inclusions,
130    so that we can track blank lines in each */
131
132 struct inclfile {
133         char *fname;
134         int lineno;
135 };
136
137 static int hash_string(const void *obj, const int flags)
138 {
139         char *str = ((struct inclfile *) obj)->fname;
140         int total;
141
142         for (total = 0; *str; str++) {
143                 unsigned int tmp = total;
144                 total <<= 1; /* multiply by 2 */
145                 total += tmp; /* multiply by 3 */
146                 total <<= 2; /* multiply by 12 */
147                 total += tmp; /* multiply by 13 */
148
149                 total += ((unsigned int) (*str));
150         }
151         if (total < 0) {
152                 total = -total;
153         }
154         return total;
155 }
156
157 static int hashtab_compare_strings(void *a, void *b, int flags)
158 {
159         const struct inclfile *ae = a, *be = b;
160         return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
161 }
162
163 static struct ast_config_map {
164         struct ast_config_map *next;
165         char *name;
166         char *driver;
167         char *database;
168         char *table;
169         char stuff[0];
170 } *config_maps = NULL;
171
172 AST_MUTEX_DEFINE_STATIC(config_lock);
173 static struct ast_config_engine *config_engine_list;
174
175 #define MAX_INCLUDE_LEVEL 10
176
177 struct ast_category_template_instance {
178         char name[80]; /* redundant? */
179         const struct ast_category *inst;
180         AST_LIST_ENTRY(ast_category_template_instance) next;
181 };
182
183 struct ast_category {
184         char name[80];
185         int ignored;                    /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
186         int include_level;
187         char *file;                /*!< the file name from whence this declaration was read */
188         int lineno;
189         AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
190         struct ast_comment *precomments;
191         struct ast_comment *sameline;
192         struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
193         struct ast_variable *root;
194         struct ast_variable *last;
195         struct ast_category *next;
196 };
197
198 struct ast_config {
199         struct ast_category *root;
200         struct ast_category *last;
201         struct ast_category *current;
202         struct ast_category *last_browse;     /*!< used to cache the last category supplied via category_browse */
203         int include_level;
204         int max_include_level;
205         struct ast_config_include *includes;  /*!< a list of inclusions, which should describe the entire tree */
206 };
207
208 struct ast_config_include {
209         char *include_location_file;     /*!< file name in which the include occurs */
210         int  include_location_lineno;    /*!< lineno where include occurred */
211         int  exec;                       /*!< set to non-zero if itsa #exec statement */
212         char *exec_file;                 /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
213         char *included_file;             /*!< file name included */
214         int inclusion_count;             /*!< if the file is included more than once, a running count thereof -- but, worry not,
215                                               we explode the instances and will include those-- so all entries will be unique */
216         int output;                      /*!< a flag to indicate if the inclusion has been output */
217         struct ast_config_include *next; /*!< ptr to next inclusion in the list */
218 };
219
220 #ifdef MALLOC_DEBUG
221 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno) 
222 #else
223 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename) 
224 #endif
225 {
226         struct ast_variable *variable;
227         int name_len = strlen(name) + 1;        
228         int val_len = strlen(value) + 1;        
229         int fn_len = strlen(filename) + 1;      
230
231 #ifdef MALLOC_DEBUG
232         if ((variable = __ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable), file, lineno, func))) {
233 #else
234         if ((variable = ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable)))) {
235 #endif
236                 char *dst = variable->stuff;    /* writable space starts here */
237                 variable->name = strcpy(dst, name);
238                 dst += name_len;
239                 variable->value = strcpy(dst, value);
240                 dst += val_len;
241                 variable->file = strcpy(dst, filename);
242         }
243         return variable;
244 }
245
246 struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size)
247 {
248         /* a file should be included ONCE. Otherwise, if one of the instances is changed,
249          * then all be changed. -- how do we know to include it? -- Handling modified 
250          * instances is possible, I'd have
251          * to create a new master for each instance. */
252         struct ast_config_include *inc;
253         struct stat statbuf;
254         
255         inc = ast_include_find(conf, included_file);
256         if (inc) {
257                 do {
258                         inc->inclusion_count++;
259                         snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
260                 } while (stat(real_included_file_name, &statbuf) == 0);
261                 ast_log(LOG_WARNING,"'%s', line %d:  Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name);
262         } else
263                 *real_included_file_name = 0;
264         
265         inc = ast_calloc(1,sizeof(struct ast_config_include));
266         inc->include_location_file = ast_strdup(from_file);
267         inc->include_location_lineno = from_lineno;
268         if (!ast_strlen_zero(real_included_file_name))
269                 inc->included_file = ast_strdup(real_included_file_name);
270         else
271                 inc->included_file = ast_strdup(included_file);
272         
273         inc->exec = is_exec;
274         if (is_exec)
275                 inc->exec_file = ast_strdup(exec_file);
276         
277         /* attach this new struct to the conf struct */
278         inc->next = conf->includes;
279         conf->includes = inc;
280         
281         return inc;
282 }
283
284 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
285 {
286         struct ast_config_include *incl;
287         struct ast_category *cat;
288         struct ast_variable *v;
289         
290         int from_len = strlen(from_file);
291         int to_len = strlen(to_file);
292         
293         if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
294                 return;
295         
296         /* the manager code allows you to read in one config file, then
297          * write it back out under a different name. But, the new arrangement
298          * ties output lines to the file name. So, before you try to write
299          * the config file to disk, better riffle thru the data and make sure
300          * the file names are changed.
301          */
302         /* file names are on categories, includes (of course), and on variables. So,
303          * traverse all this and swap names */
304
305         for (incl = conf->includes; incl; incl=incl->next) {
306                 if (strcmp(incl->include_location_file,from_file) == 0) {
307                         if (from_len >= to_len)
308                                 strcpy(incl->include_location_file, to_file);
309                         else {
310                                 free(incl->include_location_file);
311                                 incl->include_location_file = strdup(to_file);
312                         }
313                 }
314         }
315         for (cat = conf->root; cat; cat = cat->next) {
316                 if (strcmp(cat->file,from_file) == 0) {
317                         if (from_len >= to_len)
318                                 strcpy(cat->file, to_file);
319                         else {
320                                 free(cat->file);
321                                 cat->file = strdup(to_file);
322                         }
323                 }
324                 for (v = cat->root; v; v = v->next) {
325                         if (strcmp(v->file,from_file) == 0) {
326                                 if (from_len >= to_len)
327                                         strcpy(v->file, to_file);
328                                 else {
329                                         free(v->file);
330                                         v->file = strdup(to_file);
331                                 }
332                         }
333                 }
334         }
335 }
336
337 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
338 {
339         struct ast_config_include *x;
340         for (x=conf->includes;x;x=x->next) {
341                 if (strcmp(x->included_file,included_file) == 0)
342                         return x;
343         }
344         return 0;
345 }
346
347
348 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
349 {
350         if (!variable)
351                 return;
352         if (category->last)
353                 category->last->next = variable;
354         else
355                 category->root = variable;
356         category->last = variable;
357         while (category->last->next)
358                 category->last = category->last->next;
359 }
360
361 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
362 {
363         struct ast_variable *cur = category->root;
364         int lineno;
365         int insertline;
366
367         if (!variable || sscanf(line, "%30d", &insertline) != 1) {
368                 return;
369         }
370         if (!insertline) {
371                 variable->next = category->root;
372                 category->root = variable;
373         } else {
374                 for (lineno = 1; lineno < insertline; lineno++) {
375                         cur = cur->next;
376                         if (!cur->next) {
377                                 break;
378                         }
379                 }
380                 variable->next = cur->next;
381                 cur->next = variable;
382         }
383 }
384
385 static void ast_comment_destroy(struct ast_comment **comment)
386 {
387         struct ast_comment *n, *p;
388
389         for (p = *comment; p; p = n) {
390                 n = p->next;
391                 ast_free(p);
392         }
393
394         *comment = NULL;
395 }
396
397 void ast_variables_destroy(struct ast_variable *v)
398 {
399         struct ast_variable *vn;
400
401         while (v) {
402                 vn = v;
403                 v = v->next;
404                 ast_comment_destroy(&vn->precomments);
405                 ast_comment_destroy(&vn->sameline);
406                 ast_comment_destroy(&vn->trailing);
407                 ast_free(vn);
408         }
409 }
410
411 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
412 {
413         struct ast_category *cat = NULL;
414
415         if (category && config->last_browse && (config->last_browse->name == category)) {
416                 cat = config->last_browse;
417         } else {
418                 cat = ast_category_get(config, category);
419         }
420
421         return (cat) ? cat->root : NULL;
422 }
423
424 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
425 {
426         const char *tmp;
427         tmp = ast_variable_retrieve(cfg, cat, var);
428         if (!tmp) {
429                 tmp = ast_variable_retrieve(cfg, "general", var);
430         }
431         return tmp;
432 }
433
434
435 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
436 {
437         struct ast_variable *v;
438
439         if (category) {
440                 for (v = ast_variable_browse(config, category); v; v = v->next) {
441                         if (!strcasecmp(variable, v->name)) {
442                                 return v->value;
443                         }
444                 }
445         } else {
446                 struct ast_category *cat;
447
448                 for (cat = config->root; cat; cat = cat->next) {
449                         for (v = cat->root; v; v = v->next) {
450                                 if (!strcasecmp(variable, v->name)) {
451                                         return v->value;
452                                 }
453                         }
454                 }
455         }
456
457         return NULL;
458 }
459
460 static struct ast_variable *variable_clone(const struct ast_variable *old)
461 {
462         struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
463
464         if (new) {
465                 new->lineno = old->lineno;
466                 new->object = old->object;
467                 new->blanklines = old->blanklines;
468                 /* TODO: clone comments? */
469         }
470
471         return new;
472 }
473  
474 static void move_variables(struct ast_category *old, struct ast_category *new)
475 {
476         struct ast_variable *var = old->root;
477
478         old->root = NULL;
479         /* we can just move the entire list in a single op */
480         ast_variable_append(new, var);
481 }
482
483 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno) 
484 {
485         struct ast_category *category;
486
487         if ((category = ast_calloc(1, sizeof(*category))))
488                 ast_copy_string(category->name, name, sizeof(category->name));
489         category->file = strdup(in_file);
490         category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
491         return category;
492 }
493
494 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
495 {
496         struct ast_category *cat;
497
498         /* try exact match first, then case-insensitive match */
499         for (cat = config->root; cat; cat = cat->next) {
500                 if (cat->name == category_name && (ignored || !cat->ignored))
501                         return cat;
502         }
503
504         for (cat = config->root; cat; cat = cat->next) {
505                 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
506                         return cat;
507         }
508
509         return NULL;
510 }
511
512 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
513 {
514         return category_get(config, category_name, 0);
515 }
516
517 int ast_category_exist(const struct ast_config *config, const char *category_name)
518 {
519         return !!ast_category_get(config, category_name);
520 }
521
522 void ast_category_append(struct ast_config *config, struct ast_category *category)
523 {
524         if (config->last)
525                 config->last->next = category;
526         else
527                 config->root = category;
528         category->include_level = config->include_level;
529         config->last = category;
530         config->current = category;
531 }
532
533 void ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
534 {
535         struct ast_category *cur_category;
536
537         if (!cat || !match)
538                 return;
539         if (!strcasecmp(config->root->name, match)) {
540                 cat->next = config->root;
541                 config->root = cat;
542                 return;
543         } 
544         for (cur_category = config->root; cur_category; cur_category = cur_category->next) {
545                 if (!strcasecmp(cur_category->next->name, match)) {
546                         cat->next = cur_category->next;
547                         cur_category->next = cat;
548                         break;
549                 }
550         }
551 }
552
553 static void ast_destroy_template_list(struct ast_category *cat)
554 {
555         struct ast_category_template_instance *x;
556
557         while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
558                 free(x);
559 }
560
561 void ast_category_destroy(struct ast_category *cat)
562 {
563         ast_variables_destroy(cat->root);
564         if (cat->file) {
565                 free(cat->file);
566                 cat->file = 0;
567         }
568         ast_comment_destroy(&cat->precomments);
569         ast_comment_destroy(&cat->sameline);
570         ast_comment_destroy(&cat->trailing);
571         ast_destroy_template_list(cat);
572         ast_free(cat);
573 }
574
575 static void ast_includes_destroy(struct ast_config_include *incls)
576 {
577         struct ast_config_include *incl,*inclnext;
578         
579         for (incl=incls; incl; incl = inclnext) {
580                 inclnext = incl->next;
581                 if (incl->include_location_file)
582                         free(incl->include_location_file);
583                 if (incl->exec_file)
584                         free(incl->exec_file);
585                 if (incl->included_file)
586                         free(incl->included_file);
587                 free(incl);
588         }
589 }
590
591 static struct ast_category *next_available_category(struct ast_category *cat)
592 {
593         for (; cat && cat->ignored; cat = cat->next);
594
595         return cat;
596 }
597
598 /*! return the first var of a category */
599 struct ast_variable *ast_category_first(struct ast_category *cat)
600 {
601         return (cat) ? cat->root : NULL;
602 }
603
604 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
605 {
606         struct ast_category *category = ast_category_get(config, cat);
607
608         if (category)
609                 return category->root;
610         return NULL;
611 }
612
613 char *ast_category_browse(struct ast_config *config, const char *prev)
614 {       
615         struct ast_category *cat = NULL;
616
617         if (prev && config->last_browse && (config->last_browse->name == prev))
618                 cat = config->last_browse->next;
619         else if (!prev && config->root)
620                 cat = config->root;
621         else if (prev) {
622                 for (cat = config->root; cat; cat = cat->next) {
623                         if (cat->name == prev) {
624                                 cat = cat->next;
625                                 break;
626                         }
627                 }
628                 if (!cat) {
629                         for (cat = config->root; cat; cat = cat->next) {
630                                 if (!strcasecmp(cat->name, prev)) {
631                                         cat = cat->next;
632                                         break;
633                                 }
634                         }
635                 }
636         }
637         
638         if (cat)
639                 cat = next_available_category(cat);
640
641         config->last_browse = cat;
642         return (cat) ? cat->name : NULL;
643 }
644
645 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
646 {
647         struct ast_variable *v;
648
649         v = cat->root;
650         cat->root = NULL;
651         cat->last = NULL;
652
653         return v;
654 }
655
656 void ast_category_rename(struct ast_category *cat, const char *name)
657 {
658         ast_copy_string(cat->name, name, sizeof(cat->name));
659 }
660
661 static void inherit_category(struct ast_category *new, const struct ast_category *base)
662 {
663         struct ast_variable *var;
664         struct ast_category_template_instance *x = ast_calloc(1,sizeof(struct ast_category_template_instance));
665
666         strcpy(x->name, base->name);
667         x->inst = base;
668         AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
669         for (var = base->root; var; var = var->next)
670                 ast_variable_append(new, variable_clone(var));
671 }
672
673 struct ast_config *ast_config_new(void) 
674 {
675         struct ast_config *config;
676
677         if ((config = ast_calloc(1, sizeof(*config))))
678                 config->max_include_level = MAX_INCLUDE_LEVEL;
679         return config;
680 }
681
682 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
683 {
684         struct ast_variable *cur, *prev=NULL, *curn;
685         int res = -1;
686         int lineno = 0;
687
688         cur = category->root;
689         while (cur) {
690                 if (cur->name == variable) {
691                         if (prev) {
692                                 prev->next = cur->next;
693                                 if (cur == category->last)
694                                         category->last = prev;
695                         } else {
696                                 category->root = cur->next;
697                                 if (cur == category->last)
698                                         category->last = NULL;
699                         }
700                         cur->next = NULL;
701                         ast_variables_destroy(cur);
702                         return 0;
703                 }
704                 prev = cur;
705                 cur = cur->next;
706         }
707
708         prev = NULL;
709         cur = category->root;
710         while (cur) {
711                 curn = cur->next;
712                 if ((!ast_strlen_zero(line) && lineno == atoi(line)) || (ast_strlen_zero(line) && !strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
713                         if (prev) {
714                                 prev->next = cur->next;
715                                 if (cur == category->last)
716                                         category->last = prev;
717                         } else {
718                                 category->root = cur->next;
719                                 if (cur == category->last)
720                                         category->last = NULL;
721                         }
722                         cur->next = NULL;
723                         ast_variables_destroy(cur);
724                         res = 0;
725                 } else
726                         prev = cur;
727
728                 cur = curn;
729                 lineno++;
730         }
731         return res;
732 }
733
734 int ast_variable_update(struct ast_category *category, const char *variable, 
735                                                 const char *value, const char *match, unsigned int object)
736 {
737         struct ast_variable *cur, *prev=NULL, *newer=NULL;
738
739         for (cur = category->root; cur; prev = cur, cur = cur->next) {
740                 if (strcasecmp(cur->name, variable) ||
741                         (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
742                         continue;
743
744                 if (!(newer = ast_variable_new(variable, value, cur->file)))
745                         return -1;
746         
747                 newer->next = cur->next;
748                 newer->object = cur->object || object;
749
750                 /* Preserve everything */
751                 newer->lineno = cur->lineno;
752                 newer->blanklines = cur->blanklines;
753                 newer->precomments = cur->precomments; cur->precomments = NULL;
754                 newer->sameline = cur->sameline; cur->sameline = NULL;
755                 newer->trailing = cur->trailing; cur->trailing = NULL;
756
757                 if (prev)
758                         prev->next = newer;
759                 else
760                         category->root = newer;
761                 if (category->last == cur)
762                         category->last = newer;
763
764                 cur->next = NULL;
765                 ast_variables_destroy(cur);
766
767                 return 0;
768         }
769
770         /* Could not find variable to update */
771         return -1;
772 }
773
774 int ast_category_delete(struct ast_config *cfg, const char *category)
775 {
776         struct ast_category *prev=NULL, *cat;
777
778         cat = cfg->root;
779         while (cat) {
780                 if (cat->name == category) {
781                         if (prev) {
782                                 prev->next = cat->next;
783                                 if (cat == cfg->last)
784                                         cfg->last = prev;
785                         } else {
786                                 cfg->root = cat->next;
787                                 if (cat == cfg->last)
788                                         cfg->last = NULL;
789                         }
790                         ast_category_destroy(cat);
791                         return 0;
792                 }
793                 prev = cat;
794                 cat = cat->next;
795         }
796
797         prev = NULL;
798         cat = cfg->root;
799         while (cat) {
800                 if (!strcasecmp(cat->name, category)) {
801                         if (prev) {
802                                 prev->next = cat->next;
803                                 if (cat == cfg->last)
804                                         cfg->last = prev;
805                         } else {
806                                 cfg->root = cat->next;
807                                 if (cat == cfg->last)
808                                         cfg->last = NULL;
809                         }
810                         ast_category_destroy(cat);
811                         return 0;
812                 }
813                 prev = cat;
814                 cat = cat->next;
815         }
816         return -1;
817 }
818
819 int ast_category_empty(struct ast_config *cfg, const char *category)
820 {
821         struct ast_category *cat;
822
823         for (cat = cfg->root; cat; cat = cat->next) {
824                 if (!strcasecmp(cat->name, category))
825                         continue;
826                 ast_variables_destroy(cat->root);
827                 cat->root = NULL;
828                 cat->last = NULL;
829                 return 0;
830         }
831
832         return -1;
833 }
834
835 void ast_config_destroy(struct ast_config *cfg)
836 {
837         struct ast_category *cat, *catn;
838
839         if (!cfg)
840                 return;
841
842         ast_includes_destroy(cfg->includes);
843
844         cat = cfg->root;
845         while (cat) {
846                 catn = cat;
847                 cat = cat->next;
848                 ast_category_destroy(catn);
849         }
850         ast_free(cfg);
851 }
852
853 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
854 {
855         return cfg->current;
856 }
857
858 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
859 {
860         /* cast below is just to silence compiler warning about dropping "const" */
861         cfg->current = (struct ast_category *) cat;
862 }
863
864 enum config_cache_attribute_enum {
865         ATTRIBUTE_INCLUDE = 0,
866         ATTRIBUTE_EXEC = 1,
867 };
868
869 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
870 {
871         struct cache_file_mtime *cfmtime;
872         struct cache_file_include *cfinclude;
873         struct stat statbuf = { 0, };
874
875         /* Find our cached entry for this configuration file */
876         AST_LIST_LOCK(&cfmtime_head);
877         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
878                 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
879                         break;
880         }
881         if (!cfmtime) {
882                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1 + strlen(who_asked) + 1);
883                 if (!cfmtime) {
884                         AST_LIST_UNLOCK(&cfmtime_head);
885                         return;
886                 }
887                 AST_LIST_HEAD_INIT(&cfmtime->includes);
888                 strcpy(cfmtime->filename, configfile);
889                 cfmtime->who_asked = cfmtime->filename + strlen(configfile) + 1;
890                 strcpy(cfmtime->who_asked, who_asked);
891                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
892                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
893         }
894
895         if (!stat(configfile, &statbuf))
896                 cfmtime->mtime = 0;
897         else
898                 cfmtime->mtime = statbuf.st_mtime;
899
900         switch (attrtype) {
901         case ATTRIBUTE_INCLUDE:
902                 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
903                         if (!strcmp(cfinclude->include, filename)) {
904                                 AST_LIST_UNLOCK(&cfmtime_head);
905                                 return;
906                         }
907                 }
908                 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
909                 if (!cfinclude) {
910                         AST_LIST_UNLOCK(&cfmtime_head);
911                         return;
912                 }
913                 strcpy(cfinclude->include, filename);
914                 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
915                 break;
916         case ATTRIBUTE_EXEC:
917                 cfmtime->has_exec = 1;
918                 break;
919         }
920         AST_LIST_UNLOCK(&cfmtime_head);
921 }
922
923 /*! \brief parse one line in the configuration.
924  * \verbatim
925  * We can have a category header        [foo](...)
926  * a directive                          #include / #exec
927  * or a regular line                    name = value
928  * \endverbatim
929  */
930 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
931         char *buf, int lineno, const char *configfile, struct ast_flags flags,
932         struct ast_str *comment_buffer,
933         struct ast_str *lline_buffer,
934         const char *suggested_include_file,
935         struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
936 {
937         char *c;
938         char *cur = buf;
939         struct ast_variable *v;
940         char cmd[512], exec_file[512];
941
942         /* Actually parse the entry */
943         if (cur[0] == '[') { /* A category header */
944                 /* format is one of the following:
945                  * [foo]        define a new category named 'foo'
946                  * [foo](!)     define a new template category named 'foo'
947                  * [foo](+)     append to category 'foo', error if foo does not exist.
948                  * [foo](a)     define a new category and inherit from template a.
949                  *              You can put a comma-separated list of templates and '!' and '+'
950                  *              between parentheses, with obvious meaning.
951                  */
952                 struct ast_category *newcat = NULL;
953                 char *catname;
954
955                 c = strchr(cur, ']');
956                 if (!c) {
957                         ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
958                         return -1;
959                 }
960                 *c++ = '\0';
961                 cur++;
962                 if (*c++ != '(')
963                         c = NULL;
964                 catname = cur;
965                 if (!(*cat = newcat = ast_category_new(catname,
966                                 S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
967                                 lineno))) {
968                         return -1;
969                 }
970                 (*cat)->lineno = lineno;
971                 *last_var = 0;
972                 *last_cat = newcat;
973                 
974                 /* add comments */
975                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
976                         newcat->precomments = ALLOC_COMMENT(comment_buffer);
977                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
978                         newcat->sameline = ALLOC_COMMENT(lline_buffer);
979                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
980                         CB_RESET(comment_buffer, lline_buffer);
981                 
982                 /* If there are options or categories to inherit from, process them now */
983                 if (c) {
984                         if (!(cur = strchr(c, ')'))) {
985                                 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
986                                 return -1;
987                         }
988                         *cur = '\0';
989                         while ((cur = strsep(&c, ","))) {
990                                 if (!strcasecmp(cur, "!")) {
991                                         (*cat)->ignored = 1;
992                                 } else if (!strcasecmp(cur, "+")) {
993                                         *cat = category_get(cfg, catname, 1);
994                                         if (!(*cat)) {
995                                                 if (newcat)
996                                                         ast_category_destroy(newcat);
997                                                 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
998                                                 return -1;
999                                         }
1000                                         if (newcat) {
1001                                                 move_variables(newcat, *cat);
1002                                                 ast_category_destroy(newcat);
1003                                                 newcat = NULL;
1004                                         }
1005                                 } else {
1006                                         struct ast_category *base;
1007                                 
1008                                         base = category_get(cfg, cur, 1);
1009                                         if (!base) {
1010                                                 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
1011                                                 return -1;
1012                                         }
1013                                         inherit_category(*cat, base);
1014                                 }
1015                         }
1016                 }
1017                 if (newcat)
1018                         ast_category_append(cfg, *cat);
1019         } else if (cur[0] == '#') { /* A directive - #include or #exec */
1020                 char *cur2;
1021                 char real_inclusion_name[256];
1022                 struct ast_config_include *inclu;
1023                 int do_include = 0;     /* otherwise, it is exec */
1024
1025                 cur++;
1026                 c = cur;
1027                 while (*c && (*c > 32)) {
1028                         c++;
1029                 }
1030
1031                 if (*c) {
1032                         *c = '\0';
1033                         /* Find real argument */
1034                         c = ast_skip_blanks(c + 1);
1035                         if (!(*c)) {
1036                                 c = NULL;
1037                         }
1038                 } else 
1039                         c = NULL;
1040                 if (!strcasecmp(cur, "include")) {
1041                         do_include = 1;
1042                 } else if (!strcasecmp(cur, "exec")) {
1043                         if (!ast_opt_exec_includes) {
1044                                 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
1045                                 return 0;       /* XXX is this correct ? or we should return -1 ? */
1046                         }
1047                 } else {
1048                         ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
1049                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1050                 }
1051
1052                 if (c == NULL) {
1053                         ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", 
1054                                         do_include ? "include" : "exec",
1055                                         do_include ? "filename" : "/path/to/executable",
1056                                         lineno,
1057                                         configfile);
1058                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1059                 }
1060
1061                 cur = c;
1062                 /* Strip off leading and trailing "'s and <>'s */
1063                 if (*c == '"') {
1064                         /* Dequote */
1065                         while (*c) {
1066                                 if (*c == '"') {
1067                                         strcpy(c, c + 1); /* SAFE */
1068                                         c--;
1069                                 } else if (*c == '\\') {
1070                                         strcpy(c, c + 1); /* SAFE */
1071                                 }
1072                                 c++;
1073                         }
1074                 } else if (*c == '<') {
1075                         /* C-style include */
1076                         if (*(c + strlen(c) - 1) == '>') {
1077                                 cur++;
1078                                 *(c + strlen(c) - 1) = '\0';
1079                         }
1080                 }
1081                 cur2 = cur;
1082
1083                 /* #exec </path/to/executable>
1084                    We create a tmp file, then we #include it, then we delete it. */
1085                 if (!do_include) {
1086                         struct timeval now = ast_tvnow();
1087                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1088                                 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
1089                         snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
1090                         snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
1091                         ast_safe_system(cmd);
1092                         cur = exec_file;
1093                 } else {
1094                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1095                                 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
1096                         exec_file[0] = '\0';
1097                 }
1098                 /* A #include */
1099                 /* record this inclusion */
1100                 inclu = ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1101
1102                 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
1103                 if (!ast_strlen_zero(exec_file))
1104                         unlink(exec_file);
1105                 if (!do_include) {
1106                         ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1107                         return -1;
1108                 }
1109                 /* XXX otherwise what ? the default return is 0 anyways */
1110
1111         } else {
1112                 /* Just a line (variable = value) */
1113                 int object = 0;
1114                 if (!(*cat)) {
1115                         ast_log(LOG_WARNING,
1116                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
1117                         return -1;
1118                 }
1119                 c = strchr(cur, '=');
1120
1121                 if (c && c > cur && (*(c - 1) == '+')) {
1122                         struct ast_variable *var, *replace = NULL;
1123                         struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
1124
1125                         if (!str || !*str) {
1126                                 return -1;
1127                         }
1128
1129                         *(c - 1) = '\0';
1130                         c++;
1131                         cur = ast_strip(cur);
1132
1133                         /* Must iterate through category until we find last variable of same name (since there could be multiple) */
1134                         for (var = ast_category_first(*cat); var; var = var->next) {
1135                                 if (!strcmp(var->name, cur)) {
1136                                         replace = var;
1137                                 }
1138                         }
1139
1140                         if (!replace) {
1141                                 /* Nothing to replace; just set a variable normally. */
1142                                 goto set_new_variable;
1143                         }
1144
1145                         ast_str_set(str, 0, "%s", replace->value);
1146                         ast_str_append(str, 0, "%s", c);
1147                         ast_str_trim_blanks(*str);
1148                         ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
1149                 } else if (c) {
1150                         *c = 0;
1151                         c++;
1152                         /* Ignore > in => */
1153                         if (*c== '>') {
1154                                 object = 1;
1155                                 c++;
1156                         }
1157 set_new_variable:
1158                         if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
1159                                 v->lineno = lineno;
1160                                 v->object = object;
1161                                 *last_cat = 0;
1162                                 *last_var = v;
1163                                 /* Put and reset comments */
1164                                 v->blanklines = 0;
1165                                 ast_variable_append(*cat, v);
1166                                 /* add comments */
1167                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1168                                         v->precomments = ALLOC_COMMENT(comment_buffer);
1169                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1170                                         v->sameline = ALLOC_COMMENT(lline_buffer);
1171                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1172                                         CB_RESET(comment_buffer, lline_buffer);
1173                                 
1174                         } else {
1175                                 return -1;
1176                         }
1177                 } else {
1178                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1179                 }
1180         }
1181         return 0;
1182 }
1183
1184 static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file, const char *who_asked)
1185 {
1186         char fn[256];
1187 #if defined(LOW_MEMORY)
1188         char buf[512];
1189 #else
1190         char buf[8192];
1191 #endif
1192         char *new_buf, *comment_p, *process_buf;
1193         FILE *f;
1194         int lineno=0;
1195         int comment = 0, nest[MAX_NESTED_COMMENTS];
1196         struct ast_category *cat = NULL;
1197         int count = 0;
1198         struct stat statbuf;
1199         struct cache_file_mtime *cfmtime = NULL;
1200         struct cache_file_include *cfinclude;
1201         struct ast_variable *last_var = 0;
1202         struct ast_category *last_cat = 0;
1203         /*! Growable string buffer */
1204         struct ast_str *comment_buffer = NULL;  /*!< this will be a comment collector.*/
1205         struct ast_str *lline_buffer = NULL;    /*!< A buffer for stuff behind the ; */
1206
1207         if (cfg)
1208                 cat = ast_config_get_current_category(cfg);
1209
1210         if (filename[0] == '/') {
1211                 ast_copy_string(fn, filename, sizeof(fn));
1212         } else {
1213                 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
1214         }
1215
1216         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1217                 comment_buffer = ast_str_create(CB_SIZE);
1218                 if (comment_buffer)
1219                         lline_buffer = ast_str_create(CB_SIZE);
1220                 if (!lline_buffer) {
1221                         if (comment_buffer)
1222                                 ast_free(comment_buffer);
1223                         ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1224                         return NULL;
1225                 }
1226         }
1227 #ifdef AST_INCLUDE_GLOB
1228         {
1229                 int glob_ret;
1230                 glob_t globbuf;
1231                 globbuf.gl_offs = 0;    /* initialize it to silence gcc */
1232                 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1233                 if (glob_ret == GLOB_NOSPACE)
1234                         ast_log(LOG_WARNING,
1235                                 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1236                 else if (glob_ret  == GLOB_ABORTED)
1237                         ast_log(LOG_WARNING,
1238                                 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1239                 else  {
1240                         /* loop over expanded files */
1241                         int i;
1242                         for (i=0; i<globbuf.gl_pathc; i++) {
1243                                 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1244 #endif
1245         /*
1246          * The following is not a loop, but just a convenient way to define a block
1247          * (using do { } while(0) ), and be able to exit from it with 'continue'
1248          * or 'break' in case of errors. Nice trick.
1249          */
1250         do {
1251                 if (stat(fn, &statbuf))
1252                         continue;
1253
1254                 if (!S_ISREG(statbuf.st_mode)) {
1255                         ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1256                         continue;
1257                 }
1258
1259                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1260                         /* Find our cached entry for this configuration file */
1261                         AST_LIST_LOCK(&cfmtime_head);
1262                         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1263                                 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
1264                                         break;
1265                         }
1266                         if (!cfmtime) {
1267                                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1 + strlen(who_asked) + 1);
1268                                 if (!cfmtime)
1269                                         continue;
1270                                 AST_LIST_HEAD_INIT(&cfmtime->includes);
1271                                 strcpy(cfmtime->filename, fn);
1272                                 cfmtime->who_asked = cfmtime->filename + strlen(fn) + 1;
1273                                 strcpy(cfmtime->who_asked, who_asked);
1274                                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1275                                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1276                         }
1277                 }
1278
1279                 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1280                         /* File is unchanged, what about the (cached) includes (if any)? */
1281                         int unchanged = 1;
1282                         AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1283                                 /* We must glob here, because if we did not, then adding a file to globbed directory would
1284                                  * incorrectly cause no reload to be necessary. */
1285                                 char fn2[256];
1286 #ifdef AST_INCLUDE_GLOB
1287                                 int glob_return;
1288                                 glob_t glob_buf = { .gl_offs = 0 };
1289                                 glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
1290                                 /* On error, we reparse */
1291                                 if (glob_return == GLOB_NOSPACE || glob_return  == GLOB_ABORTED)
1292                                         unchanged = 0;
1293                                 else  {
1294                                         /* loop over expanded files */
1295                                         int j;
1296                                         for (j = 0; j < glob_buf.gl_pathc; j++) {
1297                                                 ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
1298 #else
1299                                                 ast_copy_string(fn2, cfinclude->include);
1300 #endif
1301                                                 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
1302                                                         /* that second-to-last field needs to be looked at in this case... TODO */
1303                                                         unchanged = 0;
1304                                                         /* One change is enough to short-circuit and reload the whole shebang */
1305                                                         break;
1306                                                 }
1307 #ifdef AST_INCLUDE_GLOB
1308                                         }
1309                                 }
1310 #endif
1311                         }
1312
1313                         if (unchanged) {
1314                                 AST_LIST_UNLOCK(&cfmtime_head);
1315                                 return CONFIG_STATUS_FILEUNCHANGED;
1316                         }
1317                 }
1318                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1319                         AST_LIST_UNLOCK(&cfmtime_head);
1320
1321                 /* If cfg is NULL, then we just want an answer */
1322                 if (cfg == NULL)
1323                         return NULL;
1324
1325                 if (cfmtime)
1326                         cfmtime->mtime = statbuf.st_mtime;
1327
1328                 ast_verb(2, "Parsing '%s': ", fn);
1329                         fflush(stdout);
1330                 if (!(f = fopen(fn, "r"))) {
1331                         ast_debug(1, "No file to parse: %s\n", fn);
1332                         ast_verb(2, "Not found (%s)\n", strerror(errno));
1333                         continue;
1334                 }
1335                 count++;
1336                 /* If we get to this point, then we're loading regardless */
1337                 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1338                 ast_debug(1, "Parsing %s\n", fn);
1339                 ast_verb(2, "Found\n");
1340                 while (!feof(f)) {
1341                         lineno++;
1342                         if (fgets(buf, sizeof(buf), f)) {
1343                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
1344                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
1345                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
1346                                 }
1347                                 
1348                                 new_buf = buf;
1349                                 if (comment) 
1350                                         process_buf = NULL;
1351                                 else
1352                                         process_buf = buf;
1353                                 
1354                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer) && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
1355                                         /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1356                                         CB_ADD(&comment_buffer, "\n");       /* add a newline to the comment buffer */
1357                                         continue; /* go get a new line, then */
1358                                 }
1359                                 
1360                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1361                                         if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
1362                                                 /* Escaped semicolons aren't comments. */
1363                                                 new_buf = comment_p + 1;
1364                                         } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1365                                                 /* Meta-Comment start detected ";--" */
1366                                                 if (comment < MAX_NESTED_COMMENTS) {
1367                                                         *comment_p = '\0';
1368                                                         new_buf = comment_p + 3;
1369                                                         comment++;
1370                                                         nest[comment-1] = lineno;
1371                                                 } else {
1372                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1373                                                 }
1374                                         } else if ((comment_p >= new_buf + 2) &&
1375                                                    (*(comment_p - 1) == COMMENT_TAG) &&
1376                                                    (*(comment_p - 2) == COMMENT_TAG)) {
1377                                                 /* Meta-Comment end detected */
1378                                                 comment--;
1379                                                 new_buf = comment_p + 1;
1380                                                 if (!comment) {
1381                                                         /* Back to non-comment now */
1382                                                         if (process_buf) {
1383                                                                 /* Actually have to move what's left over the top, then continue */
1384                                                                 char *oldptr;
1385                                                                 oldptr = process_buf + strlen(process_buf);
1386                                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1387                                                                         CB_ADD(&comment_buffer, ";");
1388                                                                         CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
1389                                                                 }
1390                                                                 
1391                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1392                                                                 new_buf = oldptr;
1393                                                         } else
1394                                                                 process_buf = new_buf;
1395                                                 }
1396                                         } else {
1397                                                 if (!comment) {
1398                                                         /* If ; is found, and we are not nested in a comment, 
1399                                                            we immediately stop all comment processing */
1400                                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1401                                                                 CB_ADD(&lline_buffer, comment_p);
1402                                                         }
1403                                                         *comment_p = '\0'; 
1404                                                         new_buf = comment_p;
1405                                                 } else
1406                                                         new_buf = comment_p + 1;
1407                                         }
1408                                 }
1409                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
1410                                         CB_ADD(&comment_buffer, buf);  /* the whole line is a comment, store it */
1411                                 }
1412                                 
1413                                 if (process_buf) {
1414                                         char *buffer = ast_strip(process_buf);
1415                                         if (!ast_strlen_zero(buffer)) {
1416                                                 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
1417                                                         cfg = CONFIG_STATUS_FILEINVALID;
1418                                                         break;
1419                                                 }
1420                                         }
1421                                 }
1422                         }
1423                 }
1424                 /* end of file-- anything in a comment buffer? */
1425                 if (last_cat) {
1426                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1427                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1428                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
1429                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
1430                                 }
1431                                 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1432                         }
1433                 } else if (last_var) {
1434                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1435                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1436                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
1437                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
1438                                 }
1439                                 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1440                         }
1441                 } else {
1442                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1443                                 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
1444                         }
1445                 }
1446                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1447                         CB_RESET(comment_buffer, lline_buffer);
1448
1449                 fclose(f);
1450         } while (0);
1451         if (comment) {
1452                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1453         }
1454 #ifdef AST_INCLUDE_GLOB
1455                                         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1456                                                 break;
1457                                         }
1458                                 }
1459                                 globfree(&globbuf);
1460                         }
1461                 }
1462 #endif
1463
1464         if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg != CONFIG_STATUS_FILEINVALID && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1465                 if (comment_buffer)
1466                         ast_free(comment_buffer);
1467                 if (lline_buffer)
1468                         ast_free(lline_buffer);
1469                 comment_buffer = NULL;
1470                 lline_buffer = NULL;
1471         }
1472         
1473         if (count == 0)
1474                 return NULL;
1475
1476         return cfg;
1477 }
1478
1479
1480 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1481    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1482    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1483    be shocked and mystified as to why things are not showing up in the files! 
1484
1485    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1486    and line number are stored for each include, plus the name of the file included, so that these statements may be
1487    included in the output files on a file_save operation. 
1488
1489    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1490    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1491    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1492    and a header gets added.
1493
1494    vars and category heads are output in the order they are stored in the config file. So, if the software
1495    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1496    file/lineno data probably won't get changed.
1497
1498 */
1499
1500 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1501 {
1502         char date[256]="";
1503         time_t t;
1504
1505         time(&t);
1506         ast_copy_string(date, ctime(&t), sizeof(date));
1507
1508         fprintf(f1, ";!\n");
1509         fprintf(f1, ";! Automatically generated configuration file\n");
1510         if (strcmp(configfile, fn))
1511                 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1512         else
1513                 fprintf(f1, ";! Filename: %s\n", configfile);
1514         fprintf(f1, ";! Generator: %s\n", generator);
1515         fprintf(f1, ";! Creation Date: %s", date);
1516         fprintf(f1, ";!\n");
1517 }
1518
1519 static void   inclfile_destroy(void *obj)
1520 {
1521         const struct inclfile *o = obj;
1522
1523         if (o->fname)
1524                 free(o->fname);
1525 }
1526
1527
1528 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
1529 {
1530         struct inclfile lookup;
1531         
1532         if (!file || file[0] == 0) {
1533                 if (configfile[0] == '/')
1534                         ast_copy_string(fn, configfile, fn_size);
1535                 else
1536                         snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1537         } else if (file[0] == '/') 
1538                 ast_copy_string(fn, file, fn_size);
1539         else
1540                 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
1541         lookup.fname = fn;
1542         *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
1543         if (!(*fi)) {
1544                 /* set up a file scratch pad */
1545                 struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
1546                 fx->fname = ast_strdup(fn);
1547                 fx->lineno = 1;
1548                 *fi = fx;
1549                 ao2_link(fileset, fx);
1550         }
1551 }
1552
1553 static int count_linefeeds(char *str)
1554 {
1555         int count = 0;
1556
1557         while (*str) {
1558                 if (*str =='\n')
1559                         count++;
1560                 str++;
1561         }
1562         return count;
1563 }
1564
1565 static int count_linefeeds_in_comments(struct ast_comment *x)
1566 {
1567         int count = 0;
1568
1569         while (x) {
1570                 count += count_linefeeds(x->cmt);
1571                 x = x->next;
1572         }
1573         return count;
1574 }
1575
1576 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1577 {
1578         int precomment_lines = count_linefeeds_in_comments(precomments);
1579         int i;
1580
1581         /* I don't have to worry about those ;! comments, they are
1582            stored in the precomments, but not printed back out.
1583            I did have to make sure that comments following
1584            the ;! header comments were not also deleted in the process */
1585         if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
1586                 return;
1587         } else if (lineno == 0) {
1588                 /* Line replacements also mess things up */
1589                 return;
1590         } else if (lineno - precomment_lines - fi->lineno < 5) {
1591                 /* Only insert less than 5 blank lines; if anything more occurs,
1592                  * it's probably due to context deletion. */
1593                 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
1594                         fprintf(fp, "\n");
1595                 }
1596         } else {
1597                 /* Deletion occurred - insert a single blank line, for separation of
1598                  * contexts. */
1599                 fprintf(fp, "\n");
1600         }
1601  
1602         fi->lineno = lineno + 1; /* Advance the file lineno */
1603 }
1604
1605 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1606 {
1607         return ast_config_text_file_save(configfile, cfg, generator);
1608 }
1609
1610 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1611 {
1612         FILE *f;
1613         char fn[256];
1614         struct ast_variable *var;
1615         struct ast_category *cat;
1616         struct ast_comment *cmt;
1617         struct ast_config_include *incl;
1618         int blanklines = 0;
1619         struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
1620         struct inclfile *fi = 0;
1621
1622         /* reset all the output flags, in case this isn't our first time saving this data */
1623
1624         for (incl=cfg->includes; incl; incl = incl->next)
1625                 incl->output = 0;
1626
1627         /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
1628            are all truncated to zero bytes and have that nice header*/
1629
1630         for (incl=cfg->includes; incl; incl = incl->next)
1631         {
1632                 if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/
1633                         FILE *f1;
1634
1635                         set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset, &fi); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
1636                         f1 = fopen(fn,"w");
1637                         if (f1) {
1638                                 gen_header(f1, configfile, fn, generator);
1639                                 fclose(f1); /* this should zero out the file */
1640                         } else {
1641                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1642                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1643                         }
1644                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1645                         fi = 0;
1646                 }
1647         }
1648
1649         set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
1650 #ifdef __CYGWIN__       
1651         if ((f = fopen(fn, "w+"))) {
1652 #else
1653         if ((f = fopen(fn, "w"))) {
1654 #endif      
1655                 ast_verb(2, "Saving '%s': ", fn);
1656                 gen_header(f, configfile, fn, generator);
1657                 cat = cfg->root;
1658                 fclose(f);
1659                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1660                 
1661                 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
1662                 /* since each var, cat, and associated comments can come from any file, we have to be 
1663                    mobile, and open each file, print, and close it on an entry-by-entry basis */
1664
1665                 while (cat) {
1666                         set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
1667                         f = fopen(fn, "a");
1668                         if (!f)
1669                         {
1670                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1671                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1672                                 ao2_ref(fileset, -1);
1673                                 return -1;
1674                         }
1675
1676                         /* dump any includes that happen before this category header */
1677                         for (incl=cfg->includes; incl; incl = incl->next) {
1678                                 if (strcmp(incl->include_location_file, cat->file) == 0){
1679                                         if (cat->lineno > incl->include_location_lineno && !incl->output) {
1680                                                 if (incl->exec)
1681                                                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1682                                                 else
1683                                                         fprintf(f,"#include \"%s\"\n", incl->included_file);
1684                                                 incl->output = 1;
1685                                         }
1686                                 }
1687                         }
1688
1689                         insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
1690                         /* Dump section with any appropriate comment */
1691                         for (cmt = cat->precomments; cmt; cmt=cmt->next) {
1692                                 char *cmtp = cmt->cmt;
1693                                 while (*cmtp == ';' && *(cmtp+1) == '!') {
1694                                         char *cmtp2 = strchr(cmtp+1, '\n');
1695                                         if (cmtp2)
1696                                                 cmtp = cmtp2+1;
1697                                         else cmtp = 0;
1698                                 }
1699                                 if (cmtp)
1700                                         fprintf(f,"%s", cmtp);
1701                         }
1702                         fprintf(f, "[%s]", cat->name);
1703                         if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
1704                                 fprintf(f, "(");
1705                                 if (cat->ignored) {
1706                                         fprintf(f, "!");
1707                                 }
1708                                 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
1709                                         fprintf(f, ",");
1710                                 }
1711                                 if (!AST_LIST_EMPTY(&cat->template_instances)) {
1712                                         struct ast_category_template_instance *x;
1713                                         AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1714                                                 fprintf(f,"%s",x->name);
1715                                                 if (x != AST_LIST_LAST(&cat->template_instances))
1716                                                         fprintf(f,",");
1717                                         }
1718                                 }
1719                                 fprintf(f, ")");
1720                         }
1721                         for(cmt = cat->sameline; cmt; cmt=cmt->next)
1722                         {
1723                                 fprintf(f,"%s", cmt->cmt);
1724                         }
1725                         if (!cat->sameline)
1726                                 fprintf(f,"\n");
1727                         for (cmt = cat->trailing; cmt; cmt=cmt->next) {
1728                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1729                                         fprintf(f,"%s", cmt->cmt);
1730                         }
1731                         fclose(f);
1732                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1733                         fi = 0;
1734                         
1735                         var = cat->root;
1736                         while (var) {
1737                                 struct ast_category_template_instance *x;
1738                                 int found = 0;
1739                                 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1740                                         struct ast_variable *v;
1741                                         for (v = x->inst->root; v; v = v->next) {
1742                                                 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
1743                                                         found = 1;
1744                                                         break;
1745                                                 }
1746                                         }
1747                                         if (found)
1748                                                 break;
1749                                 }
1750                                 if (found) {
1751                                         var = var->next;
1752                                         continue;
1753                                 }
1754                                 set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
1755                                 f = fopen(fn, "a");
1756                                 if (!f)
1757                                 {
1758                                         ast_debug(1, "Unable to open for writing: %s\n", fn);
1759                                         ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1760                                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1761                                         fi = 0;
1762                                         ao2_ref(fileset, -1);
1763                                         return -1;
1764                                 }
1765                                 
1766                                 /* dump any includes that happen before this category header */
1767                                 for (incl=cfg->includes; incl; incl = incl->next) {
1768                                         if (strcmp(incl->include_location_file, var->file) == 0){
1769                                                 if (var->lineno > incl->include_location_lineno && !incl->output) {
1770                                                         if (incl->exec)
1771                                                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1772                                                         else
1773                                                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1774                                                         incl->output = 1;
1775                                                 }
1776                                         }
1777                                 }
1778                                 
1779                                 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
1780                                 for (cmt = var->precomments; cmt; cmt=cmt->next) {
1781                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1782                                                 fprintf(f,"%s", cmt->cmt);
1783                                 }
1784                                 if (var->sameline) 
1785                                         fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
1786                                 else    
1787                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
1788                                 for (cmt = var->trailing; cmt; cmt=cmt->next) {
1789                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1790                                                 fprintf(f,"%s", cmt->cmt);
1791                                 }
1792                                 if (var->blanklines) {
1793                                         blanklines = var->blanklines;
1794                                         while (blanklines--)
1795                                                 fprintf(f, "\n");
1796                                 }
1797                                 
1798                                 fclose(f);
1799                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1800                                 fi = 0;
1801                                 
1802                                 var = var->next;
1803                         }
1804                         cat = cat->next;
1805                 }
1806                 if (!option_debug)
1807                         ast_verb(2, "Saved\n");
1808         } else {
1809                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1810                 ast_verb(2, "Unable to write (%s)", strerror(errno));
1811                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1812                 ao2_ref(fileset, -1);
1813                 return -1;
1814         }
1815
1816         /* Now, for files with trailing #include/#exec statements,
1817            we have to make sure every entry is output */
1818
1819         for (incl=cfg->includes; incl; incl = incl->next) {
1820                 if (!incl->output) {
1821                         /* open the respective file */
1822                         set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
1823                         f = fopen(fn, "a");
1824                         if (!f)
1825                         {
1826                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1827                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1828                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1829                                 fi = 0;
1830                                 ao2_ref(fileset, -1);
1831                                 return -1;
1832                         }
1833                         
1834                         /* output the respective include */
1835                         if (incl->exec)
1836                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1837                         else
1838                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1839                         fclose(f);
1840                         incl->output = 1;
1841                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1842                         fi = 0;
1843                 }
1844         }
1845         ao2_ref(fileset, -1); /* this should destroy the hash container */
1846                                 
1847         return 0;
1848 }
1849
1850 static void clear_config_maps(void) 
1851 {
1852         struct ast_config_map *map;
1853
1854         ast_mutex_lock(&config_lock);
1855
1856         while (config_maps) {
1857                 map = config_maps;
1858                 config_maps = config_maps->next;
1859                 ast_free(map);
1860         }
1861                 
1862         ast_mutex_unlock(&config_lock);
1863 }
1864
1865 static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
1866 {
1867         struct ast_config_map *map;
1868         int length;
1869
1870         length = sizeof(*map);
1871         length += strlen(name) + 1;
1872         length += strlen(driver) + 1;
1873         length += strlen(database) + 1;
1874         if (table)
1875                 length += strlen(table) + 1;
1876
1877         if (!(map = ast_calloc(1, length)))
1878                 return -1;
1879
1880         map->name = map->stuff;
1881         strcpy(map->name, name);
1882         map->driver = map->name + strlen(map->name) + 1;
1883         strcpy(map->driver, driver);
1884         map->database = map->driver + strlen(map->driver) + 1;
1885         strcpy(map->database, database);
1886         if (table) {
1887                 map->table = map->database + strlen(map->database) + 1;
1888                 strcpy(map->table, table);
1889         }
1890         map->next = config_maps;
1891
1892         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
1893
1894         config_maps = map;
1895         return 0;
1896 }
1897
1898 int read_config_maps(void) 
1899 {
1900         struct ast_config *config, *configtmp;
1901         struct ast_variable *v;
1902         char *driver, *table, *database, *stringp, *tmp;
1903         struct ast_flags flags = { 0 };
1904
1905         clear_config_maps();
1906
1907         configtmp = ast_config_new();
1908         configtmp->max_include_level = 1;
1909         config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
1910         if (!config) {
1911                 ast_config_destroy(configtmp);
1912                 return 0;
1913         }
1914
1915         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
1916                 char buf[512];
1917                 ast_copy_string(buf, v->value, sizeof(buf));
1918                 stringp = buf;
1919                 driver = strsep(&stringp, ",");
1920
1921                 if ((tmp = strchr(stringp, '\"')))
1922                         stringp = tmp;
1923
1924                 /* check if the database text starts with a double quote */
1925                 if (*stringp == '"') {
1926                         stringp++;
1927                         database = strsep(&stringp, "\"");
1928                         strsep(&stringp, ",");
1929                 } else {
1930                         /* apparently this text has no quotes */
1931                         database = strsep(&stringp, ",");
1932                 }
1933
1934                 table = strsep(&stringp, ",");
1935
1936                 if (!strcmp(v->name, extconfig_conf)) {
1937                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
1938                         continue;
1939                 }
1940
1941                 if (!strcmp(v->name, "asterisk.conf")) {
1942                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
1943                         continue;
1944                 }
1945
1946                 if (!strcmp(v->name, "logger.conf")) {
1947                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
1948                         continue;
1949                 }
1950
1951                 if (!driver || !database)
1952                         continue;
1953                 if (!strcasecmp(v->name, "sipfriends")) {
1954                         ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n");
1955                         append_mapping("sipusers", driver, database, table ? table : "sipfriends");
1956                         append_mapping("sippeers", driver, database, table ? table : "sipfriends");
1957                 } else if (!strcasecmp(v->name, "iaxfriends")) {
1958                         ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n");
1959                         append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
1960                         append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
1961                 } else 
1962                         append_mapping(v->name, driver, database, table);
1963         }
1964                 
1965         ast_config_destroy(config);
1966         return 0;
1967 }
1968
1969 int ast_config_engine_register(struct ast_config_engine *new) 
1970 {
1971         struct ast_config_engine *ptr;
1972
1973         ast_mutex_lock(&config_lock);
1974
1975         if (!config_engine_list) {
1976                 config_engine_list = new;
1977         } else {
1978                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
1979                 ptr->next = new;
1980         }
1981
1982         ast_mutex_unlock(&config_lock);
1983         ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
1984
1985         return 1;
1986 }
1987
1988 int ast_config_engine_deregister(struct ast_config_engine *del) 
1989 {
1990         struct ast_config_engine *ptr, *last=NULL;
1991
1992         ast_mutex_lock(&config_lock);
1993
1994         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
1995                 if (ptr == del) {
1996                         if (last)
1997                                 last->next = ptr->next;
1998                         else
1999                                 config_engine_list = ptr->next;
2000                         break;
2001                 }
2002                 last = ptr;
2003         }
2004
2005         ast_mutex_unlock(&config_lock);
2006
2007         return 0;
2008 }
2009
2010 /*! \brief Find realtime engine for realtime family */
2011 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz) 
2012 {
2013         struct ast_config_engine *eng, *ret = NULL;
2014         struct ast_config_map *map;
2015
2016         ast_mutex_lock(&config_lock);
2017
2018         for (map = config_maps; map; map = map->next) {
2019                 if (!strcasecmp(family, map->name)) {
2020                         if (database)
2021                                 ast_copy_string(database, map->database, dbsiz);
2022                         if (table)
2023                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
2024                         break;
2025                 }
2026         }
2027
2028         /* Check if the required driver (engine) exist */
2029         if (map) {
2030                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
2031                         if (!strcasecmp(eng->name, map->driver))
2032                                 ret = eng;
2033                 }
2034         }
2035
2036         ast_mutex_unlock(&config_lock);
2037         
2038         /* if we found a mapping, but the engine is not available, then issue a warning */
2039         if (map && !ret)
2040                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
2041
2042         return ret;
2043 }
2044
2045 static struct ast_config_engine text_file_engine = {
2046         .name = "text",
2047         .load_func = config_text_file_load,
2048 };
2049
2050 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file, const char *who_asked)
2051 {
2052         char db[256];
2053         char table[256];
2054         struct ast_config_engine *loader = &text_file_engine;
2055         struct ast_config *result; 
2056
2057         /* The config file itself bumps include_level by 1 */
2058         if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
2059                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
2060                 return NULL;
2061         }
2062
2063         cfg->include_level++;
2064
2065         if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
2066                 struct ast_config_engine *eng;
2067
2068                 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
2069
2070
2071                 if (eng && eng->load_func) {
2072                         loader = eng;
2073                 } else {
2074                         eng = find_engine("global", db, sizeof(db), table, sizeof(table));
2075                         if (eng && eng->load_func)
2076                                 loader = eng;
2077                 }
2078         }
2079
2080         result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
2081
2082         if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED)
2083                 result->include_level--;
2084         else if (result != CONFIG_STATUS_FILEINVALID)
2085                 cfg->include_level--;
2086
2087         return result;
2088 }
2089
2090 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
2091 {
2092         struct ast_config *cfg;
2093         struct ast_config *result;
2094
2095         cfg = ast_config_new();
2096         if (!cfg)
2097                 return NULL;
2098
2099         result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
2100         if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
2101                 ast_config_destroy(cfg);
2102
2103         return result;
2104 }
2105
2106 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
2107 {
2108         struct ast_config_engine *eng;
2109         char db[256];
2110         char table[256];
2111         struct ast_variable *res=NULL;
2112
2113         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2114         if (eng && eng->realtime_func) 
2115                 res = eng->realtime_func(db, table, ap);
2116
2117         return res;
2118 }
2119
2120 struct ast_variable *ast_load_realtime_all(const char *family, ...)
2121 {
2122         struct ast_variable *res;
2123         va_list ap;
2124
2125         va_start(ap, family);
2126         res = ast_load_realtime_helper(family, ap);
2127         va_end(ap);
2128
2129         return res;
2130 }
2131
2132 struct ast_variable *ast_load_realtime(const char *family, ...)
2133 {
2134         struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
2135         va_list ap;
2136
2137         va_start(ap, family);
2138         res = ast_load_realtime_helper(family, ap);
2139         va_end(ap);
2140
2141         /* Eliminate blank entries */
2142         for (cur = res; cur; cur = cur->next) {
2143                 if (freeme) {
2144                         ast_free(freeme);
2145                         freeme = NULL;
2146                 }
2147
2148                 if (ast_strlen_zero(cur->value)) {
2149                         if (prev)
2150                                 prev->next = cur->next;
2151                         else
2152                                 res = cur->next;
2153                         freeme = cur;
2154                 } else if (cur->value[0] == ' ' && cur->value[1] == '\0') {
2155                         char *vptr = (char *) cur->value;
2156                         vptr[0] = '\0';
2157                         prev = cur;
2158                 } else {
2159                         prev = cur;
2160                 }
2161         }
2162         return res;
2163 }
2164
2165 /*! \brief Check if realtime engine is configured for family */
2166 int ast_check_realtime(const char *family)
2167 {
2168         struct ast_config_engine *eng;
2169         if (!ast_realtime_enabled()) {
2170                 return 0;       /* There are no engines at all so fail early */
2171         }
2172
2173         eng = find_engine(family, NULL, 0, NULL, 0);
2174         if (eng)
2175                 return 1;
2176         return 0;
2177 }
2178
2179 /*! \brief Check if there's any realtime engines loaded */
2180 int ast_realtime_enabled()
2181 {
2182         return config_maps ? 1 : 0;
2183 }
2184
2185 int ast_realtime_require_field(const char *family, ...)
2186 {
2187         struct ast_config_engine *eng;
2188         char db[256];
2189         char table[256];
2190         va_list ap;
2191         int res = -1;
2192
2193         va_start(ap, family);
2194         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2195         if (eng && eng->require_func) {
2196                 res = eng->require_func(db, table, ap);
2197         }
2198         va_end(ap);
2199
2200         return res;
2201 }
2202
2203 int ast_unload_realtime(const char *family)
2204 {
2205         struct ast_config_engine *eng;
2206         char db[256];
2207         char table[256];
2208         int res = -1;
2209
2210         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2211         if (eng && eng->unload_func) {
2212                 res = eng->unload_func(db, table);
2213         }
2214         return res;
2215 }
2216
2217 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
2218 {
2219         struct ast_config_engine *eng;
2220         char db[256];
2221         char table[256];
2222         struct ast_config *res = NULL;
2223         va_list ap;
2224
2225         va_start(ap, family);
2226         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2227         if (eng && eng->realtime_multi_func) 
2228                 res = eng->realtime_multi_func(db, table, ap);
2229         va_end(ap);
2230
2231         return res;
2232 }
2233
2234 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2235 {
2236         struct ast_config_engine *eng;
2237         int res = -1;
2238         char db[256];
2239         char table[256];
2240         va_list ap;
2241
2242         va_start(ap, lookup);
2243         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2244         if (eng && eng->update_func) 
2245                 res = eng->update_func(db, table, keyfield, lookup, ap);
2246         va_end(ap);
2247
2248         return res;
2249 }
2250
2251 int ast_update2_realtime(const char *family, ...)
2252 {
2253         struct ast_config_engine *eng;
2254         int res = -1;
2255         char db[256];
2256         char table[256];
2257         va_list ap;
2258
2259         va_start(ap, family);
2260         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2261         if (eng && eng->update2_func) 
2262                 res = eng->update2_func(db, table, ap);
2263         va_end(ap);
2264
2265         return res;
2266 }
2267
2268 int ast_store_realtime(const char *family, ...)
2269 {
2270         struct ast_config_engine *eng;
2271         int res = -1;
2272         char db[256];
2273         char table[256];
2274         va_list ap;
2275
2276         va_start(ap, family);
2277         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2278         if (eng && eng->store_func) 
2279                 res = eng->store_func(db, table, ap);
2280         va_end(ap);
2281
2282         return res;
2283 }
2284
2285 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2286 {
2287         struct ast_config_engine *eng;
2288         int res = -1;
2289         char db[256];
2290         char table[256];
2291         va_list ap;
2292
2293         va_start(ap, lookup);
2294         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2295         if (eng && eng->destroy_func) 
2296                 res = eng->destroy_func(db, table, keyfield, lookup, ap);
2297         va_end(ap);
2298
2299         return res;
2300 }
2301
2302 /*! \brief Helper function to parse arguments
2303  * See documentation in config.h
2304  */
2305 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2306         void *p_result, ...)
2307 {
2308         va_list ap;
2309         int error = 0;
2310
2311         va_start(ap, p_result);
2312         switch (flags & PARSE_TYPE) {
2313         case PARSE_INT32:
2314             {
2315                 int32_t *result = p_result;
2316                 int32_t x, def = result ? *result : 0,
2317                         high = (int32_t)0x7fffffff,
2318                         low  = (int32_t)0x80000000;
2319                 /* optional argument: first default value, then range */
2320                 if (flags & PARSE_DEFAULT)
2321                         def = va_arg(ap, int32_t);
2322                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2323                         /* range requested, update bounds */
2324                         low = va_arg(ap, int32_t);
2325                         high = va_arg(ap, int32_t);
2326                 }
2327                 x = strtol(arg, NULL, 0);
2328                 error = (x < low) || (x > high);
2329                 if (flags & PARSE_OUT_RANGE)
2330                         error = !error;
2331                 if (result)
2332                         *result  = error ? def : x;
2333                 ast_debug(3,
2334                         "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
2335                         arg, low, high,
2336                         result ? *result : x, error);
2337                 break;
2338             }
2339
2340         case PARSE_UINT32:
2341             {
2342                 uint32_t *result = p_result;
2343                 uint32_t x, def = result ? *result : 0,
2344                         low = 0, high = (uint32_t)~0;
2345                 /* optional argument: first default value, then range */
2346                 if (flags & PARSE_DEFAULT)
2347                         def = va_arg(ap, uint32_t);
2348                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2349                         /* range requested, update bounds */
2350                         low = va_arg(ap, uint32_t);
2351                         high = va_arg(ap, uint32_t);
2352                 }
2353                 x = strtoul(arg, NULL, 0);
2354                 error = (x < low) || (x > high);
2355                 if (flags & PARSE_OUT_RANGE)
2356                         error = !error;
2357                 if (result)
2358                         *result  = error ? def : x;
2359                 ast_debug(3,
2360                         "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
2361                         arg, low, high,
2362                         result ? *result : x, error);
2363                 break;
2364             }
2365
2366         case PARSE_DOUBLE:
2367             {
2368                 double *result = p_result;
2369                 double x, def = result ? *result : 0,
2370                         low = -HUGE_VAL, high = HUGE_VAL;
2371
2372                 /* optional argument: first default value, then range */
2373                 if (flags & PARSE_DEFAULT)
2374                         def = va_arg(ap, double);
2375                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2376                         /* range requested, update bounds */
2377                         low = va_arg(ap, double);
2378                         high = va_arg(ap, double);
2379                 }
2380                 x = strtod(arg, NULL);
2381                 error = (x < low) || (x > high);
2382                 if (flags & PARSE_OUT_RANGE)
2383                         error = !error;
2384                 if (result)
2385                         *result  = error ? def : x;
2386                 ast_debug(3,
2387                         "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
2388                         arg, low, high,
2389                         result ? *result : x, error);
2390                 break;
2391             }
2392         case PARSE_INADDR:
2393             {
2394                 char *port, *buf;
2395                 struct sockaddr_in _sa_buf;     /* buffer for the result */
2396                 struct sockaddr_in *sa = p_result ?
2397                         (struct sockaddr_in *)p_result : &_sa_buf;
2398                 /* default is either the supplied value or the result itself */
2399                 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
2400                         va_arg(ap, struct sockaddr_in *) : sa;
2401                 struct hostent *hp;
2402                 struct ast_hostent ahp;
2403
2404                 memset(&_sa_buf, '\0', sizeof(_sa_buf)); /* clear buffer */
2405                 /* duplicate the string to strip away the :port */
2406                 port = ast_strdupa(arg);
2407                 buf = strsep(&port, ":");
2408                 sa->sin_family = AF_INET;       /* assign family */
2409                 /*
2410                  * honor the ports flag setting, assign default value
2411                  * in case of errors or field unset.
2412                  */
2413                 flags &= PARSE_PORT_MASK; /* the only flags left to process */
2414                 if (port) {
2415                         if (flags == PARSE_PORT_FORBID) {
2416                                 error = 1;      /* port was forbidden */
2417                                 sa->sin_port = def->sin_port;
2418                         } else if (flags == PARSE_PORT_IGNORE)
2419                                 sa->sin_port = def->sin_port;
2420                         else /* accept or require */
2421                                 sa->sin_port = htons(strtol(port, NULL, 0));
2422                 } else {
2423                         sa->sin_port = def->sin_port;
2424                         if (flags == PARSE_PORT_REQUIRE)
2425                                 error = 1;
2426                 }
2427                 /* Now deal with host part, even if we have errors before. */
2428                 hp = ast_gethostbyname(buf, &ahp);
2429                 if (hp) /* resolved successfully */
2430                         memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
2431                 else {
2432                         error = 1;
2433                         sa->sin_addr = def->sin_addr;
2434                 }
2435                 ast_debug(3,
2436                         "extract inaddr from [%s] gives [%s:%d](%d)\n",
2437                         arg, ast_inet_ntoa(sa->sin_addr),
2438                         ntohs(sa->sin_port), error);
2439                 break;
2440             }
2441         }
2442         va_end(ap);
2443         return error;
2444 }
2445
2446 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2447 {
2448         struct ast_config_engine *eng;
2449         struct ast_config_map *map;
2450
2451         switch (cmd) {
2452         case CLI_INIT:
2453                 e->command = "core show config mappings";
2454                 e->usage =
2455                         "Usage: core show config mappings\n"
2456                         "       Shows the filenames to config engines.\n";
2457                 return NULL;
2458         case CLI_GENERATE:
2459                 return NULL;
2460         }
2461         
2462         ast_mutex_lock(&config_lock);
2463
2464         if (!config_engine_list) {
2465                 ast_cli(a->fd, "No config mappings found.\n");
2466         } else {
2467                 ast_cli(a->fd, "\n\n");
2468                 for (eng = config_engine_list; eng; eng = eng->next) {
2469                         ast_cli(a->fd, "\nConfig Engine: %s\n", eng->name);
2470                         for (map = config_maps; map; map = map->next) {
2471                                 if (!strcasecmp(map->driver, eng->name)) {
2472                                         ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
2473                                                         map->table ? map->table : map->name);
2474                                 }
2475                         }
2476                 }
2477                 ast_cli(a->fd,"\n\n");
2478         }
2479         
2480         ast_mutex_unlock(&config_lock);
2481
2482         return CLI_SUCCESS;
2483 }
2484
2485 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2486 {
2487         struct cache_file_mtime *cfmtime;
2488         char *prev = "", *completion_value = NULL;
2489         int wordlen, which = 0;
2490
2491         switch (cmd) {
2492         case CLI_INIT:
2493                 e->command = "config reload";
2494                 e->usage =
2495                         "Usage: config reload <filename.conf>\n"
2496                         "   Reloads all modules that reference <filename.conf>\n";
2497                 return NULL;
2498         case CLI_GENERATE:
2499                 if (a->pos > 2) {
2500                         return NULL;
2501                 }
2502
2503                 wordlen = strlen(a->word);
2504
2505                 AST_LIST_LOCK(&cfmtime_head);
2506                 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2507                         /* Skip duplicates - this only works because the list is sorted by filename */
2508                         if (strcmp(cfmtime->filename, prev) == 0) {
2509                                 continue;
2510                         }
2511
2512                         /* Core configs cannot be reloaded */
2513                         if (ast_strlen_zero(cfmtime->who_asked)) {
2514                                 continue;
2515                         }
2516
2517                         if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
2518                                 completion_value = ast_strdup(cfmtime->filename);
2519                                 break;
2520                         }
2521
2522                         /* Otherwise save that we've seen this filename */
2523                         prev = cfmtime->filename;
2524                 }
2525                 AST_LIST_UNLOCK(&cfmtime_head);
2526
2527                 return completion_value;
2528         }
2529
2530         if (a->argc != 3) {
2531                 return CLI_SHOWUSAGE;
2532         }
2533
2534         AST_LIST_LOCK(&cfmtime_head);
2535         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2536                 if (!strcmp(cfmtime->filename, a->argv[2])) {
2537                         char *buf = alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
2538                         sprintf(buf, "module reload %s", cfmtime->who_asked);
2539                         ast_cli_command(a->fd, buf);
2540                 }
2541         }
2542         AST_LIST_UNLOCK(&cfmtime_head);
2543
2544         return CLI_SUCCESS;
2545 }
2546
2547 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2548 {
2549         struct cache_file_mtime *cfmtime;
2550
2551         switch (cmd) {
2552         case CLI_INIT:
2553                 e->command = "config list";
2554                 e->usage =
2555                         "Usage: config list\n"
2556                         "   Show all modules that have loaded a configuration file\n";
2557                 return NULL;
2558         case CLI_GENERATE:
2559                 return NULL;
2560         }
2561
2562         AST_LIST_LOCK(&cfmtime_head);
2563         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2564                 ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
2565         }
2566         AST_LIST_UNLOCK(&cfmtime_head);
2567
2568         return CLI_SUCCESS;
2569 }
2570
2571 static struct ast_cli_entry cli_config[] = {
2572         AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
2573         AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
2574         AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
2575 };
2576
2577 int register_config_cli() 
2578 {
2579         ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));
2580         return 0;
2581 }