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