f2b54bc2d05150a080662ce3fc1d5ee29a135ec4
[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                                         return -1;
1012                                 /* XXX otherwise what ? the default return is 0 anyways */
1013
1014         } else {
1015                 /* Just a line (variable = value) */
1016                 if (!(*cat)) {
1017                         ast_log(LOG_WARNING,
1018                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
1019                         return -1;
1020                 }
1021                 c = strchr(cur, '=');
1022                 if (c) {
1023                         int object;
1024                         *c = 0;
1025                         c++;
1026                         /* Ignore > in => */
1027                         if (*c== '>') {
1028                                 object = 1;
1029                                 c++;
1030                         } else
1031                                 object = 0;
1032                         if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), *suggested_include_file ? suggested_include_file : configfile))) {
1033                                 v->lineno = lineno;
1034                                 v->object = object;
1035                                 *last_cat = 0;
1036                                 *last_var = v;
1037                                 /* Put and reset comments */
1038                                 v->blanklines = 0;
1039                                 ast_variable_append(*cat, v);
1040                                 /* add comments */
1041                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1042                                         v->precomments = ALLOC_COMMENT(comment_buffer);
1043                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1044                                         v->sameline = ALLOC_COMMENT(lline_buffer);
1045                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1046                                         CB_RESET(comment_buffer, lline_buffer);
1047                                 
1048                         } else {
1049                                 return -1;
1050                         }
1051                 } else {
1052                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1053                 }
1054         }
1055         return 0;
1056 }
1057
1058 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)
1059 {
1060         char fn[256];
1061         char buf[8192];
1062         char *new_buf, *comment_p, *process_buf;
1063         FILE *f;
1064         int lineno=0;
1065         int comment = 0, nest[MAX_NESTED_COMMENTS];
1066         struct ast_category *cat = NULL;
1067         int count = 0;
1068         struct stat statbuf;
1069         struct cache_file_mtime *cfmtime = NULL;
1070         struct cache_file_include *cfinclude;
1071         struct ast_variable *last_var = 0;
1072         struct ast_category *last_cat = 0;
1073         /*! Growable string buffer */
1074         struct ast_str *comment_buffer = NULL;  /*!< this will be a comment collector.*/
1075         struct ast_str *lline_buffer = NULL;    /*!< A buffer for stuff behind the ; */
1076
1077         if (cfg)
1078                 cat = ast_config_get_current_category(cfg);
1079
1080         if (filename[0] == '/') {
1081                 ast_copy_string(fn, filename, sizeof(fn));
1082         } else {
1083                 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, filename);
1084         }
1085
1086         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1087                 comment_buffer = ast_str_create(CB_SIZE);
1088                 if (comment_buffer)
1089                         lline_buffer = ast_str_create(CB_SIZE);
1090                 if (!lline_buffer) {
1091                         if (comment_buffer)
1092                                 ast_free(comment_buffer);
1093                         ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1094                         return NULL;
1095                 }
1096         }
1097 #ifdef AST_INCLUDE_GLOB
1098         {
1099                 int glob_ret;
1100                 glob_t globbuf;
1101                 globbuf.gl_offs = 0;    /* initialize it to silence gcc */
1102                 glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1103                 if (glob_ret == GLOB_NOSPACE)
1104                         ast_log(LOG_WARNING,
1105                                 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1106                 else if (glob_ret  == GLOB_ABORTED)
1107                         ast_log(LOG_WARNING,
1108                                 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1109                 else  {
1110                         /* loop over expanded files */
1111                         int i;
1112                         for (i=0; i<globbuf.gl_pathc; i++) {
1113                                 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1114 #endif
1115         /*
1116          * The following is not a loop, but just a convenient way to define a block
1117          * (using do { } while(0) ), and be able to exit from it with 'continue'
1118          * or 'break' in case of errors. Nice trick.
1119          */
1120         do {
1121                 if (stat(fn, &statbuf))
1122                         continue;
1123
1124                 if (!S_ISREG(statbuf.st_mode)) {
1125                         ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1126                         continue;
1127                 }
1128
1129                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1130                         /* Find our cached entry for this configuration file */
1131                         AST_LIST_LOCK(&cfmtime_head);
1132                         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1133                                 if (!strcmp(cfmtime->filename, fn))
1134                                         break;
1135                         }
1136                         if (!cfmtime) {
1137                                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1);
1138                                 if (!cfmtime)
1139                                         continue;
1140                                 AST_LIST_HEAD_INIT(&cfmtime->includes);
1141                                 strcpy(cfmtime->filename, fn);
1142                                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1143                                 AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
1144                         }
1145                 }
1146
1147                 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1148                         /* File is unchanged, what about the (cached) includes (if any)? */
1149                         int unchanged = 1;
1150                         AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1151                                 /* We must glob here, because if we did not, then adding a file to globbed directory would
1152                                  * incorrectly cause no reload to be necessary. */
1153                                 char fn2[256];
1154 #ifdef AST_INCLUDE_GLOB
1155                                 int glob_ret;
1156                                 glob_t globbuf = { .gl_offs = 0 };
1157                                 glob_ret = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &globbuf);
1158                                 /* On error, we reparse */
1159                                 if (glob_ret == GLOB_NOSPACE || glob_ret  == GLOB_ABORTED)
1160                                         unchanged = 0;
1161                                 else  {
1162                                         /* loop over expanded files */
1163                                         int j;
1164                                         for (j = 0; j < globbuf.gl_pathc; j++) {
1165                                                 ast_copy_string(fn2, globbuf.gl_pathv[j], sizeof(fn2));
1166 #else
1167                                                 ast_copy_string(fn2, cfinclude->include);
1168 #endif
1169                                                 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "") == NULL) { /* that last field needs to be looked at in this case... TODO */
1170                                                         unchanged = 0;
1171                                                         /* One change is enough to short-circuit and reload the whole shebang */
1172                                                         break;
1173                                                 }
1174 #ifdef AST_INCLUDE_GLOB
1175                                         }
1176                                 }
1177 #endif
1178                         }
1179
1180                         if (unchanged) {
1181                                 AST_LIST_UNLOCK(&cfmtime_head);
1182                                 return CONFIG_STATUS_FILEUNCHANGED;
1183                         }
1184                 }
1185                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1186                         AST_LIST_UNLOCK(&cfmtime_head);
1187
1188                 /* If cfg is NULL, then we just want an answer */
1189                 if (cfg == NULL)
1190                         return NULL;
1191
1192                 if (cfmtime)
1193                         cfmtime->mtime = statbuf.st_mtime;
1194
1195                 ast_verb(2, "Parsing '%s': ", fn);
1196                         fflush(stdout);
1197                 if (!(f = fopen(fn, "r"))) {
1198                         ast_debug(1, "No file to parse: %s\n", fn);
1199                         ast_verb(2, "Not found (%s)\n", strerror(errno));
1200                         continue;
1201                 }
1202                 count++;
1203                 /* If we get to this point, then we're loading regardless */
1204                 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1205                 ast_debug(1, "Parsing %s\n", fn);
1206                 ast_verb(2, "Found\n");
1207                 while (!feof(f)) {
1208                         lineno++;
1209                         if (fgets(buf, sizeof(buf), f)) {
1210                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && lline_buffer->used) {
1211                                         CB_ADD(&comment_buffer, lline_buffer->str);       /* add the current lline buffer to the comment buffer */
1212                                         lline_buffer->used = 0;        /* erase the lline buffer */
1213                                 }
1214                                 
1215                                 new_buf = buf;
1216                                 if (comment) 
1217                                         process_buf = NULL;
1218                                 else
1219                                         process_buf = buf;
1220                                 
1221                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
1222                                         /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1223                                         CB_ADD(&comment_buffer, "\n");       /* add a newline to the comment buffer */
1224                                         continue; /* go get a new line, then */
1225                                 }
1226                                 
1227                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1228                                         if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
1229                                                 /* Escaped semicolons aren't comments. */
1230                                                 new_buf = comment_p + 1;
1231                                         } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1232                                                 /* Meta-Comment start detected ";--" */
1233                                                 if (comment < MAX_NESTED_COMMENTS) {
1234                                                         *comment_p = '\0';
1235                                                         new_buf = comment_p + 3;
1236                                                         comment++;
1237                                                         nest[comment-1] = lineno;
1238                                                 } else {
1239                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1240                                                 }
1241                                         } else if ((comment_p >= new_buf + 2) &&
1242                                                    (*(comment_p - 1) == COMMENT_TAG) &&
1243                                                    (*(comment_p - 2) == COMMENT_TAG)) {
1244                                                 /* Meta-Comment end detected */
1245                                                 comment--;
1246                                                 new_buf = comment_p + 1;
1247                                                 if (!comment) {
1248                                                         /* Back to non-comment now */
1249                                                         if (process_buf) {
1250                                                                 /* Actually have to move what's left over the top, then continue */
1251                                                                 char *oldptr;
1252                                                                 oldptr = process_buf + strlen(process_buf);
1253                                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1254                                                                         CB_ADD(&comment_buffer, ";");
1255                                                                         CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
1256                                                                 }
1257                                                                 
1258                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1259                                                                 new_buf = oldptr;
1260                                                         } else
1261                                                                 process_buf = new_buf;
1262                                                 }
1263                                         } else {
1264                                                 if (!comment) {
1265                                                         /* If ; is found, and we are not nested in a comment, 
1266                                                            we immediately stop all comment processing */
1267                                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1268                                                                 CB_ADD(&lline_buffer, comment_p);
1269                                                         }
1270                                                         *comment_p = '\0'; 
1271                                                         new_buf = comment_p;
1272                                                 } else
1273                                                         new_buf = comment_p + 1;
1274                                         }
1275                                 }
1276                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
1277                                         CB_ADD(&comment_buffer, buf);  /* the whole line is a comment, store it */
1278                                 }
1279                                 
1280                                 if (process_buf) {
1281                                         char *buf = ast_strip(process_buf);
1282                                         if (!ast_strlen_zero(buf)) {
1283                                                 if (process_text_line(cfg, &cat, buf, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var)) {
1284                                                         cfg = NULL;
1285                                                         break;
1286                                                 }
1287                                         }
1288                                 }
1289                         }
1290                 }
1291                 /* end of file-- anything in a comment buffer? */
1292                 if (last_cat) {
1293                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
1294                                 if (lline_buffer && lline_buffer->used) {
1295                                         CB_ADD(&comment_buffer, lline_buffer->str);       /* add the current lline buffer to the comment buffer */
1296                                         lline_buffer->used = 0;        /* erase the lline buffer */
1297                                 }
1298                                 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1299                         }
1300                 } else if (last_var) {
1301                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
1302                                 if (lline_buffer && lline_buffer->used) {
1303                                         CB_ADD(&comment_buffer, lline_buffer->str);       /* add the current lline buffer to the comment buffer */
1304                                         lline_buffer->used = 0;        /* erase the lline buffer */
1305                                 }
1306                                 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1307                         }
1308                 } else {
1309                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used) {
1310                                 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", comment_buffer->str);
1311                         }
1312                 }
1313                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1314                         CB_RESET(comment_buffer, lline_buffer);
1315
1316                 fclose(f);              
1317         } while (0);
1318         if (comment) {
1319                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1320         }
1321 #ifdef AST_INCLUDE_GLOB
1322                                         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1323                                                 break;
1324                                 }
1325                                 globfree(&globbuf);
1326                         }
1327                 }
1328 #endif
1329
1330         if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1331                 if (comment_buffer)
1332                         ast_free(comment_buffer);
1333                 if (lline_buffer)
1334                         ast_free(lline_buffer);
1335                 comment_buffer = NULL;
1336                 lline_buffer = NULL;
1337         }
1338         
1339         if (count == 0)
1340                 return NULL;
1341
1342         return cfg;
1343 }
1344
1345
1346 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1347    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1348    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1349    be shocked and mystified as to why things are not showing up in the files! 
1350
1351    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1352    and line number are stored for each include, plus the name of the file included, so that these statements may be
1353    included in the output files on a file_save operation. 
1354
1355    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1356    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1357    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1358    and a header gets added.
1359
1360    vars and category heads are output in the order they are stored in the config file. So, if the software
1361    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1362    file/lineno data probably won't get changed.
1363
1364 */
1365
1366 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1367 {
1368         char date[256]="";
1369         time_t t;
1370
1371         time(&t);
1372         ast_copy_string(date, ctime(&t), sizeof(date));
1373
1374         fprintf(f1, ";!\n");
1375         fprintf(f1, ";! Automatically generated configuration file\n");
1376         if (strcmp(configfile, fn))
1377                 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1378         else
1379                 fprintf(f1, ";! Filename: %s\n", configfile);
1380         fprintf(f1, ";! Generator: %s\n", generator);
1381         fprintf(f1, ";! Creation Date: %s", date);
1382         fprintf(f1, ";!\n");
1383 }
1384
1385 static void   inclfile_destroy(void *obj)
1386 {
1387         const struct inclfile *o = obj;
1388
1389         if (o->fname)
1390                 free(o->fname);
1391 }
1392
1393
1394 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
1395 {
1396         struct inclfile lookup;
1397         
1398         if (!file || file[0] == 0) {
1399                 if (configfile[0] == '/')
1400                         ast_copy_string(fn, configfile, fn_size);
1401                 else
1402                         snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1403         } else if (file[0] == '/') 
1404                 ast_copy_string(fn, file, fn_size);
1405         else
1406                 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
1407         lookup.fname = fn;
1408         *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
1409         if (!(*fi)) {
1410                 /* set up a file scratch pad */
1411                 struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
1412                 fx->fname = ast_strdup(fn);
1413                 fx->lineno = 1;
1414                 *fi = fx;
1415                 ao2_link(fileset, fx);
1416         }
1417 }
1418
1419 static int count_linefeeds(char *str)
1420 {
1421         int count = 0;
1422
1423         while (*str) {
1424                 if (*str =='\n')
1425                         count++;
1426                 str++;
1427         }
1428         return count;
1429 }
1430
1431 static int count_linefeeds_in_comments(struct ast_comment *x)
1432 {
1433         int count = 0;
1434
1435         while (x) {
1436                 count += count_linefeeds(x->cmt);
1437                 x = x->next;
1438         }
1439         return count;
1440 }
1441
1442 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1443 {
1444         int precomment_lines = count_linefeeds_in_comments(precomments);
1445         int i;
1446
1447         /* I don't have to worry about those ;! comments, they are
1448            stored in the precomments, but not printed back out.
1449            I did have to make sure that comments following
1450            the ;! header comments were not also deleted in the process */
1451         for (i=fi->lineno;i<lineno - precomment_lines; i++) {
1452                 fprintf(fp,"\n");
1453         }
1454         fi->lineno = lineno+1; /* Advance the file lineno */
1455 }
1456
1457 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1458 {
1459         FILE *f;
1460         char fn[256];
1461         struct ast_variable *var;
1462         struct ast_category *cat;
1463         struct ast_comment *cmt;
1464         struct ast_config_include *incl;
1465         int blanklines = 0;
1466         struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
1467         struct inclfile *fi = 0;
1468
1469         /* reset all the output flags, in case this isn't our first time saving this data */
1470
1471         for (incl=cfg->includes; incl; incl = incl->next)
1472                 incl->output = 0;
1473
1474         /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
1475            are all truncated to zero bytes and have that nice header*/
1476
1477         for (incl=cfg->includes; incl; incl = incl->next)
1478         {
1479                 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*/
1480                         FILE *f1;
1481
1482                         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 */
1483                         f1 = fopen(fn,"w");
1484                         if (f1) {
1485                                 gen_header(f1, configfile, fn, generator);
1486                                 fclose(f1); /* this should zero out the file */
1487                         } else {
1488                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1489                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1490                         }
1491                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1492                         fi = 0;
1493                 }
1494         }
1495
1496         set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
1497 #ifdef __CYGWIN__       
1498         if ((f = fopen(fn, "w+"))) {
1499 #else
1500         if ((f = fopen(fn, "w"))) {
1501 #endif      
1502                 ast_verb(2, "Saving '%s': ", fn);
1503                 gen_header(f, configfile, fn, generator);
1504                 cat = cfg->root;
1505                 fclose(f);
1506                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1507                 
1508                 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
1509                 /* since each var, cat, and associated comments can come from any file, we have to be 
1510                    mobile, and open each file, print, and close it on an entry-by-entry basis */
1511
1512                 while (cat) {
1513                         set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
1514                         f = fopen(fn, "a");
1515                         if (!f)
1516                         {
1517                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1518                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1519                                 ao2_ref(fileset, -1);
1520                                 return -1;
1521                         }
1522
1523                         /* dump any includes that happen before this category header */
1524                         for (incl=cfg->includes; incl; incl = incl->next) {
1525                                 if (strcmp(incl->include_location_file, cat->file) == 0){
1526                                         if (cat->lineno > incl->include_location_lineno && !incl->output) {
1527                                                 if (incl->exec)
1528                                                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1529                                                 else
1530                                                         fprintf(f,"#include \"%s\"\n", incl->included_file);
1531                                                 incl->output = 1;
1532                                         }
1533                                 }
1534                         }
1535                         
1536                         insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
1537                         /* Dump section with any appropriate comment */
1538                         for (cmt = cat->precomments; cmt; cmt=cmt->next) {
1539                                 char *cmtp = cmt->cmt;
1540                                 while (*cmtp == ';' && *(cmtp+1) == '!') {
1541                                         char *cmtp2 = strchr(cmtp+1, '\n');
1542                                         if (cmtp2)
1543                                                 cmtp = cmtp2+1;
1544                                         else cmtp = 0;
1545                                 }
1546                                 if (cmtp)
1547                                         fprintf(f,"%s", cmtp);
1548                         }
1549                         if (!cat->precomments)
1550                                 fprintf(f,"\n");
1551                         fprintf(f, "[%s]", cat->name);
1552                         if (cat->ignored)
1553                                 fprintf(f, "(!)");
1554                         if (!AST_LIST_EMPTY(&cat->template_instances)) {
1555                                 struct ast_category_template_instance *x;
1556                                 fprintf(f, "(");
1557                                 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1558                                         fprintf(f,"%s",x->name);
1559                                         if (x != AST_LIST_LAST(&cat->template_instances))
1560                                                 fprintf(f,",");
1561                                 }
1562                                 fprintf(f, ")");
1563                         }
1564                         for(cmt = cat->sameline; cmt; cmt=cmt->next)
1565                         {
1566                                 fprintf(f,"%s", cmt->cmt);
1567                         }
1568                         if (!cat->sameline)
1569                                 fprintf(f,"\n");
1570                         for (cmt = cat->trailing; cmt; cmt=cmt->next) {
1571                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1572                                         fprintf(f,"%s", cmt->cmt);
1573                         }
1574                         fclose(f);
1575                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1576                         fi = 0;
1577                         
1578                         var = cat->root;
1579                         while (var) {
1580                                 struct ast_category_template_instance *x;
1581                                 int found = 0;
1582                                 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
1583                                         struct ast_variable *v;
1584                                         for (v = x->inst->root; v; v = v->next) {
1585                                                 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
1586                                                         found = 1;
1587                                                         break;
1588                                                 }
1589                                         }
1590                                         if (found)
1591                                                 break;
1592                                 }
1593                                 if (found) {
1594                                         var = var->next;
1595                                         continue;
1596                                 }
1597                                 set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
1598                                 f = fopen(fn, "a");
1599                                 if (!f)
1600                                 {
1601                                         ast_debug(1, "Unable to open for writing: %s\n", fn);
1602                                         ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1603                                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1604                                         fi = 0;
1605                                         ao2_ref(fileset, -1);
1606                                         return -1;
1607                                 }
1608                                 
1609                                 /* dump any includes that happen before this category header */
1610                                 for (incl=cfg->includes; incl; incl = incl->next) {
1611                                         if (strcmp(incl->include_location_file, var->file) == 0){
1612                                                 if (var->lineno > incl->include_location_lineno && !incl->output) {
1613                                                         if (incl->exec)
1614                                                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1615                                                         else
1616                                                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1617                                                         incl->output = 1;
1618                                                 }
1619                                         }
1620                                 }
1621                                 
1622                                 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
1623                                 for (cmt = var->precomments; cmt; cmt=cmt->next) {
1624                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1625                                                 fprintf(f,"%s", cmt->cmt);
1626                                 }
1627                                 if (var->sameline) 
1628                                         fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
1629                                 else    
1630                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
1631                                 for (cmt = var->trailing; cmt; cmt=cmt->next) {
1632                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1633                                                 fprintf(f,"%s", cmt->cmt);
1634                                 }
1635                                 if (var->blanklines) {
1636                                         blanklines = var->blanklines;
1637                                         while (blanklines--)
1638                                                 fprintf(f, "\n");
1639                                 }
1640                                 
1641                                 fclose(f);
1642                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1643                                 fi = 0;
1644                                 
1645                                 var = var->next;
1646                         }
1647                         cat = cat->next;
1648                 }
1649                 if (!option_debug)
1650                         ast_verb(2, "Saved\n");
1651         } else {
1652                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1653                 ast_verb(2, "Unable to write (%s)", strerror(errno));
1654                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1655                 ao2_ref(fileset, -1);
1656                 return -1;
1657         }
1658
1659         /* Now, for files with trailing #include/#exec statements,
1660            we have to make sure every entry is output */
1661
1662         for (incl=cfg->includes; incl; incl = incl->next) {
1663                 if (!incl->output) {
1664                         /* open the respective file */
1665                         set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
1666                         f = fopen(fn, "a");
1667                         if (!f)
1668                         {
1669                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1670                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1671                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1672                                 fi = 0;
1673                                 ao2_ref(fileset, -1);
1674                                 return -1;
1675                         }
1676                         
1677                         /* output the respective include */
1678                         if (incl->exec)
1679                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1680                         else
1681                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1682                         fclose(f);
1683                         incl->output = 1;
1684                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1685                         fi = 0;
1686                 }
1687         }
1688         ao2_ref(fileset, -1); /* this should destroy the hash container */
1689                                 
1690         return 0;
1691 }
1692
1693 static void clear_config_maps(void) 
1694 {
1695         struct ast_config_map *map;
1696
1697         ast_mutex_lock(&config_lock);
1698
1699         while (config_maps) {
1700                 map = config_maps;
1701                 config_maps = config_maps->next;
1702                 ast_free(map);
1703         }
1704                 
1705         ast_mutex_unlock(&config_lock);
1706 }
1707
1708 static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
1709 {
1710         struct ast_config_map *map;
1711         int length;
1712
1713         length = sizeof(*map);
1714         length += strlen(name) + 1;
1715         length += strlen(driver) + 1;
1716         length += strlen(database) + 1;
1717         if (table)
1718                 length += strlen(table) + 1;
1719
1720         if (!(map = ast_calloc(1, length)))
1721                 return -1;
1722
1723         map->name = map->stuff;
1724         strcpy(map->name, name);
1725         map->driver = map->name + strlen(map->name) + 1;
1726         strcpy(map->driver, driver);
1727         map->database = map->driver + strlen(map->driver) + 1;
1728         strcpy(map->database, database);
1729         if (table) {
1730                 map->table = map->database + strlen(map->database) + 1;
1731                 strcpy(map->table, table);
1732         }
1733         map->next = config_maps;
1734
1735         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
1736
1737         config_maps = map;
1738         return 0;
1739 }
1740
1741 int read_config_maps(void) 
1742 {
1743         struct ast_config *config, *configtmp;
1744         struct ast_variable *v;
1745         char *driver, *table, *database, *stringp, *tmp;
1746         struct ast_flags flags = { 0 };
1747
1748         clear_config_maps();
1749
1750         configtmp = ast_config_new();
1751         configtmp->max_include_level = 1;
1752         config = ast_config_internal_load(extconfig_conf, configtmp, flags, "");
1753         if (!config) {
1754                 ast_config_destroy(configtmp);
1755                 return 0;
1756         }
1757
1758         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
1759                 char buf[512];
1760                 ast_copy_string(buf, v->value, sizeof(buf));
1761                 stringp = buf;
1762                 driver = strsep(&stringp, ",");
1763
1764                 if ((tmp = strchr(stringp, '\"')))
1765                         stringp = tmp;
1766
1767                 /* check if the database text starts with a double quote */
1768                 if (*stringp == '"') {
1769                         stringp++;
1770                         database = strsep(&stringp, "\"");
1771                         strsep(&stringp, ",");
1772                 } else {
1773                         /* apparently this text has no quotes */
1774                         database = strsep(&stringp, ",");
1775                 }
1776
1777                 table = strsep(&stringp, ",");
1778
1779                 if (!strcmp(v->name, extconfig_conf)) {
1780                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
1781                         continue;
1782                 }
1783
1784                 if (!strcmp(v->name, "asterisk.conf")) {
1785                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
1786                         continue;
1787                 }
1788
1789                 if (!strcmp(v->name, "logger.conf")) {
1790                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
1791                         continue;
1792                 }
1793
1794                 if (!driver || !database)
1795                         continue;
1796                 if (!strcasecmp(v->name, "sipfriends")) {
1797                         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");
1798                         append_mapping("sipusers", driver, database, table ? table : "sipfriends");
1799                         append_mapping("sippeers", driver, database, table ? table : "sipfriends");
1800                 } else if (!strcasecmp(v->name, "iaxfriends")) {
1801                         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");
1802                         append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
1803                         append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
1804                 } else 
1805                         append_mapping(v->name, driver, database, table);
1806         }
1807                 
1808         ast_config_destroy(config);
1809         return 0;
1810 }
1811
1812 int ast_config_engine_register(struct ast_config_engine *new) 
1813 {
1814         struct ast_config_engine *ptr;
1815
1816         ast_mutex_lock(&config_lock);
1817
1818         if (!config_engine_list) {
1819                 config_engine_list = new;
1820         } else {
1821                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
1822                 ptr->next = new;
1823         }
1824
1825         ast_mutex_unlock(&config_lock);
1826         ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
1827
1828         return 1;
1829 }
1830
1831 int ast_config_engine_deregister(struct ast_config_engine *del) 
1832 {
1833         struct ast_config_engine *ptr, *last=NULL;
1834
1835         ast_mutex_lock(&config_lock);
1836
1837         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
1838                 if (ptr == del) {
1839                         if (last)
1840                                 last->next = ptr->next;
1841                         else
1842                                 config_engine_list = ptr->next;
1843                         break;
1844                 }
1845                 last = ptr;
1846         }
1847
1848         ast_mutex_unlock(&config_lock);
1849
1850         return 0;
1851 }
1852
1853 /*! \brief Find realtime engine for realtime family */
1854 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz) 
1855 {
1856         struct ast_config_engine *eng, *ret = NULL;
1857         struct ast_config_map *map;
1858
1859         ast_mutex_lock(&config_lock);
1860
1861         for (map = config_maps; map; map = map->next) {
1862                 if (!strcasecmp(family, map->name)) {
1863                         if (database)
1864                                 ast_copy_string(database, map->database, dbsiz);
1865                         if (table)
1866                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
1867                         break;
1868                 }
1869         }
1870
1871         /* Check if the required driver (engine) exist */
1872         if (map) {
1873                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
1874                         if (!strcasecmp(eng->name, map->driver))
1875                                 ret = eng;
1876                 }
1877         }
1878
1879         ast_mutex_unlock(&config_lock);
1880         
1881         /* if we found a mapping, but the engine is not available, then issue a warning */
1882         if (map && !ret)
1883                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
1884
1885         return ret;
1886 }
1887
1888 static struct ast_config_engine text_file_engine = {
1889         .name = "text",
1890         .load_func = config_text_file_load,
1891 };
1892
1893 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file)
1894 {
1895         char db[256];
1896         char table[256];
1897         struct ast_config_engine *loader = &text_file_engine;
1898         struct ast_config *result; 
1899
1900         if (cfg->include_level == cfg->max_include_level) {
1901                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
1902                 return NULL;
1903         }
1904
1905         cfg->include_level++;
1906
1907         if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
1908                 struct ast_config_engine *eng;
1909
1910                 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
1911
1912
1913                 if (eng && eng->load_func) {
1914                         loader = eng;
1915                 } else {
1916                         eng = find_engine("global", db, sizeof(db), table, sizeof(table));
1917                         if (eng && eng->load_func)
1918                                 loader = eng;
1919                 }
1920         }
1921
1922         result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file);
1923
1924         if (result && result != CONFIG_STATUS_FILEUNCHANGED)
1925                 result->include_level--;
1926         else
1927                 cfg->include_level--;
1928
1929         return result;
1930 }
1931
1932 struct ast_config *ast_config_load(const char *filename, struct ast_flags flags)
1933 {
1934         struct ast_config *cfg;
1935         struct ast_config *result;
1936
1937         cfg = ast_config_new();
1938         if (!cfg)
1939                 return NULL;
1940
1941         result = ast_config_internal_load(filename, cfg, flags, "");
1942         if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
1943                 ast_config_destroy(cfg);
1944
1945         return result;
1946 }
1947
1948 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
1949 {
1950         struct ast_config_engine *eng;
1951         char db[256]="";
1952         char table[256]="";
1953         struct ast_variable *res=NULL;
1954
1955         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1956         if (eng && eng->realtime_func) 
1957                 res = eng->realtime_func(db, table, ap);
1958
1959         return res;
1960 }
1961
1962 struct ast_variable *ast_load_realtime_all(const char *family, ...)
1963 {
1964         struct ast_variable *res;
1965         va_list ap;
1966
1967         va_start(ap, family);
1968         res = ast_load_realtime_helper(family, ap);
1969         va_end(ap);
1970
1971         return res;
1972 }
1973
1974 struct ast_variable *ast_load_realtime(const char *family, ...)
1975 {
1976         struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
1977         va_list ap;
1978
1979         va_start(ap, family);
1980         res = ast_load_realtime_helper(family, ap);
1981         va_end(ap);
1982
1983         /* Eliminate blank entries */
1984         for (cur = res; cur; cur = cur->next) {
1985                 if (freeme) {
1986                         ast_free(freeme);
1987                         freeme = NULL;
1988                 }
1989
1990                 if (ast_strlen_zero(cur->value)) {
1991                         if (prev)
1992                                 prev->next = cur->next;
1993                         else
1994                                 res = cur->next;
1995                         freeme = cur;
1996                 } else {
1997                         prev = cur;
1998                 }
1999         }
2000         return res;
2001 }
2002
2003 /*! \brief Check if realtime engine is configured for family */
2004 int ast_check_realtime(const char *family)
2005 {
2006         struct ast_config_engine *eng;
2007
2008         eng = find_engine(family, NULL, 0, NULL, 0);
2009         if (eng)
2010                 return 1;
2011         return 0;
2012 }
2013
2014 /*! \brief Check if there's any realtime engines loaded */
2015 int ast_realtime_enabled()
2016 {
2017         return config_maps ? 1 : 0;
2018 }
2019
2020 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
2021 {
2022         struct ast_config_engine *eng;
2023         char db[256]="";
2024         char table[256]="";
2025         struct ast_config *res=NULL;
2026         va_list ap;
2027
2028         va_start(ap, family);
2029         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2030         if (eng && eng->realtime_multi_func) 
2031                 res = eng->realtime_multi_func(db, table, ap);
2032         va_end(ap);
2033
2034         return res;
2035 }
2036
2037 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2038 {
2039         struct ast_config_engine *eng;
2040         int res = -1;
2041         char db[256]="";
2042         char table[256]="";
2043         va_list ap;
2044
2045         va_start(ap, lookup);
2046         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2047         if (eng && eng->update_func) 
2048                 res = eng->update_func(db, table, keyfield, lookup, ap);
2049         va_end(ap);
2050
2051         return res;
2052 }
2053
2054 int ast_store_realtime(const char *family, ...) {
2055         struct ast_config_engine *eng;
2056         int res = -1;
2057         char db[256]="";
2058         char table[256]="";
2059         va_list ap;
2060
2061         va_start(ap, family);
2062         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2063         if (eng && eng->store_func) 
2064                 res = eng->store_func(db, table, ap);
2065         va_end(ap);
2066
2067         return res;
2068 }
2069
2070 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...) {
2071         struct ast_config_engine *eng;
2072         int res = -1;
2073         char db[256]="";
2074         char table[256]="";
2075         va_list ap;
2076
2077         va_start(ap, lookup);
2078         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2079         if (eng && eng->destroy_func) 
2080                 res = eng->destroy_func(db, table, keyfield, lookup, ap);
2081         va_end(ap);
2082
2083         return res;
2084 }
2085
2086 /*! \brief Helper function to parse arguments
2087  * See documentation in config.h
2088  */
2089 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2090         void *p_result, ...)
2091 {
2092         va_list ap;
2093         int error = 0;
2094
2095         va_start(ap, p_result);
2096         switch (flags & PARSE_TYPE) {
2097         case PARSE_INT32:
2098             {
2099                 int32_t *result = p_result;
2100                 int32_t x, def = result ? *result : 0,
2101                         high = (int32_t)0x7fffffff,
2102                         low  = (int32_t)0x80000000;
2103                 /* optional argument: first default value, then range */
2104                 if (flags & PARSE_DEFAULT)
2105                         def = va_arg(ap, int32_t);
2106                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2107                         /* range requested, update bounds */
2108                         low = va_arg(ap, int32_t);
2109                         high = va_arg(ap, int32_t);
2110                 }
2111                 x = strtol(arg, NULL, 0);
2112                 error = (x < low) || (x > high);
2113                 if (flags & PARSE_OUT_RANGE)
2114                         error = !error;
2115                 if (result)
2116                         *result  = error ? def : x;
2117                 ast_debug(3,
2118                         "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
2119                         arg, low, high,
2120                         result ? *result : x, error);
2121                 break;
2122             }
2123
2124         case PARSE_UINT32:
2125             {
2126                 uint32_t *result = p_result;
2127                 uint32_t x, def = result ? *result : 0,
2128                         low = 0, high = (uint32_t)~0;
2129                 /* optional argument: first default value, then range */
2130                 if (flags & PARSE_DEFAULT)
2131                         def = va_arg(ap, uint32_t);
2132                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2133                         /* range requested, update bounds */
2134                         low = va_arg(ap, uint32_t);
2135                         high = va_arg(ap, uint32_t);
2136                 }
2137                 x = strtoul(arg, NULL, 0);
2138                 error = (x < low) || (x > high);
2139                 if (flags & PARSE_OUT_RANGE)
2140                         error = !error;
2141                 if (result)
2142                         *result  = error ? def : x;
2143                 ast_debug(3,
2144                         "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
2145                         arg, low, high,
2146                         result ? *result : x, error);
2147                 break;
2148             }
2149
2150         case PARSE_INADDR:
2151             {
2152                 char *port, *buf;
2153                 struct sockaddr_in _sa_buf;     /* buffer for the result */
2154                 struct sockaddr_in *sa = p_result ?
2155                         (struct sockaddr_in *)p_result : &_sa_buf;
2156                 /* default is either the supplied value or the result itself */
2157                 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
2158                         va_arg(ap, struct sockaddr_in *) : sa;
2159                 struct hostent *hp;
2160                 struct ast_hostent ahp;
2161
2162                 bzero(&_sa_buf, sizeof(_sa_buf)); /* clear buffer */
2163                 /* duplicate the string to strip away the :port */
2164                 port = ast_strdupa(arg);
2165                 buf = strsep(&port, ":");
2166                 sa->sin_family = AF_INET;       /* assign family */
2167                 /*
2168                  * honor the ports flag setting, assign default value
2169                  * in case of errors or field unset.
2170                  */
2171                 flags &= PARSE_PORT_MASK; /* the only flags left to process */
2172                 if (port) {
2173                         if (flags == PARSE_PORT_FORBID) {
2174                                 error = 1;      /* port was forbidden */
2175                                 sa->sin_port = def->sin_port;
2176                         } else if (flags == PARSE_PORT_IGNORE)
2177                                 sa->sin_port = def->sin_port;
2178                         else /* accept or require */
2179                                 sa->sin_port = htons(strtol(port, NULL, 0));
2180                 } else {
2181                         sa->sin_port = def->sin_port;
2182                         if (flags == PARSE_PORT_REQUIRE)
2183                                 error = 1;
2184                 }
2185                 /* Now deal with host part, even if we have errors before. */
2186                 hp = ast_gethostbyname(buf, &ahp);
2187                 if (hp) /* resolved successfully */
2188                         memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
2189                 else {
2190                         error = 1;
2191                         sa->sin_addr = def->sin_addr;
2192                 }
2193                 ast_debug(3,
2194                         "extract inaddr from [%s] gives [%s:%d](%d)\n",
2195                         arg, ast_inet_ntoa(sa->sin_addr),
2196                         ntohs(sa->sin_port), error);
2197                 break;
2198             }
2199         }
2200         va_end(ap);
2201         return error;
2202 }
2203
2204 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2205 {
2206         struct ast_config_engine *eng;
2207         struct ast_config_map *map;
2208
2209         switch (cmd) {
2210         case CLI_INIT:
2211                 e->command = "core show config mappings";
2212                 e->usage =
2213                         "Usage: core show config mappings\n"
2214                         "       Shows the filenames to config engines.\n";
2215                 return NULL;
2216         case CLI_GENERATE:
2217                 return NULL;
2218         }
2219         
2220         ast_mutex_lock(&config_lock);
2221
2222         ast_cli(a->fd, "\n\n");
2223         for (eng = config_engine_list; eng; eng = eng->next) {
2224                 ast_cli(a->fd, "\nConfig Engine: %s\n", eng->name);
2225                 for (map = config_maps; map; map = map->next)
2226                         if (!strcasecmp(map->driver, eng->name)) {
2227                                 ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
2228                                         map->table ? map->table : map->name);
2229                         }
2230         }
2231         ast_cli(a->fd,"\n\n");
2232         
2233         ast_mutex_unlock(&config_lock);
2234
2235         return CLI_SUCCESS;
2236 }
2237
2238 static struct ast_cli_entry cli_config[] = {
2239         AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
2240 };
2241
2242 int register_config_cli() 
2243 {
2244         ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));
2245         return 0;
2246 }