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