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