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