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