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