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