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