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