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