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