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