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