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