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