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