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