Another big chunk of changes from the RSW branch. Bunch of stuff from main/
[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 : 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)) c++;
1019                 if (*c) {
1020                         *c = '\0';
1021                         /* Find real argument */
1022                         c = ast_skip_blanks(c + 1);
1023                         if (!(*c))
1024                                 c = NULL;
1025                 } else 
1026                         c = NULL;
1027                 if (!strcasecmp(cur, "include")) {
1028                         do_include = 1;
1029                 } else if (!strcasecmp(cur, "exec")) {
1030                         if (!ast_opt_exec_includes) {
1031                                 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
1032                                 return 0;       /* XXX is this correct ? or we should return -1 ? */
1033                         }
1034                 } else {
1035                         ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
1036                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1037                 }
1038
1039                 if (c == NULL) {
1040                         ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", 
1041                                         do_include ? "include" : "exec",
1042                                         do_include ? "filename" : "/path/to/executable",
1043                                         lineno,
1044                                         configfile);
1045                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1046                 }
1047
1048                 /* Strip off leading and trailing "'s and <>'s */
1049                 while ((*c == '<') || (*c == '>') || (*c == '\"')) c++;
1050                 /* Get rid of leading mess */
1051                 cur = c;
1052                 cur2 = cur;
1053                 while (!ast_strlen_zero(cur)) {
1054                         c = cur + strlen(cur) - 1;
1055                         if ((*c == '>') || (*c == '<') || (*c == '\"'))
1056                                 *c = '\0';
1057                         else
1058                                 break;
1059                 }
1060                 /* #exec </path/to/executable>
1061                    We create a tmp file, then we #include it, then we delete it. */
1062                 if (!do_include) {
1063                         struct timeval now = ast_tvnow();
1064                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1065                                 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
1066                         snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
1067                         snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
1068                         ast_safe_system(cmd);
1069                         cur = exec_file;
1070                 } else {
1071                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1072                                 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
1073                         exec_file[0] = '\0';
1074                 }
1075                 /* A #include */
1076                 /* record this inclusion */
1077                 inclu = ast_include_new(cfg, configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1078
1079                 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
1080                 if (!ast_strlen_zero(exec_file))
1081                         unlink(exec_file);
1082                 if (!do_include) {
1083                         ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1084                         return -1;
1085                 }
1086                 /* XXX otherwise what ? the default return is 0 anyways */
1087
1088         } else {
1089                 /* Just a line (variable = value) */
1090                 int object = 0;
1091                 if (!(*cat)) {
1092                         ast_log(LOG_WARNING,
1093                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
1094                         return -1;
1095                 }
1096                 c = strchr(cur, '=');
1097
1098                 if (c && c > cur && (*(c - 1) == '+')) {
1099                         struct ast_variable *var, *replace = NULL;
1100                         struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
1101
1102                         if (!str || !*str) {
1103                                 return -1;
1104                         }
1105
1106                         *(c - 1) = '\0';
1107                         c++;
1108                         cur = ast_strip(cur);
1109
1110                         /* Must iterate through category until we find last variable of same name (since there could be multiple) */
1111                         for (var = ast_category_first(*cat); var; var = var->next) {
1112                                 if (!strcmp(var->name, cur)) {
1113                                         replace = var;
1114                                 }
1115                         }
1116
1117                         if (!replace) {
1118                                 /* Nothing to replace; just set a variable normally. */
1119                                 goto set_new_variable;
1120                         }
1121
1122                         ast_str_set(str, 0, "%s", replace->value);
1123                         ast_str_append(str, 0, "%s", c);
1124                         ast_variable_update(*cat, replace->name, ast_strip((*str)->str), replace->value, object);
1125                 } else if (c) {
1126                         *c = 0;
1127                         c++;
1128                         /* Ignore > in => */
1129                         if (*c== '>') {
1130                                 object = 1;
1131                                 c++;
1132                         }
1133 set_new_variable:
1134                         if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, configfile)))) {
1135                                 v->lineno = lineno;
1136                                 v->object = object;
1137                                 *last_cat = 0;
1138                                 *last_var = v;
1139                                 /* Put and reset comments */
1140                                 v->blanklines = 0;
1141                                 ast_variable_append(*cat, v);
1142                                 /* add comments */
1143                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1144                                         v->precomments = ALLOC_COMMENT(comment_buffer);
1145                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1146                                         v->sameline = ALLOC_COMMENT(lline_buffer);
1147                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1148                                         CB_RESET(comment_buffer, lline_buffer);
1149                                 
1150                         } else {
1151                                 return -1;
1152                         }
1153                 } else {
1154                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1155                 }
1156         }
1157         return 0;
1158 }
1159
1160 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)
1161 {
1162         char fn[256];
1163 #if defined(LOW_MEMORY)
1164         char buf[512];
1165 #else
1166         char buf[8192];
1167 #endif
1168         char *new_buf, *comment_p, *process_buf;
1169         FILE *f;
1170         int lineno=0;
1171         int comment = 0, nest[MAX_NESTED_COMMENTS];
1172         struct ast_category *cat = NULL;
1173         int count = 0;
1174         struct stat statbuf;
1175         struct cache_file_mtime *cfmtime = NULL;
1176         struct cache_file_include *cfinclude;
1177         struct ast_variable *last_var = 0;
1178         struct ast_category *last_cat = 0;
1179         /*! Growable string buffer */
1180         struct ast_str *comment_buffer = NULL;  /*!< this will be a comment collector.*/
1181         struct ast_str *lline_buffer = NULL;    /*!< A buffer for stuff behind the ; */
1182
1183         if (cfg)
1184                 cat = ast_config_get_current_category(cfg);
1185
1186         if (filename[0] == '/') {
1187                 ast_copy_string(fn, filename, sizeof(fn));
1188         } else {
1189                 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
1190         }
1191
1192         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1193                 comment_buffer = ast_str_create(CB_SIZE);
1194                 if (comment_buffer)
1195                         lline_buffer = ast_str_create(CB_SIZE);
1196                 if (!lline_buffer) {
1197                         if (comment_buffer)
1198                                 ast_free(comment_buffer);
1199                         ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1200                         return NULL;
1201                 }
1202         }
1203 #ifdef AST_INCLUDE_GLOB
1204         {
1205                 int glob_ret;
1206                 glob_t globbuf;
1207                 globbuf.gl_offs = 0;    /* initialize it to silence gcc */
1208                 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1209                 if (glob_ret == GLOB_NOSPACE)
1210                         ast_log(LOG_WARNING,
1211                                 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1212                 else if (glob_ret  == GLOB_ABORTED)
1213                         ast_log(LOG_WARNING,
1214                                 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1215                 else  {
1216                         /* loop over expanded files */
1217                         int i;
1218                         for (i=0; i<globbuf.gl_pathc; i++) {
1219                                 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1220 #endif
1221         /*
1222          * The following is not a loop, but just a convenient way to define a block
1223          * (using do { } while(0) ), and be able to exit from it with 'continue'
1224          * or 'break' in case of errors. Nice trick.
1225          */
1226         do {
1227                 if (stat(fn, &statbuf))
1228                         continue;
1229
1230                 if (!S_ISREG(statbuf.st_mode)) {
1231                         ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1232                         continue;
1233                 }
1234
1235                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1236                         /* Find our cached entry for this configuration file */
1237                         AST_LIST_LOCK(&cfmtime_head);
1238                         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1239                                 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
1240                                         break;
1241                         }
1242                         if (!cfmtime) {
1243                                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1 + strlen(who_asked) + 1);
1244                                 if (!cfmtime)
1245                                         continue;
1246                                 AST_LIST_HEAD_INIT(&cfmtime->includes);
1247                                 strcpy(cfmtime->filename, fn);
1248                                 cfmtime->who_asked = cfmtime->filename + strlen(fn) + 1;
1249                                 strcpy(cfmtime->who_asked, who_asked);
1250                                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1251                                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1252                         }
1253                 }
1254
1255                 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1256                         /* File is unchanged, what about the (cached) includes (if any)? */
1257                         int unchanged = 1;
1258                         AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1259                                 /* We must glob here, because if we did not, then adding a file to globbed directory would
1260                                  * incorrectly cause no reload to be necessary. */
1261                                 char fn2[256];
1262 #ifdef AST_INCLUDE_GLOB
1263                                 int glob_return;
1264                                 glob_t glob_buf = { .gl_offs = 0 };
1265                                 glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
1266                                 /* On error, we reparse */
1267                                 if (glob_return == GLOB_NOSPACE || glob_return  == GLOB_ABORTED)
1268                                         unchanged = 0;
1269                                 else  {
1270                                         /* loop over expanded files */
1271                                         int j;
1272                                         for (j = 0; j < glob_buf.gl_pathc; j++) {
1273                                                 ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
1274 #else
1275                                                 ast_copy_string(fn2, cfinclude->include);
1276 #endif
1277                                                 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
1278                                                         /* that second-to-last field needs to be looked at in this case... TODO */
1279                                                         unchanged = 0;
1280                                                         /* One change is enough to short-circuit and reload the whole shebang */
1281                                                         break;
1282                                                 }
1283 #ifdef AST_INCLUDE_GLOB
1284                                         }
1285                                 }
1286 #endif
1287                         }
1288
1289                         if (unchanged) {
1290                                 AST_LIST_UNLOCK(&cfmtime_head);
1291                                 return CONFIG_STATUS_FILEUNCHANGED;
1292                         }
1293                 }
1294                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1295                         AST_LIST_UNLOCK(&cfmtime_head);
1296
1297                 /* If cfg is NULL, then we just want an answer */
1298                 if (cfg == NULL)
1299                         return NULL;
1300
1301                 if (cfmtime)
1302                         cfmtime->mtime = statbuf.st_mtime;
1303
1304                 ast_verb(2, "Parsing '%s': ", fn);
1305                         fflush(stdout);
1306                 if (!(f = fopen(fn, "r"))) {
1307                         ast_debug(1, "No file to parse: %s\n", fn);
1308                         ast_verb(2, "Not found (%s)\n", strerror(errno));
1309                         continue;
1310                 }
1311                 count++;
1312                 /* If we get to this point, then we're loading regardless */
1313                 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1314                 ast_debug(1, "Parsing %s\n", fn);
1315                 ast_verb(2, "Found\n");
1316                 while (!feof(f)) {
1317                         lineno++;
1318                         if (fgets(buf, sizeof(buf), f)) {
1319                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && lline_buffer->used) {
1320                                         CB_ADD(&comment_buffer, lline_buffer->str);       /* add the current lline buffer to the comment buffer */
1321                                         lline_buffer->used = 0;        /* erase the lline buffer */
1322                                 }
1323                                 
1324                                 new_buf = buf;
1325                                 if (comment) 
1326                                         process_buf = NULL;
1327                                 else
1328                                         process_buf = buf;
1329                                 
1330                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
1331                                         /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1332                                         CB_ADD(&comment_buffer, "\n");       /* add a newline to the comment buffer */
1333                                         continue; /* go get a new line, then */
1334                                 }
1335                                 
1336                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1337                                         if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
1338                                                 /* Escaped semicolons aren't comments. */
1339                                                 new_buf = comment_p + 1;
1340                                         } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1341                                                 /* Meta-Comment start detected ";--" */
1342                                                 if (comment < MAX_NESTED_COMMENTS) {
1343                                                         *comment_p = '\0';
1344                                                         new_buf = comment_p + 3;
1345                                                         comment++;
1346                                                         nest[comment-1] = lineno;
1347                                                 } else {
1348                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1349                                                 }
1350                                         } else if ((comment_p >= new_buf + 2) &&
1351                                                    (*(comment_p - 1) == COMMENT_TAG) &&
1352                                                    (*(comment_p - 2) == COMMENT_TAG)) {
1353                                                 /* Meta-Comment end detected */
1354                                                 comment--;
1355                                                 new_buf = comment_p + 1;
1356                                                 if (!comment) {
1357                                                         /* Back to non-comment now */
1358                                                         if (process_buf) {
1359                                                                 /* Actually have to move what's left over the top, then continue */
1360                                                                 char *oldptr;
1361                                                                 oldptr = process_buf + strlen(process_buf);
1362                                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1363                                                                         CB_ADD(&comment_buffer, ";");
1364                                                                         CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
1365                                                                 }
1366                                                                 
1367                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1368                                                                 new_buf = oldptr;
1369                                                         } else
1370                                                                 process_buf = new_buf;
1371                                                 }
1372                                         } else {
1373                                                 if (!comment) {
1374                                                         /* If ; is found, and we are not nested in a comment, 
1375                                                            we immediately stop all comment processing */
1376                                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1377                                                                 CB_ADD(&lline_buffer, comment_p);
1378                                                         }
1379                                                         *comment_p = '\0'; 
1380                                                         new_buf = comment_p;
1381                                                 } else
1382                                                         new_buf = comment_p + 1;
1383                                         }
1384                                 }
1385                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
1386                                         CB_ADD(&comment_buffer, buf);  /* the whole line is a comment, store it */
1387                                 }
1388                                 
1389                                 if (process_buf) {
1390                                         char *buffer = ast_strip(process_buf);
1391                                         if (!ast_strlen_zero(buffer)) {
1392                                                 if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
1393                                                         cfg = NULL;
1394                                                         break;
1395                                                 }
1396                                         }
1397                                 }
1398                         }
1399                 }
1400                 /* end of file-- anything in a comment buffer? */
1401                 if (last_cat) {
1402                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
1403                                 if (lline_buffer && lline_buffer->used) {
1404                                         CB_ADD(&comment_buffer, lline_buffer->str);       /* add the current lline buffer to the comment buffer */
1405                                         lline_buffer->used = 0;        /* erase the lline buffer */
1406                                 }
1407                                 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1408                         }
1409                 } else if (last_var) {
1410                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
1411                                 if (lline_buffer && lline_buffer->used) {
1412                                         CB_ADD(&comment_buffer, lline_buffer->str);       /* add the current lline buffer to the comment buffer */
1413                                         lline_buffer->used = 0;        /* erase the lline buffer */
1414                                 }
1415                                 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1416                         }
1417                 } else {
1418                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used) {
1419                                 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", comment_buffer->str);
1420                         }
1421                 }
1422                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1423                         CB_RESET(comment_buffer, lline_buffer);
1424
1425                 fclose(f);              
1426         } while (0);
1427         if (comment) {
1428                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1429         }
1430 #ifdef AST_INCLUDE_GLOB
1431                                         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1432                                                 break;
1433                                 }
1434                                 globfree(&globbuf);
1435                         }
1436                 }
1437 #endif
1438
1439         if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1440                 if (comment_buffer)
1441                         ast_free(comment_buffer);
1442                 if (lline_buffer)
1443                         ast_free(lline_buffer);
1444                 comment_buffer = NULL;
1445                 lline_buffer = NULL;
1446         }
1447         
1448         if (count == 0)
1449                 return NULL;
1450
1451         return cfg;
1452 }
1453
1454
1455 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1456    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1457    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1458    be shocked and mystified as to why things are not showing up in the files! 
1459
1460    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1461    and line number are stored for each include, plus the name of the file included, so that these statements may be
1462    included in the output files on a file_save operation. 
1463
1464    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1465    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1466    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1467    and a header gets added.
1468
1469    vars and category heads are output in the order they are stored in the config file. So, if the software
1470    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1471    file/lineno data probably won't get changed.
1472
1473 */
1474
1475 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1476 {
1477         char date[256]="";
1478         time_t t;
1479
1480         time(&t);
1481         ast_copy_string(date, ctime(&t), sizeof(date));
1482
1483         fprintf(f1, ";!\n");
1484         fprintf(f1, ";! Automatically generated configuration file\n");
1485         if (strcmp(configfile, fn))
1486                 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1487         else
1488                 fprintf(f1, ";! Filename: %s\n", configfile);
1489         fprintf(f1, ";! Generator: %s\n", generator);
1490         fprintf(f1, ";! Creation Date: %s", date);
1491         fprintf(f1, ";!\n");
1492 }
1493
1494 static void   inclfile_destroy(void *obj)
1495 {
1496         const struct inclfile *o = obj;
1497
1498         if (o->fname)
1499                 free(o->fname);
1500 }
1501
1502
1503 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
1504 {
1505         struct inclfile lookup;
1506         
1507         if (!file || file[0] == 0) {
1508                 if (configfile[0] == '/')
1509                         ast_copy_string(fn, configfile, fn_size);
1510                 else
1511                         snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1512         } else if (file[0] == '/') 
1513                 ast_copy_string(fn, file, fn_size);
1514         else
1515                 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
1516         lookup.fname = fn;
1517         *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
1518         if (!(*fi)) {
1519                 /* set up a file scratch pad */
1520                 struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
1521                 fx->fname = ast_strdup(fn);
1522                 fx->lineno = 1;
1523                 *fi = fx;
1524                 ao2_link(fileset, fx);
1525         }
1526 }
1527
1528 static int count_linefeeds(char *str)
1529 {
1530         int count = 0;
1531
1532         while (*str) {
1533                 if (*str =='\n')
1534                         count++;
1535                 str++;
1536         }
1537         return count;
1538 }
1539
1540 static int count_linefeeds_in_comments(struct ast_comment *x)
1541 {
1542         int count = 0;
1543
1544         while (x) {
1545                 count += count_linefeeds(x->cmt);
1546                 x = x->next;
1547         }
1548         return count;
1549 }
1550
1551 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1552 {
1553         int precomment_lines = count_linefeeds_in_comments(precomments);
1554         int i;
1555
1556         /* I don't have to worry about those ;! comments, they are
1557            stored in the precomments, but not printed back out.
1558            I did have to make sure that comments following
1559            the ;! header comments were not also deleted in the process */
1560         for (i=fi->lineno;i<lineno - precomment_lines; i++) {
1561                 fprintf(fp,"\n");
1562         }
1563         fi->lineno = lineno+1; /* Advance the file lineno */
1564 }
1565
1566 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1567 {
1568         FILE *f;
1569         char fn[256];
1570         struct ast_variable *var;
1571         struct ast_category *cat;
1572         struct ast_comment *cmt;
1573         struct ast_config_include *incl;
1574         int blanklines = 0;
1575         struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
1576         struct inclfile *fi = 0;
1577
1578         /* reset all the output flags, in case this isn't our first time saving this data */
1579
1580         for (incl=cfg->includes; incl; incl = incl->next)
1581                 incl->output = 0;
1582
1583         /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
1584            are all truncated to zero bytes and have that nice header*/
1585
1586         for (incl=cfg->includes; incl; incl = incl->next)
1587         {
1588                 if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/
1589                         FILE *f1;
1590
1591                         set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset, &fi); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
1592                         f1 = fopen(fn,"w");
1593                         if (f1) {
1594                                 gen_header(f1, configfile, fn, generator);
1595                                 fclose(f1); /* this should zero out the file */
1596                         } else {
1597                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1598                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1599                         }
1600                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1601                         fi = 0;
1602                 }
1603         }
1604
1605         set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
1606 #ifdef __CYGWIN__       
1607         if ((f = fopen(fn, "w+"))) {
1608 #else
1609         if ((f = fopen(fn, "w"))) {
1610 #endif      
1611                 ast_verb(2, "Saving '%s': ", fn);
1612                 gen_header(f, configfile, fn, generator);
1613                 cat = cfg->root;
1614                 fclose(f);
1615                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1616                 
1617                 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
1618                 /* since each var, cat, and associated comments can come from any file, we have to be 
1619                    mobile, and open each file, print, and close it on an entry-by-entry basis */
1620
1621                 while (cat) {
1622                         set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
1623                         f = fopen(fn, "a");
1624                         if (!f)
1625                         {
1626                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1627                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1628                                 ao2_ref(fileset, -1);
1629                                 return -1;
1630                         }
1631
1632                         /* dump any includes that happen before this category header */
1633                         for (incl=cfg->includes; incl; incl = incl->next) {
1634                                 if (strcmp(incl->include_location_file, cat->file) == 0){
1635                                         if (cat->lineno > incl->include_location_lineno && !incl->output) {
1636                                                 if (incl->exec)
1637                                                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1638                                                 else
1639                                                         fprintf(f,"#include \"%s\"\n", incl->included_file);
1640                                                 incl->output = 1;
1641                                         }
1642                                 }
1643                         }
1644                         
1645                         insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
1646                         /* Dump section with any appropriate comment */
1647                         for (cmt = cat->precomments; cmt; cmt=cmt->next) {
1648                                 char *cmtp = cmt->cmt;
1649                                 while (*cmtp == ';' && *(cmtp+1) == '!') {
1650                                         char *cmtp2 = strchr(cmtp+1, '\n');
1651                                         if (cmtp2)
1652                                                 cmtp = cmtp2+1;
1653                                         else cmtp = 0;
1654                                 }
1655                                 if (cmtp)
1656                                         fprintf(f,"%s", cmtp);
1657                         }
1658                         if (!cat->precomments)
1659                                 fprintf(f,"\n");
1660                         fprintf(f, "[%s]", cat->name);
1661                         if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
1662                                 fprintf(f, "(");
1663                                 if (cat->ignored) {
1664                                         fprintf(f, "!");
1665                                 }
1666                                 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
1667                                         fprintf(f, ",");
1668                                 }
1669                                 if (!AST_LIST_EMPTY(&cat->template_instances)) {
1670                                         struct ast_category_template_instance *x;
1671                                         AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1672                                                 fprintf(f,"%s",x->name);
1673                                                 if (x != AST_LIST_LAST(&cat->template_instances))
1674                                                         fprintf(f,",");
1675                                         }
1676                                 }
1677                                 fprintf(f, ")");
1678                         }
1679                         for(cmt = cat->sameline; cmt; cmt=cmt->next)
1680                         {
1681                                 fprintf(f,"%s", cmt->cmt);
1682                         }
1683                         if (!cat->sameline)
1684                                 fprintf(f,"\n");
1685                         for (cmt = cat->trailing; cmt; cmt=cmt->next) {
1686                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1687                                         fprintf(f,"%s", cmt->cmt);
1688                         }
1689                         fclose(f);
1690                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1691                         fi = 0;
1692                         
1693                         var = cat->root;
1694                         while (var) {
1695                                 struct ast_category_template_instance *x;
1696                                 int found = 0;
1697                                 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1698                                         struct ast_variable *v;
1699                                         for (v = x->inst->root; v; v = v->next) {
1700                                                 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
1701                                                         found = 1;
1702                                                         break;
1703                                                 }
1704                                         }
1705                                         if (found)
1706                                                 break;
1707                                 }
1708                                 if (found) {
1709                                         var = var->next;
1710                                         continue;
1711                                 }
1712                                 set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
1713                                 f = fopen(fn, "a");
1714                                 if (!f)
1715                                 {
1716                                         ast_debug(1, "Unable to open for writing: %s\n", fn);
1717                                         ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1718                                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1719                                         fi = 0;
1720                                         ao2_ref(fileset, -1);
1721                                         return -1;
1722                                 }
1723                                 
1724                                 /* dump any includes that happen before this category header */
1725                                 for (incl=cfg->includes; incl; incl = incl->next) {
1726                                         if (strcmp(incl->include_location_file, var->file) == 0){
1727                                                 if (var->lineno > incl->include_location_lineno && !incl->output) {
1728                                                         if (incl->exec)
1729                                                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1730                                                         else
1731                                                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1732                                                         incl->output = 1;
1733                                                 }
1734                                         }
1735                                 }
1736                                 
1737                                 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
1738                                 for (cmt = var->precomments; cmt; cmt=cmt->next) {
1739                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1740                                                 fprintf(f,"%s", cmt->cmt);
1741                                 }
1742                                 if (var->sameline) 
1743                                         fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
1744                                 else    
1745                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
1746                                 for (cmt = var->trailing; cmt; cmt=cmt->next) {
1747                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1748                                                 fprintf(f,"%s", cmt->cmt);
1749                                 }
1750                                 if (var->blanklines) {
1751                                         blanklines = var->blanklines;
1752                                         while (blanklines--)
1753                                                 fprintf(f, "\n");
1754                                 }
1755                                 
1756                                 fclose(f);
1757                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1758                                 fi = 0;
1759                                 
1760                                 var = var->next;
1761                         }
1762                         cat = cat->next;
1763                 }
1764                 if (!option_debug)
1765                         ast_verb(2, "Saved\n");
1766         } else {
1767                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1768                 ast_verb(2, "Unable to write (%s)", strerror(errno));
1769                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1770                 ao2_ref(fileset, -1);
1771                 return -1;
1772         }
1773
1774         /* Now, for files with trailing #include/#exec statements,
1775            we have to make sure every entry is output */
1776
1777         for (incl=cfg->includes; incl; incl = incl->next) {
1778                 if (!incl->output) {
1779                         /* open the respective file */
1780                         set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
1781                         f = fopen(fn, "a");
1782                         if (!f)
1783                         {
1784                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1785                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1786                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1787                                 fi = 0;
1788                                 ao2_ref(fileset, -1);
1789                                 return -1;
1790                         }
1791                         
1792                         /* output the respective include */
1793                         if (incl->exec)
1794                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1795                         else
1796                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1797                         fclose(f);
1798                         incl->output = 1;
1799                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1800                         fi = 0;
1801                 }
1802         }
1803         ao2_ref(fileset, -1); /* this should destroy the hash container */
1804                                 
1805         return 0;
1806 }
1807
1808 static void clear_config_maps(void) 
1809 {
1810         struct ast_config_map *map;
1811
1812         ast_mutex_lock(&config_lock);
1813
1814         while (config_maps) {
1815                 map = config_maps;
1816                 config_maps = config_maps->next;
1817                 ast_free(map);
1818         }
1819                 
1820         ast_mutex_unlock(&config_lock);
1821 }
1822
1823 static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
1824 {
1825         struct ast_config_map *map;
1826         int length;
1827
1828         length = sizeof(*map);
1829         length += strlen(name) + 1;
1830         length += strlen(driver) + 1;
1831         length += strlen(database) + 1;
1832         if (table)
1833                 length += strlen(table) + 1;
1834
1835         if (!(map = ast_calloc(1, length)))
1836                 return -1;
1837
1838         map->name = map->stuff;
1839         strcpy(map->name, name);
1840         map->driver = map->name + strlen(map->name) + 1;
1841         strcpy(map->driver, driver);
1842         map->database = map->driver + strlen(map->driver) + 1;
1843         strcpy(map->database, database);
1844         if (table) {
1845                 map->table = map->database + strlen(map->database) + 1;
1846                 strcpy(map->table, table);
1847         }
1848         map->next = config_maps;
1849
1850         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
1851
1852         config_maps = map;
1853         return 0;
1854 }
1855
1856 int read_config_maps(void) 
1857 {
1858         struct ast_config *config, *configtmp;
1859         struct ast_variable *v;
1860         char *driver, *table, *database, *stringp, *tmp;
1861         struct ast_flags flags = { 0 };
1862
1863         clear_config_maps();
1864
1865         configtmp = ast_config_new();
1866         configtmp->max_include_level = 1;
1867         config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
1868         if (!config) {
1869                 ast_config_destroy(configtmp);
1870                 return 0;
1871         }
1872
1873         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
1874                 char buf[512];
1875                 ast_copy_string(buf, v->value, sizeof(buf));
1876                 stringp = buf;
1877                 driver = strsep(&stringp, ",");
1878
1879                 if ((tmp = strchr(stringp, '\"')))
1880                         stringp = tmp;
1881
1882                 /* check if the database text starts with a double quote */
1883                 if (*stringp == '"') {
1884                         stringp++;
1885                         database = strsep(&stringp, "\"");
1886                         strsep(&stringp, ",");
1887                 } else {
1888                         /* apparently this text has no quotes */
1889                         database = strsep(&stringp, ",");
1890                 }
1891
1892                 table = strsep(&stringp, ",");
1893
1894                 if (!strcmp(v->name, extconfig_conf)) {
1895                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
1896                         continue;
1897                 }
1898
1899                 if (!strcmp(v->name, "asterisk.conf")) {
1900                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
1901                         continue;
1902                 }
1903
1904                 if (!strcmp(v->name, "logger.conf")) {
1905                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
1906                         continue;
1907                 }
1908
1909                 if (!driver || !database)
1910                         continue;
1911                 if (!strcasecmp(v->name, "sipfriends")) {
1912                         ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n");
1913                         append_mapping("sipusers", driver, database, table ? table : "sipfriends");
1914                         append_mapping("sippeers", driver, database, table ? table : "sipfriends");
1915                 } else if (!strcasecmp(v->name, "iaxfriends")) {
1916                         ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n");
1917                         append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
1918                         append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
1919                 } else 
1920                         append_mapping(v->name, driver, database, table);
1921         }
1922                 
1923         ast_config_destroy(config);
1924         return 0;
1925 }
1926
1927 int ast_config_engine_register(struct ast_config_engine *new) 
1928 {
1929         struct ast_config_engine *ptr;
1930
1931         ast_mutex_lock(&config_lock);
1932
1933         if (!config_engine_list) {
1934                 config_engine_list = new;
1935         } else {
1936                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
1937                 ptr->next = new;
1938         }
1939
1940         ast_mutex_unlock(&config_lock);
1941         ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
1942
1943         return 1;
1944 }
1945
1946 int ast_config_engine_deregister(struct ast_config_engine *del) 
1947 {
1948         struct ast_config_engine *ptr, *last=NULL;
1949
1950         ast_mutex_lock(&config_lock);
1951
1952         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
1953                 if (ptr == del) {
1954                         if (last)
1955                                 last->next = ptr->next;
1956                         else
1957                                 config_engine_list = ptr->next;
1958                         break;
1959                 }
1960                 last = ptr;
1961         }
1962
1963         ast_mutex_unlock(&config_lock);
1964
1965         return 0;
1966 }
1967
1968 /*! \brief Find realtime engine for realtime family */
1969 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz) 
1970 {
1971         struct ast_config_engine *eng, *ret = NULL;
1972         struct ast_config_map *map;
1973
1974         ast_mutex_lock(&config_lock);
1975
1976         for (map = config_maps; map; map = map->next) {
1977                 if (!strcasecmp(family, map->name)) {
1978                         if (database)
1979                                 ast_copy_string(database, map->database, dbsiz);
1980                         if (table)
1981                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
1982                         break;
1983                 }
1984         }
1985
1986         /* Check if the required driver (engine) exist */
1987         if (map) {
1988                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
1989                         if (!strcasecmp(eng->name, map->driver))
1990                                 ret = eng;
1991                 }
1992         }
1993
1994         ast_mutex_unlock(&config_lock);
1995         
1996         /* if we found a mapping, but the engine is not available, then issue a warning */
1997         if (map && !ret)
1998                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
1999
2000         return ret;
2001 }
2002
2003 static struct ast_config_engine text_file_engine = {
2004         .name = "text",
2005         .load_func = config_text_file_load,
2006 };
2007
2008 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file, const char *who_asked)
2009 {
2010         char db[256];
2011         char table[256];
2012         struct ast_config_engine *loader = &text_file_engine;
2013         struct ast_config *result; 
2014
2015         /* The config file itself bumps include_level by 1 */
2016         if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
2017                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
2018                 return NULL;
2019         }
2020
2021         cfg->include_level++;
2022
2023         if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
2024                 struct ast_config_engine *eng;
2025
2026                 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
2027
2028
2029                 if (eng && eng->load_func) {
2030                         loader = eng;
2031                 } else {
2032                         eng = find_engine("global", db, sizeof(db), table, sizeof(table));
2033                         if (eng && eng->load_func)
2034                                 loader = eng;
2035                 }
2036         }
2037
2038         result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
2039
2040         if (result && result != CONFIG_STATUS_FILEUNCHANGED)
2041                 result->include_level--;
2042         else
2043                 cfg->include_level--;
2044
2045         return result;
2046 }
2047
2048 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
2049 {
2050         struct ast_config *cfg;
2051         struct ast_config *result;
2052
2053         cfg = ast_config_new();
2054         if (!cfg)
2055                 return NULL;
2056
2057         result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
2058         if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
2059                 ast_config_destroy(cfg);
2060
2061         return result;
2062 }
2063
2064 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
2065 {
2066         struct ast_config_engine *eng;
2067         char db[256]="";
2068         char table[256]="";
2069         struct ast_variable *res=NULL;
2070
2071         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2072         if (eng && eng->realtime_func) 
2073                 res = eng->realtime_func(db, table, ap);
2074
2075         return res;
2076 }
2077
2078 struct ast_variable *ast_load_realtime_all(const char *family, ...)
2079 {
2080         struct ast_variable *res;
2081         va_list ap;
2082
2083         va_start(ap, family);
2084         res = ast_load_realtime_helper(family, ap);
2085         va_end(ap);
2086
2087         return res;
2088 }
2089
2090 struct ast_variable *ast_load_realtime(const char *family, ...)
2091 {
2092         struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
2093         va_list ap;
2094
2095         va_start(ap, family);
2096         res = ast_load_realtime_helper(family, ap);
2097         va_end(ap);
2098
2099         /* Eliminate blank entries */
2100         for (cur = res; cur; cur = cur->next) {
2101                 if (freeme) {
2102                         ast_free(freeme);
2103                         freeme = NULL;
2104                 }
2105
2106                 if (ast_strlen_zero(cur->value)) {
2107                         if (prev)
2108                                 prev->next = cur->next;
2109                         else
2110                                 res = cur->next;
2111                         freeme = cur;
2112                 } else {
2113                         prev = cur;
2114                 }
2115         }
2116         return res;
2117 }
2118
2119 /*! \brief Check if realtime engine is configured for family */
2120 int ast_check_realtime(const char *family)
2121 {
2122         struct ast_config_engine *eng;
2123
2124         eng = find_engine(family, NULL, 0, NULL, 0);
2125         if (eng)
2126                 return 1;
2127         return 0;
2128 }
2129
2130 /*! \brief Check if there's any realtime engines loaded */
2131 int ast_realtime_enabled()
2132 {
2133         return config_maps ? 1 : 0;
2134 }
2135
2136 int ast_realtime_require_field(const char *family, ...)
2137 {
2138         struct ast_config_engine *eng;
2139         char db[256] = "";
2140         char table[256] = "";
2141         va_list ap;
2142         int res = -1;
2143
2144         va_start(ap, family);
2145         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2146         if (eng && eng->require_func) {
2147                 res = eng->require_func(db, table, ap);
2148         }
2149         va_end(ap);
2150
2151         return res;
2152 }
2153
2154 int ast_unload_realtime(const char *family)
2155 {
2156         struct ast_config_engine *eng;
2157         char db[256] = "";
2158         char table[256] = "";
2159         int res = -1;
2160
2161         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2162         if (eng && eng->unload_func) {
2163                 res = eng->unload_func(db, table);
2164         }
2165         return res;
2166 }
2167
2168 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
2169 {
2170         struct ast_config_engine *eng;
2171         char db[256]="";
2172         char table[256]="";
2173         struct ast_config *res=NULL;
2174         va_list ap;
2175
2176         va_start(ap, family);
2177         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2178         if (eng && eng->realtime_multi_func) 
2179                 res = eng->realtime_multi_func(db, table, ap);
2180         va_end(ap);
2181
2182         return res;
2183 }
2184
2185 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2186 {
2187         struct ast_config_engine *eng;
2188         int res = -1;
2189         char db[256]="";
2190         char table[256]="";
2191         va_list ap;
2192
2193         va_start(ap, lookup);
2194         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2195         if (eng && eng->update_func) 
2196                 res = eng->update_func(db, table, keyfield, lookup, ap);
2197         va_end(ap);
2198
2199         return res;
2200 }
2201
2202 int ast_store_realtime(const char *family, ...)
2203 {
2204         struct ast_config_engine *eng;
2205         int res = -1;
2206         char db[256]="";
2207         char table[256]="";
2208         va_list ap;
2209
2210         va_start(ap, family);
2211         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2212         if (eng && eng->store_func) 
2213                 res = eng->store_func(db, table, ap);
2214         va_end(ap);
2215
2216         return res;
2217 }
2218
2219 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2220 {
2221         struct ast_config_engine *eng;
2222         int res = -1;
2223         char db[256]="";
2224         char table[256]="";
2225         va_list ap;
2226
2227         va_start(ap, lookup);
2228         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2229         if (eng && eng->destroy_func) 
2230                 res = eng->destroy_func(db, table, keyfield, lookup, ap);
2231         va_end(ap);
2232
2233         return res;
2234 }
2235
2236 /*! \brief Helper function to parse arguments
2237  * See documentation in config.h
2238  */
2239 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2240         void *p_result, ...)
2241 {
2242         va_list ap;
2243         int error = 0;
2244
2245         va_start(ap, p_result);
2246         switch (flags & PARSE_TYPE) {
2247         case PARSE_INT32:
2248             {
2249                 int32_t *result = p_result;
2250                 int32_t x, def = result ? *result : 0,
2251                         high = (int32_t)0x7fffffff,
2252                         low  = (int32_t)0x80000000;
2253                 /* optional argument: first default value, then range */
2254                 if (flags & PARSE_DEFAULT)
2255                         def = va_arg(ap, int32_t);
2256                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2257                         /* range requested, update bounds */
2258                         low = va_arg(ap, int32_t);
2259                         high = va_arg(ap, int32_t);
2260                 }
2261                 x = strtol(arg, NULL, 0);
2262                 error = (x < low) || (x > high);
2263                 if (flags & PARSE_OUT_RANGE)
2264                         error = !error;
2265                 if (result)
2266                         *result  = error ? def : x;
2267                 ast_debug(3,
2268                         "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
2269                         arg, low, high,
2270                         result ? *result : x, error);
2271                 break;
2272             }
2273
2274         case PARSE_UINT32:
2275             {
2276                 uint32_t *result = p_result;
2277                 uint32_t x, def = result ? *result : 0,
2278                         low = 0, high = (uint32_t)~0;
2279                 /* optional argument: first default value, then range */
2280                 if (flags & PARSE_DEFAULT)
2281                         def = va_arg(ap, uint32_t);
2282                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2283                         /* range requested, update bounds */
2284                         low = va_arg(ap, uint32_t);
2285                         high = va_arg(ap, uint32_t);
2286                 }
2287                 x = strtoul(arg, NULL, 0);
2288                 error = (x < low) || (x > high);
2289                 if (flags & PARSE_OUT_RANGE)
2290                         error = !error;
2291                 if (result)
2292                         *result  = error ? def : x;
2293                 ast_debug(3,
2294                         "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
2295                         arg, low, high,
2296                         result ? *result : x, error);
2297                 break;
2298             }
2299
2300         case PARSE_DOUBLE:
2301             {
2302                 double *result = p_result;
2303                 double x, def = result ? *result : 0,
2304                         low = -HUGE_VAL, high = HUGE_VAL;
2305
2306                 /* optional argument: first default value, then range */
2307                 if (flags & PARSE_DEFAULT)
2308                         def = va_arg(ap, double);
2309                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2310                         /* range requested, update bounds */
2311                         low = va_arg(ap, double);
2312                         high = va_arg(ap, double);
2313                 }
2314                 x = strtod(arg, NULL);
2315                 error = (x < low) || (x > high);
2316                 if (flags & PARSE_OUT_RANGE)
2317                         error = !error;
2318                 if (result)
2319                         *result  = error ? def : x;
2320                 ast_debug(3,
2321                         "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
2322                         arg, low, high,
2323                         result ? *result : x, error);
2324                 break;
2325             }
2326         case PARSE_INADDR:
2327             {
2328                 char *port, *buf;
2329                 struct sockaddr_in _sa_buf;     /* buffer for the result */
2330                 struct sockaddr_in *sa = p_result ?
2331                         (struct sockaddr_in *)p_result : &_sa_buf;
2332                 /* default is either the supplied value or the result itself */
2333                 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
2334                         va_arg(ap, struct sockaddr_in *) : sa;
2335                 struct hostent *hp;
2336                 struct ast_hostent ahp;
2337
2338                 bzero(&_sa_buf, sizeof(_sa_buf)); /* clear buffer */
2339                 /* duplicate the string to strip away the :port */
2340                 port = ast_strdupa(arg);
2341                 buf = strsep(&port, ":");
2342                 sa->sin_family = AF_INET;       /* assign family */
2343                 /*
2344                  * honor the ports flag setting, assign default value
2345                  * in case of errors or field unset.
2346                  */
2347                 flags &= PARSE_PORT_MASK; /* the only flags left to process */
2348                 if (port) {
2349                         if (flags == PARSE_PORT_FORBID) {
2350                                 error = 1;      /* port was forbidden */
2351                                 sa->sin_port = def->sin_port;
2352                         } else if (flags == PARSE_PORT_IGNORE)
2353                                 sa->sin_port = def->sin_port;
2354                         else /* accept or require */
2355                                 sa->sin_port = htons(strtol(port, NULL, 0));
2356                 } else {
2357                         sa->sin_port = def->sin_port;
2358                         if (flags == PARSE_PORT_REQUIRE)
2359                                 error = 1;
2360                 }
2361                 /* Now deal with host part, even if we have errors before. */
2362                 hp = ast_gethostbyname(buf, &ahp);
2363                 if (hp) /* resolved successfully */
2364                         memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
2365                 else {
2366                         error = 1;
2367                         sa->sin_addr = def->sin_addr;
2368                 }
2369                 ast_debug(3,
2370                         "extract inaddr from [%s] gives [%s:%d](%d)\n",
2371                         arg, ast_inet_ntoa(sa->sin_addr),
2372                         ntohs(sa->sin_port), error);
2373                 break;
2374             }
2375         }
2376         va_end(ap);
2377         return error;
2378 }
2379
2380 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2381 {
2382         struct ast_config_engine *eng;
2383         struct ast_config_map *map;
2384
2385         switch (cmd) {
2386         case CLI_INIT:
2387                 e->command = "core show config mappings";
2388                 e->usage =
2389                         "Usage: core show config mappings\n"
2390                         "       Shows the filenames to config engines.\n";
2391                 return NULL;
2392         case CLI_GENERATE:
2393                 return NULL;
2394         }
2395         
2396         ast_mutex_lock(&config_lock);
2397
2398         if (!config_engine_list) {
2399                 ast_cli(a->fd, "No config mappings found.\n");
2400         } else {
2401                 ast_cli(a->fd, "\n\n");
2402                 for (eng = config_engine_list; eng; eng = eng->next) {
2403                         ast_cli(a->fd, "\nConfig Engine: %s\n", eng->name);
2404                         for (map = config_maps; map; map = map->next) {
2405                                 if (!strcasecmp(map->driver, eng->name)) {
2406                                         ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
2407                                                         map->table ? map->table : map->name);
2408                                 }
2409                         }
2410                 }
2411                 ast_cli(a->fd,"\n\n");
2412         }
2413         
2414         ast_mutex_unlock(&config_lock);
2415
2416         return CLI_SUCCESS;
2417 }
2418
2419 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2420 {
2421         struct cache_file_mtime *cfmtime;
2422         char *prev = "", *completion_value = NULL;
2423         int wordlen, which = 0;
2424
2425         switch (cmd) {
2426         case CLI_INIT:
2427                 e->command = "config reload";
2428                 e->usage =
2429                         "Usage: config reload <filename.conf>\n"
2430                         "   Reloads all modules that reference <filename.conf>\n";
2431                 return NULL;
2432         case CLI_GENERATE:
2433                 if (a->pos > 2) {
2434                         return NULL;
2435                 }
2436
2437                 wordlen = strlen(a->word);
2438
2439                 AST_LIST_LOCK(&cfmtime_head);
2440                 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2441                         /* Skip duplicates - this only works because the list is sorted by filename */
2442                         if (strcmp(cfmtime->filename, prev) == 0) {
2443                                 continue;
2444                         }
2445
2446                         /* Core configs cannot be reloaded */
2447                         if (ast_strlen_zero(cfmtime->who_asked)) {
2448                                 continue;
2449                         }
2450
2451                         if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
2452                                 completion_value = ast_strdup(cfmtime->filename);
2453                                 break;
2454                         }
2455
2456                         /* Otherwise save that we've seen this filename */
2457                         prev = cfmtime->filename;
2458                 }
2459                 AST_LIST_UNLOCK(&cfmtime_head);
2460
2461                 return completion_value;
2462         }
2463
2464         if (a->argc != 3) {
2465                 return CLI_SHOWUSAGE;
2466         }
2467
2468         AST_LIST_LOCK(&cfmtime_head);
2469         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2470                 if (!strcmp(cfmtime->filename, a->argv[2])) {
2471                         char *buf = alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
2472                         sprintf(buf, "module reload %s", cfmtime->who_asked);
2473                         ast_cli_command(a->fd, buf);
2474                 }
2475         }
2476         AST_LIST_UNLOCK(&cfmtime_head);
2477
2478         return CLI_SUCCESS;
2479 }
2480
2481 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2482 {
2483         struct cache_file_mtime *cfmtime;
2484
2485         switch (cmd) {
2486         case CLI_INIT:
2487                 e->command = "config list";
2488                 e->usage =
2489                         "Usage: config list\n"
2490                         "   Show all modules that have loaded a configuration file\n";
2491                 return NULL;
2492         case CLI_GENERATE:
2493                 return NULL;
2494         }
2495
2496         AST_LIST_LOCK(&cfmtime_head);
2497         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2498                 ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
2499         }
2500         AST_LIST_UNLOCK(&cfmtime_head);
2501
2502         return CLI_SUCCESS;
2503 }
2504
2505 static struct ast_cli_entry cli_config[] = {
2506         AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
2507         AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
2508         AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
2509 };
2510
2511 int register_config_cli() 
2512 {
2513         ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));
2514         return 0;
2515 }