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