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