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