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