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