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