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