Merged revisions 219023 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 void ast_variables_destroy(struct ast_variable *v)
386 {
387         struct ast_variable *vn;
388
389         while (v) {
390                 vn = v;
391                 v = v->next;
392                 ast_free(vn);
393         }
394 }
395
396 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
397 {
398         struct ast_category *cat = NULL;
399
400         if (category && config->last_browse && (config->last_browse->name == category)) {
401                 cat = config->last_browse;
402         } else {
403                 cat = ast_category_get(config, category);
404         }
405
406         return (cat) ? cat->root : NULL;
407 }
408
409 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
410 {
411         const char *tmp;
412         tmp = ast_variable_retrieve(cfg, cat, var);
413         if (!tmp) {
414                 tmp = ast_variable_retrieve(cfg, "general", var);
415         }
416         return tmp;
417 }
418
419
420 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
421 {
422         struct ast_variable *v;
423
424         if (category) {
425                 for (v = ast_variable_browse(config, category); v; v = v->next) {
426                         if (!strcasecmp(variable, v->name)) {
427                                 return v->value;
428                         }
429                 }
430         } else {
431                 struct ast_category *cat;
432
433                 for (cat = config->root; cat; cat = cat->next) {
434                         for (v = cat->root; v; v = v->next) {
435                                 if (!strcasecmp(variable, v->name)) {
436                                         return v->value;
437                                 }
438                         }
439                 }
440         }
441
442         return NULL;
443 }
444
445 static struct ast_variable *variable_clone(const struct ast_variable *old)
446 {
447         struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
448
449         if (new) {
450                 new->lineno = old->lineno;
451                 new->object = old->object;
452                 new->blanklines = old->blanklines;
453                 /* TODO: clone comments? */
454         }
455
456         return new;
457 }
458  
459 static void move_variables(struct ast_category *old, struct ast_category *new)
460 {
461         struct ast_variable *var = old->root;
462
463         old->root = NULL;
464         /* we can just move the entire list in a single op */
465         ast_variable_append(new, var);
466 }
467
468 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno) 
469 {
470         struct ast_category *category;
471
472         if ((category = ast_calloc(1, sizeof(*category))))
473                 ast_copy_string(category->name, name, sizeof(category->name));
474         category->file = strdup(in_file);
475         category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
476         return category;
477 }
478
479 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
480 {
481         struct ast_category *cat;
482
483         /* try exact match first, then case-insensitive match */
484         for (cat = config->root; cat; cat = cat->next) {
485                 if (cat->name == category_name && (ignored || !cat->ignored))
486                         return cat;
487         }
488
489         for (cat = config->root; cat; cat = cat->next) {
490                 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
491                         return cat;
492         }
493
494         return NULL;
495 }
496
497 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
498 {
499         return category_get(config, category_name, 0);
500 }
501
502 int ast_category_exist(const struct ast_config *config, const char *category_name)
503 {
504         return !!ast_category_get(config, category_name);
505 }
506
507 void ast_category_append(struct ast_config *config, struct ast_category *category)
508 {
509         if (config->last)
510                 config->last->next = category;
511         else
512                 config->root = category;
513         category->include_level = config->include_level;
514         config->last = category;
515         config->current = category;
516 }
517
518 void ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
519 {
520         struct ast_category *cur_category;
521
522         if (!cat || !match)
523                 return;
524         if (!strcasecmp(config->root->name, match)) {
525                 cat->next = config->root;
526                 config->root = cat;
527                 return;
528         } 
529         for (cur_category = config->root; cur_category; cur_category = cur_category->next) {
530                 if (!strcasecmp(cur_category->next->name, match)) {
531                         cat->next = cur_category->next;
532                         cur_category->next = cat;
533                         break;
534                 }
535         }
536 }
537
538 static void ast_destroy_comments(struct ast_category *cat)
539 {
540         struct ast_comment *n, *p;
541
542         for (p=cat->precomments; p; p=n) {
543                 n = p->next;
544                 free(p);
545         }
546         for (p=cat->sameline; p; p=n) {
547                 n = p->next;
548                 free(p);
549         }
550         for (p=cat->trailing; p; p=n) {
551                 n = p->next;
552                 free(p);
553         }
554         cat->precomments = NULL;
555         cat->sameline = NULL;
556         cat->trailing = NULL;
557 }
558
559 static void ast_destroy_template_list(struct ast_category *cat)
560 {
561         struct ast_category_template_instance *x;
562
563         while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
564                 free(x);
565 }
566
567 void ast_category_destroy(struct ast_category *cat)
568 {
569         ast_variables_destroy(cat->root);
570         if (cat->file) {
571                 free(cat->file);
572                 cat->file = 0;
573         }
574         ast_destroy_comments(cat);
575         ast_destroy_template_list(cat);
576         ast_free(cat);
577 }
578
579 static void ast_includes_destroy(struct ast_config_include *incls)
580 {
581         struct ast_config_include *incl,*inclnext;
582         
583         for (incl=incls; incl; incl = inclnext) {
584                 inclnext = incl->next;
585                 if (incl->include_location_file)
586                         free(incl->include_location_file);
587                 if (incl->exec_file)
588                         free(incl->exec_file);
589                 if (incl->included_file)
590                         free(incl->included_file);
591                 free(incl);
592         }
593 }
594
595 static struct ast_category *next_available_category(struct ast_category *cat)
596 {
597         for (; cat && cat->ignored; cat = cat->next);
598
599         return cat;
600 }
601
602 /*! return the first var of a category */
603 struct ast_variable *ast_category_first(struct ast_category *cat)
604 {
605         return (cat) ? cat->root : NULL;
606 }
607
608 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
609 {
610         struct ast_category *category = ast_category_get(config, cat);
611
612         if (category)
613                 return category->root;
614         return NULL;
615 }
616
617 char *ast_category_browse(struct ast_config *config, const char *prev)
618 {       
619         struct ast_category *cat = NULL;
620
621         if (prev && config->last_browse && (config->last_browse->name == prev))
622                 cat = config->last_browse->next;
623         else if (!prev && config->root)
624                 cat = config->root;
625         else if (prev) {
626                 for (cat = config->root; cat; cat = cat->next) {
627                         if (cat->name == prev) {
628                                 cat = cat->next;
629                                 break;
630                         }
631                 }
632                 if (!cat) {
633                         for (cat = config->root; cat; cat = cat->next) {
634                                 if (!strcasecmp(cat->name, prev)) {
635                                         cat = cat->next;
636                                         break;
637                                 }
638                         }
639                 }
640         }
641         
642         if (cat)
643                 cat = next_available_category(cat);
644
645         config->last_browse = cat;
646         return (cat) ? cat->name : NULL;
647 }
648
649 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
650 {
651         struct ast_variable *v;
652
653         v = cat->root;
654         cat->root = NULL;
655         cat->last = NULL;
656
657         return v;
658 }
659
660 void ast_category_rename(struct ast_category *cat, const char *name)
661 {
662         ast_copy_string(cat->name, name, sizeof(cat->name));
663 }
664
665 static void inherit_category(struct ast_category *new, const struct ast_category *base)
666 {
667         struct ast_variable *var;
668         struct ast_category_template_instance *x = ast_calloc(1,sizeof(struct ast_category_template_instance));
669
670         strcpy(x->name, base->name);
671         x->inst = base;
672         AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
673         for (var = base->root; var; var = var->next)
674                 ast_variable_append(new, variable_clone(var));
675 }
676
677 struct ast_config *ast_config_new(void) 
678 {
679         struct ast_config *config;
680
681         if ((config = ast_calloc(1, sizeof(*config))))
682                 config->max_include_level = MAX_INCLUDE_LEVEL;
683         return config;
684 }
685
686 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
687 {
688         struct ast_variable *cur, *prev=NULL, *curn;
689         int res = -1;
690         int lineno = 0;
691
692         cur = category->root;
693         while (cur) {
694                 if (cur->name == variable) {
695                         if (prev) {
696                                 prev->next = cur->next;
697                                 if (cur == category->last)
698                                         category->last = prev;
699                         } else {
700                                 category->root = cur->next;
701                                 if (cur == category->last)
702                                         category->last = NULL;
703                         }
704                         cur->next = NULL;
705                         ast_variables_destroy(cur);
706                         return 0;
707                 }
708                 prev = cur;
709                 cur = cur->next;
710         }
711
712         prev = NULL;
713         cur = category->root;
714         while (cur) {
715                 curn = cur->next;
716                 if ((!ast_strlen_zero(line) && lineno == atoi(line)) || (ast_strlen_zero(line) && !strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
717                         if (prev) {
718                                 prev->next = cur->next;
719                                 if (cur == category->last)
720                                         category->last = prev;
721                         } else {
722                                 category->root = cur->next;
723                                 if (cur == category->last)
724                                         category->last = NULL;
725                         }
726                         cur->next = NULL;
727                         ast_variables_destroy(cur);
728                         res = 0;
729                 } else
730                         prev = cur;
731
732                 cur = curn;
733                 lineno++;
734         }
735         return res;
736 }
737
738 int ast_variable_update(struct ast_category *category, const char *variable, 
739                                                 const char *value, const char *match, unsigned int object)
740 {
741         struct ast_variable *cur, *prev=NULL, *newer=NULL;
742
743         for (cur = category->root; cur; prev = cur, cur = cur->next) {
744                 if (strcasecmp(cur->name, variable) ||
745                         (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
746                         continue;
747
748                 if (!(newer = ast_variable_new(variable, value, cur->file)))
749                         return -1;
750         
751                 newer->next = cur->next;
752                 newer->object = cur->object || object;
753
754                 /* Preserve everything */
755                 newer->lineno = cur->lineno;
756                 newer->blanklines = cur->blanklines;
757                 newer->precomments = cur->precomments; cur->precomments = NULL;
758                 newer->sameline = cur->sameline; cur->sameline = NULL;
759                 newer->trailing = cur->trailing; cur->trailing = NULL;
760
761                 if (prev)
762                         prev->next = newer;
763                 else
764                         category->root = newer;
765                 if (category->last == cur)
766                         category->last = newer;
767
768                 cur->next = NULL;
769                 ast_variables_destroy(cur);
770
771                 return 0;
772         }
773
774         /* Could not find variable to update */
775         return -1;
776 }
777
778 int ast_category_delete(struct ast_config *cfg, const char *category)
779 {
780         struct ast_category *prev=NULL, *cat;
781
782         cat = cfg->root;
783         while (cat) {
784                 if (cat->name == category) {
785                         if (prev) {
786                                 prev->next = cat->next;
787                                 if (cat == cfg->last)
788                                         cfg->last = prev;
789                         } else {
790                                 cfg->root = cat->next;
791                                 if (cat == cfg->last)
792                                         cfg->last = NULL;
793                         }
794                         ast_category_destroy(cat);
795                         return 0;
796                 }
797                 prev = cat;
798                 cat = cat->next;
799         }
800
801         prev = NULL;
802         cat = cfg->root;
803         while (cat) {
804                 if (!strcasecmp(cat->name, category)) {
805                         if (prev) {
806                                 prev->next = cat->next;
807                                 if (cat == cfg->last)
808                                         cfg->last = prev;
809                         } else {
810                                 cfg->root = cat->next;
811                                 if (cat == cfg->last)
812                                         cfg->last = NULL;
813                         }
814                         ast_category_destroy(cat);
815                         return 0;
816                 }
817                 prev = cat;
818                 cat = cat->next;
819         }
820         return -1;
821 }
822
823 int ast_category_empty(struct ast_config *cfg, const char *category)
824 {
825         struct ast_category *cat;
826
827         for (cat = cfg->root; cat; cat = cat->next) {
828                 if (!strcasecmp(cat->name, category))
829                         continue;
830                 ast_variables_destroy(cat->root);
831                 cat->root = NULL;
832                 cat->last = NULL;
833                 return 0;
834         }
835
836         return -1;
837 }
838
839 void ast_config_destroy(struct ast_config *cfg)
840 {
841         struct ast_category *cat, *catn;
842
843         if (!cfg)
844                 return;
845
846         ast_includes_destroy(cfg->includes);
847
848         cat = cfg->root;
849         while (cat) {
850                 catn = cat;
851                 cat = cat->next;
852                 ast_category_destroy(catn);
853         }
854         ast_free(cfg);
855 }
856
857 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
858 {
859         return cfg->current;
860 }
861
862 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
863 {
864         /* cast below is just to silence compiler warning about dropping "const" */
865         cfg->current = (struct ast_category *) cat;
866 }
867
868 enum config_cache_attribute_enum {
869         ATTRIBUTE_INCLUDE = 0,
870         ATTRIBUTE_EXEC = 1,
871 };
872
873 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
874 {
875         struct cache_file_mtime *cfmtime;
876         struct cache_file_include *cfinclude;
877         struct stat statbuf = { 0, };
878
879         /* Find our cached entry for this configuration file */
880         AST_LIST_LOCK(&cfmtime_head);
881         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
882                 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
883                         break;
884         }
885         if (!cfmtime) {
886                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1 + strlen(who_asked) + 1);
887                 if (!cfmtime) {
888                         AST_LIST_UNLOCK(&cfmtime_head);
889                         return;
890                 }
891                 AST_LIST_HEAD_INIT(&cfmtime->includes);
892                 strcpy(cfmtime->filename, configfile);
893                 cfmtime->who_asked = cfmtime->filename + strlen(configfile) + 1;
894                 strcpy(cfmtime->who_asked, who_asked);
895                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
896                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
897         }
898
899         if (!stat(configfile, &statbuf))
900                 cfmtime->mtime = 0;
901         else
902                 cfmtime->mtime = statbuf.st_mtime;
903
904         switch (attrtype) {
905         case ATTRIBUTE_INCLUDE:
906                 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
907                         if (!strcmp(cfinclude->include, filename)) {
908                                 AST_LIST_UNLOCK(&cfmtime_head);
909                                 return;
910                         }
911                 }
912                 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
913                 if (!cfinclude) {
914                         AST_LIST_UNLOCK(&cfmtime_head);
915                         return;
916                 }
917                 strcpy(cfinclude->include, filename);
918                 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
919                 break;
920         case ATTRIBUTE_EXEC:
921                 cfmtime->has_exec = 1;
922                 break;
923         }
924         AST_LIST_UNLOCK(&cfmtime_head);
925 }
926
927 /*! \brief parse one line in the configuration.
928  * \verbatim
929  * We can have a category header        [foo](...)
930  * a directive                          #include / #exec
931  * or a regular line                    name = value
932  * \endverbatim
933  */
934 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
935         char *buf, int lineno, const char *configfile, struct ast_flags flags,
936         struct ast_str *comment_buffer,
937         struct ast_str *lline_buffer,
938         const char *suggested_include_file,
939         struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
940 {
941         char *c;
942         char *cur = buf;
943         struct ast_variable *v;
944         char cmd[512], exec_file[512];
945
946         /* Actually parse the entry */
947         if (cur[0] == '[') { /* A category header */
948                 /* format is one of the following:
949                  * [foo]        define a new category named 'foo'
950                  * [foo](!)     define a new template category named 'foo'
951                  * [foo](+)     append to category 'foo', error if foo does not exist.
952                  * [foo](a)     define a new category and inherit from template a.
953                  *              You can put a comma-separated list of templates and '!' and '+'
954                  *              between parentheses, with obvious meaning.
955                  */
956                 struct ast_category *newcat = NULL;
957                 char *catname;
958
959                 c = strchr(cur, ']');
960                 if (!c) {
961                         ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
962                         return -1;
963                 }
964                 *c++ = '\0';
965                 cur++;
966                 if (*c++ != '(')
967                         c = NULL;
968                 catname = cur;
969                 if (!(*cat = newcat = ast_category_new(catname,
970                                 S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
971                                 lineno))) {
972                         return -1;
973                 }
974                 (*cat)->lineno = lineno;
975                 *last_var = 0;
976                 *last_cat = newcat;
977                 
978                 /* add comments */
979                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
980                         newcat->precomments = ALLOC_COMMENT(comment_buffer);
981                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
982                         newcat->sameline = ALLOC_COMMENT(lline_buffer);
983                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
984                         CB_RESET(comment_buffer, lline_buffer);
985                 
986                 /* If there are options or categories to inherit from, process them now */
987                 if (c) {
988                         if (!(cur = strchr(c, ')'))) {
989                                 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
990                                 return -1;
991                         }
992                         *cur = '\0';
993                         while ((cur = strsep(&c, ","))) {
994                                 if (!strcasecmp(cur, "!")) {
995                                         (*cat)->ignored = 1;
996                                 } else if (!strcasecmp(cur, "+")) {
997                                         *cat = category_get(cfg, catname, 1);
998                                         if (!(*cat)) {
999                                                 if (newcat)
1000                                                         ast_category_destroy(newcat);
1001                                                 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
1002                                                 return -1;
1003                                         }
1004                                         if (newcat) {
1005                                                 move_variables(newcat, *cat);
1006                                                 ast_category_destroy(newcat);
1007                                                 newcat = NULL;
1008                                         }
1009                                 } else {
1010                                         struct ast_category *base;
1011                                 
1012                                         base = category_get(cfg, cur, 1);
1013                                         if (!base) {
1014                                                 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
1015                                                 return -1;
1016                                         }
1017                                         inherit_category(*cat, base);
1018                                 }
1019                         }
1020                 }
1021                 if (newcat)
1022                         ast_category_append(cfg, *cat);
1023         } else if (cur[0] == '#') { /* A directive - #include or #exec */
1024                 char *cur2;
1025                 char real_inclusion_name[256];
1026                 struct ast_config_include *inclu;
1027                 int do_include = 0;     /* otherwise, it is exec */
1028
1029                 cur++;
1030                 c = cur;
1031                 while (*c && (*c > 32)) {
1032                         c++;
1033                 }
1034
1035                 if (*c) {
1036                         *c = '\0';
1037                         /* Find real argument */
1038                         c = ast_skip_blanks(c + 1);
1039                         if (!(*c)) {
1040                                 c = NULL;
1041                         }
1042                 } else 
1043                         c = NULL;
1044                 if (!strcasecmp(cur, "include")) {
1045                         do_include = 1;
1046                 } else if (!strcasecmp(cur, "exec")) {
1047                         if (!ast_opt_exec_includes) {
1048                                 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
1049                                 return 0;       /* XXX is this correct ? or we should return -1 ? */
1050                         }
1051                 } else {
1052                         ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
1053                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1054                 }
1055
1056                 if (c == NULL) {
1057                         ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", 
1058                                         do_include ? "include" : "exec",
1059                                         do_include ? "filename" : "/path/to/executable",
1060                                         lineno,
1061                                         configfile);
1062                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1063                 }
1064
1065                 cur = c;
1066                 /* Strip off leading and trailing "'s and <>'s */
1067                 if (*c == '"') {
1068                         /* Dequote */
1069                         while (*c) {
1070                                 if (*c == '"') {
1071                                         strcpy(c, c + 1); /* SAFE */
1072                                         c--;
1073                                 } else if (*c == '\\') {
1074                                         strcpy(c, c + 1); /* SAFE */
1075                                 }
1076                                 c++;
1077                         }
1078                 } else if (*c == '<') {
1079                         /* C-style include */
1080                         if (*(c + strlen(c) - 1) == '>') {
1081                                 cur++;
1082                                 *(c + strlen(c) - 1) = '\0';
1083                         }
1084                 }
1085                 cur2 = cur;
1086
1087                 /* #exec </path/to/executable>
1088                    We create a tmp file, then we #include it, then we delete it. */
1089                 if (!do_include) {
1090                         struct timeval now = ast_tvnow();
1091                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1092                                 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
1093                         snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
1094                         snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
1095                         ast_safe_system(cmd);
1096                         cur = exec_file;
1097                 } else {
1098                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1099                                 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
1100                         exec_file[0] = '\0';
1101                 }
1102                 /* A #include */
1103                 /* record this inclusion */
1104                 inclu = ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1105
1106                 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
1107                 if (!ast_strlen_zero(exec_file))
1108                         unlink(exec_file);
1109                 if (!do_include) {
1110                         ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1111                         return -1;
1112                 }
1113                 /* XXX otherwise what ? the default return is 0 anyways */
1114
1115         } else {
1116                 /* Just a line (variable = value) */
1117                 int object = 0;
1118                 if (!(*cat)) {
1119                         ast_log(LOG_WARNING,
1120                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
1121                         return -1;
1122                 }
1123                 c = strchr(cur, '=');
1124
1125                 if (c && c > cur && (*(c - 1) == '+')) {
1126                         struct ast_variable *var, *replace = NULL;
1127                         struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
1128
1129                         if (!str || !*str) {
1130                                 return -1;
1131                         }
1132
1133                         *(c - 1) = '\0';
1134                         c++;
1135                         cur = ast_strip(cur);
1136
1137                         /* Must iterate through category until we find last variable of same name (since there could be multiple) */
1138                         for (var = ast_category_first(*cat); var; var = var->next) {
1139                                 if (!strcmp(var->name, cur)) {
1140                                         replace = var;
1141                                 }
1142                         }
1143
1144                         if (!replace) {
1145                                 /* Nothing to replace; just set a variable normally. */
1146                                 goto set_new_variable;
1147                         }
1148
1149                         ast_str_set(str, 0, "%s", replace->value);
1150                         ast_str_append(str, 0, "%s", c);
1151                         ast_str_trim_blanks(*str);
1152                         ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
1153                 } else if (c) {
1154                         *c = 0;
1155                         c++;
1156                         /* Ignore > in => */
1157                         if (*c== '>') {
1158                                 object = 1;
1159                                 c++;
1160                         }
1161 set_new_variable:
1162                         if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
1163                                 v->lineno = lineno;
1164                                 v->object = object;
1165                                 *last_cat = 0;
1166                                 *last_var = v;
1167                                 /* Put and reset comments */
1168                                 v->blanklines = 0;
1169                                 ast_variable_append(*cat, v);
1170                                 /* add comments */
1171                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1172                                         v->precomments = ALLOC_COMMENT(comment_buffer);
1173                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1174                                         v->sameline = ALLOC_COMMENT(lline_buffer);
1175                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1176                                         CB_RESET(comment_buffer, lline_buffer);
1177                                 
1178                         } else {
1179                                 return -1;
1180                         }
1181                 } else {
1182                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1183                 }
1184         }
1185         return 0;
1186 }
1187
1188 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)
1189 {
1190         char fn[256];
1191 #if defined(LOW_MEMORY)
1192         char buf[512];
1193 #else
1194         char buf[8192];
1195 #endif
1196         char *new_buf, *comment_p, *process_buf;
1197         FILE *f;
1198         int lineno=0;
1199         int comment = 0, nest[MAX_NESTED_COMMENTS];
1200         struct ast_category *cat = NULL;
1201         int count = 0;
1202         struct stat statbuf;
1203         struct cache_file_mtime *cfmtime = NULL;
1204         struct cache_file_include *cfinclude;
1205         struct ast_variable *last_var = 0;
1206         struct ast_category *last_cat = 0;
1207         /*! Growable string buffer */
1208         struct ast_str *comment_buffer = NULL;  /*!< this will be a comment collector.*/
1209         struct ast_str *lline_buffer = NULL;    /*!< A buffer for stuff behind the ; */
1210
1211         if (cfg)
1212                 cat = ast_config_get_current_category(cfg);
1213
1214         if (filename[0] == '/') {
1215                 ast_copy_string(fn, filename, sizeof(fn));
1216         } else {
1217                 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
1218         }
1219
1220         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1221                 comment_buffer = ast_str_create(CB_SIZE);
1222                 if (comment_buffer)
1223                         lline_buffer = ast_str_create(CB_SIZE);
1224                 if (!lline_buffer) {
1225                         if (comment_buffer)
1226                                 ast_free(comment_buffer);
1227                         ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1228                         return NULL;
1229                 }
1230         }
1231 #ifdef AST_INCLUDE_GLOB
1232         {
1233                 int glob_ret;
1234                 glob_t globbuf;
1235                 globbuf.gl_offs = 0;    /* initialize it to silence gcc */
1236                 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1237                 if (glob_ret == GLOB_NOSPACE)
1238                         ast_log(LOG_WARNING,
1239                                 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1240                 else if (glob_ret  == GLOB_ABORTED)
1241                         ast_log(LOG_WARNING,
1242                                 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1243                 else  {
1244                         /* loop over expanded files */
1245                         int i;
1246                         for (i=0; i<globbuf.gl_pathc; i++) {
1247                                 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1248 #endif
1249         /*
1250          * The following is not a loop, but just a convenient way to define a block
1251          * (using do { } while(0) ), and be able to exit from it with 'continue'
1252          * or 'break' in case of errors. Nice trick.
1253          */
1254         do {
1255                 if (stat(fn, &statbuf))
1256                         continue;
1257
1258                 if (!S_ISREG(statbuf.st_mode)) {
1259                         ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1260                         continue;
1261                 }
1262
1263                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1264                         /* Find our cached entry for this configuration file */
1265                         AST_LIST_LOCK(&cfmtime_head);
1266                         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1267                                 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
1268                                         break;
1269                         }
1270                         if (!cfmtime) {
1271                                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1 + strlen(who_asked) + 1);
1272                                 if (!cfmtime)
1273                                         continue;
1274                                 AST_LIST_HEAD_INIT(&cfmtime->includes);
1275                                 strcpy(cfmtime->filename, fn);
1276                                 cfmtime->who_asked = cfmtime->filename + strlen(fn) + 1;
1277                                 strcpy(cfmtime->who_asked, who_asked);
1278                                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1279                                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1280                         }
1281                 }
1282
1283                 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1284                         /* File is unchanged, what about the (cached) includes (if any)? */
1285                         int unchanged = 1;
1286                         AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1287                                 /* We must glob here, because if we did not, then adding a file to globbed directory would
1288                                  * incorrectly cause no reload to be necessary. */
1289                                 char fn2[256];
1290 #ifdef AST_INCLUDE_GLOB
1291                                 int glob_return;
1292                                 glob_t glob_buf = { .gl_offs = 0 };
1293                                 glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
1294                                 /* On error, we reparse */
1295                                 if (glob_return == GLOB_NOSPACE || glob_return  == GLOB_ABORTED)
1296                                         unchanged = 0;
1297                                 else  {
1298                                         /* loop over expanded files */
1299                                         int j;
1300                                         for (j = 0; j < glob_buf.gl_pathc; j++) {
1301                                                 ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
1302 #else
1303                                                 ast_copy_string(fn2, cfinclude->include);
1304 #endif
1305                                                 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
1306                                                         /* that second-to-last field needs to be looked at in this case... TODO */
1307                                                         unchanged = 0;
1308                                                         /* One change is enough to short-circuit and reload the whole shebang */
1309                                                         break;
1310                                                 }
1311 #ifdef AST_INCLUDE_GLOB
1312                                         }
1313                                 }
1314 #endif
1315                         }
1316
1317                         if (unchanged) {
1318                                 AST_LIST_UNLOCK(&cfmtime_head);
1319                                 return CONFIG_STATUS_FILEUNCHANGED;
1320                         }
1321                 }
1322                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1323                         AST_LIST_UNLOCK(&cfmtime_head);
1324
1325                 /* If cfg is NULL, then we just want an answer */
1326                 if (cfg == NULL)
1327                         return NULL;
1328
1329                 if (cfmtime)
1330                         cfmtime->mtime = statbuf.st_mtime;
1331
1332                 ast_verb(2, "Parsing '%s': ", fn);
1333                         fflush(stdout);
1334                 if (!(f = fopen(fn, "r"))) {
1335                         ast_debug(1, "No file to parse: %s\n", fn);
1336                         ast_verb(2, "Not found (%s)\n", strerror(errno));
1337                         continue;
1338                 }
1339                 count++;
1340                 /* If we get to this point, then we're loading regardless */
1341                 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1342                 ast_debug(1, "Parsing %s\n", fn);
1343                 ast_verb(2, "Found\n");
1344                 while (!feof(f)) {
1345                         lineno++;
1346                         if (fgets(buf, sizeof(buf), f)) {
1347                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
1348                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
1349                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
1350                                 }
1351                                 
1352                                 new_buf = buf;
1353                                 if (comment) 
1354                                         process_buf = NULL;
1355                                 else
1356                                         process_buf = buf;
1357                                 
1358                                 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"))) {
1359                                         /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1360                                         CB_ADD(&comment_buffer, "\n");       /* add a newline to the comment buffer */
1361                                         continue; /* go get a new line, then */
1362                                 }
1363                                 
1364                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1365                                         if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
1366                                                 /* Escaped semicolons aren't comments. */
1367                                                 new_buf = comment_p + 1;
1368                                         } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1369                                                 /* Meta-Comment start detected ";--" */
1370                                                 if (comment < MAX_NESTED_COMMENTS) {
1371                                                         *comment_p = '\0';
1372                                                         new_buf = comment_p + 3;
1373                                                         comment++;
1374                                                         nest[comment-1] = lineno;
1375                                                 } else {
1376                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1377                                                 }
1378                                         } else if ((comment_p >= new_buf + 2) &&
1379                                                    (*(comment_p - 1) == COMMENT_TAG) &&
1380                                                    (*(comment_p - 2) == COMMENT_TAG)) {
1381                                                 /* Meta-Comment end detected */
1382                                                 comment--;
1383                                                 new_buf = comment_p + 1;
1384                                                 if (!comment) {
1385                                                         /* Back to non-comment now */
1386                                                         if (process_buf) {
1387                                                                 /* Actually have to move what's left over the top, then continue */
1388                                                                 char *oldptr;
1389                                                                 oldptr = process_buf + strlen(process_buf);
1390                                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1391                                                                         CB_ADD(&comment_buffer, ";");
1392                                                                         CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
1393                                                                 }
1394                                                                 
1395                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1396                                                                 new_buf = oldptr;
1397                                                         } else
1398                                                                 process_buf = new_buf;
1399                                                 }
1400                                         } else {
1401                                                 if (!comment) {
1402                                                         /* If ; is found, and we are not nested in a comment, 
1403                                                            we immediately stop all comment processing */
1404                                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1405                                                                 CB_ADD(&lline_buffer, comment_p);
1406                                                         }
1407                                                         *comment_p = '\0'; 
1408                                                         new_buf = comment_p;
1409                                                 } else
1410                                                         new_buf = comment_p + 1;
1411                                         }
1412                                 }
1413                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
1414                                         CB_ADD(&comment_buffer, buf);  /* the whole line is a comment, store it */
1415                                 }
1416                                 
1417                                 if (process_buf) {
1418                                         char *buffer = ast_strip(process_buf);
1419                                         if (!ast_strlen_zero(buffer)) {
1420                                                 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
1421                                                         cfg = CONFIG_STATUS_FILEINVALID;
1422                                                         break;
1423                                                 }
1424                                         }
1425                                 }
1426                         }
1427                 }
1428                 /* end of file-- anything in a comment buffer? */
1429                 if (last_cat) {
1430                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1431                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1432                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
1433                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
1434                                 }
1435                                 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1436                         }
1437                 } else if (last_var) {
1438                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1439                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
1440                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
1441                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
1442                                 }
1443                                 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1444                         }
1445                 } else {
1446                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
1447                                 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
1448                         }
1449                 }
1450                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1451                         CB_RESET(comment_buffer, lline_buffer);
1452
1453                 fclose(f);
1454         } while (0);
1455         if (comment) {
1456                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1457         }
1458 #ifdef AST_INCLUDE_GLOB
1459                                         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
1460                                                 break;
1461                                         }
1462                                 }
1463                                 globfree(&globbuf);
1464                         }
1465                 }
1466 #endif
1467
1468         if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg != CONFIG_STATUS_FILEINVALID && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1469                 if (comment_buffer)
1470                         ast_free(comment_buffer);
1471                 if (lline_buffer)
1472                         ast_free(lline_buffer);
1473                 comment_buffer = NULL;
1474                 lline_buffer = NULL;
1475         }
1476         
1477         if (count == 0)
1478                 return NULL;
1479
1480         return cfg;
1481 }
1482
1483
1484 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1485    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1486    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1487    be shocked and mystified as to why things are not showing up in the files! 
1488
1489    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1490    and line number are stored for each include, plus the name of the file included, so that these statements may be
1491    included in the output files on a file_save operation. 
1492
1493    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1494    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1495    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1496    and a header gets added.
1497
1498    vars and category heads are output in the order they are stored in the config file. So, if the software
1499    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1500    file/lineno data probably won't get changed.
1501
1502 */
1503
1504 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1505 {
1506         char date[256]="";
1507         time_t t;
1508
1509         time(&t);
1510         ast_copy_string(date, ctime(&t), sizeof(date));
1511
1512         fprintf(f1, ";!\n");
1513         fprintf(f1, ";! Automatically generated configuration file\n");
1514         if (strcmp(configfile, fn))
1515                 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1516         else
1517                 fprintf(f1, ";! Filename: %s\n", configfile);
1518         fprintf(f1, ";! Generator: %s\n", generator);
1519         fprintf(f1, ";! Creation Date: %s", date);
1520         fprintf(f1, ";!\n");
1521 }
1522
1523 static void   inclfile_destroy(void *obj)
1524 {
1525         const struct inclfile *o = obj;
1526
1527         if (o->fname)
1528                 free(o->fname);
1529 }
1530
1531
1532 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
1533 {
1534         struct inclfile lookup;
1535         
1536         if (!file || file[0] == 0) {
1537                 if (configfile[0] == '/')
1538                         ast_copy_string(fn, configfile, fn_size);
1539                 else
1540                         snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1541         } else if (file[0] == '/') 
1542                 ast_copy_string(fn, file, fn_size);
1543         else
1544                 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
1545         lookup.fname = fn;
1546         *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
1547         if (!(*fi)) {
1548                 /* set up a file scratch pad */
1549                 struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
1550                 fx->fname = ast_strdup(fn);
1551                 fx->lineno = 1;
1552                 *fi = fx;
1553                 ao2_link(fileset, fx);
1554         }
1555 }
1556
1557 static int count_linefeeds(char *str)
1558 {
1559         int count = 0;
1560
1561         while (*str) {
1562                 if (*str =='\n')
1563                         count++;
1564                 str++;
1565         }
1566         return count;
1567 }
1568
1569 static int count_linefeeds_in_comments(struct ast_comment *x)
1570 {
1571         int count = 0;
1572
1573         while (x) {
1574                 count += count_linefeeds(x->cmt);
1575                 x = x->next;
1576         }
1577         return count;
1578 }
1579
1580 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1581 {
1582         int precomment_lines = count_linefeeds_in_comments(precomments);
1583         int i;
1584
1585         /* I don't have to worry about those ;! comments, they are
1586            stored in the precomments, but not printed back out.
1587            I did have to make sure that comments following
1588            the ;! header comments were not also deleted in the process */
1589         if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
1590                 return;
1591         } else if (lineno == 0) {
1592                 /* Line replacements also mess things up */
1593                 return;
1594         } else if (lineno - precomment_lines - fi->lineno < 5) {
1595                 /* Only insert less than 5 blank lines; if anything more occurs,
1596                  * it's probably due to context deletion. */
1597                 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
1598                         fprintf(fp, "\n");
1599                 }
1600         } else {
1601                 /* Deletion occurred - insert a single blank line, for separation of
1602                  * contexts. */
1603                 fprintf(fp, "\n");
1604         }
1605  
1606         fi->lineno = lineno + 1; /* Advance the file lineno */
1607 }
1608
1609 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1610 {
1611         return ast_config_text_file_save(configfile, cfg, generator);
1612 }
1613
1614 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1615 {
1616         FILE *f;
1617         char fn[256];
1618         struct ast_variable *var;
1619         struct ast_category *cat;
1620         struct ast_comment *cmt;
1621         struct ast_config_include *incl;
1622         int blanklines = 0;
1623         struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
1624         struct inclfile *fi = 0;
1625
1626         /* reset all the output flags, in case this isn't our first time saving this data */
1627
1628         for (incl=cfg->includes; incl; incl = incl->next)
1629                 incl->output = 0;
1630
1631         /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
1632            are all truncated to zero bytes and have that nice header*/
1633
1634         for (incl=cfg->includes; incl; incl = incl->next)
1635         {
1636                 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*/
1637                         FILE *f1;
1638
1639                         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 */
1640                         f1 = fopen(fn,"w");
1641                         if (f1) {
1642                                 gen_header(f1, configfile, fn, generator);
1643                                 fclose(f1); /* this should zero out the file */
1644                         } else {
1645                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1646                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1647                         }
1648                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1649                         fi = 0;
1650                 }
1651         }
1652
1653         set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
1654 #ifdef __CYGWIN__       
1655         if ((f = fopen(fn, "w+"))) {
1656 #else
1657         if ((f = fopen(fn, "w"))) {
1658 #endif      
1659                 ast_verb(2, "Saving '%s': ", fn);
1660                 gen_header(f, configfile, fn, generator);
1661                 cat = cfg->root;
1662                 fclose(f);
1663                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1664                 
1665                 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
1666                 /* since each var, cat, and associated comments can come from any file, we have to be 
1667                    mobile, and open each file, print, and close it on an entry-by-entry basis */
1668
1669                 while (cat) {
1670                         set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
1671                         f = fopen(fn, "a");
1672                         if (!f)
1673                         {
1674                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1675                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1676                                 ao2_ref(fileset, -1);
1677                                 return -1;
1678                         }
1679
1680                         /* dump any includes that happen before this category header */
1681                         for (incl=cfg->includes; incl; incl = incl->next) {
1682                                 if (strcmp(incl->include_location_file, cat->file) == 0){
1683                                         if (cat->lineno > incl->include_location_lineno && !incl->output) {
1684                                                 if (incl->exec)
1685                                                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1686                                                 else
1687                                                         fprintf(f,"#include \"%s\"\n", incl->included_file);
1688                                                 incl->output = 1;
1689                                         }
1690                                 }
1691                         }
1692
1693                         insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
1694                         /* Dump section with any appropriate comment */
1695                         for (cmt = cat->precomments; cmt; cmt=cmt->next) {
1696                                 char *cmtp = cmt->cmt;
1697                                 while (*cmtp == ';' && *(cmtp+1) == '!') {
1698                                         char *cmtp2 = strchr(cmtp+1, '\n');
1699                                         if (cmtp2)
1700                                                 cmtp = cmtp2+1;
1701                                         else cmtp = 0;
1702                                 }
1703                                 if (cmtp)
1704                                         fprintf(f,"%s", cmtp);
1705                         }
1706                         fprintf(f, "[%s]", cat->name);
1707                         if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
1708                                 fprintf(f, "(");
1709                                 if (cat->ignored) {
1710                                         fprintf(f, "!");
1711                                 }
1712                                 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
1713                                         fprintf(f, ",");
1714                                 }
1715                                 if (!AST_LIST_EMPTY(&cat->template_instances)) {
1716                                         struct ast_category_template_instance *x;
1717                                         AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1718                                                 fprintf(f,"%s",x->name);
1719                                                 if (x != AST_LIST_LAST(&cat->template_instances))
1720                                                         fprintf(f,",");
1721                                         }
1722                                 }
1723                                 fprintf(f, ")");
1724                         }
1725                         for(cmt = cat->sameline; cmt; cmt=cmt->next)
1726                         {
1727                                 fprintf(f,"%s", cmt->cmt);
1728                         }
1729                         if (!cat->sameline)
1730                                 fprintf(f,"\n");
1731                         for (cmt = cat->trailing; cmt; cmt=cmt->next) {
1732                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1733                                         fprintf(f,"%s", cmt->cmt);
1734                         }
1735                         fclose(f);
1736                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1737                         fi = 0;
1738                         
1739                         var = cat->root;
1740                         while (var) {
1741                                 struct ast_category_template_instance *x;
1742                                 int found = 0;
1743                                 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1744                                         struct ast_variable *v;
1745                                         for (v = x->inst->root; v; v = v->next) {
1746                                                 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
1747                                                         found = 1;
1748                                                         break;
1749                                                 }
1750                                         }
1751                                         if (found)
1752                                                 break;
1753                                 }
1754                                 if (found) {
1755                                         var = var->next;
1756                                         continue;
1757                                 }
1758                                 set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
1759                                 f = fopen(fn, "a");
1760                                 if (!f)
1761                                 {
1762                                         ast_debug(1, "Unable to open for writing: %s\n", fn);
1763                                         ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1764                                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1765                                         fi = 0;
1766                                         ao2_ref(fileset, -1);
1767                                         return -1;
1768                                 }
1769                                 
1770                                 /* dump any includes that happen before this category header */
1771                                 for (incl=cfg->includes; incl; incl = incl->next) {
1772                                         if (strcmp(incl->include_location_file, var->file) == 0){
1773                                                 if (var->lineno > incl->include_location_lineno && !incl->output) {
1774                                                         if (incl->exec)
1775                                                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1776                                                         else
1777                                                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1778                                                         incl->output = 1;
1779                                                 }
1780                                         }
1781                                 }
1782                                 
1783                                 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
1784                                 for (cmt = var->precomments; cmt; cmt=cmt->next) {
1785                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1786                                                 fprintf(f,"%s", cmt->cmt);
1787                                 }
1788                                 if (var->sameline) 
1789                                         fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
1790                                 else    
1791                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
1792                                 for (cmt = var->trailing; cmt; cmt=cmt->next) {
1793                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1794                                                 fprintf(f,"%s", cmt->cmt);
1795                                 }
1796                                 if (var->blanklines) {
1797                                         blanklines = var->blanklines;
1798                                         while (blanklines--)
1799                                                 fprintf(f, "\n");
1800                                 }
1801                                 
1802                                 fclose(f);
1803                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1804                                 fi = 0;
1805                                 
1806                                 var = var->next;
1807                         }
1808                         cat = cat->next;
1809                 }
1810                 if (!option_debug)
1811                         ast_verb(2, "Saved\n");
1812         } else {
1813                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1814                 ast_verb(2, "Unable to write (%s)", strerror(errno));
1815                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1816                 ao2_ref(fileset, -1);
1817                 return -1;
1818         }
1819
1820         /* Now, for files with trailing #include/#exec statements,
1821            we have to make sure every entry is output */
1822
1823         for (incl=cfg->includes; incl; incl = incl->next) {
1824                 if (!incl->output) {
1825                         /* open the respective file */
1826                         set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
1827                         f = fopen(fn, "a");
1828                         if (!f)
1829                         {
1830                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1831                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1832                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1833                                 fi = 0;
1834                                 ao2_ref(fileset, -1);
1835                                 return -1;
1836                         }
1837                         
1838                         /* output the respective include */
1839                         if (incl->exec)
1840                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1841                         else
1842                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1843                         fclose(f);
1844                         incl->output = 1;
1845                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1846                         fi = 0;
1847                 }
1848         }
1849         ao2_ref(fileset, -1); /* this should destroy the hash container */
1850                                 
1851         return 0;
1852 }
1853
1854 static void clear_config_maps(void) 
1855 {
1856         struct ast_config_map *map;
1857
1858         ast_mutex_lock(&config_lock);
1859
1860         while (config_maps) {
1861                 map = config_maps;
1862                 config_maps = config_maps->next;
1863                 ast_free(map);
1864         }
1865                 
1866         ast_mutex_unlock(&config_lock);
1867 }
1868
1869 static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
1870 {
1871         struct ast_config_map *map;
1872         int length;
1873
1874         length = sizeof(*map);
1875         length += strlen(name) + 1;
1876         length += strlen(driver) + 1;
1877         length += strlen(database) + 1;
1878         if (table)
1879                 length += strlen(table) + 1;
1880
1881         if (!(map = ast_calloc(1, length)))
1882                 return -1;
1883
1884         map->name = map->stuff;
1885         strcpy(map->name, name);
1886         map->driver = map->name + strlen(map->name) + 1;
1887         strcpy(map->driver, driver);
1888         map->database = map->driver + strlen(map->driver) + 1;
1889         strcpy(map->database, database);
1890         if (table) {
1891                 map->table = map->database + strlen(map->database) + 1;
1892                 strcpy(map->table, table);
1893         }
1894         map->next = config_maps;
1895
1896         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
1897
1898         config_maps = map;
1899         return 0;
1900 }
1901
1902 int read_config_maps(void) 
1903 {
1904         struct ast_config *config, *configtmp;
1905         struct ast_variable *v;
1906         char *driver, *table, *database, *stringp, *tmp;
1907         struct ast_flags flags = { 0 };
1908
1909         clear_config_maps();
1910
1911         configtmp = ast_config_new();
1912         configtmp->max_include_level = 1;
1913         config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
1914         if (!config) {
1915                 ast_config_destroy(configtmp);
1916                 return 0;
1917         }
1918
1919         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
1920                 char buf[512];
1921                 ast_copy_string(buf, v->value, sizeof(buf));
1922                 stringp = buf;
1923                 driver = strsep(&stringp, ",");
1924
1925                 if ((tmp = strchr(stringp, '\"')))
1926                         stringp = tmp;
1927
1928                 /* check if the database text starts with a double quote */
1929                 if (*stringp == '"') {
1930                         stringp++;
1931                         database = strsep(&stringp, "\"");
1932                         strsep(&stringp, ",");
1933                 } else {
1934                         /* apparently this text has no quotes */
1935                         database = strsep(&stringp, ",");
1936                 }
1937
1938                 table = strsep(&stringp, ",");
1939
1940                 if (!strcmp(v->name, extconfig_conf)) {
1941                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
1942                         continue;
1943                 }
1944
1945                 if (!strcmp(v->name, "asterisk.conf")) {
1946                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
1947                         continue;
1948                 }
1949
1950                 if (!strcmp(v->name, "logger.conf")) {
1951                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
1952                         continue;
1953                 }
1954
1955                 if (!driver || !database)
1956                         continue;
1957                 if (!strcasecmp(v->name, "sipfriends")) {
1958                         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");
1959                         append_mapping("sipusers", driver, database, table ? table : "sipfriends");
1960                         append_mapping("sippeers", driver, database, table ? table : "sipfriends");
1961                 } else if (!strcasecmp(v->name, "iaxfriends")) {
1962                         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");
1963                         append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
1964                         append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
1965                 } else 
1966                         append_mapping(v->name, driver, database, table);
1967         }
1968                 
1969         ast_config_destroy(config);
1970         return 0;
1971 }
1972
1973 int ast_config_engine_register(struct ast_config_engine *new) 
1974 {
1975         struct ast_config_engine *ptr;
1976
1977         ast_mutex_lock(&config_lock);
1978
1979         if (!config_engine_list) {
1980                 config_engine_list = new;
1981         } else {
1982                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
1983                 ptr->next = new;
1984         }
1985
1986         ast_mutex_unlock(&config_lock);
1987         ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
1988
1989         return 1;
1990 }
1991
1992 int ast_config_engine_deregister(struct ast_config_engine *del) 
1993 {
1994         struct ast_config_engine *ptr, *last=NULL;
1995
1996         ast_mutex_lock(&config_lock);
1997
1998         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
1999                 if (ptr == del) {
2000                         if (last)
2001                                 last->next = ptr->next;
2002                         else
2003                                 config_engine_list = ptr->next;
2004                         break;
2005                 }
2006                 last = ptr;
2007         }
2008
2009         ast_mutex_unlock(&config_lock);
2010
2011         return 0;
2012 }
2013
2014 /*! \brief Find realtime engine for realtime family */
2015 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz) 
2016 {
2017         struct ast_config_engine *eng, *ret = NULL;
2018         struct ast_config_map *map;
2019
2020         ast_mutex_lock(&config_lock);
2021
2022         for (map = config_maps; map; map = map->next) {
2023                 if (!strcasecmp(family, map->name)) {
2024                         if (database)
2025                                 ast_copy_string(database, map->database, dbsiz);
2026                         if (table)
2027                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
2028                         break;
2029                 }
2030         }
2031
2032         /* Check if the required driver (engine) exist */
2033         if (map) {
2034                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
2035                         if (!strcasecmp(eng->name, map->driver))
2036                                 ret = eng;
2037                 }
2038         }
2039
2040         ast_mutex_unlock(&config_lock);
2041         
2042         /* if we found a mapping, but the engine is not available, then issue a warning */
2043         if (map && !ret)
2044                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
2045
2046         return ret;
2047 }
2048
2049 static struct ast_config_engine text_file_engine = {
2050         .name = "text",
2051         .load_func = config_text_file_load,
2052 };
2053
2054 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)
2055 {
2056         char db[256];
2057         char table[256];
2058         struct ast_config_engine *loader = &text_file_engine;
2059         struct ast_config *result; 
2060
2061         /* The config file itself bumps include_level by 1 */
2062         if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
2063                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
2064                 return NULL;
2065         }
2066
2067         cfg->include_level++;
2068
2069         if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
2070                 struct ast_config_engine *eng;
2071
2072                 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
2073
2074
2075                 if (eng && eng->load_func) {
2076                         loader = eng;
2077                 } else {
2078                         eng = find_engine("global", db, sizeof(db), table, sizeof(table));
2079                         if (eng && eng->load_func)
2080                                 loader = eng;
2081                 }
2082         }
2083
2084         result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
2085
2086         if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED)
2087                 result->include_level--;
2088         else if (result != CONFIG_STATUS_FILEINVALID)
2089                 cfg->include_level--;
2090
2091         return result;
2092 }
2093
2094 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
2095 {
2096         struct ast_config *cfg;
2097         struct ast_config *result;
2098
2099         cfg = ast_config_new();
2100         if (!cfg)
2101                 return NULL;
2102
2103         result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
2104         if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
2105                 ast_config_destroy(cfg);
2106
2107         return result;
2108 }
2109
2110 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
2111 {
2112         struct ast_config_engine *eng;
2113         char db[256];
2114         char table[256];
2115         struct ast_variable *res=NULL;
2116
2117         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2118         if (eng && eng->realtime_func) 
2119                 res = eng->realtime_func(db, table, ap);
2120
2121         return res;
2122 }
2123
2124 struct ast_variable *ast_load_realtime_all(const char *family, ...)
2125 {
2126         struct ast_variable *res;
2127         va_list ap;
2128
2129         va_start(ap, family);
2130         res = ast_load_realtime_helper(family, ap);
2131         va_end(ap);
2132
2133         return res;
2134 }
2135
2136 struct ast_variable *ast_load_realtime(const char *family, ...)
2137 {
2138         struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
2139         va_list ap;
2140
2141         va_start(ap, family);
2142         res = ast_load_realtime_helper(family, ap);
2143         va_end(ap);
2144
2145         /* Eliminate blank entries */
2146         for (cur = res; cur; cur = cur->next) {
2147                 if (freeme) {
2148                         ast_free(freeme);
2149                         freeme = NULL;
2150                 }
2151
2152                 if (ast_strlen_zero(cur->value)) {
2153                         if (prev)
2154                                 prev->next = cur->next;
2155                         else
2156                                 res = cur->next;
2157                         freeme = 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 }