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