One more typo in config.c; and missed conversions due to the constifying of ast_varia...
[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 <stdio.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <time.h>
39 #include <sys/stat.h>
40 #include <sys/socket.h>         /* for AF_INET */
41 #define AST_INCLUDE_GLOB 1
42 #ifdef AST_INCLUDE_GLOB
43 #if defined(__Darwin__) || defined(__CYGWIN__)
44 #define GLOB_ABORTED GLOB_ABEND
45 #endif
46 # include <glob.h>
47 #endif
48
49 #include "asterisk/config.h"
50 #include "asterisk/cli.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/options.h"
53 #include "asterisk/logger.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/channel.h"
56 #include "asterisk/app.h"
57 #include "asterisk/astobj2.h"
58
59 #define MAX_NESTED_COMMENTS 128
60 #define COMMENT_START ";--"
61 #define COMMENT_END "--;"
62 #define COMMENT_META ';'
63 #define COMMENT_TAG '-'
64
65 static char *extconfig_conf = "extconfig.conf";
66
67
68 /*! \brief Structure to keep comments for rewriting configuration files */
69 struct ast_comment {
70         struct ast_comment *next;
71         char cmt[0];
72 };
73
74 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
75 struct cache_file_include {
76         AST_LIST_ENTRY(cache_file_include) list;
77         char include[0];
78 };
79
80 struct cache_file_mtime {
81         AST_LIST_ENTRY(cache_file_mtime) list;
82         AST_LIST_HEAD(includes, cache_file_include) includes;
83         unsigned int has_exec:1;
84         time_t mtime;
85         char filename[0];
86 };
87
88 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
89
90 #define CB_INCR 250
91
92 static void CB_INIT(char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size)
93 {
94         if (!(*comment_buffer)) {
95                 *comment_buffer = ast_malloc(CB_INCR);
96                 if (!(*comment_buffer))
97                         return;
98                 (*comment_buffer)[0] = 0;
99                 *comment_buffer_size = CB_INCR;
100                 *lline_buffer = ast_malloc(CB_INCR);
101                 if (!(*lline_buffer))
102                         return;
103                 (*lline_buffer)[0] = 0;
104                 *lline_buffer_size = CB_INCR;
105         } else {
106                 (*comment_buffer)[0] = 0;
107                 (*lline_buffer)[0] = 0;
108         }
109 }
110
111 static void  CB_ADD(char **comment_buffer, int *comment_buffer_size, char *str)
112 {
113         int rem = *comment_buffer_size - strlen(*comment_buffer) - 1;
114         int siz = strlen(str);
115         if (rem < siz+1) {
116                 *comment_buffer = ast_realloc(*comment_buffer, *comment_buffer_size + CB_INCR + siz + 1);
117                 if (!(*comment_buffer))
118                         return;
119                 *comment_buffer_size += CB_INCR+siz+1;
120         }
121         strcat(*comment_buffer,str);
122 }
123
124 static void  CB_ADD_LEN(char **comment_buffer, int *comment_buffer_size, char *str, int len)
125 {
126         int cbl = strlen(*comment_buffer) + 1;
127         int rem = *comment_buffer_size - cbl;
128         if (rem < len+1) {
129                 *comment_buffer = ast_realloc(*comment_buffer, *comment_buffer_size + CB_INCR + len + 1);
130                 if (!(*comment_buffer))
131                         return;
132                 *comment_buffer_size += CB_INCR+len+1;
133         }
134         strncat(*comment_buffer,str,len);
135         (*comment_buffer)[cbl+len-1] = 0;
136 }
137
138 static void  LLB_ADD(char **lline_buffer, int *lline_buffer_size, char *str)
139 {
140         int rem = *lline_buffer_size - strlen(*lline_buffer) - 1;
141         int siz = strlen(str);
142         if (rem < siz+1) {
143                 *lline_buffer = ast_realloc(*lline_buffer, *lline_buffer_size + CB_INCR + siz + 1);
144                 if (!(*lline_buffer)) 
145                         return;
146                 *lline_buffer_size += CB_INCR + siz + 1;
147         }
148         strcat(*lline_buffer,str);
149 }
150
151 static void CB_RESET(char **comment_buffer, char **lline_buffer)  
152
153         (*comment_buffer)[0] = 0; 
154         (*lline_buffer)[0] = 0;
155 }
156
157
158 static struct ast_comment *ALLOC_COMMENT(const char *buffer)
159
160         struct ast_comment *x;
161         x = ast_calloc(1, sizeof(*x)+strlen(buffer)+1);
162         strcpy(x->cmt, buffer);
163         return x;
164 }
165
166 /* I need to keep track of each config file, and all its inclusions,
167    so that we can track blank lines in each */
168
169 struct inclfile
170 {
171         char *fname;
172         int lineno;
173 };
174
175 static int hash_string(const void *obj, const int flags)
176 {
177         char *str = ((struct inclfile*)obj)->fname;
178         int total;
179
180         for (total=0; *str; str++)
181         {
182                 unsigned int tmp = total;
183                 total <<= 1; /* multiply by 2 */
184                 total += tmp; /* multiply by 3 */
185                 total <<= 2; /* multiply by 12 */
186                 total += tmp; /* multiply by 13 */
187         
188                 total += ((unsigned int)(*str));
189         }
190         if (total < 0)
191                 total = -total;
192         return total;
193 }
194
195 static int hashtab_compare_strings(void *a, void *b, int flags)
196 {
197         const struct inclfile *ae = a, *be = b;
198         return !strcmp(ae->fname, be->fname) ? CMP_MATCH : 0;
199 }
200
201 static struct ast_config_map {
202         struct ast_config_map *next;
203         char *name;
204         char *driver;
205         char *database;
206         char *table;
207         char stuff[0];
208 } *config_maps = NULL;
209
210 AST_MUTEX_DEFINE_STATIC(config_lock);
211 static struct ast_config_engine *config_engine_list;
212
213 #define MAX_INCLUDE_LEVEL 10
214
215 struct ast_category_template_instance {
216         char name[80]; /* redundant? */
217         const struct ast_category *inst;
218         AST_LIST_ENTRY(ast_category_template_instance) next;
219 };
220
221 struct ast_category {
222         char name[80];
223         int ignored;                    /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
224         int include_level;
225         char *file;                /*!< the file name from whence this declaration was read */
226         int lineno;
227         AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
228         struct ast_comment *precomments;
229         struct ast_comment *sameline;
230         struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
231         struct ast_variable *root;
232         struct ast_variable *last;
233         struct ast_category *next;
234 };
235
236 struct ast_config {
237         struct ast_category *root;
238         struct ast_category *last;
239         struct ast_category *current;
240         struct ast_category *last_browse;     /*!< used to cache the last category supplied via category_browse */
241         int include_level;
242         int max_include_level;
243         struct ast_config_include *includes;  /*!< a list of inclusions, which should describe the entire tree */
244 };
245
246 struct ast_config_include {
247         char *include_location_file;     /*!< file name in which the include occurs */
248         int  include_location_lineno;    /*!< lineno where include occurred */
249         int  exec;                       /*!< set to non-zero if itsa #exec statement */
250         char *exec_file;                 /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
251         char *included_file;             /*!< file name included */
252         int inclusion_count;             /*!< if the file is included more than once, a running count thereof -- but, worry not,
253                                               we explode the instances and will include those-- so all entries will be unique */
254         int output;                      /*!< a flag to indicate if the inclusion has been output */
255         struct ast_config_include *next; /*!< ptr to next inclusion in the list */
256 };
257
258 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename) 
259 {
260         struct ast_variable *variable;
261         int name_len = strlen(name) + 1;        
262         int val_len = strlen(value) + 1;        
263         int fn_len = strlen(filename) + 1;      
264
265         if ((variable = ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable)))) {
266                 char *dst = variable->stuff;    /* writable space starts here */
267                 variable->name = strcpy(dst, name);
268                 dst += name_len;
269                 variable->value = strcpy(dst, value);
270                 dst += val_len;
271                 variable->file = strcpy(dst, filename);
272         }
273         return variable;
274 }
275
276 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)
277 {
278         /* a file should be included ONCE. Otherwise, if one of the instances is changed,
279        then all be changed. -- how do we know to include it? -- Handling modified 
280        instances is possible, I'd have
281        to create a new master for each instance. */
282         struct ast_config_include *inc;
283         struct stat statbuf;
284         
285         inc = ast_include_find(conf, included_file);
286         if (inc) {
287                 do {
288                         inc->inclusion_count++;
289                         snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
290                 } while (stat(real_included_file_name, &statbuf) == 0);
291                 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);
292         } else
293                 *real_included_file_name = 0;
294         
295         inc = ast_calloc(1,sizeof(struct ast_config_include));
296         inc->include_location_file = ast_strdup(from_file);
297         inc->include_location_lineno = from_lineno;
298         if (!ast_strlen_zero(real_included_file_name))
299                 inc->included_file = ast_strdup(real_included_file_name);
300         else
301                 inc->included_file = ast_strdup(included_file);
302         
303         inc->exec = is_exec;
304         if (is_exec)
305                 inc->exec_file = ast_strdup(exec_file);
306         
307         /* attach this new struct to the conf struct */
308         inc->next = conf->includes;
309         conf->includes = inc;
310         
311         return inc;
312 }
313
314 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
315 {
316         struct ast_config_include *incl;
317         struct ast_category *cat;
318         struct ast_variable *v;
319         
320         int from_len = strlen(from_file);
321         int to_len = strlen(to_file);
322         
323         if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
324                 return;
325         
326         /* the manager code allows you to read in one config file, then
327        write it back out under a different name. But, the new arrangement
328            ties output lines to the file name. So, before you try to write
329        the config file to disk, better riffle thru the data and make sure
330        the file names are changed.
331         */
332         /* file names are on categories, includes (of course), and on variables. So,
333            traverse all this and swap names */
334
335         for (incl = conf->includes; incl; incl=incl->next) {
336                 if (strcmp(incl->include_location_file,from_file) == 0) {
337                         if (from_len >= to_len)
338                                 strcpy(incl->include_location_file, to_file);
339                         else {
340                                 free(incl->include_location_file);
341                                 incl->include_location_file = strdup(to_file);
342                         }
343                 }
344         }
345         for (cat = conf->root; cat; cat = cat->next) {
346                 if (strcmp(cat->file,from_file) == 0) {
347                         if (from_len >= to_len)
348                                 strcpy(cat->file, to_file);
349                         else {
350                                 free(cat->file);
351                                 cat->file = strdup(to_file);
352                         }
353                 }
354                 for (v = cat->root; v; v = v->next) {
355                         if (strcmp(v->file,from_file) == 0) {
356                                 if (from_len >= to_len)
357                                         strcpy(v->file, to_file);
358                                 else {
359                                         free(v->file);
360                                         v->file = strdup(to_file);
361                                 }
362                         }
363                 }
364         }
365 }
366
367 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
368 {
369         struct ast_config_include *x;
370         for (x=conf->includes;x;x=x->next)
371         {
372                 if (strcmp(x->included_file,included_file) == 0)
373                         return x;
374         }
375         return 0;
376 }
377
378
379 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
380 {
381         if (!variable)
382                 return;
383         if (category->last)
384                 category->last->next = variable;
385         else
386                 category->root = variable;
387         category->last = variable;
388         while (category->last->next)
389                 category->last = category->last->next;
390 }
391
392 void ast_variables_destroy(struct ast_variable *v)
393 {
394         struct ast_variable *vn;
395
396         while (v) {
397                 vn = v;
398                 v = v->next;
399                 ast_free(vn);
400         }
401 }
402
403 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
404 {
405         struct ast_category *cat = NULL;
406
407         if (category && config->last_browse && (config->last_browse->name == category))
408                 cat = config->last_browse;
409         else
410                 cat = ast_category_get(config, category);
411
412         return (cat) ? cat->root : NULL;
413 }
414
415 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
416 {
417         const char *tmp;
418         tmp = ast_variable_retrieve(cfg, cat, var);
419         if (!tmp)
420                 tmp = ast_variable_retrieve(cfg, "general", var);
421         return tmp;
422 }
423
424
425 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
426 {
427         struct ast_variable *v;
428
429         if (category) {
430                 for (v = ast_variable_browse(config, category); v; v = v->next) {
431                         if (!strcasecmp(variable, v->name))
432                                 return v->value;
433                 }
434         } else {
435                 struct ast_category *cat;
436
437                 for (cat = config->root; cat; cat = cat->next)
438                         for (v = cat->root; v; v = v->next)
439                                 if (!strcasecmp(variable, v->name))
440                                         return v->value;
441         }
442
443         return NULL;
444 }
445
446 static struct ast_variable *variable_clone(const struct ast_variable *old)
447 {
448         struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
449
450         if (new) {
451                 new->lineno = old->lineno;
452                 new->object = old->object;
453                 new->blanklines = old->blanklines;
454                 /* TODO: clone comments? */
455         }
456
457         return new;
458 }
459  
460 static void move_variables(struct ast_category *old, struct ast_category *new)
461 {
462         struct ast_variable *var = old->root;
463         old->root = NULL;
464 #if 1
465         /* we can just move the entire list in a single op */
466         ast_variable_append(new, var);
467 #else
468         while (var) {
469                 struct ast_variable *next = var->next;
470                 var->next = NULL;
471                 ast_variable_append(new, var);
472                 var = next;
473         }
474 #endif
475 }
476
477 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno) 
478 {
479         struct ast_category *category;
480
481         if ((category = ast_calloc(1, sizeof(*category))))
482                 ast_copy_string(category->name, name, sizeof(category->name));
483         category->file = strdup(in_file);
484         category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
485         return category;
486 }
487
488 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
489 {
490         struct ast_category *cat;
491
492         /* try exact match first, then case-insensitive match */
493         for (cat = config->root; cat; cat = cat->next) {
494                 if (cat->name == category_name && (ignored || !cat->ignored))
495                         return cat;
496         }
497
498         for (cat = config->root; cat; cat = cat->next) {
499                 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
500                         return cat;
501         }
502
503         return NULL;
504 }
505
506 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
507 {
508         return category_get(config, category_name, 0);
509 }
510
511 int ast_category_exist(const struct ast_config *config, const char *category_name)
512 {
513         return !!ast_category_get(config, category_name);
514 }
515
516 void ast_category_append(struct ast_config *config, struct ast_category *category)
517 {
518         if (config->last)
519                 config->last->next = category;
520         else
521                 config->root = category;
522         category->include_level = config->include_level;
523         config->last = category;
524         config->current = category;
525 }
526
527 static void ast_destroy_comments(struct ast_category *cat)
528 {
529         struct ast_comment *n, *p;
530         for (p=cat->precomments; p; p=n) {
531                 n = p->next;
532                 free(p);
533         }
534         for (p=cat->sameline; p; p=n) {
535                 n = p->next;
536                 free(p);
537         }
538         for (p=cat->trailing; p; p=n) {
539                 n = p->next;
540                 free(p);
541         }
542         cat->precomments = NULL;
543         cat->sameline = NULL;
544         cat->trailing = NULL;
545 }
546
547 static void ast_destroy_template_list(struct ast_category *cat)
548 {
549         struct ast_category_template_instance *x;
550
551         while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
552                 free(x);
553 }
554
555 void ast_category_destroy(struct ast_category *cat)
556 {
557         ast_variables_destroy(cat->root);
558         if (cat->file) {
559                 free(cat->file);
560                 cat->file = 0;
561         }
562         ast_destroy_comments(cat);
563         ast_destroy_template_list(cat);
564         ast_free(cat);
565 }
566
567 static void ast_includes_destroy(struct ast_config_include *incls)
568 {
569         struct ast_config_include *incl,*inclnext;
570         
571         for (incl=incls; incl; incl = inclnext) {
572                 inclnext = incl->next;
573                 if (incl->include_location_file)
574                         free(incl->include_location_file);
575                 if (incl->exec_file)
576                         free(incl->exec_file);
577                 if (incl->included_file)
578                         free(incl->included_file);
579                 free(incl);
580         }
581 }
582
583 static struct ast_category *next_available_category(struct ast_category *cat)
584 {
585         for (; cat && cat->ignored; cat = cat->next);
586
587         return cat;
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                 ao2_ref(fx,1); /* bump the ref count, so it looks like we just got the ref from find */
1435         }
1436 }
1437
1438 static int count_linefeeds(char *str)
1439 {
1440         int count = 0;
1441         while (*str) {
1442                 if (*str =='\n')
1443                         count++;
1444                 str++;
1445         }
1446         return count;
1447 }
1448
1449 static int count_linefeeds_in_comments(struct ast_comment *x)
1450 {
1451         int count = 0;
1452         while (x)
1453         {
1454                 count += count_linefeeds(x->cmt);
1455                 x = x->next;
1456         }
1457         return count;
1458 }
1459
1460 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1461 {
1462         int precomment_lines = count_linefeeds_in_comments(precomments);
1463         int i;
1464
1465         /* I don't have to worry about those ;! comments, they are
1466            stored in the precomments, but not printed back out.
1467            I did have to make sure that comments following
1468            the ;! header comments were not also deleted in the process */
1469         for (i=fi->lineno;i<lineno - precomment_lines; i++) {
1470                 fprintf(fp,"\n");
1471         }
1472         fi->lineno = lineno+1; /* Advance the file lineno */
1473 }
1474
1475 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1476 {
1477         FILE *f;
1478         char fn[256];
1479         struct ast_variable *var;
1480         struct ast_category *cat;
1481         struct ast_comment *cmt;
1482         struct ast_config_include *incl;
1483         int blanklines = 0;
1484         struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
1485         struct inclfile *fi = 0;
1486
1487         /* reset all the output flags, in case this isn't our first time saving this data */
1488
1489         for (incl=cfg->includes; incl; incl = incl->next)
1490                 incl->output = 0;
1491
1492         /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
1493            are all truncated to zero bytes and have that nice header*/
1494
1495         for (incl=cfg->includes; incl; incl = incl->next)
1496         {
1497                 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*/
1498                         FILE *f1;
1499
1500                         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 */
1501                         f1 = fopen(fn,"w");
1502                         if (f1) {
1503                                 gen_header(f1, configfile, fn, generator);
1504                                 fclose(f1); /* this should zero out the file */
1505                         } else {
1506                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1507                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1508                         }
1509                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1510                         fi = 0;
1511                 }
1512         }
1513
1514         set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
1515 #ifdef __CYGWIN__       
1516         if ((f = fopen(fn, "w+"))) {
1517 #else
1518         if ((f = fopen(fn, "w"))) {
1519 #endif      
1520                 ast_verb(2, "Saving '%s': ", fn);
1521                 gen_header(f, configfile, fn, generator);
1522                 cat = cfg->root;
1523                 fclose(f);
1524                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1525                 
1526                 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
1527                 /* since each var, cat, and associated comments can come from any file, we have to be 
1528                    mobile, and open each file, print, and close it on an entry-by-entry basis */
1529
1530                 while (cat) {
1531                         set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
1532                         f = fopen(fn, "a");
1533                         if (!f)
1534                         {
1535                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1536                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1537                                 ao2_ref(fileset, -1);
1538                                 return -1;
1539                         }
1540
1541                         /* dump any includes that happen before this category header */
1542                         for (incl=cfg->includes; incl; incl = incl->next) {
1543                                 if (strcmp(incl->include_location_file, cat->file) == 0){
1544                                         if (cat->lineno > incl->include_location_lineno && !incl->output) {
1545                                                 if (incl->exec)
1546                                                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1547                                                 else
1548                                                         fprintf(f,"#include \"%s\"\n", incl->included_file);
1549                                                 incl->output = 1;
1550                                         }
1551                                 }
1552                         }
1553                         
1554                         insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
1555                         /* Dump section with any appropriate comment */
1556                         for (cmt = cat->precomments; cmt; cmt=cmt->next) {
1557                                 char *cmtp = cmt->cmt;
1558                                 while (*cmtp == ';' && *(cmtp+1) == '!') {
1559                                         char *cmtp2 = strchr(cmtp+1, '\n');
1560                                         if (cmtp2)
1561                                                 cmtp = cmtp2+1;
1562                                         else cmtp = 0;
1563                                 }
1564                                 if (cmtp)
1565                                         fprintf(f,"%s", cmtp);
1566                         }
1567                         if (!cat->precomments)
1568                                 fprintf(f,"\n");
1569                         fprintf(f, "[%s]", cat->name);
1570                         if (cat->ignored)
1571                                 fprintf(f, "(!)");
1572                         if (!AST_LIST_EMPTY(&cat->template_instances)) {
1573                                 struct ast_category_template_instance *x;
1574                                 fprintf(f, "(");
1575                                 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1576                                         fprintf(f,"%s",x->name);
1577                                         if (x != AST_LIST_LAST(&cat->template_instances))
1578                                                 fprintf(f,",");
1579                                 }
1580                                 fprintf(f, ")");
1581                         }
1582                         for(cmt = cat->sameline; cmt; cmt=cmt->next)
1583                         {
1584                                 fprintf(f,"%s", cmt->cmt);
1585                         }
1586                         if (!cat->sameline)
1587                                 fprintf(f,"\n");
1588                         for (cmt = cat->trailing; cmt; cmt=cmt->next) {
1589                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1590                                         fprintf(f,"%s", cmt->cmt);
1591                         }
1592                         fclose(f);
1593                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1594                         fi = 0;
1595                         
1596                         var = cat->root;
1597                         while (var) {
1598                                 struct ast_category_template_instance *x;
1599                                 int found = 0;
1600                                 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1601                                         struct ast_variable *v;
1602                                         for (v = x->inst->root; v; v = v->next) {
1603                                                 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
1604                                                         found = 1;
1605                                                         break;
1606                                                 }
1607                                         }
1608                                         if (found)
1609                                                 break;
1610                                 }
1611                                 if (found) {
1612                                         var = var->next;
1613                                         continue;
1614                                 }
1615                                 set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
1616                                 f = fopen(fn, "a");
1617                                 if (!f)
1618                                 {
1619                                         ast_debug(1, "Unable to open for writing: %s\n", fn);
1620                                         ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1621                                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1622                                         fi = 0;
1623                                         ao2_ref(fileset, -1);
1624                                         return -1;
1625                                 }
1626                                 
1627                                 /* dump any includes that happen before this category header */
1628                                 for (incl=cfg->includes; incl; incl = incl->next) {
1629                                         if (strcmp(incl->include_location_file, var->file) == 0){
1630                                                 if (var->lineno > incl->include_location_lineno && !incl->output) {
1631                                                         if (incl->exec)
1632                                                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1633                                                         else
1634                                                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1635                                                         incl->output = 1;
1636                                                 }
1637                                         }
1638                                 }
1639                                 
1640                                 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
1641                                 for (cmt = var->precomments; cmt; cmt=cmt->next) {
1642                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1643                                                 fprintf(f,"%s", cmt->cmt);
1644                                 }
1645                                 if (var->sameline) 
1646                                         fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
1647                                 else    
1648                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
1649                                 for (cmt = var->trailing; cmt; cmt=cmt->next) {
1650                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1651                                                 fprintf(f,"%s", cmt->cmt);
1652                                 }
1653                                 if (var->blanklines) {
1654                                         blanklines = var->blanklines;
1655                                         while (blanklines--)
1656                                                 fprintf(f, "\n");
1657                                 }
1658                                 
1659                                 fclose(f);
1660                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1661                                 fi = 0;
1662                                 
1663                                 var = var->next;
1664                         }
1665                         cat = cat->next;
1666                 }
1667                 if (!option_debug)
1668                         ast_verb(2, "Saved\n");
1669         } else {
1670                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1671                 ast_verb(2, "Unable to write (%s)", strerror(errno));
1672                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1673                 ao2_ref(fileset, -1);
1674                 return -1;
1675         }
1676
1677         /* Now, for files with trailing #include/#exec statements,
1678            we have to make sure every entry is output */
1679
1680         for (incl=cfg->includes; incl; incl = incl->next) {
1681                 if (!incl->output) {
1682                         /* open the respective file */
1683                         set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
1684                         f = fopen(fn, "a");
1685                         if (!f)
1686                         {
1687                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1688                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1689                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1690                                 fi = 0;
1691                                 ao2_ref(fileset, -1);
1692                                 return -1;
1693                         }
1694                         
1695                         /* output the respective include */
1696                         if (incl->exec)
1697                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1698                         else
1699                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1700                         fclose(f);
1701                         incl->output = 1;
1702                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1703                         fi = 0;
1704                 }
1705         }
1706         ao2_ref(fileset, -1); /* this should destroy the hash container */
1707                                 
1708         return 0;
1709 }
1710
1711 static void clear_config_maps(void) 
1712 {
1713         struct ast_config_map *map;
1714
1715         ast_mutex_lock(&config_lock);
1716
1717         while (config_maps) {
1718                 map = config_maps;
1719                 config_maps = config_maps->next;
1720                 ast_free(map);
1721         }
1722                 
1723         ast_mutex_unlock(&config_lock);
1724 }
1725
1726 static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
1727 {
1728         struct ast_config_map *map;
1729         int length;
1730
1731         length = sizeof(*map);
1732         length += strlen(name) + 1;
1733         length += strlen(driver) + 1;
1734         length += strlen(database) + 1;
1735         if (table)
1736                 length += strlen(table) + 1;
1737
1738         if (!(map = ast_calloc(1, length)))
1739                 return -1;
1740
1741         map->name = map->stuff;
1742         strcpy(map->name, name);
1743         map->driver = map->name + strlen(map->name) + 1;
1744         strcpy(map->driver, driver);
1745         map->database = map->driver + strlen(map->driver) + 1;
1746         strcpy(map->database, database);
1747         if (table) {
1748                 map->table = map->database + strlen(map->database) + 1;
1749                 strcpy(map->table, table);
1750         }
1751         map->next = config_maps;
1752
1753         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
1754
1755         config_maps = map;
1756         return 0;
1757 }
1758
1759 int read_config_maps(void) 
1760 {
1761         struct ast_config *config, *configtmp;
1762         struct ast_variable *v;
1763         char *driver, *table, *database, *stringp, *tmp;
1764         struct ast_flags flags = { 0 };
1765
1766         clear_config_maps();
1767
1768         configtmp = ast_config_new();
1769         configtmp->max_include_level = 1;
1770         config = ast_config_internal_load(extconfig_conf, configtmp, flags, "");
1771         if (!config) {
1772                 ast_config_destroy(configtmp);
1773                 return 0;
1774         }
1775
1776         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
1777                 char buf[512];
1778                 ast_copy_string(buf, v->value, sizeof(buf));
1779                 stringp = buf;
1780                 driver = strsep(&stringp, ",");
1781
1782                 if ((tmp = strchr(stringp, '\"')))
1783                         stringp = tmp;
1784
1785                 /* check if the database text starts with a double quote */
1786                 if (*stringp == '"') {
1787                         stringp++;
1788                         database = strsep(&stringp, "\"");
1789                         strsep(&stringp, ",");
1790                 } else {
1791                         /* apparently this text has no quotes */
1792                         database = strsep(&stringp, ",");
1793                 }
1794
1795                 table = strsep(&stringp, ",");
1796
1797                 if (!strcmp(v->name, extconfig_conf)) {
1798                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
1799                         continue;
1800                 }
1801
1802                 if (!strcmp(v->name, "asterisk.conf")) {
1803                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
1804                         continue;
1805                 }
1806
1807                 if (!strcmp(v->name, "logger.conf")) {
1808                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
1809                         continue;
1810                 }
1811
1812                 if (!driver || !database)
1813                         continue;
1814                 if (!strcasecmp(v->name, "sipfriends")) {
1815                         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");
1816                         append_mapping("sipusers", driver, database, table ? table : "sipfriends");
1817                         append_mapping("sippeers", driver, database, table ? table : "sipfriends");
1818                 } else if (!strcasecmp(v->name, "iaxfriends")) {
1819                         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");
1820                         append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
1821                         append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
1822                 } else 
1823                         append_mapping(v->name, driver, database, table);
1824         }
1825                 
1826         ast_config_destroy(config);
1827         return 0;
1828 }
1829
1830 int ast_config_engine_register(struct ast_config_engine *new) 
1831 {
1832         struct ast_config_engine *ptr;
1833
1834         ast_mutex_lock(&config_lock);
1835
1836         if (!config_engine_list) {
1837                 config_engine_list = new;
1838         } else {
1839                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
1840                 ptr->next = new;
1841         }
1842
1843         ast_mutex_unlock(&config_lock);
1844         ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
1845
1846         return 1;
1847 }
1848
1849 int ast_config_engine_deregister(struct ast_config_engine *del) 
1850 {
1851         struct ast_config_engine *ptr, *last=NULL;
1852
1853         ast_mutex_lock(&config_lock);
1854
1855         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
1856                 if (ptr == del) {
1857                         if (last)
1858                                 last->next = ptr->next;
1859                         else
1860                                 config_engine_list = ptr->next;
1861                         break;
1862                 }
1863                 last = ptr;
1864         }
1865
1866         ast_mutex_unlock(&config_lock);
1867
1868         return 0;
1869 }
1870
1871 /*! \brief Find realtime engine for realtime family */
1872 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz) 
1873 {
1874         struct ast_config_engine *eng, *ret = NULL;
1875         struct ast_config_map *map;
1876
1877         ast_mutex_lock(&config_lock);
1878
1879         for (map = config_maps; map; map = map->next) {
1880                 if (!strcasecmp(family, map->name)) {
1881                         if (database)
1882                                 ast_copy_string(database, map->database, dbsiz);
1883                         if (table)
1884                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
1885                         break;
1886                 }
1887         }
1888
1889         /* Check if the required driver (engine) exist */
1890         if (map) {
1891                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
1892                         if (!strcasecmp(eng->name, map->driver))
1893                                 ret = eng;
1894                 }
1895         }
1896
1897         ast_mutex_unlock(&config_lock);
1898         
1899         /* if we found a mapping, but the engine is not available, then issue a warning */
1900         if (map && !ret)
1901                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
1902
1903         return ret;
1904 }
1905
1906 static struct ast_config_engine text_file_engine = {
1907         .name = "text",
1908         .load_func = config_text_file_load,
1909 };
1910
1911 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file)
1912 {
1913         char db[256];
1914         char table[256];
1915         struct ast_config_engine *loader = &text_file_engine;
1916         struct ast_config *result; 
1917
1918         if (cfg->include_level == cfg->max_include_level) {
1919                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
1920                 return NULL;
1921         }
1922
1923         cfg->include_level++;
1924
1925         if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
1926                 struct ast_config_engine *eng;
1927
1928                 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
1929
1930
1931                 if (eng && eng->load_func) {
1932                         loader = eng;
1933                 } else {
1934                         eng = find_engine("global", db, sizeof(db), table, sizeof(table));
1935                         if (eng && eng->load_func)
1936                                 loader = eng;
1937                 }
1938         }
1939
1940         result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file);
1941
1942         if (result && result != CONFIG_STATUS_FILEUNCHANGED)
1943                 result->include_level--;
1944         else
1945                 cfg->include_level--;
1946
1947         return result;
1948 }
1949
1950 struct ast_config *ast_config_load(const char *filename, struct ast_flags flags)
1951 {
1952         struct ast_config *cfg;
1953         struct ast_config *result;
1954
1955         cfg = ast_config_new();
1956         if (!cfg)
1957                 return NULL;
1958
1959         result = ast_config_internal_load(filename, cfg, flags, "");
1960         if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
1961                 ast_config_destroy(cfg);
1962
1963         return result;
1964 }
1965
1966 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
1967 {
1968         struct ast_config_engine *eng;
1969         char db[256]="";
1970         char table[256]="";
1971         struct ast_variable *res=NULL;
1972
1973         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1974         if (eng && eng->realtime_func) 
1975                 res = eng->realtime_func(db, table, ap);
1976
1977         return res;
1978 }
1979
1980 struct ast_variable *ast_load_realtime_all(const char *family, ...)
1981 {
1982         struct ast_variable *res;
1983         va_list ap;
1984
1985         va_start(ap, family);
1986         res = ast_load_realtime_helper(family, ap);
1987         va_end(ap);
1988
1989         return res;
1990 }
1991
1992 struct ast_variable *ast_load_realtime(const char *family, ...)
1993 {
1994         struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
1995         va_list ap;
1996
1997         va_start(ap, family);
1998         res = ast_load_realtime_helper(family, ap);
1999         va_end(ap);
2000
2001         /* Eliminate blank entries */
2002         for (cur = res; cur; cur = cur->next) {
2003                 if (freeme) {
2004                         ast_free(freeme);
2005                         freeme = NULL;
2006                 }
2007
2008                 if (ast_strlen_zero(cur->value)) {
2009                         if (prev)
2010                                 prev->next = cur->next;
2011                         else
2012                                 res = cur->next;
2013                         freeme = cur;
2014                 } else {
2015                         prev = cur;
2016                 }
2017         }
2018         return res;
2019 }
2020
2021 /*! \brief Check if realtime engine is configured for family */
2022 int ast_check_realtime(const char *family)
2023 {
2024         struct ast_config_engine *eng;
2025
2026         eng = find_engine(family, NULL, 0, NULL, 0);
2027         if (eng)
2028                 return 1;
2029         return 0;
2030
2031 }
2032
2033 /*! \brief Check if there's any realtime engines loaded */
2034 int ast_realtime_enabled()
2035 {
2036         return config_maps ? 1 : 0;
2037 }
2038
2039 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
2040 {
2041         struct ast_config_engine *eng;
2042         char db[256]="";
2043         char table[256]="";
2044         struct ast_config *res=NULL;
2045         va_list ap;
2046
2047         va_start(ap, family);
2048         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2049         if (eng && eng->realtime_multi_func) 
2050                 res = eng->realtime_multi_func(db, table, ap);
2051         va_end(ap);
2052
2053         return res;
2054 }
2055
2056 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2057 {
2058         struct ast_config_engine *eng;
2059         int res = -1;
2060         char db[256]="";
2061         char table[256]="";
2062         va_list ap;
2063
2064         va_start(ap, lookup);
2065         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2066         if (eng && eng->update_func) 
2067                 res = eng->update_func(db, table, keyfield, lookup, ap);
2068         va_end(ap);
2069
2070         return res;
2071 }
2072
2073 int ast_store_realtime(const char *family, ...) {
2074         struct ast_config_engine *eng;
2075         int res = -1;
2076         char db[256]="";
2077         char table[256]="";
2078         va_list ap;
2079
2080         va_start(ap, family);
2081         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2082         if (eng && eng->store_func) 
2083                 res = eng->store_func(db, table, ap);
2084         va_end(ap);
2085
2086         return res;
2087 }
2088
2089 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...) {
2090         struct ast_config_engine *eng;
2091         int res = -1;
2092         char db[256]="";
2093         char table[256]="";
2094         va_list ap;
2095
2096         va_start(ap, lookup);
2097         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2098         if (eng && eng->destroy_func) 
2099                 res = eng->destroy_func(db, table, keyfield, lookup, ap);
2100         va_end(ap);
2101
2102         return res;
2103 }
2104
2105 /*! \brief Helper function to parse arguments
2106  * See documentation in config.h
2107  */
2108 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2109         void *p_result, ...)
2110 {
2111         va_list ap;
2112         int error = 0;
2113
2114         va_start(ap, p_result);
2115         switch (flags & PARSE_TYPE) {
2116         case PARSE_INT32:
2117             {
2118                 int32_t *result = p_result;
2119                 int32_t x, def = result ? *result : 0,
2120                         high = (int32_t)0x7fffffff,
2121                         low  = (int32_t)0x80000000;
2122                 /* optional argument: first default value, then range */
2123                 if (flags & PARSE_DEFAULT)
2124                         def = va_arg(ap, int32_t);
2125                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2126                         /* range requested, update bounds */
2127                         low = va_arg(ap, int32_t);
2128                         high = va_arg(ap, int32_t);
2129                 }
2130                 x = strtol(arg, NULL, 0);
2131                 error = (x < low) || (x > high);
2132                 if (flags & PARSE_OUT_RANGE)
2133                         error = !error;
2134                 if (result)
2135                         *result  = error ? def : x;
2136                 ast_debug(3,
2137                         "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
2138                         arg, low, high,
2139                         result ? *result : x, error);
2140                 break;
2141             }
2142
2143         case PARSE_UINT32:
2144             {
2145                 uint32_t *result = p_result;
2146                 uint32_t x, def = result ? *result : 0,
2147                         low = 0, high = (uint32_t)~0;
2148                 /* optional argument: first default value, then range */
2149                 if (flags & PARSE_DEFAULT)
2150                         def = va_arg(ap, uint32_t);
2151                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2152                         /* range requested, update bounds */
2153                         low = va_arg(ap, uint32_t);
2154                         high = va_arg(ap, uint32_t);
2155                 }
2156                 x = strtoul(arg, NULL, 0);
2157                 error = (x < low) || (x > high);
2158                 if (flags & PARSE_OUT_RANGE)
2159                         error = !error;
2160                 if (result)
2161                         *result  = error ? def : x;
2162                 ast_debug(3,
2163                         "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
2164                         arg, low, high,
2165                         result ? *result : x, error);
2166                 break;
2167             }
2168
2169         case PARSE_INADDR:
2170             {
2171                 char *port, *buf;
2172                 struct sockaddr_in _sa_buf;     /* buffer for the result */
2173                 struct sockaddr_in *sa = p_result ?
2174                         (struct sockaddr_in *)p_result : &_sa_buf;
2175                 /* default is either the supplied value or the result itself */
2176                 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
2177                         va_arg(ap, struct sockaddr_in *) : sa;
2178                 struct hostent *hp;
2179                 struct ast_hostent ahp;
2180
2181                 bzero(&_sa_buf, sizeof(_sa_buf)); /* clear buffer */
2182                 /* duplicate the string to strip away the :port */
2183                 port = ast_strdupa(arg);
2184                 buf = strsep(&port, ":");
2185                 sa->sin_family = AF_INET;       /* assign family */
2186                 /*
2187                  * honor the ports flag setting, assign default value
2188                  * in case of errors or field unset.
2189                  */
2190                 flags &= PARSE_PORT_MASK; /* the only flags left to process */
2191                 if (port) {
2192                         if (flags == PARSE_PORT_FORBID) {
2193                                 error = 1;      /* port was forbidden */
2194                                 sa->sin_port = def->sin_port;
2195                         } else if (flags == PARSE_PORT_IGNORE)
2196                                 sa->sin_port = def->sin_port;
2197                         else /* accept or require */
2198                                 sa->sin_port = htons(strtol(port, NULL, 0));
2199                 } else {
2200                         sa->sin_port = def->sin_port;
2201                         if (flags == PARSE_PORT_REQUIRE)
2202                                 error = 1;
2203                 }
2204                 /* Now deal with host part, even if we have errors before. */
2205                 hp = ast_gethostbyname(buf, &ahp);
2206                 if (hp) /* resolved successfully */
2207                         memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
2208                 else {
2209                         error = 1;
2210                         sa->sin_addr = def->sin_addr;
2211                 }
2212                 ast_debug(3,
2213                         "extract inaddr from [%s] gives [%s:%d](%d)\n",
2214                         arg, ast_inet_ntoa(sa->sin_addr),
2215                         ntohs(sa->sin_port), error);
2216                 break;
2217             }
2218         }
2219         va_end(ap);
2220         return error;
2221 }
2222
2223 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2224 {
2225         struct ast_config_engine *eng;
2226         struct ast_config_map *map;
2227
2228         switch (cmd) {
2229         case CLI_INIT:
2230                 e->command = "core show config mappings";
2231                 e->usage =
2232                         "Usage: core show config mappings\n"
2233                         "       Shows the filenames to config engines.\n";
2234                 return NULL;
2235         case CLI_GENERATE:
2236                 return NULL;
2237         }
2238         
2239         ast_mutex_lock(&config_lock);
2240
2241         ast_cli(a->fd, "\n\n");
2242         for (eng = config_engine_list; eng; eng = eng->next) {
2243                 ast_cli(a->fd, "\nConfig Engine: %s\n", eng->name);
2244                 for (map = config_maps; map; map = map->next)
2245                         if (!strcasecmp(map->driver, eng->name)) {
2246                                 ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
2247                                         map->table ? map->table : map->name);
2248                         }
2249         }
2250         ast_cli(a->fd,"\n\n");
2251         
2252         ast_mutex_unlock(&config_lock);
2253
2254         return CLI_SUCCESS;
2255 }
2256
2257 static struct ast_cli_entry cli_config[] = {
2258         AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
2259 };
2260
2261 int register_config_cli() 
2262 {
2263         ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));
2264         return 0;
2265 }