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