config.c: Fix potential memory corruption after [section](+).
[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;
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                 *cat = newcat = ast_category_new(catname,
1664                         S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
1665                         lineno);
1666                 if (!newcat) {
1667                         return -1;
1668                 }
1669                 (*cat)->lineno = lineno;
1670
1671                 /* add comments */
1672                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1673                         newcat->precomments = ALLOC_COMMENT(comment_buffer);
1674                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1675                         newcat->sameline = ALLOC_COMMENT(lline_buffer);
1676                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1677                         CB_RESET(comment_buffer, lline_buffer);
1678
1679                 /* If there are options or categories to inherit from, process them now */
1680                 if (c) {
1681                         if (!(cur = strchr(c, ')'))) {
1682                                 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
1683                                 return -1;
1684                         }
1685                         *cur = '\0';
1686                         while ((cur = strsep(&c, ","))) {
1687                                 if (!strcasecmp(cur, "!")) {
1688                                         (*cat)->ignored = 1;
1689                                 } else if (!strcasecmp(cur, "+")) {
1690                                         *cat = ast_category_get(cfg, catname, NULL);
1691                                         if (!(*cat)) {
1692                                                 if (newcat) {
1693                                                         ast_category_destroy(newcat);
1694                                                 }
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                                                 ast_config_set_current_category(cfg, *cat);
1700                                                 (*cat)->ignored |= newcat->ignored;
1701                                                 move_variables(newcat, *cat);
1702                                                 ast_category_destroy(newcat);
1703                                                 newcat = NULL;
1704                                         }
1705                                 } else {
1706                                         struct ast_category *base;
1707
1708                                         base = ast_category_get(cfg, cur, "TEMPLATES=include");
1709                                         if (!base) {
1710                                                 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
1711                                                 return -1;
1712                                         }
1713                                         if (ast_category_inherit(*cat, base)) {
1714                                                 ast_log(LOG_ERROR, "Inheritence requested, but allocation failed\n");
1715                                                 return -1;
1716                                         }
1717                                 }
1718                         }
1719                 }
1720
1721                 /*
1722                  * We need to set *last_cat to newcat here regardless.  If the
1723                  * category is being appended to we have no place for trailing
1724                  * comments on the appended category.  The appended category
1725                  * may be in another file or it already has trailing comments
1726                  * that we would then leak.
1727                  */
1728                 *last_var = NULL;
1729                 *last_cat = newcat;
1730                 if (newcat) {
1731                         ast_category_append(cfg, newcat);
1732                 }
1733         } else if (cur[0] == '#') { /* A directive - #include or #exec */
1734                 char *cur2;
1735                 char real_inclusion_name[256];
1736                 int do_include = 0;     /* otherwise, it is exec */
1737                 int try_include = 0;
1738
1739                 cur++;
1740                 c = cur;
1741                 while (*c && (*c > 32)) {
1742                         c++;
1743                 }
1744
1745                 if (*c) {
1746                         *c = '\0';
1747                         /* Find real argument */
1748                         c = ast_strip(c + 1);
1749                         if (!(*c)) {
1750                                 c = NULL;
1751                         }
1752                 } else {
1753                         c = NULL;
1754                 }
1755                 if (!strcasecmp(cur, "include")) {
1756                         do_include = 1;
1757                 } else if (!strcasecmp(cur, "tryinclude")) {
1758                         do_include = 1;
1759                         try_include = 1;
1760                 } else if (!strcasecmp(cur, "exec")) {
1761                         if (!ast_opt_exec_includes) {
1762                                 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
1763                                 return 0;       /* XXX is this correct ? or we should return -1 ? */
1764                         }
1765                 } else {
1766                         ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
1767                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1768                 }
1769
1770                 if (c == NULL) {
1771                         ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
1772                                         do_include ? "include / tryinclude" : "exec",
1773                                         do_include ? "filename" : "/path/to/executable",
1774                                         lineno,
1775                                         configfile);
1776                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1777                 }
1778
1779                 cur = c;
1780                 /* Strip off leading and trailing "'s and <>'s */
1781                 /* Dequote */
1782                 if ((*c == '"') || (*c == '<')) {
1783                         char quote_char = *c;
1784                         if (quote_char == '<') {
1785                                 quote_char = '>';
1786                         }
1787
1788                         if (*(c + strlen(c) - 1) == quote_char) {
1789                                 cur++;
1790                                 *(c + strlen(c) - 1) = '\0';
1791                         }
1792                 }
1793                 cur2 = cur;
1794
1795                 /* #exec </path/to/executable>
1796                    We create a tmp file, then we #include it, then we delete it. */
1797                 if (!do_include) {
1798                         struct timeval now = ast_tvnow();
1799                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1800                                 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
1801                         snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
1802                         snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
1803                         ast_safe_system(cmd);
1804                         cur = exec_file;
1805                 } else {
1806                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1807                                 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
1808                         exec_file[0] = '\0';
1809                 }
1810                 /* A #include */
1811                 /* record this inclusion */
1812                 ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1813
1814                 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
1815                 if (!ast_strlen_zero(exec_file))
1816                         unlink(exec_file);
1817                 if (!do_include && !try_include) {
1818                         ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1819                         return -1;
1820                 }
1821                 /* XXX otherwise what ? the default return is 0 anyways */
1822
1823         } else {
1824                 /* Just a line (variable = value) */
1825                 int object = 0;
1826                 int is_escaped;
1827
1828                 if (!(*cat)) {
1829                         ast_log(LOG_WARNING,
1830                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
1831                         return -1;
1832                 }
1833
1834                 is_escaped = cur[0] == '\\';
1835                 if (is_escaped) {
1836                         /* First character is escaped. */
1837                         ++cur;
1838                         if (cur[0] < 33) {
1839                                 ast_log(LOG_ERROR, "Invalid escape in line %d of %s\n", lineno, configfile);
1840                                 return -1;
1841                         }
1842                 }
1843                 c = strchr(cur + is_escaped, '=');
1844
1845                 if (c && c > cur + is_escaped && (*(c - 1) == '+')) {
1846                         struct ast_variable *var, *replace = NULL;
1847                         struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
1848
1849                         if (!str || !*str) {
1850                                 return -1;
1851                         }
1852
1853                         *(c - 1) = '\0';
1854                         c++;
1855                         cur = ast_strip(cur);
1856
1857                         /* Must iterate through category until we find last variable of same name (since there could be multiple) */
1858                         for (var = ast_category_first(*cat); var; var = var->next) {
1859                                 if (!strcmp(var->name, cur)) {
1860                                         replace = var;
1861                                 }
1862                         }
1863
1864                         if (!replace) {
1865                                 /* Nothing to replace; just set a variable normally. */
1866                                 goto set_new_variable;
1867                         }
1868
1869                         ast_str_set(str, 0, "%s", replace->value);
1870                         ast_str_append(str, 0, "%s", c);
1871                         ast_str_trim_blanks(*str);
1872                         ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
1873                 } else if (c) {
1874                         *c = 0;
1875                         c++;
1876                         /* Ignore > in => */
1877                         if (*c== '>') {
1878                                 object = 1;
1879                                 c++;
1880                         }
1881                         cur = ast_strip(cur);
1882 set_new_variable:
1883                         if (ast_strlen_zero(cur)) {
1884                                 ast_log(LOG_WARNING, "No variable name in line %d of %s\n", lineno, configfile);
1885                         } else if ((v = ast_variable_new(cur, ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
1886                                 v->lineno = lineno;
1887                                 v->object = object;
1888                                 *last_cat = NULL;
1889                                 *last_var = v;
1890                                 /* Put and reset comments */
1891                                 v->blanklines = 0;
1892                                 ast_variable_append(*cat, v);
1893                                 /* add comments */
1894                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1895                                         v->precomments = ALLOC_COMMENT(comment_buffer);
1896                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1897                                         v->sameline = ALLOC_COMMENT(lline_buffer);
1898                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1899                                         CB_RESET(comment_buffer, lline_buffer);
1900
1901                         } else {
1902                                 return -1;
1903                         }
1904                 } else {
1905                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1906                 }
1907         }
1908         return 0;
1909 }
1910
1911 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)
1912 {
1913         char fn[256];
1914 #if defined(LOW_MEMORY)
1915         char buf[512];
1916 #else
1917         char buf[8192];
1918 #endif
1919         char *new_buf, *comment_p, *process_buf;
1920         FILE *f;
1921         int lineno=0;
1922         int comment = 0, nest[MAX_NESTED_COMMENTS];
1923         struct ast_category *cat = NULL;
1924         int count = 0;
1925         struct stat statbuf;
1926         struct cache_file_mtime *cfmtime = NULL;
1927         struct cache_file_include *cfinclude;
1928         struct ast_variable *last_var = NULL;
1929         struct ast_category *last_cat = NULL;
1930         /*! Growable string buffer */
1931         struct ast_str *comment_buffer = NULL;  /*!< this will be a comment collector.*/
1932         struct ast_str *lline_buffer = NULL;    /*!< A buffer for stuff behind the ; */
1933 #ifdef AST_INCLUDE_GLOB
1934         int glob_ret;
1935         glob_t globbuf;
1936 #endif
1937
1938         if (cfg) {
1939                 cat = ast_config_get_current_category(cfg);
1940         }
1941
1942         if (filename[0] == '/') {
1943                 ast_copy_string(fn, filename, sizeof(fn));
1944         } else {
1945                 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
1946         }
1947
1948         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1949                 comment_buffer = ast_str_create(CB_SIZE);
1950                 if (comment_buffer) {
1951                         lline_buffer = ast_str_create(CB_SIZE);
1952                 }
1953                 if (!lline_buffer) {
1954                         ast_free(comment_buffer);
1955                         ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1956                         return NULL;
1957                 }
1958         }
1959 #ifdef AST_INCLUDE_GLOB
1960         globbuf.gl_offs = 0;    /* initialize it to silence gcc */
1961         glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1962         if (glob_ret == GLOB_NOSPACE) {
1963                 ast_log(LOG_WARNING,
1964                         "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1965         } else if (glob_ret  == GLOB_ABORTED) {
1966                 ast_log(LOG_WARNING,
1967                         "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1968         } else {
1969                 /* loop over expanded files */
1970                 int i;
1971
1972                 if (!cfg && (globbuf.gl_pathc != 1 || strcmp(fn, globbuf.gl_pathv[0]))) {
1973                         /*
1974                          * We just want a file changed answer and since we cannot
1975                          * tell if a file was deleted with wildcard matching we will
1976                          * assume that something has always changed.  Also without
1977                          * a lot of refactoring we couldn't check more than one file
1978                          * for changes in the glob loop anyway.
1979                          */
1980                         globfree(&globbuf);
1981                         ast_free(comment_buffer);
1982                         ast_free(lline_buffer);
1983                         return NULL;
1984                 }
1985                 for (i=0; i<globbuf.gl_pathc; i++) {
1986                         ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1987 #endif
1988                         /*
1989                          * The following is not a loop, but just a convenient way to define a block
1990                          * (using do { } while(0) ), and be able to exit from it with 'continue'
1991                          * or 'break' in case of errors. Nice trick.
1992                          */
1993                         do {
1994                                 if (stat(fn, &statbuf)) {
1995                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1996                                                 config_cache_remove(fn, who_asked);
1997                                         }
1998                                         continue;
1999                                 }
2000
2001                                 if (!S_ISREG(statbuf.st_mode)) {
2002                                         ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
2003                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2004                                                 config_cache_remove(fn, who_asked);
2005                                         }
2006                                         continue;
2007                                 }
2008
2009                                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
2010                                         /* Find our cached entry for this configuration file */
2011                                         AST_LIST_LOCK(&cfmtime_head);
2012                                         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
2013                                                 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked)) {
2014                                                         break;
2015                                                 }
2016                                         }
2017                                         if (!cfmtime) {
2018                                                 cfmtime = cfmtime_new(fn, who_asked);
2019                                                 if (!cfmtime) {
2020                                                         AST_LIST_UNLOCK(&cfmtime_head);
2021                                                         continue;
2022                                                 }
2023                                                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
2024                                                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
2025                                         }
2026                                 }
2027
2028                                 if (cfmtime
2029                                         && !cfmtime->has_exec
2030                                         && !cfmstat_cmp(cfmtime, &statbuf)
2031                                         && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
2032                                         int unchanged = 1;
2033
2034                                         /* File is unchanged, what about the (cached) includes (if any)? */
2035                                         AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
2036                                                 if (!config_text_file_load(NULL, NULL, cfinclude->include,
2037                                                         NULL, flags, "", who_asked)) {
2038                                                         /* One change is enough to short-circuit and reload the whole shebang */
2039                                                         unchanged = 0;
2040                                                         break;
2041                                                 }
2042                                         }
2043
2044                                         if (unchanged) {
2045                                                 AST_LIST_UNLOCK(&cfmtime_head);
2046 #ifdef AST_INCLUDE_GLOB
2047                                                 globfree(&globbuf);
2048 #endif
2049                                                 ast_free(comment_buffer);
2050                                                 ast_free(lline_buffer);
2051                                                 return CONFIG_STATUS_FILEUNCHANGED;
2052                                         }
2053                                 }
2054
2055                                 /* If cfg is NULL, then we just want a file changed answer. */
2056                                 if (cfg == NULL) {
2057                                         if (cfmtime) {
2058                                                 AST_LIST_UNLOCK(&cfmtime_head);
2059                                         }
2060                                         continue;
2061                                 }
2062
2063                                 if (cfmtime) {
2064                                         /* Forget about what we thought we knew about this file's includes. */
2065                                         cfmtime->has_exec = 0;
2066                                         config_cache_flush_includes(cfmtime);
2067
2068                                         cfmstat_save(cfmtime, &statbuf);
2069                                         AST_LIST_UNLOCK(&cfmtime_head);
2070                                 }
2071
2072                                 if (!(f = fopen(fn, "r"))) {
2073                                         ast_debug(1, "No file to parse: %s\n", fn);
2074                                         ast_verb(2, "Parsing '%s': Not found (%s)\n", fn, strerror(errno));
2075                                         continue;
2076                                 }
2077                                 count++;
2078                                 /* If we get to this point, then we're loading regardless */
2079                                 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
2080                                 ast_debug(1, "Parsing %s\n", fn);
2081                                 ast_verb(2, "Parsing '%s': Found\n", fn);
2082                                 while (!feof(f)) {
2083                                         lineno++;
2084                                         if (fgets(buf, sizeof(buf), f)) {
2085                                                 /* Skip lines that are too long */
2086                                                 if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 1] != '\n') {
2087                                                         ast_log(LOG_WARNING, "Line %d too long, skipping. It begins with: %.32s...\n", lineno, buf);
2088                                                         while (fgets(buf, sizeof(buf), f)) {
2089                                                                 if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 1] == '\n') {
2090                                                                         break;
2091                                                                 }
2092                                                         }
2093                                                         continue;
2094                                                 }
2095
2096                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)
2097                                                         && lline_buffer
2098                                                         && ast_str_strlen(lline_buffer)) {
2099                                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2100                                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
2101                                                 }
2102
2103                                                 new_buf = buf;
2104                                                 if (comment) {
2105                                                         process_buf = NULL;
2106                                                 } else {
2107                                                         process_buf = buf;
2108                                                 }
2109
2110                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)
2111                                                         && comment_buffer
2112                                                         && ast_str_strlen(comment_buffer)
2113                                                         && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
2114                                                         /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
2115                                                         CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
2116                                                         continue; /* go get a new line, then */
2117                                                 }
2118
2119                                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
2120                                                         if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
2121                                                                 /* Escaped semicolons aren't comments. */
2122                                                                 new_buf = comment_p;
2123                                                                 /* write over the \ and bring the null terminator with us */
2124                                                                 memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
2125                                                         } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
2126                                                                 /* Meta-Comment start detected ";--" */
2127                                                                 if (comment < MAX_NESTED_COMMENTS) {
2128                                                                         *comment_p = '\0';
2129                                                                         new_buf = comment_p + 3;
2130                                                                         comment++;
2131                                                                         nest[comment-1] = lineno;
2132                                                                 } else {
2133                                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
2134                                                                 }
2135                                                         } else if ((comment_p >= new_buf + 2) &&
2136                                                                    (*(comment_p - 1) == COMMENT_TAG) &&
2137                                                                    (*(comment_p - 2) == COMMENT_TAG)) {
2138                                                                 /* Meta-Comment end detected "--;" */
2139                                                                 comment--;
2140                                                                 new_buf = comment_p + 1;
2141                                                                 if (!comment) {
2142                                                                         /* Back to non-comment now */
2143                                                                         if (process_buf) {
2144                                                                                 /* Actually have to move what's left over the top, then continue */
2145                                                                                 char *oldptr;
2146
2147                                                                                 oldptr = process_buf + strlen(process_buf);
2148                                                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2149                                                                                         CB_ADD(&comment_buffer, ";");
2150                                                                                         CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
2151                                                                                 }
2152
2153                                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
2154                                                                                 new_buf = oldptr;
2155                                                                         } else {
2156                                                                                 process_buf = new_buf;
2157                                                                         }
2158                                                                 }
2159                                                         } else {
2160                                                                 if (!comment) {
2161                                                                         /* If ; is found, and we are not nested in a comment,
2162                                                                            we immediately stop all comment processing */
2163                                                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2164                                                                                 CB_ADD(&lline_buffer, comment_p);
2165                                                                         }
2166                                                                         *comment_p = '\0';
2167                                                                         new_buf = comment_p;
2168                                                                 } else {
2169                                                                         new_buf = comment_p + 1;
2170                                                                 }
2171                                                         }
2172                                                 }
2173                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
2174                                                         CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
2175                                                 }
2176
2177                                                 if (process_buf) {
2178                                                         char *buffer = ast_strip(process_buf);
2179
2180                                                         if (!ast_strlen_zero(buffer)) {
2181                                                                 if (process_text_line(cfg, &cat, buffer, lineno, fn,
2182                                                                         flags, comment_buffer, lline_buffer,
2183                                                                         suggested_include_file, &last_cat, &last_var,
2184                                                                         who_asked)) {
2185                                                                         cfg = CONFIG_STATUS_FILEINVALID;
2186                                                                         break;
2187                                                                 }
2188                                                         }
2189                                                 }
2190                                         }
2191                                 }
2192                                 /* end of file-- anything in a comment buffer? */
2193                                 if (last_cat) {
2194                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
2195                                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
2196                                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2197                                                         ast_str_reset(lline_buffer); /* erase the lline buffer */
2198                                                 }
2199                                                 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
2200                                         }
2201                                 } else if (last_var) {
2202                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
2203                                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
2204                                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2205                                                         ast_str_reset(lline_buffer); /* erase the lline buffer */
2206                                                 }
2207                                                 last_var->trailing = ALLOC_COMMENT(comment_buffer);
2208                                         }
2209                                 } else {
2210                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
2211                                                 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
2212                                         }
2213                                 }
2214                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2215                                         CB_RESET(comment_buffer, lline_buffer);
2216                                 }
2217
2218                                 fclose(f);
2219                         } while (0);
2220                         if (comment) {
2221                                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
2222                         }
2223 #ifdef AST_INCLUDE_GLOB
2224                         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
2225                                 break;
2226                         }
2227                 }
2228                 globfree(&globbuf);
2229         }
2230 #endif
2231
2232         ast_free(comment_buffer);
2233         ast_free(lline_buffer);
2234
2235         if (count == 0) {
2236                 return NULL;
2237         }
2238
2239         return cfg;
2240 }
2241
2242
2243 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
2244    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
2245    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
2246    be shocked and mystified as to why things are not showing up in the files!
2247
2248    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
2249    and line number are stored for each include, plus the name of the file included, so that these statements may be
2250    included in the output files on a file_save operation.
2251
2252    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
2253    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
2254    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
2255    and a header gets added.
2256
2257    vars and category heads are output in the order they are stored in the config file. So, if the software
2258    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
2259    file/lineno data probably won't get changed.
2260
2261 */
2262
2263 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
2264 {
2265         char date[256]="";
2266         time_t t;
2267
2268         time(&t);
2269         ast_copy_string(date, ctime(&t), sizeof(date));
2270
2271         fprintf(f1, ";!\n");
2272         fprintf(f1, ";! Automatically generated configuration file\n");
2273         if (strcmp(configfile, fn))
2274                 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
2275         else
2276                 fprintf(f1, ";! Filename: %s\n", configfile);
2277         fprintf(f1, ";! Generator: %s\n", generator);
2278         fprintf(f1, ";! Creation Date: %s", date);
2279         fprintf(f1, ";!\n");
2280 }
2281
2282 static void inclfile_destroy(void *obj)
2283 {
2284         const struct inclfile *o = obj;
2285
2286         ast_free(o->fname);
2287 }
2288
2289 static void make_fn(char *fn, size_t fn_size, const char *file, const char *configfile)
2290 {
2291         if (ast_strlen_zero(file)) {
2292                 if (configfile[0] == '/') {
2293                         ast_copy_string(fn, configfile, fn_size);
2294                 } else {
2295                         snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
2296                 }
2297         } else if (file[0] == '/') {
2298                 ast_copy_string(fn, file, fn_size);
2299         } else {
2300                 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
2301         }
2302 }
2303
2304 static struct inclfile *set_fn(char *fn, size_t fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
2305 {
2306         struct inclfile lookup;
2307         struct inclfile *fi;
2308
2309         make_fn(fn, fn_size, file, configfile);
2310         lookup.fname = fn;
2311         fi = ao2_find(fileset, &lookup, OBJ_POINTER);
2312         if (fi) {
2313                 /* Found existing include file scratch pad. */
2314                 return fi;
2315         }
2316
2317         /* set up a file scratch pad */
2318         fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
2319         if (!fi) {
2320                 /* Scratch pad creation failed. */
2321                 return NULL;
2322         }
2323         fi->fname = ast_strdup(fn);
2324         if (!fi->fname) {
2325                 /* Scratch pad creation failed. */
2326                 ao2_ref(fi, -1);
2327                 return NULL;
2328         }
2329         fi->lineno = 1;
2330
2331         ao2_link(fileset, fi);
2332
2333         return fi;
2334 }
2335
2336 static int count_linefeeds(char *str)
2337 {
2338         int count = 0;
2339
2340         while (*str) {
2341                 if (*str =='\n')
2342                         count++;
2343                 str++;
2344         }
2345         return count;
2346 }
2347
2348 static int count_linefeeds_in_comments(struct ast_comment *x)
2349 {
2350         int count = 0;
2351
2352         while (x) {
2353                 count += count_linefeeds(x->cmt);
2354                 x = x->next;
2355         }
2356         return count;
2357 }
2358
2359 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
2360 {
2361         int precomment_lines;
2362         int i;
2363
2364         if (!fi) {
2365                 /* No file scratch pad object so insert no blank lines. */
2366                 return;
2367         }
2368
2369         precomment_lines = count_linefeeds_in_comments(precomments);
2370
2371         /* I don't have to worry about those ;! comments, they are
2372            stored in the precomments, but not printed back out.
2373            I did have to make sure that comments following
2374            the ;! header comments were not also deleted in the process */
2375         if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
2376                 return;
2377         } else if (lineno == 0) {
2378                 /* Line replacements also mess things up */
2379                 return;
2380         } else if (lineno - precomment_lines - fi->lineno < 5) {
2381                 /* Only insert less than 5 blank lines; if anything more occurs,
2382                  * it's probably due to context deletion. */
2383                 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
2384                         fprintf(fp, "\n");
2385                 }
2386         } else {
2387                 /* Deletion occurred - insert a single blank line, for separation of
2388                  * contexts. */
2389                 fprintf(fp, "\n");
2390         }
2391
2392         fi->lineno = lineno + 1; /* Advance the file lineno */
2393 }
2394
2395 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2396 {
2397         return ast_config_text_file_save2(configfile, cfg, generator, CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT);
2398 }
2399
2400 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2401 {
2402         return ast_config_text_file_save2(configfile, cfg, generator, CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT);
2403 }
2404
2405 int ast_config_text_file_save2(const char *configfile, const struct ast_config *cfg, const char *generator, uint32_t flags)
2406 {
2407         FILE *f;
2408         char fn[PATH_MAX];
2409         struct ast_variable *var;
2410         struct ast_category *cat;
2411         struct ast_comment *cmt;
2412         struct ast_config_include *incl;
2413         int blanklines = 0;
2414         struct ao2_container *fileset;
2415         struct inclfile *fi;
2416
2417         fileset = ao2_container_alloc(1023, hash_string, hashtab_compare_strings);
2418         if (!fileset) {
2419                 /* Container creation failed. */
2420                 return -1;
2421         }
2422
2423         /* Check all the files for write access before attempting to modify any of them */
2424         for (incl = cfg->includes; incl; incl = incl->next) {
2425                 /* reset all the output flags in case this isn't our first time saving this data */
2426                 incl->output = 0;
2427                 /* now make sure we have write access */
2428                 if (!incl->exec) {
2429                         make_fn(fn, sizeof(fn), incl->included_file, configfile);
2430                         if (access(fn, R_OK | W_OK)) {
2431                                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2432                                 return -1;
2433                         }
2434                 }
2435         }
2436
2437         /* now make sure we have write access to the main config file */
2438         make_fn(fn, sizeof(fn), 0, configfile);
2439         if (access(fn, R_OK | W_OK)) {
2440                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2441                 return -1;
2442         }
2443
2444         /* Now that we know we have write access to all files, it's safe to start truncating them */
2445
2446         /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
2447            are all truncated to zero bytes and have that nice header*/
2448         for (incl = cfg->includes; incl; incl = incl->next) {
2449                 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*/
2450                         /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
2451                         fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
2452                         f = fopen(fn, "w");
2453                         if (f) {
2454                                 gen_header(f, configfile, fn, generator);
2455                                 fclose(f); /* this should zero out the file */
2456                         } else {
2457                                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2458                         }
2459                         if (fi) {
2460                                 ao2_ref(fi, -1);
2461                         }
2462                 }
2463         }
2464
2465         /* just set fn to absolute ver of configfile */
2466         fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
2467         if (
2468 #ifdef __CYGWIN__
2469                 (f = fopen(fn, "w+"))
2470 #else
2471                 (f = fopen(fn, "w"))
2472 #endif
2473                 ) {
2474                 ast_verb(2, "Saving '%s'\n", fn);
2475                 gen_header(f, configfile, fn, generator);
2476                 cat = cfg->root;
2477                 fclose(f);
2478                 if (fi) {
2479                         ao2_ref(fi, -1);
2480                 }
2481
2482                 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
2483                 /* since each var, cat, and associated comments can come from any file, we have to be
2484                    mobile, and open each file, print, and close it on an entry-by-entry basis */
2485
2486                 while (cat) {
2487                         fi = set_fn(fn, sizeof(fn), cat->file, configfile, fileset);
2488                         f = fopen(fn, "a");
2489                         if (!f) {
2490                                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2491                                 if (fi) {
2492                                         ao2_ref(fi, -1);
2493                                 }
2494                                 ao2_ref(fileset, -1);
2495                                 return -1;
2496                         }
2497
2498                         /* dump any includes that happen before this category header */
2499                         for (incl=cfg->includes; incl; incl = incl->next) {
2500                                 if (strcmp(incl->include_location_file, cat->file) == 0){
2501                                         if (cat->lineno > incl->include_location_lineno && !incl->output) {
2502                                                 if (incl->exec)
2503                                                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2504                                                 else
2505                                                         fprintf(f,"#include \"%s\"\n", incl->included_file);
2506                                                 incl->output = 1;
2507                                         }
2508                                 }
2509                         }
2510
2511                         insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
2512                         /* Dump section with any appropriate comment */
2513                         for (cmt = cat->precomments; cmt; cmt=cmt->next) {
2514                                 char *cmtp = cmt->cmt;
2515                                 while (cmtp && *cmtp == ';' && *(cmtp+1) == '!') {
2516                                         char *cmtp2 = strchr(cmtp+1, '\n');
2517                                         if (cmtp2)
2518                                                 cmtp = cmtp2+1;
2519                                         else cmtp = 0;
2520                                 }
2521                                 if (cmtp)
2522                                         fprintf(f,"%s", cmtp);
2523                         }
2524                         fprintf(f, "[%s]", cat->name);
2525                         if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
2526                                 fprintf(f, "(");
2527                                 if (cat->ignored) {
2528                                         fprintf(f, "!");
2529                                 }
2530                                 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
2531                                         fprintf(f, ",");
2532                                 }
2533                                 if (!AST_LIST_EMPTY(&cat->template_instances)) {
2534                                         struct ast_category_template_instance *x;
2535                                         AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2536                                                 fprintf(f,"%s",x->name);
2537                                                 if (x != AST_LIST_LAST(&cat->template_instances))
2538                                                         fprintf(f,",");
2539                                         }
2540                                 }
2541                                 fprintf(f, ")");
2542                         }
2543                         for(cmt = cat->sameline; cmt; cmt=cmt->next)
2544                         {
2545                                 fprintf(f,"%s", cmt->cmt);
2546                         }
2547                         if (!cat->sameline)
2548                                 fprintf(f,"\n");
2549                         for (cmt = cat->trailing; cmt; cmt=cmt->next) {
2550                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2551                                         fprintf(f,"%s", cmt->cmt);
2552                         }
2553                         fclose(f);
2554                         if (fi) {
2555                                 ao2_ref(fi, -1);
2556                         }
2557
2558                         var = cat->root;
2559                         while (var) {
2560                                 struct ast_category_template_instance *x;
2561                                 int found = 0;
2562
2563                                 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2564                                         struct ast_variable *v;
2565                                         for (v = x->inst->root; v; v = v->next) {
2566
2567                                                 if (flags & CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT) {
2568                                                         if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2569                                                                 found = 1;
2570                                                                 break;
2571                                                         }
2572                                                 } else {
2573                                                         if (var->inherited) {
2574                                                                 found = 1;
2575                                                                 break;
2576                                                         } else {
2577                                                                 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2578                                                                         found = 1;
2579                                                                         break;
2580                                                                 }
2581                                                         }
2582                                                 }
2583                                         }
2584                                         if (found) {
2585                                                 break;
2586                                         }
2587                                 }
2588                                 if (found) {
2589                                         var = var->next;
2590                                         continue;
2591                                 }
2592                                 fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
2593                                 f = fopen(fn, "a");
2594                                 if (!f) {
2595                                         ast_debug(1, "Unable to open for writing: %s\n", fn);
2596                                         ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2597                                         if (fi) {
2598                                                 ao2_ref(fi, -1);
2599                                         }
2600                                         ao2_ref(fileset, -1);
2601                                         return -1;
2602                                 }
2603
2604                                 /* dump any includes that happen before this category header */
2605                                 for (incl=cfg->includes; incl; incl = incl->next) {
2606                                         if (strcmp(incl->include_location_file, var->file) == 0){
2607                                                 if (var->lineno > incl->include_location_lineno && !incl->output) {
2608                                                         if (incl->exec)
2609                                                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2610                                                         else
2611                                                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
2612                                                         incl->output = 1;
2613                                                 }
2614                                         }
2615                                 }
2616
2617                                 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
2618                                 for (cmt = var->precomments; cmt; cmt=cmt->next) {
2619                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2620                                                 fprintf(f,"%s", cmt->cmt);
2621                                 }
2622
2623                                 { /* Block for 'escaped' scope */
2624                                         int escaped_len = 2 * strlen(var->value) + 1;
2625                                         char escaped[escaped_len];
2626
2627                                         ast_escape_semicolons(var->value, escaped, escaped_len);
2628
2629                                         if (var->sameline) {
2630                                                 fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="),
2631                                                         escaped, var->sameline->cmt);
2632                                         } else {
2633                                                 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="),
2634                                                         escaped);
2635                                         }
2636                                 }
2637
2638                                 for (cmt = var->trailing; cmt; cmt=cmt->next) {
2639                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2640                                                 fprintf(f,"%s", cmt->cmt);
2641                                 }
2642                                 if (var->blanklines) {
2643                                         blanklines = var->blanklines;
2644                                         while (blanklines--)
2645                                                 fprintf(f, "\n");
2646                                 }
2647
2648                                 fclose(f);
2649                                 if (fi) {
2650                                         ao2_ref(fi, -1);
2651                                 }
2652
2653                                 var = var->next;
2654                         }
2655                         cat = cat->next;
2656                 }
2657                 if (!option_debug) {
2658                         ast_verb(2, "Saving '%s': saved\n", fn);
2659                 }
2660         } else {
2661                 ast_debug(1, "Unable to open for writing: %s\n", fn);
2662                 ast_verb(2, "Unable to write '%s' (%s)\n", fn, strerror(errno));
2663                 if (fi) {
2664                         ao2_ref(fi, -1);
2665                 }
2666                 ao2_ref(fileset, -1);
2667                 return -1;
2668         }
2669
2670         /* Now, for files with trailing #include/#exec statements,
2671            we have to make sure every entry is output */
2672         for (incl=cfg->includes; incl; incl = incl->next) {
2673                 if (!incl->output) {
2674                         /* open the respective file */
2675                         fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
2676                         f = fopen(fn, "a");
2677                         if (!f) {
2678                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
2679                                 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2680                                 if (fi) {
2681                                         ao2_ref(fi, -1);
2682                                 }
2683                                 ao2_ref(fileset, -1);
2684                                 return -1;
2685                         }
2686
2687                         /* output the respective include */
2688                         if (incl->exec)
2689                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2690                         else
2691                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
2692                         fclose(f);
2693                         incl->output = 1;
2694                         if (fi) {
2695                                 ao2_ref(fi, -1);
2696                         }
2697                 }
2698         }
2699         ao2_ref(fileset, -1); /* this should destroy the hash container */
2700
2701         /* pass new configuration to any config hooks */
2702         config_hook_exec(configfile, generator, cfg);
2703
2704         return 0;
2705 }
2706
2707 static void clear_config_maps(void)
2708 {
2709         struct ast_config_map *map;
2710
2711         SCOPED_MUTEX(lock, &config_lock);
2712
2713         while (config_maps) {
2714                 map = config_maps;
2715                 config_maps = config_maps->next;
2716                 ast_free(map);
2717         }
2718 }
2719
2720 #ifdef TEST_FRAMEWORK
2721 int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2722 #else
2723 static int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2724 #endif
2725 {
2726         struct ast_config_map *map;
2727         char *dst;
2728         int length;
2729
2730         length = sizeof(*map);
2731         length += strlen(name) + 1;
2732         length += strlen(driver) + 1;
2733         length += strlen(database) + 1;
2734         if (table)
2735                 length += strlen(table) + 1;
2736
2737         if (!(map = ast_calloc(1, length)))
2738                 return -1;
2739
2740         dst = map->stuff;       /* writable space starts here */
2741         map->name = strcpy(dst, name);
2742         dst += strlen(dst) + 1;
2743         map->driver = strcpy(dst, driver);
2744         dst += strlen(dst) + 1;
2745         map->database = strcpy(dst, database);
2746         if (table) {
2747                 dst += strlen(dst) + 1;
2748                 map->table = strcpy(dst, table);
2749         }
2750         map->priority = priority;
2751         map->next = config_maps;
2752         config_maps = map;
2753
2754         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
2755
2756         return 0;
2757 }
2758
2759 int read_config_maps(void)
2760 {
2761         struct ast_config *config, *configtmp;
2762         struct ast_variable *v;
2763         char *driver, *table, *database, *textpri, *stringp, *tmp;
2764         struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
2765         int pri;
2766
2767         clear_config_maps();
2768
2769         configtmp = ast_config_new();
2770         if (!configtmp) {
2771                 ast_log(LOG_ERROR, "Unable to allocate memory for new config\n");
2772                 return -1;
2773         }
2774         config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
2775         if (config == CONFIG_STATUS_FILEINVALID) {
2776                 return -1;
2777         } else if (!config) {
2778                 ast_config_destroy(configtmp);
2779                 return 0;
2780         }
2781
2782         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
2783                 char buf[512];
2784                 ast_copy_string(buf, v->value, sizeof(buf));
2785                 stringp = buf;
2786                 driver = strsep(&stringp, ",");
2787
2788                 if ((tmp = strchr(stringp, '\"')))
2789                         stringp = tmp;
2790
2791                 /* check if the database text starts with a double quote */
2792                 if (*stringp == '"') {
2793                         stringp++;
2794                         database = strsep(&stringp, "\"");
2795                         strsep(&stringp, ",");
2796                 } else {
2797                         /* apparently this text has no quotes */
2798                         database = strsep(&stringp, ",");
2799                 }
2800
2801                 table = strsep(&stringp, ",");
2802                 textpri = strsep(&stringp, ",");
2803                 if (!textpri || !(pri = atoi(textpri))) {
2804                         pri = 1;
2805                 }
2806
2807                 if (!strcmp(v->name, extconfig_conf)) {
2808                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
2809                         continue;
2810                 }
2811
2812                 if (!strcmp(v->name, "asterisk.conf")) {
2813                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
2814                         continue;
2815                 }
2816
2817                 if (!strcmp(v->name, "logger.conf")) {
2818                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
2819                         continue;
2820                 }
2821
2822                 if (!driver || !database)
2823                         continue;
2824                 if (!strcasecmp(v->name, "sipfriends")) {
2825                         ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sippeers instead.\n");
2826                         ast_realtime_append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
2827                 } else if (!strcasecmp(v->name, "iaxfriends")) {
2828                         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");
2829                         ast_realtime_append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
2830                         ast_realtime_append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
2831                 } else
2832                         ast_realtime_append_mapping(v->name, driver, database, table, pri);
2833         }
2834
2835         ast_config_destroy(config);
2836         return 0;
2837 }
2838
2839 int ast_config_engine_register(struct ast_config_engine *new)
2840 {
2841         struct ast_config_engine *ptr;
2842
2843         SCOPED_MUTEX(lock, &config_lock);
2844
2845         if (!config_engine_list) {
2846                 config_engine_list = new;
2847         } else {
2848                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
2849                 ptr->next = new;
2850         }
2851
2852         return 1;
2853 }
2854
2855 int ast_config_engine_deregister(struct ast_config_engine *del)
2856 {
2857         struct ast_config_engine *ptr, *last=NULL;
2858
2859         SCOPED_MUTEX(lock, &config_lock);
2860
2861         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
2862                 if (ptr == del) {
2863                         if (last)
2864                                 last->next = ptr->next;
2865                         else
2866                                 config_engine_list = ptr->next;
2867                         break;
2868                 }
2869                 last = ptr;
2870         }
2871
2872         return 0;
2873 }
2874
2875 int ast_realtime_is_mapping_defined(const char *family)
2876 {
2877         struct ast_config_map *map;
2878         SCOPED_MUTEX(lock, &config_lock);
2879
2880         for (map = config_maps; map; map = map->next) {
2881                 if (!strcasecmp(family, map->name)) {
2882                         return 1;
2883                 }
2884         }
2885         ast_debug(5, "Failed to find a realtime mapping for %s\n", family);
2886
2887         return 0;
2888 }
2889
2890 /*! \brief Find realtime engine for realtime family */
2891 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
2892 {
2893         struct ast_config_engine *eng, *ret = NULL;
2894         struct ast_config_map *map;
2895
2896         SCOPED_MUTEX(lock, &config_lock);
2897
2898         for (map = config_maps; map; map = map->next) {
2899                 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
2900                         if (database)
2901                                 ast_copy_string(database, map->database, dbsiz);
2902                         if (table)
2903                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
2904                         break;
2905                 }
2906         }
2907
2908         /* Check if the required driver (engine) exist */
2909         if (map) {
2910                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
2911                         if (!strcasecmp(eng->name, map->driver))
2912                                 ret = eng;
2913                 }
2914         }
2915
2916         /* if we found a mapping, but the engine is not available, then issue a warning */
2917         if (map && !ret)
2918                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
2919
2920         return ret;
2921 }
2922
2923 static struct ast_config_engine text_file_engine = {
2924         .name = "text",
2925         .load_func = config_text_file_load,
2926 };
2927
2928 struct ast_config *ast_config_copy(const struct ast_config *old)
2929 {
2930         struct ast_config *new_config = ast_config_new();
2931         struct ast_category *cat_iter;
2932
2933         if (!new_config) {
2934                 return NULL;
2935         }
2936
2937         for (cat_iter = old->root; cat_iter; cat_iter = cat_iter->next) {
2938                 struct ast_category *new_cat =
2939                         ast_category_new(cat_iter->name, cat_iter->file, cat_iter->lineno);
2940                 if (!new_cat) {
2941                         goto fail;
2942                 }
2943                 ast_category_append(new_config, new_cat);
2944                 if (cat_iter->root) {
2945                         new_cat->root = ast_variables_dup(cat_iter->root);
2946                         if (!new_cat->root) {
2947                                 goto fail;
2948                         }
2949                         new_cat->last = cat_iter->last;
2950                 }
2951         }
2952
2953         return new_config;
2954
2955 fail:
2956         ast_config_destroy(new_config);
2957         return NULL;
2958 }
2959
2960
2961 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)
2962 {
2963         char db[256];
2964         char table[256];
2965         struct ast_config_engine *loader = &text_file_engine;
2966         struct ast_config *result;
2967
2968         /* The config file itself bumps include_level by 1 */
2969         if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
2970                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
2971                 return NULL;
2972         }
2973
2974         cfg->include_level++;
2975
2976         if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
2977                 struct ast_config_engine *eng;
2978
2979                 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
2980
2981
2982                 if (eng && eng->load_func) {
2983                         loader = eng;
2984                 } else {
2985                         eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
2986                         if (eng && eng->load_func)
2987                                 loader = eng;
2988                 }
2989         }
2990
2991         result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
2992
2993         if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED) {
2994                 result->include_level--;
2995                 config_hook_exec(filename, who_asked, result);
2996         } else if (result != CONFIG_STATUS_FILEINVALID) {
2997                 cfg->include_level--;
2998         }
2999
3000         return result;
3001 }
3002
3003 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
3004 {
3005         struct ast_config *cfg;
3006         struct ast_config *result;
3007
3008         cfg = ast_config_new();
3009         if (!cfg)
3010                 return NULL;
3011
3012         result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
3013         if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
3014                 ast_config_destroy(cfg);
3015
3016         return result;
3017 }
3018
3019 #define realtime_arguments_to_fields(ap, result) realtime_arguments_to_fields2(ap, 0, result)
3020
3021 /*!
3022  * \internal
3023  * \brief
3024  *
3025  * \param ap list of variable arguments
3026  * \param skip Skip argument pairs for this number of variables
3027  * \param result Address of a variables pointer to store the results
3028  *               May be NULL if no arguments are parsed
3029  *               Will be NULL on failure.
3030  *
3031  * \retval 0 on success or empty ap list
3032  * \retval -1 on failure
3033  */
3034 static int realtime_arguments_to_fields2(va_list ap, int skip, struct ast_variable **result)
3035 {
3036         struct ast_variable *first, *fields = NULL;
3037         const char *newparam;
3038         const char *newval;
3039
3040         /*
3041          * Previously we would do:
3042          *
3043          *     va_start(ap, last);
3044          *     x = realtime_arguments_to_fields(ap);
3045          *     y = realtime_arguments_to_fields(ap);
3046          *     va_end(ap);
3047          *
3048          * While this works on generic amd64 machines (2014), it doesn't on the
3049          * raspberry PI. The va_arg() manpage says:
3050          *
3051          *     If ap is passed to a function that uses va_arg(ap,type) then
3052          *     the value of ap is undefined after the return of that function.
3053          *
3054          * On the raspberry, ap seems to get reset after the call: the contents
3055          * of y would be equal to the contents of x.
3056          *
3057          * So, instead we allow the caller to skip past earlier argument sets
3058          * using the skip parameter:
3059          *
3060          *     va_start(ap, last);
3061          *     if (realtime_arguments_to_fields(ap, &x)) {
3062          *         // FAILURE CONDITIONS
3063          *     }
3064          *     va_end(ap);
3065          *     va_start(ap, last);
3066          *     if (realtime_arguments_to_fields2(ap, 1, &y)) {
3067          *         // FAILURE CONDITIONS
3068          *     }
3069          *     va_end(ap);
3070          */
3071         while (skip--) {
3072                 /* There must be at least one argument. */
3073                 newparam = va_arg(ap, const char *);
3074                 newval = va_arg(ap, const char *);
3075                 while ((newparam = va_arg(ap, const char *))) {
3076                         newval = va_arg(ap, const char *);
3077                 }
3078         }
3079
3080         /* Load up the first vars. */
3081         newparam = va_arg(ap, const char *);
3082         if (!newparam) {
3083                 *result = NULL;
3084                 return 0;
3085         }
3086         newval = va_arg(ap, const char *);
3087
3088         if (!(first = ast_variable_new(newparam, newval, ""))) {
3089                 *result = NULL;
3090                 return -1;
3091         }
3092
3093         while ((newparam = va_arg(ap, const char *))) {
3094                 struct ast_variable *field;
3095
3096                 newval = va_arg(ap, const char *);
3097                 if (!(field = ast_variable_new(newparam, newval, ""))) {
3098                         ast_variables_destroy(fields);
3099                         ast_variables_destroy(first);
3100                         *result = NULL;
3101                         return -1;
3102                 }
3103
3104                 field->next = fields;
3105                 fields = field;
3106         }
3107
3108         first->next = fields;
3109         fields = first;
3110
3111         *result = fields;
3112         return 0;
3113 }
3114
3115 struct ast_variable *ast_load_realtime_all_fields(const char *family, const struct ast_variable *fields)
3116 {
3117         struct ast_config_engine *eng;
3118         char db[256];
3119         char table[256];
3120         struct ast_variable *res=NULL;
3121         int i;
3122
3123         for (i = 1; ; i++) {
3124                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3125                         if (eng->realtime_func && (res = eng->realtime_func(db, table, fields))) {
3126                                 return res;
3127                         }
3128                 } else {
3129                         return NULL;
3130                 }
3131         }
3132
3133         return res;
3134 }
3135
3136 struct ast_variable *ast_load_realtime_all(const char *family, ...)
3137 {
3138         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3139         struct ast_variable *res = NULL;
3140         va_list ap;
3141
3142         va_start(ap, family);
3143         realtime_arguments_to_fields(ap, &fields);
3144         va_end(ap);
3145
3146         if (fields) {
3147                 res = ast_load_realtime_all_fields(family, fields);
3148         }
3149
3150         return res;
3151 }
3152
3153 struct ast_variable *ast_load_realtime_fields(const char *family, const struct ast_variable *fields)
3154 {
3155         struct ast_variable *res;
3156         struct ast_variable *cur;
3157         struct ast_variable **prev;
3158
3159         res = ast_load_realtime_all_fields(family, fields);
3160
3161         /* Filter the list. */
3162         prev = &res;
3163         cur = res;
3164         while (cur) {
3165                 if (ast_strlen_zero(cur->value)) {
3166                         /* Eliminate empty entries */
3167                         struct ast_variable *next;
3168
3169                         next = cur->next;
3170                         *prev = next;
3171                         ast_variable_destroy(cur);
3172                         cur = next;
3173                 } else {
3174                         /* Make blank entries empty and keep them. */
3175                         if (cur->value[0] == ' ' && cur->value[1] == '\0') {
3176                                 char *vptr = (char *) cur->value;
3177
3178                                 vptr[0] = '\0';
3179                         }
3180
3181                         prev = &cur->next;
3182                         cur = cur->next;
3183                 }
3184         }
3185         return res;
3186 }
3187
3188 struct ast_variable *ast_load_realtime(const char *family, ...)
3189 {
3190         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3191         int field_res = 0;
3192         va_list ap;
3193
3194         va_start(ap, family);
3195         if (realtime_arguments_to_fields(ap, &fields)) {
3196                 field_res = -1;
3197         }
3198         va_end(ap);
3199
3200         if (field_res) {
3201                 return NULL;
3202         }
3203
3204         if (!fields) {
3205                 return NULL;
3206         }
3207
3208         return ast_load_realtime_fields(family, fields);
3209 }
3210
3211 /*! \brief Check if realtime engine is configured for family */
3212 int ast_check_realtime(const char *family)
3213 {
3214         struct ast_config_engine *eng;
3215         if (!ast_realtime_enabled()) {
3216                 return 0;       /* There are no engines at all so fail early */
3217         }
3218
3219         eng = find_engine(family, 1, NULL, 0, NULL, 0);
3220         if (eng)
3221                 return 1;
3222         return 0;
3223 }
3224
3225 /*! \brief Check if there's any realtime engines loaded */
3226 int ast_realtime_enabled(void)
3227 {
3228         return config_maps ? 1 : 0;
3229 }
3230
3231 int ast_realtime_require_field(const char *family, ...)
3232 {
3233         struct ast_config_engine *eng;
3234         char db[256];
3235         char table[256];
3236         va_list ap;
3237         int res = -1, i;
3238
3239         va_start(ap, family);
3240         for (i = 1; ; i++) {
3241                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3242                         /* If the require succeeds, it returns 0. */
3243                         if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
3244                                 break;
3245                         }
3246                 } else {
3247                         break;
3248                 }
3249         }
3250         va_end(ap);
3251
3252         return res;
3253 }
3254
3255 int ast_unload_realtime(const char *family)
3256 {
3257         struct ast_config_engine *eng;
3258         char db[256];
3259         char table[256];
3260         int res = -1, i;
3261
3262         for (i = 1; ; i++) {
3263                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3264                         if (eng->unload_func) {
3265                                 /* Do this for ALL engines */
3266                                 res = eng->unload_func(db, table);
3267                         }
3268                 } else {
3269                         break;
3270                 }
3271         }
3272         return res;
3273 }
3274
3275 struct ast_config *ast_load_realtime_multientry_fields(const char *family, const struct ast_variable *fields)
3276 {
3277         struct ast_config_engine *eng;
3278         char db[256];
3279         char table[256];
3280         struct ast_config *res = NULL;
3281         int i;
3282
3283         for (i = 1; ; i++) {
3284                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3285                         if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, fields))) {
3286                                 /* If we were returned an empty cfg, destroy it and return NULL */
3287                                 if (!res->root) {
3288                                         ast_config_destroy(res);
3289                                         res = NULL;
3290                                 }
3291                                 break;
3292                         }
3293                 } else {
3294                         break;
3295                 }
3296         }
3297
3298         return res;
3299 }
3300
3301 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
3302 {
3303         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3304         va_list ap;
3305
3306         va_start(ap, family);
3307         realtime_arguments_to_fields(ap, &fields);
3308         va_end(ap);
3309
3310         if (!fields) {
3311                 return NULL;
3312         }
3313
3314         return ast_load_realtime_multientry_fields(family, fields);
3315 }
3316
3317 int ast_update_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
3318 {
3319         struct ast_config_engine *eng;
3320         int res = -1, i;
3321         char db[256];
3322         char table[256];
3323
3324         for (i = 1; ; i++) {
3325                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3326                         /* If the update succeeds, it returns >= 0. */
3327                         if (eng->update_func && ((res = eng->update_func(db, table, keyfield, lookup, fields)) >= 0)) {
3328                                 break;
3329                         }
3330                 } else {
3331                         break;
3332                 }
3333         }
3334
3335         return res;
3336 }
3337
3338 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
3339 {
3340         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3341         va_list ap;
3342
3343         va_start(ap, lookup);
3344         realtime_arguments_to_fields(ap, &fields);
3345         va_end(ap);
3346
3347         if (!fields) {
3348                 return -1;
3349         }
3350
3351         return ast_update_realtime_fields(family, keyfield, lookup, fields);
3352 }
3353
3354 int ast_update2_realtime_fields(const char *family, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
3355 {
3356         struct ast_config_engine *eng;
3357         int res = -1, i;
3358         char db[256];
3359         char table[256];
3360
3361         for (i = 1; ; i++) {
3362                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3363                         if (eng->update2_func && !(res = eng->update2_func(db, table, lookup_fields, update_fields))) {
3364                                 break;
3365                         }
3366                 } else {
3367                         break;
3368                 }
3369         }
3370
3371         return res;
3372 }
3373
3374 int ast_update2_realtime(const char *family, ...)
3375 {
3376         RAII_VAR(struct ast_variable *, lookup_fields, NULL, ast_variables_destroy);
3377         RAII_VAR(struct ast_variable *, update_fields, NULL, ast_variables_destroy);
3378         va_list ap;
3379
3380         va_start(ap, family);
3381         /* XXX: If we wanted to pass no lookup fields (select all), we'd be
3382          * out of luck. realtime_arguments_to_fields expects at least one key
3383          * value pair. */
3384         realtime_arguments_to_fields(ap, &lookup_fields);
3385         va_end(ap);
3386
3387         va_start(ap, family);
3388         realtime_arguments_to_fields2(ap, 1, &update_fields);
3389         va_end(ap);
3390
3391         if (!lookup_fields || !update_fields) {
3392                 return -1;
3393         }
3394
3395         return ast_update2_realtime_fields(family, lookup_fields, update_fields);
3396 }
3397
3398 int ast_store_realtime_fields(const char *family, const struct ast_variable *fields)
3399 {
3400         struct ast_config_engine *eng;
3401         int res = -1, i;
3402         char db[256];
3403         char table[256];
3404
3405         for (i = 1; ; i++) {
3406                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3407                         /* If the store succeeds, it returns >= 0*/
3408                         if (eng->store_func && ((res = eng->store_func(db, table, fields)) >= 0)) {
3409                                 break;
3410                         }
3411                 } else {
3412                         break;
3413                 }
3414         }
3415
3416         return res;
3417 }
3418
3419 int ast_store_realtime(const char *family, ...)
3420 {
3421         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3422         va_list ap;
3423
3424         va_start(ap, family);
3425         realtime_arguments_to_fields(ap, &fields);
3426         va_end(ap);
3427
3428         if (!fields) {
3429                 return -1;
3430         }
3431
3432         return ast_store_realtime_fields(family, fields);
3433 }
3434
3435 int ast_destroy_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
3436 {
3437         struct ast_config_engine *eng;
3438         int res = -1, i;
3439         char db[256];
3440         char table[256];
3441
3442         for (i = 1; ; i++) {
3443                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3444                         if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, fields))) {
3445                                 break;
3446                         }
3447                 } else {
3448                         break;
3449                 }
3450         }
3451
3452         return res;
3453 }
3454
3455 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
3456 {
3457         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3458         int res = 0;
3459         va_list ap;
3460
3461         va_start(ap, lookup);
3462         if (realtime_arguments_to_fields(ap, &fields)) {
3463                 res = -1;
3464         }
3465         va_end(ap);
3466
3467         if (res) {
3468                 return -1;
3469         }
3470
3471         return ast_destroy_realtime_fields(family, keyfield, lookup, fields);
3472 }
3473
3474 char *ast_realtime_decode_chunk(char *chunk)
3475 {
3476         char *orig = chunk;
3477         for (; *chunk; chunk++) {
3478                 if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
3479                         sscanf(chunk + 1, "%02hhX", (unsigned char *)chunk);
3480                         memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
3481                 }
3482         }
3483         return orig;
3484 }
3485
3486 char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
3487 {
3488         if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
3489                 ast_str_set(dest, maxlen, "%s", chunk);
3490         } else {
3491                 ast_str_reset(*dest);
3492                 for (; *chunk; chunk++) {
3493                         if (strchr(";^", *chunk)) {
3494                                 ast_str_append(dest, maxlen, "^%02hhX", *chunk);
3495                         } else {
3496                                 ast_str_append(dest, maxlen, "%c", *chunk);
3497                         }
3498                 }
3499         }
3500         return ast_str_buffer(*dest);
3501 }
3502
3503 /*! \brief Helper function to parse arguments
3504  * See documentation in config.h
3505  */
3506 int ast_parse_arg(con