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