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