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