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