1dc16fdc72f6da1c2f9cb4460e36df684decd001
[asterisk/asterisk.git] / main / config.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, 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 doc/realtime.txt and doc/extconfig.txt
27  */
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <time.h>
39 #include <sys/stat.h>
40 #include <sys/socket.h>         /* for AF_INET */
41 #define AST_INCLUDE_GLOB 1
42 #ifdef AST_INCLUDE_GLOB
43 #if defined(__Darwin__) || defined(__CYGWIN__)
44 #define GLOB_ABORTED GLOB_ABEND
45 #endif
46 # include <glob.h>
47 #endif
48
49 #include "asterisk/config.h"
50 #include "asterisk/cli.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/options.h"
53 #include "asterisk/logger.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/channel.h"
56 #include "asterisk/app.h"
57 #include "asterisk/astobj2.h"
58
59 #define MAX_NESTED_COMMENTS 128
60 #define COMMENT_START ";--"
61 #define COMMENT_END "--;"
62 #define COMMENT_META ';'
63 #define COMMENT_TAG '-'
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         char cmt[0];
72 };
73
74 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
75 struct cache_file_include {
76         AST_LIST_ENTRY(cache_file_include) list;
77         char include[0];
78 };
79
80 struct cache_file_mtime {
81         AST_LIST_ENTRY(cache_file_mtime) list;
82         AST_LIST_HEAD(includes, cache_file_include) includes;
83         unsigned int has_exec:1;
84         time_t mtime;
85         char filename[0];
86 };
87
88 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
89
90 #define CB_INCR 250
91
92 static void CB_INIT(char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size)
93 {
94         if (!(*comment_buffer)) {
95                 *comment_buffer = ast_malloc(CB_INCR);
96                 if (!(*comment_buffer))
97                         return;
98                 (*comment_buffer)[0] = 0;
99                 *comment_buffer_size = CB_INCR;
100                 *lline_buffer = ast_malloc(CB_INCR);
101                 if (!(*lline_buffer))
102                         return;
103                 (*lline_buffer)[0] = 0;
104                 *lline_buffer_size = CB_INCR;
105         } else {
106                 (*comment_buffer)[0] = 0;
107                 (*lline_buffer)[0] = 0;
108         }
109 }
110
111 static void  CB_ADD(char **comment_buffer, int *comment_buffer_size, char *str)
112 {
113         int rem = *comment_buffer_size - strlen(*comment_buffer) - 1;
114         int siz = strlen(str);
115         if (rem < siz+1) {
116                 *comment_buffer = ast_realloc(*comment_buffer, *comment_buffer_size + CB_INCR + siz + 1);
117                 if (!(*comment_buffer))
118                         return;
119                 *comment_buffer_size += CB_INCR+siz+1;
120         }
121         strcat(*comment_buffer,str);
122 }
123
124 static void  CB_ADD_LEN(char **comment_buffer, int *comment_buffer_size, char *str, int len)
125 {
126         int cbl = strlen(*comment_buffer) + 1;
127         int rem = *comment_buffer_size - cbl;
128         if (rem < len+1) {
129                 *comment_buffer = ast_realloc(*comment_buffer, *comment_buffer_size + CB_INCR + len + 1);
130                 if (!(*comment_buffer))
131                         return;
132                 *comment_buffer_size += CB_INCR+len+1;
133         }
134         strncat(*comment_buffer,str,len);
135         (*comment_buffer)[cbl+len-1] = 0;
136 }
137
138 static void  LLB_ADD(char **lline_buffer, int *lline_buffer_size, char *str)
139 {
140         int rem = *lline_buffer_size - strlen(*lline_buffer) - 1;
141         int siz = strlen(str);
142         if (rem < siz+1) {
143                 *lline_buffer = ast_realloc(*lline_buffer, *lline_buffer_size + CB_INCR + siz + 1);
144                 if (!(*lline_buffer)) 
145                         return;
146                 *lline_buffer_size += CB_INCR + siz + 1;
147         }
148         strcat(*lline_buffer,str);
149 }
150
151 static void CB_RESET(char **comment_buffer, char **lline_buffer)  
152
153         (*comment_buffer)[0] = 0; 
154         (*lline_buffer)[0] = 0;
155 }
156
157
158 static struct ast_comment *ALLOC_COMMENT(const char *buffer)
159
160         struct ast_comment *x;
161         x = ast_calloc(1, sizeof(*x)+strlen(buffer)+1);
162         strcpy(x->cmt, buffer);
163         return x;
164 }
165
166 /* I need to keep track of each config file, and all its inclusions,
167    so that we can track blank lines in each */
168
169 struct inclfile
170 {
171         char *fname;
172         int lineno;
173 };
174
175 static int hash_string(const void *obj, const int flags)
176 {
177         char *str = ((struct inclfile*)obj)->fname;
178         int total;
179
180         for (total=0; *str; str++)
181         {
182                 unsigned int tmp = total;
183                 total <<= 1; /* multiply by 2 */
184                 total += tmp; /* multiply by 3 */
185                 total <<= 2; /* multiply by 12 */
186                 total += tmp; /* multiply by 13 */
187         
188                 total += ((unsigned int)(*str));
189         }
190         if (total < 0)
191                 total = -total;
192         return total;
193 }
194
195 static int hashtab_compare_strings(void *a, void *b, int flags)
196 {
197         const struct inclfile *ae = a, *be = b;
198         return !strcmp(ae->fname, be->fname) ? CMP_MATCH : 0;
199 }
200
201 static struct ast_config_map {
202         struct ast_config_map *next;
203         char *name;
204         char *driver;
205         char *database;
206         char *table;
207         char stuff[0];
208 } *config_maps = NULL;
209
210 AST_MUTEX_DEFINE_STATIC(config_lock);
211 static struct ast_config_engine *config_engine_list;
212
213 #define MAX_INCLUDE_LEVEL 10
214
215 struct ast_category {
216         char name[80];
217         int ignored;                    /*!< do not let user of the config see this category */
218         int include_level;
219         char *file;                /*!< the file name from whence this declaration was read */
220         int lineno;
221         struct ast_comment *precomments;
222         struct ast_comment *sameline;
223         struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
224         struct ast_variable *root;
225         struct ast_variable *last;
226         struct ast_category *next;
227 };
228
229 struct ast_config {
230         struct ast_category *root;
231         struct ast_category *last;
232         struct ast_category *current;
233         struct ast_category *last_browse;     /*!< used to cache the last category supplied via category_browse */
234         int include_level;
235         int max_include_level;
236         struct ast_config_include *includes;  /*!< a list of inclusions, which should describe the entire tree */
237 };
238
239 struct ast_config_include {
240         char *include_location_file;     /*!< file name in which the include occurs */
241         int  include_location_lineno;    /*!< lineno where include occurred */
242         int  exec;                       /*!< set to non-zero if itsa #exec statement */
243         char *exec_file;                 /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
244         char *included_file;             /*!< file name included */
245         int inclusion_count;             /*!< if the file is included more than once, a running count thereof -- but, worry not,
246                                               we explode the instances and will include those-- so all entries will be unique */
247         int output;                      /*!< a flag to indicate if the inclusion has been output */
248         struct ast_config_include *next; /*!< ptr to next inclusion in the list */
249 };
250
251 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename) 
252 {
253         struct ast_variable *variable;
254         int name_len = strlen(name) + 1;        
255
256         if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + strlen(filename) + 1 + sizeof(*variable)))) {
257                 variable->name = variable->stuff;
258                 variable->value = variable->stuff + name_len;           
259                 variable->file = variable->stuff + name_len + strlen(value) + 1;
260                 strcpy(variable->name,name);
261                 strcpy(variable->value,value);
262                 strcpy(variable->file,filename);
263         }
264         return variable;
265 }
266
267 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)
268 {
269         /* a file should be included ONCE. Otherwise, if one of the instances is changed,
270        then all be changed. -- how do we know to include it? -- Handling modified 
271        instances is possible, I'd have
272        to create a new master for each instance. */
273         struct ast_config_include *inc;
274         struct stat statbuf;
275         
276         inc = ast_include_find(conf, included_file);
277         if (inc) {
278                 do {
279                         inc->inclusion_count++;
280                         snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
281                 } while (stat(real_included_file_name, &statbuf) == 0);
282                 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);
283         } else
284                 *real_included_file_name = 0;
285         
286         inc = ast_calloc(1,sizeof(struct ast_config_include));
287         inc->include_location_file = ast_strdup(from_file);
288         inc->include_location_lineno = from_lineno;
289         if (!ast_strlen_zero(real_included_file_name))
290                 inc->included_file = ast_strdup(real_included_file_name);
291         else
292                 inc->included_file = ast_strdup(included_file);
293         
294         inc->exec = is_exec;
295         if (is_exec)
296                 inc->exec_file = ast_strdup(exec_file);
297         
298         /* attach this new struct to the conf struct */
299         inc->next = conf->includes;
300         conf->includes = inc;
301         
302         return inc;
303 }
304
305 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
306 {
307         struct ast_config_include *incl;
308         struct ast_category *cat;
309         struct ast_variable *v;
310         
311         int from_len = strlen(from_file);
312         int to_len = strlen(to_file);
313         
314         if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
315                 return;
316         
317         /* the manager code allows you to read in one config file, then
318        write it back out under a different name. But, the new arrangement
319            ties output lines to the file name. So, before you try to write
320        the config file to disk, better riffle thru the data and make sure
321        the file names are changed.
322         */
323         /* file names are on categories, includes (of course), and on variables. So,
324            traverse all this and swap names */
325
326         for (incl = conf->includes; incl; incl=incl->next) {
327                 if (strcmp(incl->include_location_file,from_file) == 0) {
328                         if (from_len >= to_len)
329                                 strcpy(incl->include_location_file, to_file);
330                         else {
331                                 free(incl->include_location_file);
332                                 incl->include_location_file = strdup(to_file);
333                         }
334                 }
335         }
336         for (cat = conf->root; cat; cat = cat->next) {
337                 if (strcmp(cat->file,from_file) == 0) {
338                         if (from_len >= to_len)
339                                 strcpy(cat->file, to_file);
340                         else {
341                                 free(cat->file);
342                                 cat->file = strdup(to_file);
343                         }
344                 }
345                 for (v = cat->root; v; v = v->next) {
346                         if (strcmp(v->file,from_file) == 0) {
347                                 if (from_len >= to_len)
348                                         strcpy(v->file, to_file);
349                                 else {
350                                         free(v->file);
351                                         v->file = strdup(to_file);
352                                 }
353                         }
354                 }
355         }
356 }
357
358 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
359 {
360         struct ast_config_include *x;
361         for (x=conf->includes;x;x=x->next)
362         {
363                 if (strcmp(x->included_file,included_file) == 0)
364                         return x;
365         }
366         return 0;
367 }
368
369
370 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
371 {
372         if (!variable)
373                 return;
374         if (category->last)
375                 category->last->next = variable;
376         else
377                 category->root = variable;
378         category->last = variable;
379         while (category->last->next)
380                 category->last = category->last->next;
381 }
382
383 void ast_variables_destroy(struct ast_variable *v)
384 {
385         struct ast_variable *vn;
386
387         while (v) {
388                 vn = v;
389                 v = v->next;
390                 ast_free(vn);
391         }
392 }
393
394 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
395 {
396         struct ast_category *cat = NULL;
397
398         if (category && config->last_browse && (config->last_browse->name == category))
399                 cat = config->last_browse;
400         else
401                 cat = ast_category_get(config, category);
402
403         return (cat) ? cat->root : NULL;
404 }
405
406 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
407 {
408         const char *tmp;
409         tmp = ast_variable_retrieve(cfg, cat, var);
410         if (!tmp)
411                 tmp = ast_variable_retrieve(cfg, "general", var);
412         return tmp;
413 }
414
415
416 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
417 {
418         struct ast_variable *v;
419
420         if (category) {
421                 for (v = ast_variable_browse(config, category); v; v = v->next) {
422                         if (!strcasecmp(variable, v->name))
423                                 return v->value;
424                 }
425         } else {
426                 struct ast_category *cat;
427
428                 for (cat = config->root; cat; cat = cat->next)
429                         for (v = cat->root; v; v = v->next)
430                                 if (!strcasecmp(variable, v->name))
431                                         return v->value;
432         }
433
434         return NULL;
435 }
436
437 static struct ast_variable *variable_clone(const struct ast_variable *old)
438 {
439         struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
440
441         if (new) {
442                 new->lineno = old->lineno;
443                 new->object = old->object;
444                 new->blanklines = old->blanklines;
445                 /* TODO: clone comments? */
446         }
447
448         return new;
449 }
450  
451 static void move_variables(struct ast_category *old, struct ast_category *new)
452 {
453         struct ast_variable *var = old->root;
454         old->root = NULL;
455 #if 1
456         /* we can just move the entire list in a single op */
457         ast_variable_append(new, var);
458 #else
459         while (var) {
460                 struct ast_variable *next = var->next;
461                 var->next = NULL;
462                 ast_variable_append(new, var);
463                 var = next;
464         }
465 #endif
466 }
467
468 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno) 
469 {
470         struct ast_category *category;
471
472         if ((category = ast_calloc(1, sizeof(*category))))
473                 ast_copy_string(category->name, name, sizeof(category->name));
474         category->file = strdup(in_file);
475         category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
476         return category;
477 }
478
479 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
480 {
481         struct ast_category *cat;
482
483         /* try exact match first, then case-insensitive match */
484         for (cat = config->root; cat; cat = cat->next) {
485                 if (cat->name == category_name && (ignored || !cat->ignored))
486                         return cat;
487         }
488
489         for (cat = config->root; cat; cat = cat->next) {
490                 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
491                         return cat;
492         }
493
494         return NULL;
495 }
496
497 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
498 {
499         return category_get(config, category_name, 0);
500 }
501
502 int ast_category_exist(const struct ast_config *config, const char *category_name)
503 {
504         return !!ast_category_get(config, category_name);
505 }
506
507 void ast_category_append(struct ast_config *config, struct ast_category *category)
508 {
509         if (config->last)
510                 config->last->next = category;
511         else
512                 config->root = category;
513         category->include_level = config->include_level;
514         config->last = category;
515         config->current = category;
516 }
517
518 void ast_category_destroy(struct ast_category *cat)
519 {
520         ast_variables_destroy(cat->root);
521         if (cat->file)
522                 free(cat->file);
523         
524         ast_free(cat);
525 }
526
527 static void ast_includes_destroy(struct ast_config_include *incls)
528 {
529         struct ast_config_include *incl,*inclnext;
530         
531         for (incl=incls; incl; incl = inclnext) {
532                 inclnext = incl->next;
533                 if (incl->include_location_file)
534                         free(incl->include_location_file);
535                 if (incl->exec_file)
536                         free(incl->exec_file);
537                 if (incl->included_file)
538                         free(incl->included_file);
539                 free(incl);
540         }
541 }
542
543 static struct ast_category *next_available_category(struct ast_category *cat)
544 {
545         for (; cat && cat->ignored; cat = cat->next);
546
547         return cat;
548 }
549
550 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
551 {
552         struct ast_category *category = ast_category_get(config, cat);
553         if (category)
554                 return category->root;
555         return NULL;
556 }
557
558 char *ast_category_browse(struct ast_config *config, const char *prev)
559 {       
560         struct ast_category *cat = NULL;
561
562         if (prev && config->last_browse && (config->last_browse->name == prev))
563                 cat = config->last_browse->next;
564         else if (!prev && config->root)
565                 cat = config->root;
566         else if (prev) {
567                 for (cat = config->root; cat; cat = cat->next) {
568                         if (cat->name == prev) {
569                                 cat = cat->next;
570                                 break;
571                         }
572                 }
573                 if (!cat) {
574                         for (cat = config->root; cat; cat = cat->next) {
575                                 if (!strcasecmp(cat->name, prev)) {
576                                         cat = cat->next;
577                                         break;
578                                 }
579                         }
580                 }
581         }
582         
583         if (cat)
584                 cat = next_available_category(cat);
585
586         config->last_browse = cat;
587         return (cat) ? cat->name : NULL;
588 }
589
590 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
591 {
592         struct ast_variable *v;
593
594         v = cat->root;
595         cat->root = NULL;
596         cat->last = NULL;
597
598         return v;
599 }
600
601 void ast_category_rename(struct ast_category *cat, const char *name)
602 {
603         ast_copy_string(cat->name, name, sizeof(cat->name));
604 }
605
606 static void inherit_category(struct ast_category *new, const struct ast_category *base)
607 {
608         struct ast_variable *var;
609
610         for (var = base->root; var; var = var->next)
611                 ast_variable_append(new, variable_clone(var));
612 }
613
614 struct ast_config *ast_config_new(void) 
615 {
616         struct ast_config *config;
617
618         if ((config = ast_calloc(1, sizeof(*config))))
619                 config->max_include_level = MAX_INCLUDE_LEVEL;
620         return config;
621 }
622
623 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match)
624 {
625         struct ast_variable *cur, *prev=NULL, *curn;
626         int res = -1;
627         cur = category->root;
628         while (cur) {
629                 if (cur->name == variable) {
630                         if (prev) {
631                                 prev->next = cur->next;
632                                 if (cur == category->last)
633                                         category->last = prev;
634                         } else {
635                                 category->root = cur->next;
636                                 if (cur == category->last)
637                                         category->last = NULL;
638                         }
639                         cur->next = NULL;
640                         ast_variables_destroy(cur);
641                         return 0;
642                 }
643                 prev = cur;
644                 cur = cur->next;
645         }
646
647         prev = NULL;
648         cur = category->root;
649         while (cur) {
650                 curn = cur->next;
651                 if (!strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match))) {
652                         if (prev) {
653                                 prev->next = cur->next;
654                                 if (cur == category->last)
655                                         category->last = prev;
656                         } else {
657                                 category->root = cur->next;
658                                 if (cur == category->last)
659                                         category->last = NULL;
660                         }
661                         cur->next = NULL;
662                         ast_variables_destroy(cur);
663                         res = 0;
664                 } else
665                         prev = cur;
666
667                 cur = curn;
668         }
669         return res;
670 }
671
672 int ast_variable_update(struct ast_category *category, const char *variable, 
673                                                 const char *value, const char *match, unsigned int object)
674 {
675         struct ast_variable *cur, *prev=NULL, *newer=NULL;
676
677         for (cur = category->root; cur; prev = cur, cur = cur->next) {
678                 if (strcasecmp(cur->name, variable) ||
679                         (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
680                         continue;
681
682                 if (!(newer = ast_variable_new(variable, value, cur->file)))
683                         return -1;
684         
685                 newer->next = cur->next;
686                 newer->object = cur->object || object;
687                 if (prev)
688                         prev->next = newer;
689                 else
690                         category->root = newer;
691                 if (category->last == cur)
692                         category->last = newer;
693
694                 cur->next = NULL;
695                 ast_variables_destroy(cur);
696
697                 return 0;
698         }
699
700         if (prev)
701                 prev->next = newer;
702         else
703                 category->root = newer;
704
705         return 0;
706 }
707
708 int ast_category_delete(struct ast_config *cfg, const char *category)
709 {
710         struct ast_category *prev=NULL, *cat;
711         cat = cfg->root;
712         while (cat) {
713                 if (cat->name == category) {
714                         ast_variables_destroy(cat->root);
715                         if (prev) {
716                                 prev->next = cat->next;
717                                 if (cat == cfg->last)
718                                         cfg->last = prev;
719                         } else {
720                                 cfg->root = cat->next;
721                                 if (cat == cfg->last)
722                                         cfg->last = NULL;
723                         }
724                         ast_free(cat);
725                         return 0;
726                 }
727                 prev = cat;
728                 cat = cat->next;
729         }
730
731         prev = NULL;
732         cat = cfg->root;
733         while (cat) {
734                 if (!strcasecmp(cat->name, category)) {
735                         ast_variables_destroy(cat->root);
736                         if (prev) {
737                                 prev->next = cat->next;
738                                 if (cat == cfg->last)
739                                         cfg->last = prev;
740                         } else {
741                                 cfg->root = cat->next;
742                                 if (cat == cfg->last)
743                                         cfg->last = NULL;
744                         }
745                         ast_free(cat);
746                         return 0;
747                 }
748                 prev = cat;
749                 cat = cat->next;
750         }
751         return -1;
752 }
753
754 void ast_config_destroy(struct ast_config *cfg)
755 {
756         struct ast_category *cat, *catn;
757
758         if (!cfg)
759                 return;
760
761         ast_includes_destroy(cfg->includes);
762
763         cat = cfg->root;
764         while (cat) {
765                 ast_variables_destroy(cat->root);
766                 catn = cat;
767                 cat = cat->next;
768                 ast_free(catn->file);
769                 ast_free(catn);
770         }
771         ast_free(cfg);
772 }
773
774 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
775 {
776         return cfg->current;
777 }
778
779 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
780 {
781         /* cast below is just to silence compiler warning about dropping "const" */
782         cfg->current = (struct ast_category *) cat;
783 }
784
785 enum config_cache_attribute_enum {
786         ATTRIBUTE_INCLUDE = 0,
787         ATTRIBUTE_EXEC = 1,
788 };
789
790 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename)
791 {
792         struct cache_file_mtime *cfmtime;
793         struct cache_file_include *cfinclude;
794         struct stat statbuf = { 0, };
795
796         /* Find our cached entry for this configuration file */
797         AST_LIST_LOCK(&cfmtime_head);
798         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
799                 if (!strcmp(cfmtime->filename, configfile))
800                         break;
801         }
802         if (!cfmtime) {
803                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1);
804                 if (!cfmtime) {
805                         AST_LIST_UNLOCK(&cfmtime_head);
806                         return;
807                 }
808                 AST_LIST_HEAD_INIT(&cfmtime->includes);
809                 strcpy(cfmtime->filename, configfile);
810                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
811                 AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
812         }
813
814         if (!stat(configfile, &statbuf))
815                 cfmtime->mtime = 0;
816         else
817                 cfmtime->mtime = statbuf.st_mtime;
818
819         switch (attrtype) {
820         case ATTRIBUTE_INCLUDE:
821                 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
822                 if (!cfinclude) {
823                         AST_LIST_UNLOCK(&cfmtime_head);
824                         return;
825                 }
826                 strcpy(cfinclude->include, filename);
827                 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
828                 break;
829         case ATTRIBUTE_EXEC:
830                 cfmtime->has_exec = 1;
831                 break;
832         }
833         AST_LIST_UNLOCK(&cfmtime_head);
834 }
835
836 static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, struct ast_flags flags,
837                                                          char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size, const char *suggested_include_file, struct ast_category **last_cat, struct ast_variable **last_var)
838 {
839         char *c;
840         char *cur = buf;
841         struct ast_variable *v;
842         char cmd[512], exec_file[512];
843         int object, do_exec, do_include;
844
845         /* Actually parse the entry */
846         if (cur[0] == '[') {
847                 struct ast_category *newcat = NULL;
848                 char *catname;
849
850                 /* A category header */
851                 c = strchr(cur, ']');
852                 if (!c) {
853                         ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
854                         return -1;
855                 }
856                 *c++ = '\0';
857                 cur++;
858                 if (*c++ != '(')
859                         c = NULL;
860                 catname = cur;
861                 if (!(*cat = newcat = ast_category_new(catname, ast_strlen_zero(suggested_include_file)?configfile:suggested_include_file, lineno))) {
862                         return -1;
863                 }
864                 (*cat)->lineno = lineno;
865                 *last_var = 0;
866                 *last_cat = newcat;
867                 
868                 /* add comments */
869                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *comment_buffer && (*comment_buffer)[0] ) {
870                         newcat->precomments = ALLOC_COMMENT(*comment_buffer);
871                 }
872                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *lline_buffer && (*lline_buffer)[0] ) {
873                         newcat->sameline = ALLOC_COMMENT(*lline_buffer);
874                 }
875                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
876                         CB_RESET(comment_buffer, lline_buffer);
877                 
878                 /* If there are options or categories to inherit from, process them now */
879                 if (c) {
880                         if (!(cur = strchr(c, ')'))) {
881                                 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
882                                 return -1;
883                         }
884                         *cur = '\0';
885                         while ((cur = strsep(&c, ","))) {
886                                 if (!strcasecmp(cur, "!")) {
887                                         (*cat)->ignored = 1;
888                                 } else if (!strcasecmp(cur, "+")) {
889                                         *cat = category_get(cfg, catname, 1);
890                                         if (!(*cat)) {
891                                                 if (newcat)
892                                                         ast_category_destroy(newcat);
893                                                 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
894                                                 return -1;
895                                         }
896                                         if (newcat) {
897                                                 move_variables(newcat, *cat);
898                                                 ast_category_destroy(newcat);
899                                                 newcat = NULL;
900                                         }
901                                 } else {
902                                         struct ast_category *base;
903                                 
904                                         base = category_get(cfg, cur, 1);
905                                         if (!base) {
906                                                 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
907                                                 return -1;
908                                         }
909                                         inherit_category(*cat, base);
910                                 }
911                         }
912                 }
913                 if (newcat)
914                         ast_category_append(cfg, *cat);
915         } else if (cur[0] == '#') {
916                 /* A directive */
917                 cur++;
918                 c = cur;
919                 while (*c && (*c > 32)) c++;
920                 if (*c) {
921                         *c = '\0';
922                         /* Find real argument */
923                         c = ast_skip_blanks(c + 1);
924                         if (!(*c))
925                                 c = NULL;
926                 } else 
927                         c = NULL;
928                 do_include = !strcasecmp(cur, "include");
929                 if (!do_include)
930                         do_exec = !strcasecmp(cur, "exec");
931                 else
932                         do_exec = 0;
933                 if (do_exec && !ast_opt_exec_includes) {
934                         ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
935                         do_exec = 0;
936                 }
937                 if (do_include || do_exec) {
938                         if (c) {
939                                 char *cur2;
940                                 char real_inclusion_name[256];
941                                 struct ast_config_include *inclu;
942                                 
943                                 /* Strip off leading and trailing "'s and <>'s */
944                                 while ((*c == '<') || (*c == '>') || (*c == '\"')) c++;
945                                 /* Get rid of leading mess */
946                                 cur = c;
947                                 cur2 = cur;
948                                 while (!ast_strlen_zero(cur)) {
949                                         c = cur + strlen(cur) - 1;
950                                         if ((*c == '>') || (*c == '<') || (*c == '\"'))
951                                                 *c = '\0';
952                                         else
953                                                 break;
954                                 }
955                                 /* #exec </path/to/executable>
956                                    We create a tmp file, then we #include it, then we delete it. */
957                                 if (do_exec) {
958                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
959                                                 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL);
960                                         snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d.%ld", (int)time(NULL), (long)pthread_self());
961                                         snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
962                                         ast_safe_system(cmd);
963                                         cur = exec_file;
964                                 } else {
965                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
966                                                 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur);
967                                         exec_file[0] = '\0';
968                                 }
969                                 /* A #include */
970                                 /* record this inclusion */
971                                 inclu = ast_include_new(cfg, configfile, cur, do_exec, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
972
973                                 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name) ? 1 : 0;
974                                 if (!ast_strlen_zero(exec_file))
975                                         unlink(exec_file);
976                                 if (!do_include)
977                                         return 0;
978
979                         } else {
980                                 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", 
981                                                 do_exec ? "exec" : "include",
982                                                 do_exec ? "/path/to/executable" : "filename",
983                                                 lineno,
984                                                 configfile);
985                         }
986                 }
987                 else 
988                         ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
989         } else {
990                 /* Just a line (variable = value) */
991                 if (!(*cat)) {
992                         ast_log(LOG_WARNING,
993                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
994                         return -1;
995                 }
996                 c = strchr(cur, '=');
997                 if (c) {
998                         *c = 0;
999                         c++;
1000                         /* Ignore > in => */
1001                         if (*c== '>') {
1002                                 object = 1;
1003                                 c++;
1004                         } else
1005                                 object = 0;
1006                         if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), *suggested_include_file ? suggested_include_file : configfile))) {
1007                                 v->lineno = lineno;
1008                                 v->object = object;
1009                                 *last_cat = 0;
1010                                 *last_var = v;
1011                                 /* Put and reset comments */
1012                                 v->blanklines = 0;
1013                                 ast_variable_append(*cat, v);
1014                                 /* add comments */
1015                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *comment_buffer && (*comment_buffer)[0] ) {
1016                                         v->precomments = ALLOC_COMMENT(*comment_buffer);
1017                                 }
1018                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *lline_buffer && (*lline_buffer)[0] ) {
1019                                         v->sameline = ALLOC_COMMENT(*lline_buffer);
1020                                 }
1021                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1022                                         CB_RESET(comment_buffer, lline_buffer);
1023                                 
1024                         } else {
1025                                 return -1;
1026                         }
1027                 } else {
1028                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1029                 }
1030         }
1031         return 0;
1032 }
1033
1034 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)
1035 {
1036         char fn[256];
1037         char buf[8192];
1038         char *new_buf, *comment_p, *process_buf;
1039         FILE *f;
1040         int lineno=0;
1041         int comment = 0, nest[MAX_NESTED_COMMENTS];
1042         struct ast_category *cat = NULL;
1043         int count = 0;
1044         struct stat statbuf;
1045         struct cache_file_mtime *cfmtime = NULL;
1046         struct cache_file_include *cfinclude;
1047         struct ast_variable *last_var = 0;
1048         struct ast_category *last_cat = 0;
1049         /*! Growable string buffer */
1050         char *comment_buffer=0;   /*!< this will be a comment collector.*/
1051         int   comment_buffer_size=0;  /*!< the amount of storage so far alloc'd for the comment_buffer */
1052
1053         char *lline_buffer=0;    /*!< A buffer for stuff behind the ; */
1054         int  lline_buffer_size=0;
1055
1056         if (cfg)
1057                 cat = ast_config_get_current_category(cfg);
1058
1059         if (filename[0] == '/') {
1060                 ast_copy_string(fn, filename, sizeof(fn));
1061         } else {
1062                 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, filename);
1063         }
1064
1065         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1066                 CB_INIT(&comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size);
1067                 if (!lline_buffer || !comment_buffer) {
1068                         ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1069                         return NULL;
1070                 }
1071         }
1072 #ifdef AST_INCLUDE_GLOB
1073         {
1074                 int glob_ret;
1075                 glob_t globbuf;
1076                 globbuf.gl_offs = 0;    /* initialize it to silence gcc */
1077 #ifdef SOLARIS
1078                 glob_ret = glob(fn, GLOB_NOCHECK, NULL, &globbuf);
1079 #else
1080                 glob_ret = glob(fn, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
1081 #endif
1082                 if (glob_ret == GLOB_NOSPACE)
1083                         ast_log(LOG_WARNING,
1084                                 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1085                 else if (glob_ret  == GLOB_ABORTED)
1086                         ast_log(LOG_WARNING,
1087                                 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1088                 else  {
1089                         /* loop over expanded files */
1090                         int i;
1091                         for (i=0; i<globbuf.gl_pathc; i++) {
1092                                 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1093 #endif
1094         do {
1095                 if (stat(fn, &statbuf))
1096                         continue;
1097
1098                 if (!S_ISREG(statbuf.st_mode)) {
1099                         ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1100                         continue;
1101                 }
1102
1103                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1104                         /* Find our cached entry for this configuration file */
1105                         AST_LIST_LOCK(&cfmtime_head);
1106                         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1107                                 if (!strcmp(cfmtime->filename, fn))
1108                                         break;
1109                         }
1110                         if (!cfmtime) {
1111                                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1);
1112                                 if (!cfmtime)
1113                                         continue;
1114                                 AST_LIST_HEAD_INIT(&cfmtime->includes);
1115                                 strcpy(cfmtime->filename, fn);
1116                                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1117                                 AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
1118                         }
1119                 }
1120
1121                 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1122                         /* File is unchanged, what about the (cached) includes (if any)? */
1123                         int unchanged = 1;
1124                         AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1125                                 /* We must glob here, because if we did not, then adding a file to globbed directory would
1126                                  * incorrectly cause no reload to be necessary. */
1127                                 char fn2[256];
1128 #ifdef AST_INCLUDE_GLOB
1129                                 int glob_ret;
1130                                 glob_t globbuf = { .gl_offs = 0 };
1131 #ifdef SOLARIS
1132                                 glob_ret = glob(cfinclude->include, GLOB_NOCHECK, NULL, &globbuf);
1133 #else
1134                                 glob_ret = glob(cfinclude->include, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
1135 #endif
1136                                 /* On error, we reparse */
1137                                 if (glob_ret == GLOB_NOSPACE || glob_ret  == GLOB_ABORTED)
1138                                         unchanged = 0;
1139                                 else  {
1140                                         /* loop over expanded files */
1141                                         int j;
1142                                         for (j = 0; j < globbuf.gl_pathc; j++) {
1143                                                 ast_copy_string(fn2, globbuf.gl_pathv[j], sizeof(fn2));
1144 #else
1145                                                 ast_copy_string(fn2, cfinclude->include);
1146 #endif
1147                                                 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "") == NULL) { /* that last field needs to be looked at in this case... TODO */
1148                                                         unchanged = 0;
1149                                                         /* One change is enough to short-circuit and reload the whole shebang */
1150                                                         break;
1151                                                 }
1152 #ifdef AST_INCLUDE_GLOB
1153                                         }
1154                                 }
1155 #endif
1156                         }
1157
1158                         if (unchanged) {
1159                                 AST_LIST_UNLOCK(&cfmtime_head);
1160                                 return CONFIG_STATUS_FILEUNCHANGED;
1161                         }
1162                 }
1163                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1164                         AST_LIST_UNLOCK(&cfmtime_head);
1165
1166                 /* If cfg is NULL, then we just want an answer */
1167                 if (cfg == NULL)
1168                         return NULL;
1169
1170                 if (cfmtime)
1171                         cfmtime->mtime = statbuf.st_mtime;
1172
1173                 ast_verb(2, "Parsing '%s': ", fn);
1174                         fflush(stdout);
1175                 if (!(f = fopen(fn, "r"))) {
1176                         ast_debug(1, "No file to parse: %s\n", fn);
1177                         ast_verb(2, "Not found (%s)\n", strerror(errno));
1178                         continue;
1179                 }
1180                 count++;
1181                 /* If we get to this point, then we're loading regardless */
1182                 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1183                 ast_debug(1, "Parsing %s\n", fn);
1184                 ast_verb(2, "Found\n");
1185                 while (!feof(f)) {
1186                         lineno++;
1187                         if (fgets(buf, sizeof(buf), f)) {
1188                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1189                                         CB_ADD(&comment_buffer, &comment_buffer_size, lline_buffer);       /* add the current lline buffer to the comment buffer */
1190                                         lline_buffer[0] = 0;        /* erase the lline buffer */
1191                                 }
1192                                 
1193                                 new_buf = buf;
1194                                 if (comment) 
1195                                         process_buf = NULL;
1196                                 else
1197                                         process_buf = buf;
1198                                 
1199                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer[0] && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
1200                                         /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1201                                         CB_ADD(&comment_buffer, &comment_buffer_size, "\n");       /* add a newline to the comment buffer */
1202                                         continue; /* go get a new line, then */
1203                                 }
1204                                 
1205                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1206                                         if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
1207                                                 /* Escaped semicolons aren't comments. */
1208                                                 new_buf = comment_p + 1;
1209                                         } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1210                                                 /* Meta-Comment start detected ";--" */
1211                                                 if (comment < MAX_NESTED_COMMENTS) {
1212                                                         *comment_p = '\0';
1213                                                         new_buf = comment_p + 3;
1214                                                         comment++;
1215                                                         nest[comment-1] = lineno;
1216                                                 } else {
1217                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1218                                                 }
1219                                         } else if ((comment_p >= new_buf + 2) &&
1220                                                    (*(comment_p - 1) == COMMENT_TAG) &&
1221                                                    (*(comment_p - 2) == COMMENT_TAG)) {
1222                                                 /* Meta-Comment end detected */
1223                                                 comment--;
1224                                                 new_buf = comment_p + 1;
1225                                                 if (!comment) {
1226                                                         /* Back to non-comment now */
1227                                                         if (process_buf) {
1228                                                                 /* Actually have to move what's left over the top, then continue */
1229                                                                 char *oldptr;
1230                                                                 oldptr = process_buf + strlen(process_buf);
1231                                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1232                                                                         CB_ADD(&comment_buffer, &comment_buffer_size, ";");
1233                                                                         CB_ADD_LEN(&comment_buffer, &comment_buffer_size, oldptr+1, new_buf-oldptr-1);
1234                                                                 }
1235                                                                 
1236                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1237                                                                 new_buf = oldptr;
1238                                                         } else
1239                                                                 process_buf = new_buf;
1240                                                 }
1241                                         } else {
1242                                                 if (!comment) {
1243                                                         /* If ; is found, and we are not nested in a comment, 
1244                                                            we immediately stop all comment processing */
1245                                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1246                                                                 LLB_ADD(&lline_buffer, &lline_buffer_size, comment_p);
1247                                                         }
1248                                                         *comment_p = '\0'; 
1249                                                         new_buf = comment_p;
1250                                                 } else
1251                                                         new_buf = comment_p + 1;
1252                                         }
1253                                 }
1254                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf )
1255                                 {
1256                                         CB_ADD(&comment_buffer, &comment_buffer_size, buf);  /* the whole line is a comment, store it */
1257                                 }
1258                                 
1259                                 if (process_buf) {
1260                                         char *buf = ast_strip(process_buf);
1261                                         if (!ast_strlen_zero(buf)) {
1262                                                 if (process_text_line(cfg, &cat, buf, lineno, fn, flags, &comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size, suggested_include_file, &last_cat, &last_var)) {
1263                                                         cfg = NULL;
1264                                                         break;
1265                                                 }
1266                                         }
1267                                 }
1268                         }
1269                 }
1270                 /* end of file-- anything in a comment buffer? */
1271                 if (last_cat) {
1272                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer[0] ) {
1273                                 CB_ADD(&comment_buffer, &comment_buffer_size, lline_buffer);       /* add the current lline buffer to the comment buffer */
1274                                 lline_buffer[0] = 0;        /* erase the lline buffer */
1275                                 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1276                         }
1277                 } else if (last_var) {
1278                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer[0] ) {
1279                                 CB_ADD(&comment_buffer, &comment_buffer_size, lline_buffer);       /* add the current lline buffer to the comment buffer */
1280                                 lline_buffer[0] = 0;        /* erase the lline buffer */
1281                                 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1282                         }
1283                 } else {
1284                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && (comment_buffer)[0] ) {
1285                                 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", comment_buffer);
1286                         }
1287                 }
1288                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1289                         CB_RESET(&comment_buffer, &lline_buffer);
1290
1291                 fclose(f);              
1292         } while (0);
1293         if (comment) {
1294                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1295         }
1296 #ifdef AST_INCLUDE_GLOB
1297                                         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1298                                                 break;
1299                                 }
1300                                 globfree(&globbuf);
1301                         }
1302                 }
1303 #endif
1304
1305         if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer) {
1306                 ast_free(comment_buffer);
1307                 ast_free(lline_buffer);
1308                 comment_buffer = NULL;
1309                 lline_buffer = NULL;
1310                 comment_buffer_size = 0;
1311                 lline_buffer_size = 0;
1312         }
1313         
1314         if (count == 0)
1315                 return NULL;
1316
1317         return cfg;
1318 }
1319
1320
1321 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1322    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1323    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1324    be shocked and mystified as to why things are not showing up in the files! 
1325
1326    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1327    and line number are stored for each include, plus the name of the file included, so that these statements may be
1328    included in the output files on a file_save operation. 
1329
1330    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1331    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1332    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1333    and a header gets added.
1334
1335    vars and category heads are output in the order they are stored in the config file. So, if the software
1336    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1337    file/lineno data probably won't get changed.
1338
1339 */
1340
1341 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1342 {
1343         char date[256]="";
1344         time_t t;
1345         time(&t);
1346         ast_copy_string(date, ctime(&t), sizeof(date));
1347
1348         fprintf(f1, ";!\n");
1349         fprintf(f1, ";! Automatically generated configuration file\n");
1350         if (strcmp(configfile, fn))
1351                 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1352         else
1353                 fprintf(f1, ";! Filename: %s\n", configfile);
1354         fprintf(f1, ";! Generator: %s\n", generator);
1355         fprintf(f1, ";! Creation Date: %s", date);
1356         fprintf(f1, ";!\n");
1357 }
1358
1359 static void   inclfile_destroy(void *obj)
1360 {
1361         const struct inclfile *o = obj;
1362         if (o->fname)
1363                 free(o->fname);
1364 }
1365
1366
1367 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
1368 {
1369         struct inclfile lookup;
1370         
1371         if (!file || file[0] == 0) {
1372                 if (configfile[0] == '/')
1373                         ast_copy_string(fn, configfile, fn_size);
1374                 else
1375                         snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1376         } else if (file[0] == '/') 
1377                 ast_copy_string(fn, file, fn_size);
1378         else
1379                 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
1380         lookup.fname = fn;
1381         *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
1382         if (!(*fi)) {
1383                 /* set up a file scratch pad */
1384                 struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
1385                 fx->fname = ast_strdup(fn);
1386                 fx->lineno = 1;
1387                 *fi = fx;
1388                 ao2_link(fileset, fx);
1389                 ao2_ref(fx,1); /* bump the ref count, so it looks like we just got the ref from find */
1390         }
1391 }
1392
1393 static int count_linefeeds(char *str)
1394 {
1395         int count = 0;
1396         while (*str) {
1397                 if (*str =='\n')
1398                         count++;
1399                 str++;
1400         }
1401         return count;
1402 }
1403
1404 static int count_linefeeds_in_comments(struct ast_comment *x)
1405 {
1406         int count = 0;
1407         while (x)
1408         {
1409                 count += count_linefeeds(x->cmt);
1410                 x = x->next;
1411         }
1412         return count;
1413 }
1414
1415 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1416 {
1417         int precomment_lines = count_linefeeds_in_comments(precomments);
1418         int i;
1419
1420         /* I don't have to worry about those ;! comments, they are
1421            stored in the precomments, but not printed back out.
1422            I did have to make sure that comments following
1423            the ;! header comments were not also deleted in the process */
1424         for (i=fi->lineno;i<lineno - precomment_lines; i++) {
1425                 fprintf(fp,"\n");
1426         }
1427         fi->lineno = lineno+1; /* Advance the file lineno */
1428 }
1429
1430 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1431 {
1432         FILE *f;
1433         char fn[256];
1434         struct ast_variable *var;
1435         struct ast_category *cat;
1436         struct ast_comment *cmt;
1437         struct ast_config_include *incl;
1438         int blanklines = 0;
1439         struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
1440         struct inclfile *fi = 0;
1441
1442         /* reset all the output flags, in case this isn't our first time saving this data */
1443
1444         for (incl=cfg->includes; incl; incl = incl->next)
1445                 incl->output = 0;
1446
1447         /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
1448            are all truncated to zero bytes and have that nice header*/
1449
1450         for (incl=cfg->includes; incl; incl = incl->next)
1451         {
1452                 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*/
1453                         FILE *f1;
1454
1455                         set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset, &fi); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
1456                         f1 = fopen(fn,"w");
1457                         if (f1) {
1458                                 gen_header(f1, configfile, fn, generator);
1459                                 fclose(f1); /* this should zero out the file */
1460                         } else {
1461                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1462                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1463                         }
1464                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1465                         fi = 0;
1466                 }
1467         }
1468
1469         set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
1470 #ifdef __CYGWIN__       
1471         if ((f = fopen(fn, "w+"))) {
1472 #else
1473         if ((f = fopen(fn, "w"))) {
1474 #endif      
1475                 ast_verb(2, "Saving '%s': ", fn);
1476                 gen_header(f, configfile, fn, generator);
1477                 cat = cfg->root;
1478                 fclose(f);
1479                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1480                 
1481                 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
1482                 /* since each var, cat, and associated comments can come from any file, we have to be 
1483                    mobile, and open each file, print, and close it on an entry-by-entry basis */
1484
1485                 while (cat) {
1486                         set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
1487                         f = fopen(fn, "a");
1488                         if (!f)
1489                         {
1490                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1491                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1492                                 ao2_ref(fileset, -1);
1493                                 return -1;
1494                         }
1495
1496                         /* dump any includes that happen before this category header */
1497                         for (incl=cfg->includes; incl; incl = incl->next) {
1498                                 if (strcmp(incl->include_location_file, cat->file) == 0){
1499                                         if (cat->lineno > incl->include_location_lineno && !incl->output) {
1500                                                 if (incl->exec)
1501                                                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1502                                                 else
1503                                                         fprintf(f,"#include \"%s\"\n", incl->included_file);
1504                                                 incl->output = 1;
1505                                         }
1506                                 }
1507                         }
1508                         
1509                         insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
1510                         /* Dump section with any appropriate comment */
1511                         for (cmt = cat->precomments; cmt; cmt=cmt->next) {
1512                                 char *cmtp = cmt->cmt;
1513                                 while (*cmtp == ';' && *(cmtp+1) == '!') {
1514                                         char *cmtp2 = strchr(cmtp+1, '\n');
1515                                         if (cmtp2)
1516                                                 cmtp = cmtp2+1;
1517                                         else cmtp = 0;
1518                                 }
1519                                 if (cmtp)
1520                                         fprintf(f,"%s", cmtp);
1521                         }
1522                         if (!cat->precomments)
1523                                 fprintf(f,"\n");
1524                         fprintf(f, "[%s]", cat->name);
1525                         for (cmt = cat->sameline; cmt; cmt=cmt->next) {
1526                                 fprintf(f,"%s", cmt->cmt);
1527                         }
1528                         if (!cat->sameline)
1529                                 fprintf(f,"\n");
1530                         for (cmt = cat->trailing; cmt; cmt=cmt->next) {
1531                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1532                                         fprintf(f,"%s", cmt->cmt);
1533                         }
1534                         fclose(f);
1535                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1536                         fi = 0;
1537                         
1538                         var = cat->root;
1539                         while (var) {
1540                                 set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
1541                                 f = fopen(fn, "a");
1542                                 if (!f)
1543                                 {
1544                                         ast_debug(1, "Unable to open for writing: %s\n", fn);
1545                                         ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1546                                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1547                                         fi = 0;
1548                                         ao2_ref(fileset, -1);
1549                                         return -1;
1550                                 }
1551                                 
1552                                 /* dump any includes that happen before this category header */
1553                                 for (incl=cfg->includes; incl; incl = incl->next) {
1554                                         if (strcmp(incl->include_location_file, var->file) == 0){
1555                                                 if (var->lineno > incl->include_location_lineno && !incl->output) {
1556                                                         if (incl->exec)
1557                                                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1558                                                         else
1559                                                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1560                                                         incl->output = 1;
1561                                                 }
1562                                         }
1563                                 }
1564                                 
1565                                 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
1566                                 for (cmt = var->precomments; cmt; cmt=cmt->next) {
1567                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1568                                                 fprintf(f,"%s", cmt->cmt);
1569                                 }
1570                                 if (var->sameline) 
1571                                         fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
1572                                 else    
1573                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
1574                                 for (cmt = var->trailing; cmt; cmt=cmt->next) {
1575                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1576                                                 fprintf(f,"%s", cmt->cmt);
1577                                 }
1578                                 if (var->blanklines) {
1579                                         blanklines = var->blanklines;
1580                                         while (blanklines--)
1581                                                 fprintf(f, "\n");
1582                                 }
1583                                 
1584                                 fclose(f);
1585                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1586                                 fi = 0;
1587                                 
1588                                 var = var->next;
1589                         }
1590                         cat = cat->next;
1591                 }
1592                 if (!option_debug)
1593                         ast_verb(2, "Saved\n");
1594         } else {
1595                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1596                 ast_verb(2, "Unable to write (%s)", strerror(errno));
1597                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1598                 ao2_ref(fileset, -1);
1599                 return -1;
1600         }
1601
1602         /* Now, for files with trailing #include/#exec statements,
1603            we have to make sure every entry is output */
1604
1605         for (incl=cfg->includes; incl; incl = incl->next) {
1606                 if (!incl->output) {
1607                         /* open the respective file */
1608                         set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
1609                         f = fopen(fn, "a");
1610                         if (!f)
1611                         {
1612                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1613                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1614                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1615                                 fi = 0;
1616                                 ao2_ref(fileset, -1);
1617                                 return -1;
1618                         }
1619                         
1620                         /* output the respective include */
1621                         if (incl->exec)
1622                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1623                         else
1624                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1625                         fclose(f);
1626                         incl->output = 1;
1627                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1628                         fi = 0;
1629                 }
1630         }
1631         ao2_ref(fileset, -1); /* this should destroy the hash container */
1632                                 
1633         return 0;
1634 }
1635
1636 static void clear_config_maps(void) 
1637 {
1638         struct ast_config_map *map;
1639
1640         ast_mutex_lock(&config_lock);
1641
1642         while (config_maps) {
1643                 map = config_maps;
1644                 config_maps = config_maps->next;
1645                 ast_free(map);
1646         }
1647                 
1648         ast_mutex_unlock(&config_lock);
1649 }
1650
1651 static int append_mapping(char *name, char *driver, char *database, char *table)
1652 {
1653         struct ast_config_map *map;
1654         int length;
1655
1656         length = sizeof(*map);
1657         length += strlen(name) + 1;
1658         length += strlen(driver) + 1;
1659         length += strlen(database) + 1;
1660         if (table)
1661                 length += strlen(table) + 1;
1662
1663         if (!(map = ast_calloc(1, length)))
1664                 return -1;
1665
1666         map->name = map->stuff;
1667         strcpy(map->name, name);
1668         map->driver = map->name + strlen(map->name) + 1;
1669         strcpy(map->driver, driver);
1670         map->database = map->driver + strlen(map->driver) + 1;
1671         strcpy(map->database, database);
1672         if (table) {
1673                 map->table = map->database + strlen(map->database) + 1;
1674                 strcpy(map->table, table);
1675         }
1676         map->next = config_maps;
1677
1678         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
1679
1680         config_maps = map;
1681         return 0;
1682 }
1683
1684 int read_config_maps(void) 
1685 {
1686         struct ast_config *config, *configtmp;
1687         struct ast_variable *v;
1688         char *driver, *table, *database, *stringp, *tmp;
1689         struct ast_flags flags = { 0 };
1690
1691         clear_config_maps();
1692
1693         configtmp = ast_config_new();
1694         configtmp->max_include_level = 1;
1695         config = ast_config_internal_load(extconfig_conf, configtmp, flags, "");
1696         if (!config) {
1697                 ast_config_destroy(configtmp);
1698                 return 0;
1699         }
1700
1701         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
1702                 stringp = v->value;
1703                 driver = strsep(&stringp, ",");
1704
1705                 if ((tmp = strchr(stringp, '\"')))
1706                         stringp = tmp;
1707
1708                 /* check if the database text starts with a double quote */
1709                 if (*stringp == '"') {
1710                         stringp++;
1711                         database = strsep(&stringp, "\"");
1712                         strsep(&stringp, ",");
1713                 } else {
1714                         /* apparently this text has no quotes */
1715                         database = strsep(&stringp, ",");
1716                 }
1717
1718                 table = strsep(&stringp, ",");
1719
1720                 if (!strcmp(v->name, extconfig_conf)) {
1721                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
1722                         continue;
1723                 }
1724
1725                 if (!strcmp(v->name, "asterisk.conf")) {
1726                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
1727                         continue;
1728                 }
1729
1730                 if (!strcmp(v->name, "logger.conf")) {
1731                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
1732                         continue;
1733                 }
1734
1735                 if (!driver || !database)
1736                         continue;
1737                 if (!strcasecmp(v->name, "sipfriends")) {
1738                         ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n");
1739                         append_mapping("sipusers", driver, database, table ? table : "sipfriends");
1740                         append_mapping("sippeers", driver, database, table ? table : "sipfriends");
1741                 } else if (!strcasecmp(v->name, "iaxfriends")) {
1742                         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");
1743                         append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
1744                         append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
1745                 } else 
1746                         append_mapping(v->name, driver, database, table);
1747         }
1748                 
1749         ast_config_destroy(config);
1750         return 0;
1751 }
1752
1753 int ast_config_engine_register(struct ast_config_engine *new) 
1754 {
1755         struct ast_config_engine *ptr;
1756
1757         ast_mutex_lock(&config_lock);
1758
1759         if (!config_engine_list) {
1760                 config_engine_list = new;
1761         } else {
1762                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
1763                 ptr->next = new;
1764         }
1765
1766         ast_mutex_unlock(&config_lock);
1767         ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
1768
1769         return 1;
1770 }
1771
1772 int ast_config_engine_deregister(struct ast_config_engine *del) 
1773 {
1774         struct ast_config_engine *ptr, *last=NULL;
1775
1776         ast_mutex_lock(&config_lock);
1777
1778         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
1779                 if (ptr == del) {
1780                         if (last)
1781                                 last->next = ptr->next;
1782                         else
1783                                 config_engine_list = ptr->next;
1784                         break;
1785                 }
1786                 last = ptr;
1787         }
1788
1789         ast_mutex_unlock(&config_lock);
1790
1791         return 0;
1792 }
1793
1794 /*! \brief Find realtime engine for realtime family */
1795 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz) 
1796 {
1797         struct ast_config_engine *eng, *ret = NULL;
1798         struct ast_config_map *map;
1799
1800         ast_mutex_lock(&config_lock);
1801
1802         for (map = config_maps; map; map = map->next) {
1803                 if (!strcasecmp(family, map->name)) {
1804                         if (database)
1805                                 ast_copy_string(database, map->database, dbsiz);
1806                         if (table)
1807                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
1808                         break;
1809                 }
1810         }
1811
1812         /* Check if the required driver (engine) exist */
1813         if (map) {
1814                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
1815                         if (!strcasecmp(eng->name, map->driver))
1816                                 ret = eng;
1817                 }
1818         }
1819
1820         ast_mutex_unlock(&config_lock);
1821         
1822         /* if we found a mapping, but the engine is not available, then issue a warning */
1823         if (map && !ret)
1824                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
1825
1826         return ret;
1827 }
1828
1829 static struct ast_config_engine text_file_engine = {
1830         .name = "text",
1831         .load_func = config_text_file_load,
1832 };
1833
1834 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file)
1835 {
1836         char db[256];
1837         char table[256];
1838         struct ast_config_engine *loader = &text_file_engine;
1839         struct ast_config *result; 
1840
1841         if (cfg->include_level == cfg->max_include_level) {
1842                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
1843                 return NULL;
1844         }
1845
1846         cfg->include_level++;
1847
1848         if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
1849                 struct ast_config_engine *eng;
1850
1851                 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
1852
1853
1854                 if (eng && eng->load_func) {
1855                         loader = eng;
1856                 } else {
1857                         eng = find_engine("global", db, sizeof(db), table, sizeof(table));
1858                         if (eng && eng->load_func)
1859                                 loader = eng;
1860                 }
1861         }
1862
1863         result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file);
1864
1865         if (result && result != CONFIG_STATUS_FILEUNCHANGED)
1866                 result->include_level--;
1867         else
1868                 cfg->include_level--;
1869
1870         return result;
1871 }
1872
1873 struct ast_config *ast_config_load(const char *filename, struct ast_flags flags)
1874 {
1875         struct ast_config *cfg;
1876         struct ast_config *result;
1877
1878         cfg = ast_config_new();
1879         if (!cfg)
1880                 return NULL;
1881
1882         result = ast_config_internal_load(filename, cfg, flags, "");
1883         if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
1884                 ast_config_destroy(cfg);
1885
1886         return result;
1887 }
1888
1889 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
1890 {
1891         struct ast_config_engine *eng;
1892         char db[256]="";
1893         char table[256]="";
1894         struct ast_variable *res=NULL;
1895
1896         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1897         if (eng && eng->realtime_func) 
1898                 res = eng->realtime_func(db, table, ap);
1899
1900         return res;
1901 }
1902
1903 struct ast_variable *ast_load_realtime_all(const char *family, ...)
1904 {
1905         struct ast_variable *res;
1906         va_list ap;
1907
1908         va_start(ap, family);
1909         res = ast_load_realtime_helper(family, ap);
1910         va_end(ap);
1911
1912         return res;
1913 }
1914
1915 struct ast_variable *ast_load_realtime(const char *family, ...)
1916 {
1917         struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
1918         va_list ap;
1919
1920         va_start(ap, family);
1921         res = ast_load_realtime_helper(family, ap);
1922         va_end(ap);
1923
1924         /* Eliminate blank entries */
1925         for (cur = res; cur; cur = cur->next) {
1926                 if (freeme) {
1927                         ast_free(freeme);
1928                         freeme = NULL;
1929                 }
1930
1931                 if (ast_strlen_zero(cur->value)) {
1932                         if (prev)
1933                                 prev->next = cur->next;
1934                         else
1935                                 res = cur->next;
1936                         freeme = cur;
1937                 } else {
1938                         prev = cur;
1939                 }
1940         }
1941         return res;
1942 }
1943
1944 /*! \brief Check if realtime engine is configured for family */
1945 int ast_check_realtime(const char *family)
1946 {
1947         struct ast_config_engine *eng;
1948
1949         eng = find_engine(family, NULL, 0, NULL, 0);
1950         if (eng)
1951                 return 1;
1952         return 0;
1953
1954 }
1955
1956 /*! \brief Check if there's any realtime engines loaded */
1957 int ast_realtime_enabled()
1958 {
1959         return config_maps ? 1 : 0;
1960 }
1961
1962 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
1963 {
1964         struct ast_config_engine *eng;
1965         char db[256]="";
1966         char table[256]="";
1967         struct ast_config *res=NULL;
1968         va_list ap;
1969
1970         va_start(ap, family);
1971         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1972         if (eng && eng->realtime_multi_func) 
1973                 res = eng->realtime_multi_func(db, table, ap);
1974         va_end(ap);
1975
1976         return res;
1977 }
1978
1979 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
1980 {
1981         struct ast_config_engine *eng;
1982         int res = -1;
1983         char db[256]="";
1984         char table[256]="";
1985         va_list ap;
1986
1987         va_start(ap, lookup);
1988         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1989         if (eng && eng->update_func) 
1990                 res = eng->update_func(db, table, keyfield, lookup, ap);
1991         va_end(ap);
1992
1993         return res;
1994 }
1995
1996 int ast_store_realtime(const char *family, ...) {
1997         struct ast_config_engine *eng;
1998         int res = -1;
1999         char db[256]="";
2000         char table[256]="";
2001         va_list ap;
2002
2003         va_start(ap, family);
2004         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2005         if (eng && eng->store_func) 
2006                 res = eng->store_func(db, table, ap);
2007         va_end(ap);
2008
2009         return res;
2010 }
2011
2012 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...) {
2013         struct ast_config_engine *eng;
2014         int res = -1;
2015         char db[256]="";
2016         char table[256]="";
2017         va_list ap;
2018
2019         va_start(ap, lookup);
2020         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2021         if (eng && eng->destroy_func) 
2022                 res = eng->destroy_func(db, table, keyfield, lookup, ap);
2023         va_end(ap);
2024
2025         return res;
2026 }
2027
2028 /*! \brief Helper function to parse arguments
2029  * See documentation in config.h
2030  */
2031 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2032         void *p_result, ...)
2033 {
2034         va_list ap;
2035         int error = 0;
2036
2037         va_start(ap, p_result);
2038         switch (flags & PARSE_TYPE) {
2039         case PARSE_INT32:
2040             {
2041                 int32_t *result = p_result;
2042                 int32_t x, def = result ? *result : 0,
2043                         high = (int32_t)0x7fffffff,
2044                         low  = (int32_t)0x80000000;
2045                 /* optional argument: first default value, then range */
2046                 if (flags & PARSE_DEFAULT)
2047                         def = va_arg(ap, int32_t);
2048                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2049                         /* range requested, update bounds */
2050                         low = va_arg(ap, int32_t);
2051                         high = va_arg(ap, int32_t);
2052                 }
2053                 x = strtol(arg, NULL, 0);
2054                 error = (x < low) || (x > high);
2055                 if (flags & PARSE_OUT_RANGE)
2056                         error = !error;
2057                 if (result)
2058                         *result  = error ? def : x;
2059                 ast_debug(3,
2060                         "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
2061                         arg, low, high,
2062                         result ? *result : x, error);
2063                 break;
2064             }
2065
2066         case PARSE_UINT32:
2067             {
2068                 uint32_t *result = p_result;
2069                 uint32_t x, def = result ? *result : 0,
2070                         low = 0, high = (uint32_t)~0;
2071                 /* optional argument: first default value, then range */
2072                 if (flags & PARSE_DEFAULT)
2073                         def = va_arg(ap, uint32_t);
2074                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2075                         /* range requested, update bounds */
2076                         low = va_arg(ap, uint32_t);
2077                         high = va_arg(ap, uint32_t);
2078                 }
2079                 x = strtoul(arg, NULL, 0);
2080                 error = (x < low) || (x > high);
2081                 if (flags & PARSE_OUT_RANGE)
2082                         error = !error;
2083                 if (result)
2084                         *result  = error ? def : x;
2085                 ast_debug(3,
2086                         "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
2087                         arg, low, high,
2088                         result ? *result : x, error);
2089                 break;
2090             }
2091
2092         case PARSE_INADDR:
2093             {
2094                 char *port, *buf;
2095                 struct sockaddr_in _sa_buf;     /* buffer for the result */
2096                 struct sockaddr_in *sa = p_result ?
2097                         (struct sockaddr_in *)p_result : &_sa_buf;
2098                 /* default is either the supplied value or the result itself */
2099                 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
2100                         va_arg(ap, struct sockaddr_in *) : sa;
2101                 struct hostent *hp;
2102                 struct ast_hostent ahp;
2103
2104                 bzero(&_sa_buf, sizeof(_sa_buf)); /* clear buffer */
2105                 /* duplicate the string to strip away the :port */
2106                 port = ast_strdupa(arg);
2107                 buf = strsep(&port, ":");
2108                 sa->sin_family = AF_INET;       /* assign family */
2109                 /*
2110                  * honor the ports flag setting, assign default value
2111                  * in case of errors or field unset.
2112                  */
2113                 flags &= PARSE_PORT_MASK; /* the only flags left to process */
2114                 if (port) {
2115                         if (flags == PARSE_PORT_FORBID) {
2116                                 error = 1;      /* port was forbidden */
2117                                 sa->sin_port = def->sin_port;
2118                         } else if (flags == PARSE_PORT_IGNORE)
2119                                 sa->sin_port = def->sin_port;
2120                         else /* accept or require */
2121                                 sa->sin_port = htons(strtol(port, NULL, 0));
2122                 } else {
2123                         sa->sin_port = def->sin_port;
2124                         if (flags == PARSE_PORT_REQUIRE)
2125                                 error = 1;
2126                 }
2127                 /* Now deal with host part, even if we have errors before. */
2128                 hp = ast_gethostbyname(buf, &ahp);
2129                 if (hp) /* resolved successfully */
2130                         memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
2131                 else {
2132                         error = 1;
2133                         sa->sin_addr = def->sin_addr;
2134                 }
2135                 ast_debug(3,
2136                         "extract inaddr from [%s] gives [%s:%d](%d)\n",
2137                         arg, ast_inet_ntoa(sa->sin_addr),
2138                         ntohs(sa->sin_port), error);
2139                 break;
2140             }
2141         }
2142         va_end(ap);
2143         return error;
2144 }
2145
2146 static int config_command(int fd, int argc, char **argv) 
2147 {
2148         struct ast_config_engine *eng;
2149         struct ast_config_map *map;
2150         
2151         ast_mutex_lock(&config_lock);
2152
2153         ast_cli(fd, "\n\n");
2154         for (eng = config_engine_list; eng; eng = eng->next) {
2155                 ast_cli(fd, "\nConfig Engine: %s\n", eng->name);
2156                 for (map = config_maps; map; map = map->next)
2157                         if (!strcasecmp(map->driver, eng->name)) {
2158                                 ast_cli(fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
2159                                         map->table ? map->table : map->name);
2160                         }
2161         }
2162         ast_cli(fd,"\n\n");
2163         
2164         ast_mutex_unlock(&config_lock);
2165
2166         return 0;
2167 }
2168
2169 static char show_config_help[] =
2170         "Usage: core show config mappings\n"
2171         "       Shows the filenames to config engines.\n";
2172
2173 static struct ast_cli_entry cli_config[] = {
2174         { { "core", "show", "config", "mappings", NULL },
2175         config_command, "Display config mappings (file names to config engines)",
2176         show_config_help },
2177 };
2178
2179 int register_config_cli() 
2180 {
2181         ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));
2182         return 0;
2183 }