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