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