git migration: Refactor the ASTERISK_FILE_VERSION macro
[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 static struct ast_variable *variable_clone(const struct ast_variable *old)
739 {
740         struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
741
742         if (new) {
743                 new->lineno = old->lineno;
744                 new->object = old->object;
745                 new->blanklines = old->blanklines;
746                 /* TODO: clone comments? */
747         }
748
749         return new;
750 }
751
752 static void move_variables(struct ast_category *old, struct ast_category *new)
753 {
754         struct ast_variable *var = old->root;
755
756         old->root = NULL;
757         /* we can just move the entire list in a single op */
758         ast_variable_append(new, var);
759 }
760
761 /*! \brief Returns true if ALL of the regex expressions and category name match.
762  * Both can be NULL (I.E. no predicate) which results in a true return;
763  */
764 static int does_category_match(struct ast_category *cat, const char *category_name, const char *match)
765 {
766         char *dupmatch;
767         char *nvp = NULL;
768         int match_found = 0, match_expressions = 0;
769         int template_ok = 0;
770
771         /* Only match on category name if it's not a NULL or empty string */
772         if (!ast_strlen_zero(category_name) && strcasecmp(cat->name, category_name)) {
773                 return 0;
774         }
775
776         /* If match is NULL or empty, automatically match if not a template */
777         if (ast_strlen_zero(match)) {
778                 return !cat->ignored;
779         }
780
781         dupmatch = ast_strdupa(match);
782
783         while ((nvp = ast_strsep(&dupmatch, ',', AST_STRSEP_STRIP))) {
784                 struct ast_variable *v;
785                 char *match_name;
786                 char *match_value = NULL;
787                 char *regerr;
788                 int rc;
789                 regex_t r_name, r_value;
790
791                 match_expressions++;
792
793                 match_name = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
794                 match_value = ast_strsep(&nvp, '=', AST_STRSEP_STRIP);
795
796                 /* an empty match value is OK.  A NULL match value (no =) is NOT. */
797                 if (match_value == NULL) {
798                         break;
799                 }
800
801                 if (!strcmp("TEMPLATES", match_name)) {
802                         if (!strcasecmp("include", match_value)) {
803                                 if (cat->ignored) {
804                                         template_ok = 1;
805                                 }
806                                 match_found++;
807                         } else if (!strcasecmp("restrict", match_value)) {
808                                 if (cat->ignored) {
809                                         match_found++;
810                                         template_ok = 1;
811                                 } else {
812                                         break;
813                                 }
814                         }
815                         continue;
816                 }
817
818                 if ((rc = regcomp(&r_name, match_name, REG_EXTENDED | REG_NOSUB))) {
819                         regerr = ast_alloca(128);
820                         regerror(rc, &r_name, regerr, 128);
821                         ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
822                                 match_name, regerr);
823                         regfree(&r_name);
824                         return 0;
825                 }
826                 if ((rc = regcomp(&r_value, match_value, REG_EXTENDED | REG_NOSUB))) {
827                         regerr = ast_alloca(128);
828                         regerror(rc, &r_value, regerr, 128);
829                         ast_log(LOG_ERROR, "Regular expression '%s' failed to compile: %s\n",
830                                 match_value, regerr);
831                         regfree(&r_name);
832                         regfree(&r_value);
833                         return 0;
834                 }
835
836                 for (v = cat->root; v; v = v->next) {
837                         if (!regexec(&r_name, v->name, 0, NULL, 0)
838                                 && !regexec(&r_value, v->value, 0, NULL, 0)) {
839                                 match_found++;
840                                 break;
841                         }
842                 }
843                 regfree(&r_name);
844                 regfree(&r_value);
845         }
846         if (match_found == match_expressions && (!cat->ignored || template_ok)) {
847                 return 1;
848         }
849         return 0;
850 }
851
852
853 static struct ast_category *new_category(const char *name, const char *in_file, int lineno, int template)
854 {
855         struct ast_category *category;
856
857         category = ast_calloc(1, sizeof(*category));
858         if (!category) {
859                 return NULL;
860         }
861         category->file = ast_strdup(in_file);
862         if (!category->file) {
863                 ast_category_destroy(category);
864                 return NULL;
865         }
866         ast_copy_string(category->name, name, sizeof(category->name));
867         category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
868         category->ignored = template;
869         return category;
870 }
871
872 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
873 {
874         return new_category(name, in_file, lineno, 0);
875 }
876
877 struct ast_category *ast_category_new_template(const char *name, const char *in_file, int lineno)
878 {
879         return new_category(name, in_file, lineno, 1);
880 }
881
882 struct ast_category *ast_category_get(const struct ast_config *config,
883         const char *category_name, const char *filter)
884 {
885         struct ast_category *cat;
886
887         for (cat = config->root; cat; cat = cat->next) {
888                 if (cat->name == category_name && does_category_match(cat, category_name, filter)) {
889                         return cat;
890                 }
891         }
892
893         for (cat = config->root; cat; cat = cat->next) {
894                 if (does_category_match(cat, category_name, filter)) {
895                         return cat;
896                 }
897         }
898
899         return NULL;
900 }
901
902 const char *ast_category_get_name(const struct ast_category *category)
903 {
904         return category->name;
905 }
906
907 int ast_category_is_template(const struct ast_category *category)
908 {
909         return category->ignored;
910 }
911
912 struct ast_str *ast_category_get_templates(const struct ast_category *category)
913 {
914         struct ast_category_template_instance *template;
915         struct ast_str *str;
916         int first = 1;
917
918         if (AST_LIST_EMPTY(&category->template_instances)) {
919                 return NULL;
920         }
921
922         str = ast_str_create(128);
923         if (!str) {
924                 return NULL;
925         }
926
927         AST_LIST_TRAVERSE(&category->template_instances, template, next) {
928                 ast_str_append(&str, 0, "%s%s", first ? "" : ",", template->name);
929                 first = 0;
930         }
931
932         return str;
933 }
934
935 int ast_category_exist(const struct ast_config *config, const char *category_name,
936         const char *filter)
937 {
938         return !!ast_category_get(config, category_name, filter);
939 }
940
941 void ast_category_append(struct ast_config *config, struct ast_category *category)
942 {
943         if (config->last) {
944                 config->last->next = category;
945                 category->prev = config->last;
946         } else {
947                 config->root = category;
948                 category->prev = NULL;
949         }
950         category->next = NULL;
951         category->include_level = config->include_level;
952
953         config->last = category;
954         config->current = category;
955 }
956
957 int ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
958 {
959         struct ast_category *cur_category;
960
961         if (!config || !config->root || !cat || !match) {
962                 return -1;
963         }
964
965         if (!strcasecmp(config->root->name, match)) {
966                 cat->next = config->root;
967                 cat->prev = NULL;
968                 config->root->prev = cat;
969                 config->root = cat;
970                 return 0;
971         }
972
973         for (cur_category = config->root->next; cur_category; cur_category = cur_category->next) {
974                 if (!strcasecmp(cur_category->name, match)) {
975                         cat->prev = cur_category->prev;
976                         cat->prev->next = cat;
977
978                         cat->next = cur_category;
979                         cur_category->prev = cat;
980
981                         return 0;
982                 }
983         }
984
985         return -1;
986 }
987
988 static void ast_destroy_template_list(struct ast_category *cat)
989 {
990         struct ast_category_template_instance *x;
991
992         while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
993                 ast_free(x);
994 }
995
996 void ast_category_destroy(struct ast_category *cat)
997 {
998         ast_variables_destroy(cat->root);
999         cat->root = NULL;
1000         cat->last = NULL;
1001         ast_comment_destroy(&cat->precomments);
1002         ast_comment_destroy(&cat->sameline);
1003         ast_comment_destroy(&cat->trailing);
1004         ast_destroy_template_list(cat);
1005         ast_free(cat->file);
1006         ast_free(cat);
1007 }
1008
1009 static void ast_includes_destroy(struct ast_config_include *incls)
1010 {
1011         struct ast_config_include *incl,*inclnext;
1012
1013         for (incl=incls; incl; incl = inclnext) {
1014                 inclnext = incl->next;
1015                 ast_free(incl->include_location_file);
1016                 ast_free(incl->exec_file);
1017                 ast_free(incl->included_file);
1018                 ast_free(incl);
1019         }
1020 }
1021
1022 static struct ast_category *next_available_category(struct ast_category *cat,
1023         const char *name, const char *filter)
1024 {
1025         for (; cat && !does_category_match(cat, name, filter); cat = cat->next);
1026
1027         return cat;
1028 }
1029
1030 /*! return the first var of a category */
1031 struct ast_variable *ast_category_first(struct ast_category *cat)
1032 {
1033         return (cat) ? cat->root : NULL;
1034 }
1035
1036 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
1037 {
1038         struct ast_category *category = ast_category_get(config, cat, NULL);
1039
1040         if (category)
1041                 return category->root;
1042         return NULL;
1043 }
1044
1045 void ast_config_sort_categories(struct ast_config *config, int descending,
1046                                                                 int (*comparator)(struct ast_category *p, struct ast_category *q))
1047 {
1048         /*
1049          * The contents of this function are adapted from
1050          * an example of linked list merge sorting
1051          * copyright 2001 Simon Tatham.
1052          *
1053          * Permission is hereby granted, free of charge, to any person
1054          * obtaining a copy of this software and associated documentation
1055          * files (the "Software"), to deal in the Software without
1056          * restriction, including without limitation the rights to use,
1057          * copy, modify, merge, publish, distribute, sublicense, and/or
1058          * sell copies of the Software, and to permit persons to whom the
1059          * Software is furnished to do so, subject to the following
1060          * conditions:
1061          *
1062          * The above copyright notice and this permission notice shall be
1063          * included in all copies or substantial portions of the Software.
1064          *
1065          * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1066          * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
1067          * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1068          * NONINFRINGEMENT.  IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
1069          * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
1070          * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
1071          * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1072          * SOFTWARE.
1073          */
1074
1075         int insize = 1;
1076         struct ast_category *p, *q, *e, *tail;
1077         int nmerges, psize, qsize, i;
1078
1079         /* If the descending flag was sent, we'll apply inversion to the comparison function's return. */
1080         if (descending) {
1081                 descending = -1;
1082         } else {
1083                 descending = 1;
1084         }
1085
1086         if (!config->root) {
1087                 return;
1088         }
1089
1090         while (1) {
1091                 p = config->root;
1092                 config->root = NULL;
1093                 tail = NULL;
1094
1095                 nmerges = 0; /* count number of merges we do in this pass */
1096
1097                 while (p) {
1098                         nmerges++; /* there exists a merge to be done */
1099
1100                         /* step `insize' places along from p */
1101                         q = p;
1102                         psize = 0;
1103                         for (i = 0; i < insize; i++) {
1104                                 psize++;
1105                                 q = q->next;
1106                                 if (!q) {
1107                                         break;
1108                                 }
1109                         }
1110
1111                         /* if q hasn't fallen off end, we have two lists to merge */
1112                         qsize = insize;
1113
1114                         /* now we have two lists; merge them */
1115                         while (psize > 0 || (qsize > 0 && q)) {
1116                                 /* decide whether next element of merge comes from p or q */
1117                                 if (psize == 0) {
1118                                         /* p is empty; e must come from q. */
1119                                         e = q;
1120                                         q = q->next;
1121                                         qsize--;
1122                                 } else if (qsize == 0 || !q) {
1123                                         /* q is empty; e must come from p. */
1124                                         e = p; p = p->next; psize--;
1125                                 } else if ((comparator(p,q) * descending) <= 0) {
1126                                         /* First element of p is lower (or same) e must come from p. */
1127                                         e = p;
1128                                         p = p->next;
1129                                         psize--;
1130                                 } else {
1131                                         /* First element of q is lower; e must come from q. */
1132                                         e = q;
1133                                         q = q->next;
1134                                         qsize--;
1135                                 }
1136
1137                                 /* add the next element to the merged list */
1138                                 if (tail) {
1139                                         tail->next = e;
1140                                 } else {
1141                                         config->root = e;
1142                                 }
1143                                 tail = e;
1144                         }
1145
1146                         /* now p has stepped `insize' places along, and q has too */
1147                         p = q;
1148                 }
1149
1150                 tail->next = NULL;
1151
1152                 /* If we have done only one merge, we're finished. */
1153                 if (nmerges <= 1) { /* allow for nmerges==0, the empty list case */
1154                         return;
1155                 }
1156
1157                 /* Otherwise repeat, merging lists twice the size */
1158                 insize *= 2;
1159         }
1160
1161 }
1162
1163 char *ast_category_browse(struct ast_config *config, const char *prev)
1164 {
1165         struct ast_category *cat;
1166
1167         if (!prev) {
1168                 /* First time browse. */
1169                 cat = config->root;
1170         } else if (config->last_browse && (config->last_browse->name == prev)) {
1171                 /* Simple last browse found. */
1172                 cat = config->last_browse->next;
1173         } else {
1174                 /*
1175                  * Config changed since last browse.
1176                  *
1177                  * First try cheap last browse search. (Rebrowsing a different
1178                  * previous category?)
1179                  */
1180                 for (cat = config->root; cat; cat = cat->next) {
1181                         if (cat->name == prev) {
1182                                 /* Found it. */
1183                                 cat = cat->next;
1184                                 break;
1185                         }
1186                 }
1187                 if (!cat) {
1188                         /*
1189                          * Have to do it the hard way. (Last category was deleted and
1190                          * re-added?)
1191                          */
1192                         for (cat = config->root; cat; cat = cat->next) {
1193                                 if (!strcasecmp(cat->name, prev)) {
1194                                         /* Found it. */
1195                                         cat = cat->next;
1196                                         break;
1197                                 }
1198                         }
1199                 }
1200         }
1201
1202         if (cat)
1203                 cat = next_available_category(cat, NULL, NULL);
1204
1205         config->last_browse = cat;
1206         return (cat) ? cat->name : NULL;
1207 }
1208
1209 struct ast_category *ast_category_browse_filtered(struct ast_config *config,
1210         const char *category_name, struct ast_category *prev, const char *filter)
1211 {
1212         struct ast_category *cat;
1213
1214         if (!prev) {
1215                 prev = config->root;
1216         } else {
1217                 prev = prev->next;
1218         }
1219
1220         cat = next_available_category(prev, category_name, filter);
1221
1222         return cat;
1223 }
1224
1225 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
1226 {
1227         struct ast_variable *v;
1228
1229         v = cat->root;
1230         cat->root = NULL;
1231         cat->last = NULL;
1232
1233         return v;
1234 }
1235
1236 void ast_category_rename(struct ast_category *cat, const char *name)
1237 {
1238         ast_copy_string(cat->name, name, sizeof(cat->name));
1239 }
1240
1241 int ast_category_inherit(struct ast_category *new, const struct ast_category *base)
1242 {
1243         struct ast_variable *var;
1244         struct ast_category_template_instance *x;
1245
1246         x = ast_calloc(1, sizeof(*x));
1247         if (!x) {
1248                 return -1;
1249         }
1250         strcpy(x->name, base->name);
1251         x->inst = base;
1252         AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
1253         for (var = base->root; var; var = var->next) {
1254                 struct ast_variable *cloned = variable_clone(var);
1255                 if (!cloned) {
1256                         return -1;
1257                 }
1258                 cloned->inherited = 1;
1259                 ast_variable_append(new, cloned);
1260         }
1261         return 0;
1262 }
1263
1264 struct ast_config *ast_config_new(void)
1265 {
1266         struct ast_config *config;
1267
1268         if ((config = ast_calloc(1, sizeof(*config))))
1269                 config->max_include_level = MAX_INCLUDE_LEVEL;
1270         return config;
1271 }
1272
1273 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
1274 {
1275         struct ast_variable *cur, *prev=NULL, *curn;
1276         int res = -1;
1277         int num_item = 0;
1278         int req_item;
1279
1280         req_item = -1;
1281         if (!ast_strlen_zero(line)) {
1282                 /* Requesting to delete by item number. */
1283                 if (sscanf(line, "%30d", &req_item) != 1
1284                         || req_item < 0) {
1285                         /* Invalid item number to delete. */
1286                         return -1;
1287                 }
1288         }
1289
1290         prev = NULL;
1291         cur = category->root;
1292         while (cur) {
1293                 curn = cur->next;
1294                 /* Delete by item number or by variable name with optional value. */
1295                 if ((0 <= req_item && num_item == req_item)
1296                         || (req_item < 0 && !strcasecmp(cur->name, variable)
1297                                 && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
1298                         if (prev) {
1299                                 prev->next = cur->next;
1300                                 if (cur == category->last)
1301                                         category->last = prev;
1302                         } else {
1303                                 category->root = cur->next;
1304                                 if (cur == category->last)
1305                                         category->last = NULL;
1306                         }
1307                         ast_variable_destroy(cur);
1308                         res = 0;
1309                 } else
1310                         prev = cur;
1311
1312                 cur = curn;
1313                 ++num_item;
1314         }
1315         return res;
1316 }
1317
1318 int ast_variable_update(struct ast_category *category, const char *variable,
1319                                                 const char *value, const char *match, unsigned int object)
1320 {
1321         struct ast_variable *cur, *prev=NULL, *newer=NULL;
1322
1323         for (cur = category->root; cur; prev = cur, cur = cur->next) {
1324                 if (strcasecmp(cur->name, variable) ||
1325                         (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
1326                         continue;
1327
1328                 if (!(newer = ast_variable_new(variable, value, cur->file)))
1329                         return -1;
1330
1331                 ast_variable_move(newer, cur);
1332                 newer->object = newer->object || object;
1333
1334                 /* Replace the old node in the list with the new node. */
1335                 newer->next = cur->next;
1336                 if (prev)
1337                         prev->next = newer;
1338                 else
1339                         category->root = newer;
1340                 if (category->last == cur)
1341                         category->last = newer;
1342
1343                 ast_variable_destroy(cur);
1344
1345                 return 0;
1346         }
1347
1348         /* Could not find variable to update */
1349         return -1;
1350 }
1351
1352 struct ast_category *ast_category_delete(struct ast_config *config,
1353         struct ast_category *category)
1354 {
1355         struct ast_category *prev;
1356
1357         if (!config || !category) {
1358                 return NULL;
1359         }
1360
1361         if (category->prev) {
1362                 category->prev->next = category->next;
1363         } else {
1364                 config->root = category->next;
1365         }
1366
1367         if (category->next) {
1368                 category->next->prev = category->prev;
1369         } else {
1370                 config->last = category->prev;
1371         }
1372
1373         prev = category->prev;
1374
1375         if (config->last_browse == category) {
1376                 config->last_browse = prev;
1377         }
1378
1379         ast_category_destroy(category);
1380
1381         return prev;
1382 }
1383
1384 int ast_category_empty(struct ast_category *category)
1385 {
1386         if (!category) {
1387                 return -1;
1388         }
1389
1390         ast_variables_destroy(category->root);
1391         category->root = NULL;
1392         category->last = NULL;
1393
1394         return 0;
1395 }
1396
1397 void ast_config_destroy(struct ast_config *cfg)
1398 {
1399         struct ast_category *cat, *catn;
1400
1401         if (!cfg)
1402                 return;
1403
1404         ast_includes_destroy(cfg->includes);
1405
1406         cat = cfg->root;
1407         while (cat) {
1408                 catn = cat;
1409                 cat = cat->next;
1410                 ast_category_destroy(catn);
1411         }
1412         ast_free(cfg);
1413 }
1414
1415 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
1416 {
1417         return cfg->current;
1418 }
1419
1420 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
1421 {
1422         /* cast below is just to silence compiler warning about dropping "const" */
1423         cfg->current = (struct ast_category *) cat;
1424 }
1425
1426 /*!
1427  * \internal
1428  * \brief Create a new cfmtime list node.
1429  *
1430  * \param filename Config filename caching.
1431  * \param who_asked Who wanted to know.
1432  *
1433  * \retval cfmtime New node on success.
1434  * \retval NULL on error.
1435  */
1436 static struct cache_file_mtime *cfmtime_new(const char *filename, const char *who_asked)
1437 {
1438         struct cache_file_mtime *cfmtime;
1439         char *dst;
1440
1441         cfmtime = ast_calloc(1,
1442                 sizeof(*cfmtime) + strlen(filename) + 1 + strlen(who_asked) + 1);
1443         if (!cfmtime) {
1444                 return NULL;
1445         }
1446         dst = cfmtime->filename;        /* writable space starts here */
1447         strcpy(dst, filename); /* Safe */
1448         dst += strlen(dst) + 1;
1449         cfmtime->who_asked = strcpy(dst, who_asked); /* Safe */
1450
1451         return cfmtime;
1452 }
1453
1454 enum config_cache_attribute_enum {
1455         ATTRIBUTE_INCLUDE = 0,
1456         ATTRIBUTE_EXEC = 1,
1457 };
1458
1459 /*!
1460  * \internal
1461  * \brief Save the stat() data to the cached file modtime struct.
1462  *
1463  * \param cfmtime Cached file modtime.
1464  * \param statbuf Buffer filled in by stat().
1465  *
1466  * \return Nothing
1467  */
1468 static void cfmstat_save(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1469 {
1470         cfmtime->stat_size = statbuf->st_size;
1471 #if defined(HAVE_STRUCT_STAT_ST_MTIM)
1472         cfmtime->stat_mtime_nsec = statbuf->st_mtim.tv_nsec;
1473 #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
1474         cfmtime->stat_mtime_nsec = statbuf->st_mtimensec;
1475 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
1476         cfmtime->stat_mtime_nsec = statbuf->st_mtimespec.tv_nsec;
1477 #else
1478         cfmtime->stat_mtime_nsec = 0;
1479 #endif
1480         cfmtime->stat_mtime = statbuf->st_mtime;
1481 }
1482
1483 /*!
1484  * \internal
1485  * \brief Compare the stat() data with the cached file modtime struct.
1486  *
1487  * \param cfmtime Cached file modtime.
1488  * \param statbuf Buffer filled in by stat().
1489  *
1490  * \retval non-zero if different.
1491  */
1492 static int cfmstat_cmp(struct cache_file_mtime *cfmtime, struct stat *statbuf)
1493 {
1494         struct cache_file_mtime cfm_buf;
1495
1496         cfmstat_save(&cfm_buf, statbuf);
1497
1498         return cfmtime->stat_size != cfm_buf.stat_size
1499                 || cfmtime->stat_mtime != cfm_buf.stat_mtime
1500                 || cfmtime->stat_mtime_nsec != cfm_buf.stat_mtime_nsec;
1501 }
1502
1503 /*!
1504  * \internal
1505  * \brief Clear the cached file modtime include list.
1506  *
1507  * \param cfmtime Cached file modtime.
1508  *
1509  * \note cfmtime_head is assumed already locked.
1510  *
1511  * \return Nothing
1512  */
1513 static void config_cache_flush_includes(struct cache_file_mtime *cfmtime)
1514 {
1515         struct cache_file_include *cfinclude;
1516
1517         while ((cfinclude = AST_LIST_REMOVE_HEAD(&cfmtime->includes, list))) {
1518                 ast_free(cfinclude);
1519         }
1520 }
1521
1522 /*!
1523  * \internal
1524  * \brief Destroy the given cached file modtime entry.
1525  *
1526  * \param cfmtime Cached file modtime.
1527  *
1528  * \note cfmtime_head is assumed already locked.
1529  *
1530  * \return Nothing
1531  */
1532 static void config_cache_destroy_entry(struct cache_file_mtime *cfmtime)
1533 {
1534         config_cache_flush_includes(cfmtime);
1535         ast_free(cfmtime);
1536 }
1537
1538 /*!
1539  * \internal
1540  * \brief Remove and destroy the config cache entry for the filename and who_asked.
1541  *
1542  * \param filename Config filename.
1543  * \param who_asked Which module asked.
1544  *
1545  * \return Nothing
1546  */
1547 static void config_cache_remove(const char *filename, const char *who_asked)
1548 {
1549         struct cache_file_mtime *cfmtime;
1550
1551         AST_LIST_LOCK(&cfmtime_head);
1552         AST_LIST_TRAVERSE_SAFE_BEGIN(&cfmtime_head, cfmtime, list) {
1553                 if (!strcmp(cfmtime->filename, filename)
1554                         && !strcmp(cfmtime->who_asked, who_asked)) {
1555                         AST_LIST_REMOVE_CURRENT(list);
1556                         config_cache_destroy_entry(cfmtime);
1557                         break;
1558                 }
1559         }
1560         AST_LIST_TRAVERSE_SAFE_END;
1561         AST_LIST_UNLOCK(&cfmtime_head);
1562 }
1563
1564 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
1565 {
1566         struct cache_file_mtime *cfmtime;
1567         struct cache_file_include *cfinclude;
1568
1569         /* Find our cached entry for this configuration file */
1570         AST_LIST_LOCK(&cfmtime_head);
1571         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1572                 if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
1573                         break;
1574         }
1575         if (!cfmtime) {
1576                 cfmtime = cfmtime_new(configfile, who_asked);
1577                 if (!cfmtime) {
1578                         AST_LIST_UNLOCK(&cfmtime_head);
1579                         return;
1580                 }
1581                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1582                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1583         }
1584
1585         switch (attrtype) {
1586         case ATTRIBUTE_INCLUDE:
1587                 AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1588                         if (!strcmp(cfinclude->include, filename)) {
1589                                 AST_LIST_UNLOCK(&cfmtime_head);
1590                                 return;
1591                         }
1592                 }
1593                 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
1594                 if (!cfinclude) {
1595                         AST_LIST_UNLOCK(&cfmtime_head);
1596                         return;
1597                 }
1598                 strcpy(cfinclude->include, filename); /* Safe */
1599                 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
1600                 break;
1601         case ATTRIBUTE_EXEC:
1602                 cfmtime->has_exec = 1;
1603                 break;
1604         }
1605         AST_LIST_UNLOCK(&cfmtime_head);
1606 }
1607
1608 /*! \brief parse one line in the configuration.
1609  * \verbatim
1610  * We can have a category header        [foo](...)
1611  * a directive                          #include / #exec
1612  * or a regular line                    name = value
1613  * \endverbatim
1614  */
1615 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
1616         char *buf, int lineno, const char *configfile, struct ast_flags flags,
1617         struct ast_str *comment_buffer,
1618         struct ast_str *lline_buffer,
1619         const char *suggested_include_file,
1620         struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
1621 {
1622         char *c;
1623         char *cur = buf;
1624         struct ast_variable *v;
1625         char cmd[512], exec_file[512];
1626
1627         /* Actually parse the entry */
1628         if (cur[0] == '[') { /* A category header */
1629                 /* format is one of the following:
1630                  * [foo]        define a new category named 'foo'
1631                  * [foo](!)     define a new template category named 'foo'
1632                  * [foo](+)     append to category 'foo', error if foo does not exist.
1633                  * [foo](a)     define a new category and inherit from category or template a.
1634                  *              You can put a comma-separated list of categories and templates
1635                  *              and '!' and '+' between parentheses, with obvious meaning.
1636                  */
1637                 struct ast_category *newcat = NULL;
1638                 char *catname;
1639
1640                 c = strchr(cur, ']');
1641                 if (!c) {
1642                         ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
1643                         return -1;
1644                 }
1645                 *c++ = '\0';
1646                 cur++;
1647                 if (*c++ != '(')
1648                         c = NULL;
1649                 catname = cur;
1650                 if (!(*cat = newcat = ast_category_new(catname,
1651                                 S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
1652                                 lineno))) {
1653                         return -1;
1654                 }
1655                 (*cat)->lineno = lineno;
1656                 *last_var = 0;
1657                 *last_cat = newcat;
1658
1659                 /* add comments */
1660                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1661                         newcat->precomments = ALLOC_COMMENT(comment_buffer);
1662                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1663                         newcat->sameline = ALLOC_COMMENT(lline_buffer);
1664                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1665                         CB_RESET(comment_buffer, lline_buffer);
1666
1667                 /* If there are options or categories to inherit from, process them now */
1668                 if (c) {
1669                         if (!(cur = strchr(c, ')'))) {
1670                                 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
1671                                 return -1;
1672                         }
1673                         *cur = '\0';
1674                         while ((cur = strsep(&c, ","))) {
1675                                 if (!strcasecmp(cur, "!")) {
1676                                         (*cat)->ignored = 1;
1677                                 } else if (!strcasecmp(cur, "+")) {
1678                                         *cat = ast_category_get(cfg, catname, NULL);
1679                                         if (!(*cat)) {
1680                                                 if (newcat)
1681                                                         ast_category_destroy(newcat);
1682                                                 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
1683                                                 return -1;
1684                                         }
1685                                         if (newcat) {
1686                                                 move_variables(newcat, *cat);
1687                                                 ast_category_destroy(newcat);
1688                                                 newcat = NULL;
1689                                         }
1690                                 } else {
1691                                         struct ast_category *base;
1692
1693                                         base = ast_category_get(cfg, cur, "TEMPLATES=include");
1694                                         if (!base) {
1695                                                 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
1696                                                 return -1;
1697                                         }
1698                                         if (ast_category_inherit(*cat, base)) {
1699                                                 ast_log(LOG_ERROR, "Inheritence requested, but allocation failed\n");
1700                                                 return -1;
1701                                         }
1702                                 }
1703                         }
1704                 }
1705                 if (newcat)
1706                         ast_category_append(cfg, *cat);
1707         } else if (cur[0] == '#') { /* A directive - #include or #exec */
1708                 char *cur2;
1709                 char real_inclusion_name[256];
1710                 int do_include = 0;     /* otherwise, it is exec */
1711                 int try_include = 0;
1712
1713                 cur++;
1714                 c = cur;
1715                 while (*c && (*c > 32)) {
1716                         c++;
1717                 }
1718
1719                 if (*c) {
1720                         *c = '\0';
1721                         /* Find real argument */
1722                         c = ast_strip(c + 1);
1723                         if (!(*c)) {
1724                                 c = NULL;
1725                         }
1726                 } else {
1727                         c = NULL;
1728                 }
1729                 if (!strcasecmp(cur, "include")) {
1730                         do_include = 1;
1731                 } else if (!strcasecmp(cur, "tryinclude")) {
1732                         do_include = 1;
1733                         try_include = 1;
1734                 } else if (!strcasecmp(cur, "exec")) {
1735                         if (!ast_opt_exec_includes) {
1736                                 ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
1737                                 return 0;       /* XXX is this correct ? or we should return -1 ? */
1738                         }
1739                 } else {
1740                         ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
1741                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1742                 }
1743
1744                 if (c == NULL) {
1745                         ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
1746                                         do_include ? "include / tryinclude" : "exec",
1747                                         do_include ? "filename" : "/path/to/executable",
1748                                         lineno,
1749                                         configfile);
1750                         return 0;       /* XXX is this correct ? or we should return -1 ? */
1751                 }
1752
1753                 cur = c;
1754                 /* Strip off leading and trailing "'s and <>'s */
1755                 /* Dequote */
1756                 if ((*c == '"') || (*c == '<')) {
1757                         char quote_char = *c;
1758                         if (quote_char == '<') {
1759                                 quote_char = '>';
1760                         }
1761
1762                         if (*(c + strlen(c) - 1) == quote_char) {
1763                                 cur++;
1764                                 *(c + strlen(c) - 1) = '\0';
1765                         }
1766                 }
1767                 cur2 = cur;
1768
1769                 /* #exec </path/to/executable>
1770                    We create a tmp file, then we #include it, then we delete it. */
1771                 if (!do_include) {
1772                         struct timeval now = ast_tvnow();
1773                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1774                                 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
1775                         snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
1776                         snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
1777                         ast_safe_system(cmd);
1778                         cur = exec_file;
1779                 } else {
1780                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1781                                 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
1782                         exec_file[0] = '\0';
1783                 }
1784                 /* A #include */
1785                 /* record this inclusion */
1786                 ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
1787
1788                 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
1789                 if (!ast_strlen_zero(exec_file))
1790                         unlink(exec_file);
1791                 if (!do_include && !try_include) {
1792                         ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
1793                         return -1;
1794                 }
1795                 /* XXX otherwise what ? the default return is 0 anyways */
1796
1797         } else {
1798                 /* Just a line (variable = value) */
1799                 int object = 0;
1800                 int is_escaped;
1801
1802                 if (!(*cat)) {
1803                         ast_log(LOG_WARNING,
1804                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
1805                         return -1;
1806                 }
1807
1808                 is_escaped = cur[0] == '\\';
1809                 if (is_escaped) {
1810                         /* First character is escaped. */
1811                         ++cur;
1812                         if (cur[0] < 33) {
1813                                 ast_log(LOG_ERROR, "Invalid escape in line %d of %s\n", lineno, configfile);
1814                                 return -1;
1815                         }
1816                 }
1817                 c = strchr(cur + is_escaped, '=');
1818
1819                 if (c && c > cur + is_escaped && (*(c - 1) == '+')) {
1820                         struct ast_variable *var, *replace = NULL;
1821                         struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
1822
1823                         if (!str || !*str) {
1824                                 return -1;
1825                         }
1826
1827                         *(c - 1) = '\0';
1828                         c++;
1829                         cur = ast_strip(cur);
1830
1831                         /* Must iterate through category until we find last variable of same name (since there could be multiple) */
1832                         for (var = ast_category_first(*cat); var; var = var->next) {
1833                                 if (!strcmp(var->name, cur)) {
1834                                         replace = var;
1835                                 }
1836                         }
1837
1838                         if (!replace) {
1839                                 /* Nothing to replace; just set a variable normally. */
1840                                 goto set_new_variable;
1841                         }
1842
1843                         ast_str_set(str, 0, "%s", replace->value);
1844                         ast_str_append(str, 0, "%s", c);
1845                         ast_str_trim_blanks(*str);
1846                         ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
1847                 } else if (c) {
1848                         *c = 0;
1849                         c++;
1850                         /* Ignore > in => */
1851                         if (*c== '>') {
1852                                 object = 1;
1853                                 c++;
1854                         }
1855                         cur = ast_strip(cur);
1856 set_new_variable:
1857                         if (ast_strlen_zero(cur)) {
1858                                 ast_log(LOG_WARNING, "No variable name in line %d of %s\n", lineno, configfile);
1859                         } else if ((v = ast_variable_new(cur, ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
1860                                 v->lineno = lineno;
1861                                 v->object = object;
1862                                 *last_cat = 0;
1863                                 *last_var = v;
1864                                 /* Put and reset comments */
1865                                 v->blanklines = 0;
1866                                 ast_variable_append(*cat, v);
1867                                 /* add comments */
1868                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1869                                         v->precomments = ALLOC_COMMENT(comment_buffer);
1870                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1871                                         v->sameline = ALLOC_COMMENT(lline_buffer);
1872                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1873                                         CB_RESET(comment_buffer, lline_buffer);
1874
1875                         } else {
1876                                 return -1;
1877                         }
1878                 } else {
1879                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1880                 }
1881         }
1882         return 0;
1883 }
1884
1885 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)
1886 {
1887         char fn[256];
1888 #if defined(LOW_MEMORY)
1889         char buf[512];
1890 #else
1891         char buf[8192];
1892 #endif
1893         char *new_buf, *comment_p, *process_buf;
1894         FILE *f;
1895         int lineno=0;
1896         int comment = 0, nest[MAX_NESTED_COMMENTS];
1897         struct ast_category *cat = NULL;
1898         int count = 0;
1899         struct stat statbuf;
1900         struct cache_file_mtime *cfmtime = NULL;
1901         struct cache_file_include *cfinclude;
1902         struct ast_variable *last_var = 0;
1903         struct ast_category *last_cat = 0;
1904         /*! Growable string buffer */
1905         struct ast_str *comment_buffer = NULL;  /*!< this will be a comment collector.*/
1906         struct ast_str *lline_buffer = NULL;    /*!< A buffer for stuff behind the ; */
1907 #ifdef AST_INCLUDE_GLOB
1908         int glob_ret;
1909         glob_t globbuf;
1910 #endif
1911
1912         if (cfg) {
1913                 cat = ast_config_get_current_category(cfg);
1914         }
1915
1916         if (filename[0] == '/') {
1917                 ast_copy_string(fn, filename, sizeof(fn));
1918         } else {
1919                 snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
1920         }
1921
1922         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1923                 comment_buffer = ast_str_create(CB_SIZE);
1924                 if (comment_buffer) {
1925                         lline_buffer = ast_str_create(CB_SIZE);
1926                 }
1927                 if (!lline_buffer) {
1928                         ast_free(comment_buffer);
1929                         ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1930                         return NULL;
1931                 }
1932         }
1933 #ifdef AST_INCLUDE_GLOB
1934         globbuf.gl_offs = 0;    /* initialize it to silence gcc */
1935         glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
1936         if (glob_ret == GLOB_NOSPACE) {
1937                 ast_log(LOG_WARNING,
1938                         "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1939         } else if (glob_ret  == GLOB_ABORTED) {
1940                 ast_log(LOG_WARNING,
1941                         "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1942         } else {
1943                 /* loop over expanded files */
1944                 int i;
1945
1946                 if (!cfg && (globbuf.gl_pathc != 1 || strcmp(fn, globbuf.gl_pathv[0]))) {
1947                         /*
1948                          * We just want a file changed answer and since we cannot
1949                          * tell if a file was deleted with wildcard matching we will
1950                          * assume that something has always changed.  Also without
1951                          * a lot of refactoring we couldn't check more than one file
1952                          * for changes in the glob loop anyway.
1953                          */
1954                         globfree(&globbuf);
1955                         ast_free(comment_buffer);
1956                         ast_free(lline_buffer);
1957                         return NULL;
1958                 }
1959                 for (i=0; i<globbuf.gl_pathc; i++) {
1960                         ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1961 #endif
1962                         /*
1963                          * The following is not a loop, but just a convenient way to define a block
1964                          * (using do { } while(0) ), and be able to exit from it with 'continue'
1965                          * or 'break' in case of errors. Nice trick.
1966                          */
1967                         do {
1968                                 if (stat(fn, &statbuf)) {
1969                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1970                                                 config_cache_remove(fn, who_asked);
1971                                         }
1972                                         continue;
1973                                 }
1974
1975                                 if (!S_ISREG(statbuf.st_mode)) {
1976                                         ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1977                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1978                                                 config_cache_remove(fn, who_asked);
1979                                         }
1980                                         continue;
1981                                 }
1982
1983                                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1984                                         /* Find our cached entry for this configuration file */
1985                                         AST_LIST_LOCK(&cfmtime_head);
1986                                         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1987                                                 if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked)) {
1988                                                         break;
1989                                                 }
1990                                         }
1991                                         if (!cfmtime) {
1992                                                 cfmtime = cfmtime_new(fn, who_asked);
1993                                                 if (!cfmtime) {
1994                                                         AST_LIST_UNLOCK(&cfmtime_head);
1995                                                         continue;
1996                                                 }
1997                                                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1998                                                 AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
1999                                         }
2000                                 }
2001
2002                                 if (cfmtime
2003                                         && !cfmtime->has_exec
2004                                         && !cfmstat_cmp(cfmtime, &statbuf)
2005                                         && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
2006                                         int unchanged = 1;
2007
2008                                         /* File is unchanged, what about the (cached) includes (if any)? */
2009                                         AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
2010                                                 if (!config_text_file_load(NULL, NULL, cfinclude->include,
2011                                                         NULL, flags, "", who_asked)) {
2012                                                         /* One change is enough to short-circuit and reload the whole shebang */
2013                                                         unchanged = 0;
2014                                                         break;
2015                                                 }
2016                                         }
2017
2018                                         if (unchanged) {
2019                                                 AST_LIST_UNLOCK(&cfmtime_head);
2020 #ifdef AST_INCLUDE_GLOB
2021                                                 globfree(&globbuf);
2022 #endif
2023                                                 ast_free(comment_buffer);
2024                                                 ast_free(lline_buffer);
2025                                                 return CONFIG_STATUS_FILEUNCHANGED;
2026                                         }
2027                                 }
2028
2029                                 /* If cfg is NULL, then we just want a file changed answer. */
2030                                 if (cfg == NULL) {
2031                                         if (cfmtime) {
2032                                                 AST_LIST_UNLOCK(&cfmtime_head);
2033                                         }
2034                                         continue;
2035                                 }
2036
2037                                 if (cfmtime) {
2038                                         /* Forget about what we thought we knew about this file's includes. */
2039                                         cfmtime->has_exec = 0;
2040                                         config_cache_flush_includes(cfmtime);
2041
2042                                         cfmstat_save(cfmtime, &statbuf);
2043                                         AST_LIST_UNLOCK(&cfmtime_head);
2044                                 }
2045
2046                                 if (!(f = fopen(fn, "r"))) {
2047                                         ast_debug(1, "No file to parse: %s\n", fn);
2048                                         ast_verb(2, "Parsing '%s': Not found (%s)\n", fn, strerror(errno));
2049                                         continue;
2050                                 }
2051                                 count++;
2052                                 /* If we get to this point, then we're loading regardless */
2053                                 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
2054                                 ast_debug(1, "Parsing %s\n", fn);
2055                                 ast_verb(2, "Parsing '%s': Found\n", fn);
2056                                 while (!feof(f)) {
2057                                         lineno++;
2058                                         if (fgets(buf, sizeof(buf), f)) {
2059                                                 /* Skip lines that are too long */
2060                                                 if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 1] != '\n') {
2061                                                         ast_log(LOG_WARNING, "Line %d too long, skipping. It begins with: %.32s...\n", lineno, buf);
2062                                                         while (fgets(buf, sizeof(buf), f)) {
2063                                                                 if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 1] == '\n') {
2064                                                                         break;
2065                                                                 }
2066                                                         }
2067                                                         continue;
2068                                                 }
2069
2070                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)
2071                                                         && lline_buffer
2072                                                         && ast_str_strlen(lline_buffer)) {
2073                                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2074                                                         ast_str_reset(lline_buffer);        /* erase the lline buffer */
2075                                                 }
2076
2077                                                 new_buf = buf;
2078                                                 if (comment) {
2079                                                         process_buf = NULL;
2080                                                 } else {
2081                                                         process_buf = buf;
2082                                                 }
2083
2084                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)
2085                                                         && comment_buffer
2086                                                         && ast_str_strlen(comment_buffer)
2087                                                         && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
2088                                                         /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
2089                                                         CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
2090                                                         continue; /* go get a new line, then */
2091                                                 }
2092
2093                                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
2094                                                         if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
2095                                                                 /* Escaped semicolons aren't comments. */
2096                                                                 new_buf = comment_p;
2097                                                                 /* write over the \ and bring the null terminator with us */
2098                                                                 memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
2099                                                         } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
2100                                                                 /* Meta-Comment start detected ";--" */
2101                                                                 if (comment < MAX_NESTED_COMMENTS) {
2102                                                                         *comment_p = '\0';
2103                                                                         new_buf = comment_p + 3;
2104                                                                         comment++;
2105                                                                         nest[comment-1] = lineno;
2106                                                                 } else {
2107                                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
2108                                                                 }
2109                                                         } else if ((comment_p >= new_buf + 2) &&
2110                                                                    (*(comment_p - 1) == COMMENT_TAG) &&
2111                                                                    (*(comment_p - 2) == COMMENT_TAG)) {
2112                                                                 /* Meta-Comment end detected "--;" */
2113                                                                 comment--;
2114                                                                 new_buf = comment_p + 1;
2115                                                                 if (!comment) {
2116                                                                         /* Back to non-comment now */
2117                                                                         if (process_buf) {
2118                                                                                 /* Actually have to move what's left over the top, then continue */
2119                                                                                 char *oldptr;
2120
2121                                                                                 oldptr = process_buf + strlen(process_buf);
2122                                                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2123                                                                                         CB_ADD(&comment_buffer, ";");
2124                                                                                         CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
2125                                                                                 }
2126
2127                                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
2128                                                                                 new_buf = oldptr;
2129                                                                         } else {
2130                                                                                 process_buf = new_buf;
2131                                                                         }
2132                                                                 }
2133                                                         } else {
2134                                                                 if (!comment) {
2135                                                                         /* If ; is found, and we are not nested in a comment,
2136                                                                            we immediately stop all comment processing */
2137                                                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2138                                                                                 CB_ADD(&lline_buffer, comment_p);
2139                                                                         }
2140                                                                         *comment_p = '\0';
2141                                                                         new_buf = comment_p;
2142                                                                 } else {
2143                                                                         new_buf = comment_p + 1;
2144                                                                 }
2145                                                         }
2146                                                 }
2147                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
2148                                                         CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
2149                                                 }
2150
2151                                                 if (process_buf) {
2152                                                         char *buffer = ast_strip(process_buf);
2153
2154                                                         if (!ast_strlen_zero(buffer)) {
2155                                                                 if (process_text_line(cfg, &cat, buffer, lineno, fn,
2156                                                                         flags, comment_buffer, lline_buffer,
2157                                                                         suggested_include_file, &last_cat, &last_var,
2158                                                                         who_asked)) {
2159                                                                         cfg = CONFIG_STATUS_FILEINVALID;
2160                                                                         break;
2161                                                                 }
2162                                                         }
2163                                                 }
2164                                         }
2165                                 }
2166                                 /* end of file-- anything in a comment buffer? */
2167                                 if (last_cat) {
2168                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
2169                                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
2170                                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2171                                                         ast_str_reset(lline_buffer); /* erase the lline buffer */
2172                                                 }
2173                                                 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
2174                                         }
2175                                 } else if (last_var) {
2176                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
2177                                                 if (lline_buffer && ast_str_strlen(lline_buffer)) {
2178                                                         CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
2179                                                         ast_str_reset(lline_buffer); /* erase the lline buffer */
2180                                                 }
2181                                                 last_var->trailing = ALLOC_COMMENT(comment_buffer);
2182                                         }
2183                                 } else {
2184                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
2185                                                 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
2186                                         }
2187                                 }
2188                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
2189                                         CB_RESET(comment_buffer, lline_buffer);
2190                                 }
2191
2192                                 fclose(f);
2193                         } while (0);
2194                         if (comment) {
2195                                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
2196                         }
2197 #ifdef AST_INCLUDE_GLOB
2198                         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
2199                                 break;
2200                         }
2201                 }
2202                 globfree(&globbuf);
2203         }
2204 #endif
2205
2206         ast_free(comment_buffer);
2207         ast_free(lline_buffer);
2208
2209         if (count == 0) {
2210                 return NULL;
2211         }
2212
2213         return cfg;
2214 }
2215
2216
2217 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
2218    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
2219    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
2220    be shocked and mystified as to why things are not showing up in the files!
2221
2222    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
2223    and line number are stored for each include, plus the name of the file included, so that these statements may be
2224    included in the output files on a file_save operation.
2225
2226    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
2227    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
2228    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
2229    and a header gets added.
2230
2231    vars and category heads are output in the order they are stored in the config file. So, if the software
2232    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
2233    file/lineno data probably won't get changed.
2234
2235 */
2236
2237 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
2238 {
2239         char date[256]="";
2240         time_t t;
2241
2242         time(&t);
2243         ast_copy_string(date, ctime(&t), sizeof(date));
2244
2245         fprintf(f1, ";!\n");
2246         fprintf(f1, ";! Automatically generated configuration file\n");
2247         if (strcmp(configfile, fn))
2248                 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
2249         else
2250                 fprintf(f1, ";! Filename: %s\n", configfile);
2251         fprintf(f1, ";! Generator: %s\n", generator);
2252         fprintf(f1, ";! Creation Date: %s", date);
2253         fprintf(f1, ";!\n");
2254 }
2255
2256 static void inclfile_destroy(void *obj)
2257 {
2258         const struct inclfile *o = obj;
2259
2260         ast_free(o->fname);
2261 }
2262
2263 static void make_fn(char *fn, size_t fn_size, const char *file, const char *configfile)
2264 {
2265         if (ast_strlen_zero(file)) {
2266                 if (configfile[0] == '/') {
2267                         ast_copy_string(fn, configfile, fn_size);
2268                 } else {
2269                         snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
2270                 }
2271         } else if (file[0] == '/') {
2272                 ast_copy_string(fn, file, fn_size);
2273         } else {
2274                 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
2275         }
2276 }
2277
2278 static struct inclfile *set_fn(char *fn, size_t fn_size, const char *file, const char *configfile, struct ao2_container *fileset)
2279 {
2280         struct inclfile lookup;
2281         struct inclfile *fi;
2282
2283         make_fn(fn, fn_size, file, configfile);
2284         lookup.fname = fn;
2285         fi = ao2_find(fileset, &lookup, OBJ_POINTER);
2286         if (fi) {
2287                 /* Found existing include file scratch pad. */
2288                 return fi;
2289         }
2290
2291         /* set up a file scratch pad */
2292         fi = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
2293         if (!fi) {
2294                 /* Scratch pad creation failed. */
2295                 return NULL;
2296         }
2297         fi->fname = ast_strdup(fn);
2298         if (!fi->fname) {
2299                 /* Scratch pad creation failed. */
2300                 ao2_ref(fi, -1);
2301                 return NULL;
2302         }
2303         fi->lineno = 1;
2304
2305         ao2_link(fileset, fi);
2306
2307         return fi;
2308 }
2309
2310 static int count_linefeeds(char *str)
2311 {
2312         int count = 0;
2313
2314         while (*str) {
2315                 if (*str =='\n')
2316                         count++;
2317                 str++;
2318         }
2319         return count;
2320 }
2321
2322 static int count_linefeeds_in_comments(struct ast_comment *x)
2323 {
2324         int count = 0;
2325
2326         while (x) {
2327                 count += count_linefeeds(x->cmt);
2328                 x = x->next;
2329         }
2330         return count;
2331 }
2332
2333 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
2334 {
2335         int precomment_lines;
2336         int i;
2337
2338         if (!fi) {
2339                 /* No file scratch pad object so insert no blank lines. */
2340                 return;
2341         }
2342
2343         precomment_lines = count_linefeeds_in_comments(precomments);
2344
2345         /* I don't have to worry about those ;! comments, they are
2346            stored in the precomments, but not printed back out.
2347            I did have to make sure that comments following
2348            the ;! header comments were not also deleted in the process */
2349         if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
2350                 return;
2351         } else if (lineno == 0) {
2352                 /* Line replacements also mess things up */
2353                 return;
2354         } else if (lineno - precomment_lines - fi->lineno < 5) {
2355                 /* Only insert less than 5 blank lines; if anything more occurs,
2356                  * it's probably due to context deletion. */
2357                 for (i = fi->lineno; i < lineno - precomment_lines; i++) {
2358                         fprintf(fp, "\n");
2359                 }
2360         } else {
2361                 /* Deletion occurred - insert a single blank line, for separation of
2362                  * contexts. */
2363                 fprintf(fp, "\n");
2364         }
2365
2366         fi->lineno = lineno + 1; /* Advance the file lineno */
2367 }
2368
2369 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2370 {
2371         return ast_config_text_file_save2(configfile, cfg, generator, CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT);
2372 }
2373
2374 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
2375 {
2376         return ast_config_text_file_save2(configfile, cfg, generator, CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT);
2377 }
2378
2379 int ast_config_text_file_save2(const char *configfile, const struct ast_config *cfg, const char *generator, uint32_t flags)
2380 {
2381         FILE *f;
2382         char fn[PATH_MAX];
2383         struct ast_variable *var;
2384         struct ast_category *cat;
2385         struct ast_comment *cmt;
2386         struct ast_config_include *incl;
2387         int blanklines = 0;
2388         struct ao2_container *fileset;
2389         struct inclfile *fi;
2390
2391         fileset = ao2_container_alloc(1023, hash_string, hashtab_compare_strings);
2392         if (!fileset) {
2393                 /* Container creation failed. */
2394                 return -1;
2395         }
2396
2397         /* Check all the files for write access before attempting to modify any of them */
2398         for (incl = cfg->includes; incl; incl = incl->next) {
2399                 /* reset all the output flags in case this isn't our first time saving this data */
2400                 incl->output = 0;
2401                 /* now make sure we have write access */
2402                 if (!incl->exec) {
2403                         make_fn(fn, sizeof(fn), incl->included_file, configfile);
2404                         if (access(fn, R_OK | W_OK)) {
2405                                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2406                                 return -1;
2407                         }
2408                 }
2409         }
2410
2411         /* now make sure we have write access to the main config file */
2412         make_fn(fn, sizeof(fn), 0, configfile);
2413         if (access(fn, R_OK | W_OK)) {
2414                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2415                 return -1;
2416         }
2417
2418         /* Now that we know we have write access to all files, it's safe to start truncating them */
2419
2420         /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
2421            are all truncated to zero bytes and have that nice header*/
2422         for (incl = cfg->includes; incl; incl = incl->next) {
2423                 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*/
2424                         /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
2425                         fi = set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset);
2426                         f = fopen(fn, "w");
2427                         if (f) {
2428                                 gen_header(f, configfile, fn, generator);
2429                                 fclose(f); /* this should zero out the file */
2430                         } else {
2431                                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2432                         }
2433                         if (fi) {
2434                                 ao2_ref(fi, -1);
2435                         }
2436                 }
2437         }
2438
2439         /* just set fn to absolute ver of configfile */
2440         fi = set_fn(fn, sizeof(fn), 0, configfile, fileset);
2441         if (
2442 #ifdef __CYGWIN__
2443                 (f = fopen(fn, "w+"))
2444 #else
2445                 (f = fopen(fn, "w"))
2446 #endif
2447                 ) {
2448                 ast_verb(2, "Saving '%s'\n", fn);
2449                 gen_header(f, configfile, fn, generator);
2450                 cat = cfg->root;
2451                 fclose(f);
2452                 if (fi) {
2453                         ao2_ref(fi, -1);
2454                 }
2455
2456                 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
2457                 /* since each var, cat, and associated comments can come from any file, we have to be
2458                    mobile, and open each file, print, and close it on an entry-by-entry basis */
2459
2460                 while (cat) {
2461                         fi = set_fn(fn, sizeof(fn), cat->file, configfile, fileset);
2462                         f = fopen(fn, "a");
2463                         if (!f) {
2464                                 ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
2465                                 if (fi) {
2466                                         ao2_ref(fi, -1);
2467                                 }
2468                                 ao2_ref(fileset, -1);
2469                                 return -1;
2470                         }
2471
2472                         /* dump any includes that happen before this category header */
2473                         for (incl=cfg->includes; incl; incl = incl->next) {
2474                                 if (strcmp(incl->include_location_file, cat->file) == 0){
2475                                         if (cat->lineno > incl->include_location_lineno && !incl->output) {
2476                                                 if (incl->exec)
2477                                                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2478                                                 else
2479                                                         fprintf(f,"#include \"%s\"\n", incl->included_file);
2480                                                 incl->output = 1;
2481                                         }
2482                                 }
2483                         }
2484
2485                         insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
2486                         /* Dump section with any appropriate comment */
2487                         for (cmt = cat->precomments; cmt; cmt=cmt->next) {
2488                                 char *cmtp = cmt->cmt;
2489                                 while (cmtp && *cmtp == ';' && *(cmtp+1) == '!') {
2490                                         char *cmtp2 = strchr(cmtp+1, '\n');
2491                                         if (cmtp2)
2492                                                 cmtp = cmtp2+1;
2493                                         else cmtp = 0;
2494                                 }
2495                                 if (cmtp)
2496                                         fprintf(f,"%s", cmtp);
2497                         }
2498                         fprintf(f, "[%s]", cat->name);
2499                         if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
2500                                 fprintf(f, "(");
2501                                 if (cat->ignored) {
2502                                         fprintf(f, "!");
2503                                 }
2504                                 if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
2505                                         fprintf(f, ",");
2506                                 }
2507                                 if (!AST_LIST_EMPTY(&cat->template_instances)) {
2508                                         struct ast_category_template_instance *x;
2509                                         AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2510                                                 fprintf(f,"%s",x->name);
2511                                                 if (x != AST_LIST_LAST(&cat->template_instances))
2512                                                         fprintf(f,",");
2513                                         }
2514                                 }
2515                                 fprintf(f, ")");
2516                         }
2517                         for(cmt = cat->sameline; cmt; cmt=cmt->next)
2518                         {
2519                                 fprintf(f,"%s", cmt->cmt);
2520                         }
2521                         if (!cat->sameline)
2522                                 fprintf(f,"\n");
2523                         for (cmt = cat->trailing; cmt; cmt=cmt->next) {
2524                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2525                                         fprintf(f,"%s", cmt->cmt);
2526                         }
2527                         fclose(f);
2528                         if (fi) {
2529                                 ao2_ref(fi, -1);
2530                         }
2531
2532                         var = cat->root;
2533                         while (var) {
2534                                 struct ast_category_template_instance *x;
2535                                 int found = 0;
2536
2537                                 AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
2538                                         struct ast_variable *v;
2539                                         for (v = x->inst->root; v; v = v->next) {
2540
2541                                                 if (flags & CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT) {
2542                                                         if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2543                                                                 found = 1;
2544                                                                 break;
2545                                                         }
2546                                                 } else {
2547                                                         if (var->inherited) {
2548                                                                 found = 1;
2549                                                                 break;
2550                                                         } else {
2551                                                                 if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
2552                                                                         found = 1;
2553                                                                         break;
2554                                                                 }
2555                                                         }
2556                                                 }
2557                                         }
2558                                         if (found) {
2559                                                 break;
2560                                         }
2561                                 }
2562                                 if (found) {
2563                                         var = var->next;
2564                                         continue;
2565                                 }
2566                                 fi = set_fn(fn, sizeof(fn), var->file, configfile, fileset);
2567                                 f = fopen(fn, "a");
2568                                 if (!f) {
2569                                         ast_debug(1, "Unable to open for writing: %s\n", fn);
2570                                         ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2571                                         if (fi) {
2572                                                 ao2_ref(fi, -1);
2573                                         }
2574                                         ao2_ref(fileset, -1);
2575                                         return -1;
2576                                 }
2577
2578                                 /* dump any includes that happen before this category header */
2579                                 for (incl=cfg->includes; incl; incl = incl->next) {
2580                                         if (strcmp(incl->include_location_file, var->file) == 0){
2581                                                 if (var->lineno > incl->include_location_lineno && !incl->output) {
2582                                                         if (incl->exec)
2583                                                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2584                                                         else
2585                                                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
2586                                                         incl->output = 1;
2587                                                 }
2588                                         }
2589                                 }
2590
2591                                 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
2592                                 for (cmt = var->precomments; cmt; cmt=cmt->next) {
2593                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2594                                                 fprintf(f,"%s", cmt->cmt);
2595                                 }
2596
2597                                 { /* Block for 'escaped' scope */
2598                                         int escaped_len = 2 * strlen(var->value) + 1;
2599                                         char escaped[escaped_len];
2600
2601                                         ast_escape_semicolons(var->value, escaped, escaped_len);
2602
2603                                         if (var->sameline) {
2604                                                 fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="),
2605                                                         escaped, var->sameline->cmt);
2606                                         } else {
2607                                                 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="),
2608                                                         escaped);
2609                                         }
2610                                 }
2611
2612                                 for (cmt = var->trailing; cmt; cmt=cmt->next) {
2613                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
2614                                                 fprintf(f,"%s", cmt->cmt);
2615                                 }
2616                                 if (var->blanklines) {
2617                                         blanklines = var->blanklines;
2618                                         while (blanklines--)
2619                                                 fprintf(f, "\n");
2620                                 }
2621
2622                                 fclose(f);
2623                                 if (fi) {
2624                                         ao2_ref(fi, -1);
2625                                 }
2626
2627                                 var = var->next;
2628                         }
2629                         cat = cat->next;
2630                 }
2631                 if (!option_debug) {
2632                         ast_verb(2, "Saving '%s': saved\n", fn);
2633                 }
2634         } else {
2635                 ast_debug(1, "Unable to open for writing: %s\n", fn);
2636                 ast_verb(2, "Unable to write '%s' (%s)\n", fn, strerror(errno));
2637                 if (fi) {
2638                         ao2_ref(fi, -1);
2639                 }
2640                 ao2_ref(fileset, -1);
2641                 return -1;
2642         }
2643
2644         /* Now, for files with trailing #include/#exec statements,
2645            we have to make sure every entry is output */
2646         for (incl=cfg->includes; incl; incl = incl->next) {
2647                 if (!incl->output) {
2648                         /* open the respective file */
2649                         fi = set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset);
2650                         f = fopen(fn, "a");
2651                         if (!f) {
2652                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
2653                                 ast_verb(2, "Unable to write %s (%s)\n", fn, strerror(errno));
2654                                 if (fi) {
2655                                         ao2_ref(fi, -1);
2656                                 }
2657                                 ao2_ref(fileset, -1);
2658                                 return -1;
2659                         }
2660
2661                         /* output the respective include */
2662                         if (incl->exec)
2663                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
2664                         else
2665                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
2666                         fclose(f);
2667                         incl->output = 1;
2668                         if (fi) {
2669                                 ao2_ref(fi, -1);
2670                         }
2671                 }
2672         }
2673         ao2_ref(fileset, -1); /* this should destroy the hash container */
2674
2675         /* pass new configuration to any config hooks */
2676         config_hook_exec(configfile, generator, cfg);
2677
2678         return 0;
2679 }
2680
2681 static void clear_config_maps(void)
2682 {
2683         struct ast_config_map *map;
2684
2685         SCOPED_MUTEX(lock, &config_lock);
2686
2687         while (config_maps) {
2688                 map = config_maps;
2689                 config_maps = config_maps->next;
2690                 ast_free(map);
2691         }
2692 }
2693
2694 #ifdef TEST_FRAMEWORK
2695 int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2696 #else
2697 static int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
2698 #endif
2699 {
2700         struct ast_config_map *map;
2701         char *dst;
2702         int length;
2703
2704         length = sizeof(*map);
2705         length += strlen(name) + 1;
2706         length += strlen(driver) + 1;
2707         length += strlen(database) + 1;
2708         if (table)
2709                 length += strlen(table) + 1;
2710
2711         if (!(map = ast_calloc(1, length)))
2712                 return -1;
2713
2714         dst = map->stuff;       /* writable space starts here */
2715         map->name = strcpy(dst, name);
2716         dst += strlen(dst) + 1;
2717         map->driver = strcpy(dst, driver);
2718         dst += strlen(dst) + 1;
2719         map->database = strcpy(dst, database);
2720         if (table) {
2721                 dst += strlen(dst) + 1;
2722                 map->table = strcpy(dst, table);
2723         }
2724         map->priority = priority;
2725         map->next = config_maps;
2726         config_maps = map;
2727
2728         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
2729
2730         return 0;
2731 }
2732
2733 int read_config_maps(void)
2734 {
2735         struct ast_config *config, *configtmp;
2736         struct ast_variable *v;
2737         char *driver, *table, *database, *textpri, *stringp, *tmp;
2738         struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
2739         int pri;
2740
2741         clear_config_maps();
2742
2743         configtmp = ast_config_new();
2744         if (!configtmp) {
2745                 ast_log(LOG_ERROR, "Unable to allocate memory for new config\n");
2746                 return -1;
2747         }
2748         config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
2749         if (config == CONFIG_STATUS_FILEINVALID) {
2750                 return -1;
2751         } else if (!config) {
2752                 ast_config_destroy(configtmp);
2753                 return 0;
2754         }
2755
2756         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
2757                 char buf[512];
2758                 ast_copy_string(buf, v->value, sizeof(buf));
2759                 stringp = buf;
2760                 driver = strsep(&stringp, ",");
2761
2762                 if ((tmp = strchr(stringp, '\"')))
2763                         stringp = tmp;
2764
2765                 /* check if the database text starts with a double quote */
2766                 if (*stringp == '"') {
2767                         stringp++;
2768                         database = strsep(&stringp, "\"");
2769                         strsep(&stringp, ",");
2770                 } else {
2771                         /* apparently this text has no quotes */
2772                         database = strsep(&stringp, ",");
2773                 }
2774
2775                 table = strsep(&stringp, ",");
2776                 textpri = strsep(&stringp, ",");
2777                 if (!textpri || !(pri = atoi(textpri))) {
2778                         pri = 1;
2779                 }
2780
2781                 if (!strcmp(v->name, extconfig_conf)) {
2782                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
2783                         continue;
2784                 }
2785
2786                 if (!strcmp(v->name, "asterisk.conf")) {
2787                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
2788                         continue;
2789                 }
2790
2791                 if (!strcmp(v->name, "logger.conf")) {
2792                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
2793                         continue;
2794                 }
2795
2796                 if (!driver || !database)
2797                         continue;
2798                 if (!strcasecmp(v->name, "sipfriends")) {
2799                         ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sippeers instead.\n");
2800                         ast_realtime_append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
2801                 } else if (!strcasecmp(v->name, "iaxfriends")) {
2802                         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");
2803                         ast_realtime_append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
2804                         ast_realtime_append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
2805                 } else
2806                         ast_realtime_append_mapping(v->name, driver, database, table, pri);
2807         }
2808
2809         ast_config_destroy(config);
2810         return 0;
2811 }
2812
2813 int ast_config_engine_register(struct ast_config_engine *new)
2814 {
2815         struct ast_config_engine *ptr;
2816
2817         SCOPED_MUTEX(lock, &config_lock);
2818
2819         if (!config_engine_list) {
2820                 config_engine_list = new;
2821         } else {
2822                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
2823                 ptr->next = new;
2824         }
2825
2826         return 1;
2827 }
2828
2829 int ast_config_engine_deregister(struct ast_config_engine *del)
2830 {
2831         struct ast_config_engine *ptr, *last=NULL;
2832
2833         SCOPED_MUTEX(lock, &config_lock);
2834
2835         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
2836                 if (ptr == del) {
2837                         if (last)
2838                                 last->next = ptr->next;
2839                         else
2840                                 config_engine_list = ptr->next;
2841                         break;
2842                 }
2843                 last = ptr;
2844         }
2845
2846         return 0;
2847 }
2848
2849 int ast_realtime_is_mapping_defined(const char *family)
2850 {
2851         struct ast_config_map *map;
2852         SCOPED_MUTEX(lock, &config_lock);
2853
2854         for (map = config_maps; map; map = map->next) {
2855                 if (!strcasecmp(family, map->name)) {
2856                         return 1;
2857                 }
2858         }
2859
2860         return 0;
2861 }
2862
2863 /*! \brief Find realtime engine for realtime family */
2864 static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz)
2865 {
2866         struct ast_config_engine *eng, *ret = NULL;
2867         struct ast_config_map *map;
2868
2869         SCOPED_MUTEX(lock, &config_lock);
2870
2871         for (map = config_maps; map; map = map->next) {
2872                 if (!strcasecmp(family, map->name) && (priority == map->priority)) {
2873                         if (database)
2874                                 ast_copy_string(database, map->database, dbsiz);
2875                         if (table)
2876                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
2877                         break;
2878                 }
2879         }
2880
2881         /* Check if the required driver (engine) exist */
2882         if (map) {
2883                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
2884                         if (!strcasecmp(eng->name, map->driver))
2885                                 ret = eng;
2886                 }
2887         }
2888
2889         /* if we found a mapping, but the engine is not available, then issue a warning */
2890         if (map && !ret)
2891                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
2892
2893         return ret;
2894 }
2895
2896 static struct ast_config_engine text_file_engine = {
2897         .name = "text",
2898         .load_func = config_text_file_load,
2899 };
2900
2901 struct ast_config *ast_config_copy(const struct ast_config *old)
2902 {
2903         struct ast_config *new_config = ast_config_new();
2904         struct ast_category *cat_iter;
2905
2906         if (!new_config) {
2907                 return NULL;
2908         }
2909
2910         for (cat_iter = old->root; cat_iter; cat_iter = cat_iter->next) {
2911                 struct ast_category *new_cat =
2912                         ast_category_new(cat_iter->name, cat_iter->file, cat_iter->lineno);
2913                 if (!new_cat) {
2914                         goto fail;
2915                 }
2916                 ast_category_append(new_config, new_cat);
2917                 if (cat_iter->root) {
2918                         new_cat->root = ast_variables_dup(cat_iter->root);
2919                         if (!new_cat->root) {
2920                                 goto fail;
2921                         }
2922                         new_cat->last = cat_iter->last;
2923                 }
2924         }
2925
2926         return new_config;
2927
2928 fail:
2929         ast_config_destroy(new_config);
2930         return NULL;
2931 }
2932
2933
2934 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)
2935 {
2936         char db[256];
2937         char table[256];
2938         struct ast_config_engine *loader = &text_file_engine;
2939         struct ast_config *result;
2940
2941         /* The config file itself bumps include_level by 1 */
2942         if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
2943                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
2944                 return NULL;
2945         }
2946
2947         cfg->include_level++;
2948
2949         if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
2950                 struct ast_config_engine *eng;
2951
2952                 eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
2953
2954
2955                 if (eng && eng->load_func) {
2956                         loader = eng;
2957                 } else {
2958                         eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
2959                         if (eng && eng->load_func)
2960                                 loader = eng;
2961                 }
2962         }
2963
2964         result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
2965
2966         if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED) {
2967                 result->include_level--;
2968                 config_hook_exec(filename, who_asked, result);
2969         } else if (result != CONFIG_STATUS_FILEINVALID) {
2970                 cfg->include_level--;
2971         }
2972
2973         return result;
2974 }
2975
2976 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
2977 {
2978         struct ast_config *cfg;
2979         struct ast_config *result;
2980
2981         cfg = ast_config_new();
2982         if (!cfg)
2983                 return NULL;
2984
2985         result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
2986         if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
2987                 ast_config_destroy(cfg);
2988
2989         return result;
2990 }
2991
2992 #define realtime_arguments_to_fields(ap, result) realtime_arguments_to_fields2(ap, 0, result)
2993
2994 /*!
2995  * \internal
2996  * \brief
2997  *
2998  * \param ap list of variable arguments
2999  * \param skip Skip argument pairs for this number of variables
3000  * \param result Address of a variables pointer to store the results
3001  *               May be NULL if no arguments are parsed
3002  *               Will be NULL on failure.
3003  *
3004  * \retval 0 on success or empty ap list
3005  * \retval -1 on failure
3006  */
3007 static int realtime_arguments_to_fields2(va_list ap, int skip, struct ast_variable **result)
3008 {
3009         struct ast_variable *first, *fields = NULL;
3010         const char *newparam;
3011         const char *newval;
3012
3013         /*
3014          * Previously we would do:
3015          *
3016          *     va_start(ap, last);
3017          *     x = realtime_arguments_to_fields(ap);
3018          *     y = realtime_arguments_to_fields(ap);
3019          *     va_end(ap);
3020          *
3021          * While this works on generic amd64 machines (2014), it doesn't on the
3022          * raspberry PI. The va_arg() manpage says:
3023          *
3024          *     If ap is passed to a function that uses va_arg(ap,type) then
3025          *     the value of ap is undefined after the return of that function.
3026          *
3027          * On the raspberry, ap seems to get reset after the call: the contents
3028          * of y would be equal to the contents of x.
3029          *
3030          * So, instead we allow the caller to skip past earlier argument sets
3031          * using the skip parameter:
3032          *
3033          *     va_start(ap, last);
3034          *     if (realtime_arguments_to_fields(ap, &x)) {
3035          *         // FAILURE CONDITIONS
3036          *     }
3037          *     va_end(ap);
3038          *     va_start(ap, last);
3039          *     if (realtime_arguments_to_fields2(ap, 1, &y)) {
3040          *         // FAILURE CONDITIONS
3041          *     }
3042          *     va_end(ap);
3043          */
3044         while (skip--) {
3045                 /* There must be at least one argument. */
3046                 newparam = va_arg(ap, const char *);
3047                 newval = va_arg(ap, const char *);
3048                 while ((newparam = va_arg(ap, const char *))) {
3049                         newval = va_arg(ap, const char *);
3050                 }
3051         }
3052
3053         /* Load up the first vars. */
3054         newparam = va_arg(ap, const char *);
3055         if (!newparam) {
3056                 *result = NULL;
3057                 return 0;
3058         }
3059         newval = va_arg(ap, const char *);
3060
3061         if (!(first = ast_variable_new(newparam, newval, ""))) {
3062                 *result = NULL;
3063                 return -1;
3064         }
3065
3066         while ((newparam = va_arg(ap, const char *))) {
3067                 struct ast_variable *field;
3068
3069                 newval = va_arg(ap, const char *);
3070                 if (!(field = ast_variable_new(newparam, newval, ""))) {
3071                         ast_variables_destroy(fields);
3072                         ast_variables_destroy(first);
3073                         *result = NULL;
3074                         return -1;
3075                 }
3076
3077                 field->next = fields;
3078                 fields = field;
3079         }
3080
3081         first->next = fields;
3082         fields = first;
3083
3084         *result = fields;
3085         return 0;
3086 }
3087
3088 struct ast_variable *ast_load_realtime_all_fields(const char *family, const struct ast_variable *fields)
3089 {
3090         struct ast_config_engine *eng;
3091         char db[256];
3092         char table[256];
3093         struct ast_variable *res=NULL;
3094         int i;
3095
3096         for (i = 1; ; i++) {
3097                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3098                         if (eng->realtime_func && (res = eng->realtime_func(db, table, fields))) {
3099                                 return res;
3100                         }
3101                 } else {
3102                         return NULL;
3103                 }
3104         }
3105
3106         return res;
3107 }
3108
3109 struct ast_variable *ast_load_realtime_all(const char *family, ...)
3110 {
3111         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3112         struct ast_variable *res = NULL;
3113         va_list ap;
3114
3115         va_start(ap, family);
3116         realtime_arguments_to_fields(ap, &fields);
3117         va_end(ap);
3118
3119         if (fields) {
3120                 res = ast_load_realtime_all_fields(family, fields);
3121         }
3122
3123         return res;
3124 }
3125
3126 struct ast_variable *ast_load_realtime_fields(const char *family, const struct ast_variable *fields)
3127 {
3128         struct ast_variable *res;
3129         struct ast_variable *cur;
3130         struct ast_variable **prev;
3131
3132         res = ast_load_realtime_all_fields(family, fields);
3133
3134         /* Filter the list. */
3135         prev = &res;
3136         cur = res;
3137         while (cur) {
3138                 if (ast_strlen_zero(cur->value)) {
3139                         /* Eliminate empty entries */
3140                         struct ast_variable *next;
3141
3142                         next = cur->next;
3143                         *prev = next;
3144                         ast_variable_destroy(cur);
3145                         cur = next;
3146                 } else {
3147                         /* Make blank entries empty and keep them. */
3148                         if (cur->value[0] == ' ' && cur->value[1] == '\0') {
3149                                 char *vptr = (char *) cur->value;
3150
3151                                 vptr[0] = '\0';
3152                         }
3153
3154                         prev = &cur->next;
3155                         cur = cur->next;
3156                 }
3157         }
3158         return res;
3159 }
3160
3161 struct ast_variable *ast_load_realtime(const char *family, ...)
3162 {
3163         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3164         int field_res = 0;
3165         va_list ap;
3166
3167         va_start(ap, family);
3168         if (realtime_arguments_to_fields(ap, &fields)) {
3169                 field_res = -1;
3170         }
3171         va_end(ap);
3172
3173         if (field_res) {
3174                 return NULL;
3175         }
3176
3177         if (!fields) {
3178                 return NULL;
3179         }
3180
3181         return ast_load_realtime_fields(family, fields);
3182 }
3183
3184 /*! \brief Check if realtime engine is configured for family */
3185 int ast_check_realtime(const char *family)
3186 {
3187         struct ast_config_engine *eng;
3188         if (!ast_realtime_enabled()) {
3189                 return 0;       /* There are no engines at all so fail early */
3190         }
3191
3192         eng = find_engine(family, 1, NULL, 0, NULL, 0);
3193         if (eng)
3194                 return 1;
3195         return 0;
3196 }
3197
3198 /*! \brief Check if there's any realtime engines loaded */
3199 int ast_realtime_enabled(void)
3200 {
3201         return config_maps ? 1 : 0;
3202 }
3203
3204 int ast_realtime_require_field(const char *family, ...)
3205 {
3206         struct ast_config_engine *eng;
3207         char db[256];
3208         char table[256];
3209         va_list ap;
3210         int res = -1, i;
3211
3212         va_start(ap, family);
3213         for (i = 1; ; i++) {
3214                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3215                         /* If the require succeeds, it returns 0. */
3216                         if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
3217                                 break;
3218                         }
3219                 } else {
3220                         break;
3221                 }
3222         }
3223         va_end(ap);
3224
3225         return res;
3226 }
3227
3228 int ast_unload_realtime(const char *family)
3229 {
3230         struct ast_config_engine *eng;
3231         char db[256];
3232         char table[256];
3233         int res = -1, i;
3234
3235         for (i = 1; ; i++) {
3236                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3237                         if (eng->unload_func) {
3238                                 /* Do this for ALL engines */
3239                                 res = eng->unload_func(db, table);
3240                         }
3241                 } else {
3242                         break;
3243                 }
3244         }
3245         return res;
3246 }
3247
3248 struct ast_config *ast_load_realtime_multientry_fields(const char *family, const struct ast_variable *fields)
3249 {
3250         struct ast_config_engine *eng;
3251         char db[256];
3252         char table[256];
3253         struct ast_config *res = NULL;
3254         int i;
3255
3256         for (i = 1; ; i++) {
3257                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3258                         if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, fields))) {
3259                                 /* If we were returned an empty cfg, destroy it and return NULL */
3260                                 if (!res->root) {
3261                                         ast_config_destroy(res);
3262                                         res = NULL;
3263                                 }
3264                                 break;
3265                         }
3266                 } else {
3267                         break;
3268                 }
3269         }
3270
3271         return res;
3272 }
3273
3274 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
3275 {
3276         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3277         va_list ap;
3278
3279         va_start(ap, family);
3280         realtime_arguments_to_fields(ap, &fields);
3281         va_end(ap);
3282
3283         if (!fields) {
3284                 return NULL;
3285         }
3286
3287         return ast_load_realtime_multientry_fields(family, fields);
3288 }
3289
3290 int ast_update_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
3291 {
3292         struct ast_config_engine *eng;
3293         int res = -1, i;
3294         char db[256];
3295         char table[256];
3296
3297         for (i = 1; ; i++) {
3298                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3299                         /* If the update succeeds, it returns >= 0. */
3300                         if (eng->update_func && ((res = eng->update_func(db, table, keyfield, lookup, fields)) >= 0)) {
3301                                 break;
3302                         }
3303                 } else {
3304                         break;
3305                 }
3306         }
3307
3308         return res;
3309 }
3310
3311 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
3312 {
3313         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3314         va_list ap;
3315
3316         va_start(ap, lookup);
3317         realtime_arguments_to_fields(ap, &fields);
3318         va_end(ap);
3319
3320         if (!fields) {
3321                 return -1;
3322         }
3323
3324         return ast_update_realtime_fields(family, keyfield, lookup, fields);
3325 }
3326
3327 int ast_update2_realtime_fields(const char *family, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields)
3328 {
3329         struct ast_config_engine *eng;
3330         int res = -1, i;
3331         char db[256];
3332         char table[256];
3333
3334         for (i = 1; ; i++) {
3335                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3336                         if (eng->update2_func && !(res = eng->update2_func(db, table, lookup_fields, update_fields))) {
3337                                 break;
3338                         }
3339                 } else {
3340                         break;
3341                 }
3342         }
3343
3344         return res;
3345 }
3346
3347 int ast_update2_realtime(const char *family, ...)
3348 {
3349         RAII_VAR(struct ast_variable *, lookup_fields, NULL, ast_variables_destroy);
3350         RAII_VAR(struct ast_variable *, update_fields, NULL, ast_variables_destroy);
3351         va_list ap;
3352
3353         va_start(ap, family);
3354         /* XXX: If we wanted to pass no lookup fields (select all), we'd be
3355          * out of luck. realtime_arguments_to_fields expects at least one key
3356          * value pair. */
3357         realtime_arguments_to_fields(ap, &lookup_fields);
3358         va_end(ap);
3359
3360         va_start(ap, family);
3361         realtime_arguments_to_fields2(ap, 1, &update_fields);
3362         va_end(ap);
3363
3364         if (!lookup_fields || !update_fields) {
3365                 return -1;
3366         }
3367
3368         return ast_update2_realtime_fields(family, lookup_fields, update_fields);
3369 }
3370
3371 int ast_store_realtime_fields(const char *family, const struct ast_variable *fields)
3372 {
3373         struct ast_config_engine *eng;
3374         int res = -1, i;
3375         char db[256];
3376         char table[256];
3377
3378         for (i = 1; ; i++) {
3379                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3380                         /* If the store succeeds, it returns >= 0*/
3381                         if (eng->store_func && ((res = eng->store_func(db, table, fields)) >= 0)) {
3382                                 break;
3383                         }
3384                 } else {
3385                         break;
3386                 }
3387         }
3388
3389         return res;
3390 }
3391
3392 int ast_store_realtime(const char *family, ...)
3393 {
3394         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3395         va_list ap;
3396
3397         va_start(ap, family);
3398         realtime_arguments_to_fields(ap, &fields);
3399         va_end(ap);
3400
3401         if (!fields) {
3402                 return -1;
3403         }
3404
3405         return ast_store_realtime_fields(family, fields);
3406 }
3407
3408 int ast_destroy_realtime_fields(const char *family, const char *keyfield, const char *lookup, const struct ast_variable *fields)
3409 {
3410         struct ast_config_engine *eng;
3411         int res = -1, i;
3412         char db[256];
3413         char table[256];
3414
3415         for (i = 1; ; i++) {
3416                 if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
3417                         if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, fields))) {
3418                                 break;
3419                         }
3420                 } else {
3421                         break;
3422                 }
3423         }
3424
3425         return res;
3426 }
3427
3428 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
3429 {
3430         RAII_VAR(struct ast_variable *, fields, NULL, ast_variables_destroy);
3431         int res = 0;
3432         va_list ap;
3433
3434         va_start(ap, lookup);
3435         if (realtime_arguments_to_fields(ap, &fields)) {
3436                 res = -1;
3437         }
3438         va_end(ap);
3439
3440         if (res) {
3441                 return -1;
3442         }
3443
3444         return ast_destroy_realtime_fields(family, keyfield, lookup, fields);
3445 }
3446
3447 char *ast_realtime_decode_chunk(char *chunk)
3448 {
3449         char *orig = chunk;
3450         for (; *chunk; chunk++) {
3451                 if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
3452                         sscanf(chunk + 1, "%02hhX", (unsigned char *)chunk);
3453                         memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
3454                 }
3455         }
3456         return orig;
3457 }
3458
3459 char *ast_realtime_encode_chunk(struct ast_str **dest, ssize_t maxlen, const char *chunk)
3460 {
3461         if (!strchr(chunk, ';') && !strchr(chunk, '^')) {
3462                 ast_str_set(dest, maxlen, "%s", chunk);
3463         } else {
3464                 ast_str_reset(*dest);
3465                 for (; *chunk; chunk++) {
3466                         if (strchr(";^", *chunk)) {
3467                                 ast_str_append(dest, maxlen, "^%02hhX", *chunk);
3468                         } else {
3469                                 ast_str_append(dest, maxlen, "%c", *chunk);
3470                         }
3471                 }
3472         }
3473         return ast_str_buffer(*dest);
3474 }
3475
3476 /*! \brief Helper function to parse arguments
3477  * See documentation in config.h
3478  */
3479 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
3480         void *p_result, ...)
3481 {
3482         va_list ap;
3483         int error = 0;
3484
3485         va_start(ap, p_result);
3486         switch (flags & PARSE_TYPE) {
3487         case PARSE_INT32:
3488         {
3489                 long int x = 0;
3490                 int32_t *result = p_result;
3491                 int32_t def = result ? *result : 0, high = INT32_MAX, low = INT32_MIN;
3492                 char *endptr = NULL;
3493
3494                 /* optional arguments: default value and/or (low, high) */
3495                 if (flags & PARSE_DEFAULT) {
3496                         def = va_arg(ap, int32_t);
3497                 }
3498                 if (flags & (PARSE_IN_RANGE | PARSE_OUT_RANGE)) {
3499                         low = va_arg(ap, int32_t);
3500                         high = va_arg(ap, int32_t);
3501                 }
3502                 if (ast_strlen_zero(arg)) {
3503                         error = 1;
3504                         goto int32_done;
3505                 }
3506                 errno = 0;
3507                 x = strtol(arg,&