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