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