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