Cleanup references to sipusers and sipfriends dynamic realtime families
[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 sippeers instead.\n");
2150                         append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
2151                 } else if (!strcasecmp(v->name, "iaxfriends")) {
2152                         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");
2153                         append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
2154                         append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
2155                 } else 
2156                         append_mapping(v->name, driver, database, table, pri);
2157         }
2158                 
2159         ast_config_destroy(config);
2160         return 0;
2161 }
2162
2163 int ast_config_engine_register(struct ast_config_engine *new) 
2164 {
2165         struct ast_config_engine *ptr;
2166
2167         ast_mutex_lock(&config_lock);
2168
2169         if (!config_engine_list) {
2170                 config_engine_list = new;
2171         } else {
2172                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
2173                 ptr->next = new;
2174         }
2175
2176         ast_mutex_unlock(&config_lock);
2177         ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
2178
2179         return 1;
2180 }
2181
2182 int ast_config_engine_deregister(struct ast_config_engine *del) 
2183 {
2184         struct ast_config_engine *ptr, *last=NULL;
2185
2186         ast_mutex_lock(&config_lock);
2187
2188         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
2189                 if (ptr == del) {
2190                         if (last)
2191                                 last->next = ptr->next;
2192                         else
2193                                 config_engine_list = ptr->next;
2194                         break;
2195                 }
2196                 last = ptr;
2197         }
2198
2199         ast_mutex_unlock(&config_lock);
2200
2201         return 0;
2202 }
2203
2204 /*! \brief Find realtime engine for realtime family */
2205 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz) 
2206 {
2207         struct ast_config_engine *eng, *ret = NULL;
2208         struct ast_config_map *map;
2209
2210         ast_mutex_lock(&config_lock);
2211
2212         for (map = config_maps; map; map = map->next) {
2213                 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
2214                         if (database)
2215                                 ast_copy_string(database, map->database, dbsiz);
2216                         if (table)
2217                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
2218                         break;
2219                 }
2220         }
2221
2222         /* Check if the required driver (engine) exist */
2223         if (map) {
2224                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
2225                         if (!strcasecmp(eng->name, map->driver))
2226                                 ret = eng;
2227                 }
2228         }
2229
2230         ast_mutex_unlock(&config_lock);
2231         
2232         /* if we found a mapping, but the engine is not available, then issue a warning */
2233         if (map && !ret)
2234                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
2235
2236         return ret;
2237 }
2238
2239 static struct ast_config_engine text_file_engine = {
2240         .name = "text",
2241         .load_func = config_text_file_load,
2242 };
2243
2244 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)
2245 {
2246         char db[256];
2247         char table[256];
2248         struct ast_config_engine *loader = &text_file_engine;
2249         struct ast_config *result; 
2250
2251         /* The config file itself bumps include_level by 1 */
2252         if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
2253                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
2254                 return NULL;
2255         }
2256
2257         cfg->include_level++;
2258
2259         if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
2260                 struct ast_config_engine *eng;
2261
2262                 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
2263
2264
2265                 if (eng && eng->load_func) {
2266                         loader = eng;
2267                 } else {
2268                         eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
2269                         if (eng && eng->load_func)
2270                                 loader = eng;
2271                 }
2272         }
2273
2274         result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
2275
2276         if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED)
2277                 result->include_level--;
2278         else if (result != CONFIG_STATUS_FILEINVALID)
2279                 cfg->include_level--;
2280
2281         return result;
2282 }
2283
2284 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
2285 {
2286         struct ast_config *cfg;
2287         struct ast_config *result;
2288
2289         cfg = ast_config_new();
2290         if (!cfg)
2291                 return NULL;
2292
2293         result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
2294         if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
2295                 ast_config_destroy(cfg);
2296
2297         return result;
2298 }
2299
2300 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
2301 {
2302         struct ast_config_engine *eng;
2303         char db[256];
2304         char table[256];
2305         struct ast_variable *res=NULL;
2306         int i;
2307
2308         for (i = 1; ; i++) {
2309                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2310                         if (eng->realtime_func && (res = eng->realtime_func(db, table, ap))) {
2311                                 return res;
2312                         }
2313                 } else {
2314                         return NULL;
2315                 }
2316         }
2317
2318         return res;
2319 }
2320
2321 struct ast_variable *ast_load_realtime_all(const char *family, ...)
2322 {
2323         struct ast_variable *res;
2324         va_list ap;
2325
2326         va_start(ap, family);
2327         res = ast_load_realtime_helper(family, ap);
2328         va_end(ap);
2329
2330         return res;
2331 }
2332
2333 struct ast_variable *ast_load_realtime(const char *family, ...)
2334 {
2335         struct ast_variable *res;
2336         struct ast_variable *cur;
2337         struct ast_variable **prev;
2338         va_list ap;
2339
2340         va_start(ap, family);
2341         res = ast_load_realtime_helper(family, ap);
2342         va_end(ap);
2343
2344         /* Filter the list. */
2345         prev = &res;
2346         cur = res;
2347         while (cur) {
2348                 if (ast_strlen_zero(cur->value)) {
2349                         /* Eliminate empty entries */
2350                         struct ast_variable *next;
2351
2352                         next = cur->next;
2353                         *prev = next;
2354                         ast_variable_destroy(cur);
2355                         cur = next;
2356                 } else {
2357                         /* Make blank entries empty and keep them. */
2358                         if (cur->value[0] == ' ' && cur->value[1] == '\0') {
2359                                 char *vptr = (char *) cur->value;
2360
2361                                 vptr[0] = '\0';
2362                         }
2363
2364                         prev = &cur->next;
2365                         cur = cur->next;
2366                 }
2367         }
2368         return res;
2369 }
2370
2371 /*! \brief Check if realtime engine is configured for family */
2372 int ast_check_realtime(const char *family)
2373 {
2374         struct ast_config_engine *eng;
2375         if (!ast_realtime_enabled()) {
2376                 return 0;       /* There are no engines at all so fail early */
2377         }
2378
2379         eng = find_engine(family, 1, NULL, 0, NULL, 0);
2380         if (eng)
2381                 return 1;
2382         return 0;
2383 }
2384
2385 /*! \brief Check if there's any realtime engines loaded */
2386 int ast_realtime_enabled(void)
2387 {
2388         return config_maps ? 1 : 0;
2389 }
2390
2391 int ast_realtime_require_field(const char *family, ...)
2392 {
2393         struct ast_config_engine *eng;
2394         char db[256];
2395         char table[256];
2396         va_list ap;
2397         int res = -1, i;
2398
2399         va_start(ap, family);
2400         for (i = 1; ; i++) {
2401                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2402                         /* If the require succeeds, it returns 0. */
2403                         if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
2404                                 break;
2405                         }
2406                 } else {
2407                         break;
2408                 }
2409         }
2410         va_end(ap);
2411
2412         return res;
2413 }
2414
2415 int ast_unload_realtime(const char *family)
2416 {
2417         struct ast_config_engine *eng;
2418         char db[256];
2419         char table[256];
2420         int res = -1, i;
2421
2422         for (i = 1; ; i++) {
2423                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2424                         if (eng->unload_func) {
2425                                 /* Do this for ALL engines */
2426                                 res = eng->unload_func(db, table);
2427                         }
2428                 } else {
2429                         break;
2430                 }
2431         }
2432         return res;
2433 }
2434
2435 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
2436 {
2437         struct ast_config_engine *eng;
2438         char db[256];
2439         char table[256];
2440         struct ast_config *res = NULL;
2441         va_list ap;
2442         int i;
2443
2444         va_start(ap, family);
2445         for (i = 1; ; i++) {
2446                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2447                         if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, ap))) {
2448                                 /* If we were returned an empty cfg, destroy it and return NULL */
2449                                 if (!res->root) {
2450                                         ast_config_destroy(res);
2451                                         res = NULL;
2452                                 }
2453                                 break;
2454                         }
2455                 } else {
2456                         break;
2457                 }
2458         }
2459         va_end(ap);
2460
2461         return res;
2462 }
2463
2464 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2465 {
2466         struct ast_config_engine *eng;
2467         int res = -1, i;
2468         char db[256];
2469         char table[256];
2470         va_list ap;
2471
2472         va_start(ap, lookup);
2473         for (i = 1; ; i++) {
2474                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2475                         /* If the update succeeds, it returns 0. */
2476                         if (eng->update_func && !(res = eng->update_func(db, table, keyfield, lookup, ap))) {
2477                                 break;
2478                         }
2479                 } else {
2480                         break;
2481                 }
2482         }
2483         va_end(ap);
2484
2485         return res;
2486 }
2487
2488 int ast_update2_realtime(const char *family, ...)
2489 {
2490         struct ast_config_engine *eng;
2491         int res = -1, i;
2492         char db[256];
2493         char table[256];
2494         va_list ap;
2495
2496         va_start(ap, family);
2497         for (i = 1; ; i++) {
2498                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2499                         if (eng->update2_func && !(res = eng->update2_func(db, table, ap))) {
2500                                 break;
2501                         }
2502                 } else {
2503                         break;
2504                 }
2505         }
2506         va_end(ap);
2507
2508         return res;
2509 }
2510
2511 int ast_store_realtime(const char *family, ...)
2512 {
2513         struct ast_config_engine *eng;
2514         int res = -1, i;
2515         char db[256];
2516         char table[256];
2517         va_list ap;
2518
2519         va_start(ap, family);
2520         for (i = 1; ; i++) {
2521                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2522                         /* If the store succeeds, it returns 0. */
2523                         if (eng->store_func && !(res = eng->store_func(db, table, ap))) {
2524                                 break;
2525                         }
2526                 } else {
2527                         break;
2528                 }
2529         }
2530         va_end(ap);
2531
2532         return res;
2533 }
2534
2535 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
2536 {
2537         struct ast_config_engine *eng;
2538         int res = -1, i;
2539         char db[256];
2540         char table[256];
2541         va_list ap;
2542
2543         va_start(ap, lookup);
2544         for (i = 1; ; i++) {
2545                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
2546                         if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, ap))) {
2547                                 break;
2548                         }
2549                 } else {
2550                         break;
2551                 }
2552         }
2553         va_end(ap);
2554
2555         return res;
2556 }
2557
2558 char *ast_realtime_decode_chunk(char *chunk)
2559 {
2560         char *orig = chunk;
2561         for (; *chunk; chunk++) {
2562                 if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
2563                         sscanf(chunk + 1, "%02hhX", chunk);
2564                         memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
2565                 }
2566         }
2567         return orig;
2568 }
2569
2570 char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
2571 {
2572         if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
2573                 ast_str_set(dest, maxlen, "%s", chunk);
2574         } else {
2575                 ast_str_reset(*dest);
2576                 for (; *chunk; chunk++) {
2577                         if (strchr(";^", *chunk)) {
2578                                 ast_str_append(dest, maxlen, "^%02hhX", *chunk);
2579                         } else {
2580                                 ast_str_append(dest, maxlen, "%c", *chunk);
2581                         }
2582                 }
2583         }
2584         return ast_str_buffer(*dest);
2585 }
2586
2587 /*! \brief Helper function to parse arguments
2588  * See documentation in config.h
2589  */
2590 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2591         void *p_result, ...)
2592 {
2593         va_list ap;
2594         int error = 0;
2595
2596         va_start(ap, p_result);
2597         switch (flags & PARSE_TYPE) {
2598         case PARSE_INT32:
2599             {
2600                 int32_t *result = p_result;
2601                 int32_t x, def = result ? *result : 0,
2602                         high = (int32_t)0x7fffffff,
2603                         low  = (int32_t)0x80000000;
2604                 /* optional argument: first default value, then range */
2605                 if (flags & PARSE_DEFAULT)
2606                         def = va_arg(ap, int32_t);
2607                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2608                         /* range requested, update bounds */
2609                         low = va_arg(ap, int32_t);
2610                         high = va_arg(ap, int32_t);
2611                 }
2612                 x = strtol(arg, NULL, 0);
2613                 error = (x < low) || (x > high);
2614                 if (flags & PARSE_OUT_RANGE)
2615                         error = !error;
2616                 if (result)
2617                         *result  = error ? def : x;
2618                 ast_debug(3,
2619                         "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
2620                         arg, low, high,
2621                         result ? *result : x, error);
2622                 break;
2623             }
2624
2625         case PARSE_UINT32:
2626             {
2627                 uint32_t *result = p_result;
2628                 uint32_t x, def = result ? *result : 0,
2629                         low = 0, high = (uint32_t)~0;
2630                 /* optional argument: first default value, then range */
2631                 if (flags & PARSE_DEFAULT)
2632                         def = va_arg(ap, uint32_t);
2633                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2634                         /* range requested, update bounds */
2635                         low = va_arg(ap, uint32_t);
2636                         high = va_arg(ap, uint32_t);
2637                 }
2638                 x = strtoul(arg, NULL, 0);
2639                 error = (x < low) || (x > high);
2640                 if (flags & PARSE_OUT_RANGE)
2641                         error = !error;
2642                 if (result)
2643                         *result  = error ? def : x;
2644                 ast_debug(3,
2645                         "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
2646                         arg, low, high,
2647                         result ? *result : x, error);
2648                 break;
2649             }
2650
2651         case PARSE_DOUBLE:
2652             {
2653                 double *result = p_result;
2654                 double x, def = result ? *result : 0,
2655                         low = -HUGE_VAL, high = HUGE_VAL;
2656
2657                 /* optional argument: first default value, then range */
2658                 if (flags & PARSE_DEFAULT)
2659                         def = va_arg(ap, double);
2660                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2661                         /* range requested, update bounds */
2662                         low = va_arg(ap, double);
2663                         high = va_arg(ap, double);
2664                 }
2665                 x = strtod(arg, NULL);
2666                 error = (x < low) || (x > high);
2667                 if (flags & PARSE_OUT_RANGE)
2668                         error = !error;
2669                 if (result)
2670                         *result  = error ? def : x;
2671                 ast_debug(3,
2672                         "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
2673                         arg, low, high,
2674                         result ? *result : x, error);
2675                 break;
2676             }
2677         case PARSE_ADDR:
2678             {
2679                 struct ast_sockaddr *addr = (struct ast_sockaddr *)p_result;
2680
2681                 if (!ast_sockaddr_parse(addr, arg, flags & PARSE_PORT_MASK)) {
2682                         error = 1;
2683                 }
2684
2685                 ast_debug(3, "extract addr from %s gives %s(%d)\n",
2686                           arg, ast_sockaddr_stringify(addr), error);
2687
2688                 break;
2689             }
2690         case PARSE_INADDR:      /* TODO Remove this (use PARSE_ADDR instead). */
2691             {
2692                 char *port, *buf;
2693                 struct sockaddr_in _sa_buf;     /* buffer for the result */
2694                 struct sockaddr_in *sa = p_result ?
2695                         (struct sockaddr_in *)p_result : &_sa_buf;
2696                 /* default is either the supplied value or the result itself */
2697                 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
2698                         va_arg(ap, struct sockaddr_in *) : sa;
2699                 struct hostent *hp;
2700                 struct ast_hostent ahp;
2701
2702                 memset(&_sa_buf, '\0', sizeof(_sa_buf)); /* clear buffer */
2703                 /* duplicate the string to strip away the :port */
2704                 port = ast_strdupa(arg);
2705                 buf = strsep(&port, ":");
2706                 sa->sin_family = AF_INET;       /* assign family */
2707                 /*
2708                  * honor the ports flag setting, assign default value
2709                  * in case of errors or field unset.
2710                  */
2711                 flags &= PARSE_PORT_MASK; /* the only flags left to process */
2712                 if (port) {
2713                         if (flags == PARSE_PORT_FORBID) {
2714                                 error = 1;      /* port was forbidden */
2715                                 sa->sin_port = def->sin_port;
2716                         } else if (flags == PARSE_PORT_IGNORE)
2717                                 sa->sin_port = def->sin_port;
2718                         else /* accept or require */
2719                                 sa->sin_port = htons(strtol(port, NULL, 0));
2720                 } else {
2721                         sa->sin_port = def->sin_port;
2722                         if (flags == PARSE_PORT_REQUIRE)
2723                                 error = 1;
2724                 }
2725                 /* Now deal with host part, even if we have errors before. */
2726                 hp = ast_gethostbyname(buf, &ahp);
2727                 if (hp) /* resolved successfully */
2728                         memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
2729                 else {
2730                         error = 1;
2731                         sa->sin_addr = def->sin_addr;
2732                 }
2733                 ast_debug(3,
2734                         "extract inaddr from [%s] gives [%s:%d](%d)\n",
2735                         arg, ast_inet_ntoa(sa->sin_addr),
2736                         ntohs(sa->sin_port), error);
2737                 break;
2738             }
2739         }
2740         va_end(ap);
2741         return error;
2742 }
2743
2744 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2745 {
2746         struct ast_config_engine *eng;
2747         struct ast_config_map *map;
2748
2749         switch (cmd) {
2750         case CLI_INIT:
2751                 e->command = "core show config mappings";
2752                 e->usage =
2753                         "Usage: core show config mappings\n"
2754                         "       Shows the filenames to config engines.\n";
2755                 return NULL;
2756         case CLI_GENERATE:
2757                 return NULL;
2758         }
2759         
2760         ast_mutex_lock(&config_lock);
2761
2762         if (!config_engine_list) {
2763                 ast_cli(a->fd, "No config mappings found.\n");
2764         } else {
2765                 for (eng = config_engine_list; eng; eng = eng->next) {
2766                         ast_cli(a->fd, "Config Engine: %s\n", eng->name);
2767                         for (map = config_maps; map; map = map->next) {
2768                                 if (!strcasecmp(map->driver, eng->name)) {
2769                                         ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
2770                                                         map->table ? map->table : map->name);
2771                                 }
2772                         }
2773                 }
2774         }
2775         
2776         ast_mutex_unlock(&config_lock);
2777
2778         return CLI_SUCCESS;
2779 }
2780
2781 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2782 {
2783         struct cache_file_mtime *cfmtime;
2784         char *prev = "", *completion_value = NULL;
2785         int wordlen, which = 0;
2786
2787         switch (cmd) {
2788         case CLI_INIT:
2789                 e->command = "config reload";
2790                 e->usage =
2791                         "Usage: config reload <filename.conf>\n"
2792                         "   Reloads all modules that reference <filename.conf>\n";
2793                 return NULL;
2794         case CLI_GENERATE:
2795                 if (a->pos > 2) {
2796                         return NULL;
2797                 }
2798
2799                 wordlen = strlen(a->word);
2800
2801                 AST_LIST_LOCK(&cfmtime_head);
2802                 AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2803                         /* Skip duplicates - this only works because the list is sorted by filename */
2804                         if (strcmp(cfmtime->filename, prev) == 0) {
2805                                 continue;
2806                         }
2807
2808                         /* Core configs cannot be reloaded */
2809                         if (ast_strlen_zero(cfmtime->who_asked)) {
2810                                 continue;
2811                         }
2812
2813                         if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
2814                                 completion_value = ast_strdup(cfmtime->filename);
2815                                 break;
2816                         }
2817
2818                         /* Otherwise save that we've seen this filename */
2819                         prev = cfmtime->filename;
2820                 }
2821                 AST_LIST_UNLOCK(&cfmtime_head);
2822
2823                 return completion_value;
2824         }
2825
2826         if (a->argc != 3) {
2827                 return CLI_SHOWUSAGE;
2828         }
2829
2830         AST_LIST_LOCK(&cfmtime_head);
2831         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2832                 if (!strcmp(cfmtime->filename, a->argv[2])) {
2833                         char *buf = alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
2834                         sprintf(buf, "module reload %s", cfmtime->who_asked);
2835                         ast_cli_command(a->fd, buf);
2836                 }
2837         }
2838         AST_LIST_UNLOCK(&cfmtime_head);
2839
2840         return CLI_SUCCESS;
2841 }
2842
2843 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2844 {
2845         struct cache_file_mtime *cfmtime;
2846
2847         switch (cmd) {
2848         case CLI_INIT:
2849                 e->command = "config list";
2850                 e->usage =
2851                         "Usage: config list\n"
2852                         "   Show all modules that have loaded a configuration file\n";
2853                 return NULL;
2854         case CLI_GENERATE:
2855                 return NULL;
2856         }
2857
2858         AST_LIST_LOCK(&cfmtime_head);
2859         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2860                 ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
2861         }
2862         AST_LIST_UNLOCK(&cfmtime_head);
2863
2864         return CLI_SUCCESS;
2865 }
2866
2867 static struct ast_cli_entry cli_config[] = {
2868         AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
2869         AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
2870         AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
2871 };
2872
2873 int register_config_cli(void)
2874 {
2875         ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));
2876         return 0;
2877 }