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