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