phase 1 of header include cleanup (bug #4067)
[asterisk/asterisk.git] / config.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Configuration File Parser
5  * 
6  * Copyright (C) 1999 - 2005, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <time.h>
20 #define AST_INCLUDE_GLOB 1
21 #ifdef AST_INCLUDE_GLOB
22 #ifdef __OSX__
23 #define GLOB_ABORTED GLOB_ABEND
24 #endif
25 # include <glob.h>
26 #endif
27
28 #include "asterisk/config.h"
29 #include "asterisk/cli.h"
30 #include "asterisk/lock.h"
31 #include "asterisk/options.h"
32 #include "asterisk/logger.h"
33 #include "asterisk/utils.h"
34 #include "asterisk/channel.h"
35 #include "asterisk/app.h"
36 #include "asterisk.h"
37 #include "astconf.h"
38
39 #define MAX_NESTED_COMMENTS 128
40 #define COMMENT_START ";--"
41 #define COMMENT_END "--;"
42 #define COMMENT_META ';'
43 #define COMMENT_TAG '-'
44
45 static char *extconfig_conf = "extconfig.conf";
46
47 static struct ast_config_map {
48         struct ast_config_map *next;
49         char *name;
50         char *driver;
51         char *database;
52         char *table;
53         char stuff[0];
54 } *config_maps = NULL;
55
56 AST_MUTEX_DEFINE_STATIC(config_lock);
57 static struct ast_config_engine *config_engine_list;
58
59 #define MAX_INCLUDE_LEVEL 10
60
61 struct ast_comment {
62         struct ast_comment *next;
63         char cmt[0];
64 };
65
66 struct ast_category {
67         char name[80];
68         int ignored;                    /* do not let user of the config see this category */
69         struct ast_variable *root;
70         struct ast_variable *last;
71         struct ast_category *next;
72 };
73
74 struct ast_config {
75         struct ast_category *root;
76         struct ast_category *last;
77         struct ast_category *current;
78         struct ast_category *last_browse;               /* used to cache the last category supplied via category_browse */
79         int include_level;
80         int max_include_level;
81 };
82
83 int ast_true(const char *s)
84 {
85         if (!s)
86                 return 0;
87         /* Determine if this is a true value */
88         if (!strcasecmp(s, "yes") ||
89             !strcasecmp(s, "true") ||
90             !strcasecmp(s, "y") ||
91             !strcasecmp(s, "t") ||
92             !strcasecmp(s, "1") ||
93             !strcasecmp(s, "on"))
94                 return -1;
95         return 0;
96 }
97
98 int ast_false(const char *s)
99 {
100         if (!s)
101                 return 0;
102         /* Determine if this is a false value */
103         if (!strcasecmp(s, "no") ||
104             !strcasecmp(s, "false") ||
105             !strcasecmp(s, "n") ||
106             !strcasecmp(s, "f") ||
107             !strcasecmp(s, "0") ||
108             !strcasecmp(s, "off"))
109                 return -1;
110         return 0;
111 }
112
113 struct ast_variable *ast_variable_new(const char *name, const char *value) 
114 {
115         struct ast_variable *variable;
116
117         int length = strlen(name) + strlen(value) + 2 + sizeof(struct ast_variable);
118         variable = malloc(length);
119         if (variable) {
120                 memset(variable, 0, length);
121                 variable->name = variable->stuff;
122                 variable->value = variable->stuff + strlen(name) + 1;           
123                 strcpy(variable->name,name);
124                 strcpy(variable->value,value);
125         }
126
127         return variable;
128 }
129
130 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
131 {
132         if (category->last)
133                 category->last->next = variable;
134         else
135                 category->root = variable;
136         category->last = variable;
137 }
138
139 void ast_variables_destroy(struct ast_variable *v)
140 {
141         struct ast_variable *vn;
142
143         while(v) {
144                 vn = v;
145                 v = v->next;
146                 free(vn);
147         }
148 }
149
150 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
151 {
152         struct ast_category *cat = NULL;
153
154         if (category && config->last_browse && (config->last_browse->name == category))
155                 cat = config->last_browse;
156         else
157                 cat = ast_category_get(config, category);
158
159         if (cat)
160                 return cat->root;
161         else
162                 return NULL;
163 }
164
165 char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
166 {
167         struct ast_variable *v;
168
169         if (category) {
170                 for (v = ast_variable_browse(config, category); v; v = v->next)
171                         if (variable == v->name)
172                                 return v->value;
173                 for (v = ast_variable_browse(config, category); v; v = v->next)
174                         if (!strcasecmp(variable, v->name))
175                                 return v->value;
176         } else {
177                 struct ast_category *cat;
178
179                 for (cat = config->root; cat; cat = cat->next)
180                         for (v = cat->root; v; v = v->next)
181                                 if (!strcasecmp(variable, v->name))
182                                         return v->value;
183         }
184
185         return NULL;
186 }
187
188 static struct ast_variable *variable_clone(const struct ast_variable *old)
189 {
190         struct ast_variable *new = ast_variable_new(old->name, old->value);
191
192         if (new) {
193                 new->lineno = old->lineno;
194                 new->object = old->object;
195                 new->blanklines = old->blanklines;
196                 /* TODO: clone comments? */
197         }
198
199         return new;
200 }
201  
202 static void move_variables(struct ast_category *old, struct ast_category *new)
203 {
204         struct ast_variable *var;
205         struct ast_variable *next;
206
207         next = old->root;
208         old->root = NULL;
209         for (var = next; var; var = next) {
210                 next = var->next;
211                 var->next = NULL;
212                 ast_variable_append(new, var);
213         }
214 }
215
216 struct ast_category *ast_category_new(const char *name) 
217 {
218         struct ast_category *category;
219
220         category = malloc(sizeof(struct ast_category));
221         if (category) {
222                 memset(category, 0, sizeof(struct ast_category));
223                 strncpy(category->name, name, sizeof(category->name) - 1);
224         }
225
226         return category;
227 }
228
229 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
230 {
231         struct ast_category *cat;
232
233         for (cat = config->root; cat; cat = cat->next) {
234                 if (cat->name == category_name && (ignored || !cat->ignored))
235                         return cat;
236         }
237
238         for (cat = config->root; cat; cat = cat->next) {
239                 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
240                         return cat;
241         }
242
243         return NULL;
244 }
245
246 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
247 {
248         return category_get(config, category_name, 0);
249 }
250
251 int ast_category_exist(const struct ast_config *config, const char *category_name)
252 {
253         return !!ast_category_get(config, category_name);
254 }
255
256 void ast_category_append(struct ast_config *config, struct ast_category *category)
257 {
258         if (config->last)
259                 config->last->next = category;
260         else
261                 config->root = category;
262         config->last = category;
263         config->current = category;
264 }
265
266 void ast_category_destroy(struct ast_category *cat)
267 {
268         ast_variables_destroy(cat->root);
269         free(cat);
270 }
271
272 static struct ast_category *next_available_category(struct ast_category *cat)
273 {
274         for (; cat && cat->ignored; cat = cat->next);
275
276         return cat;
277 }
278
279 char *ast_category_browse(struct ast_config *config, const char *prev)
280 {       
281         struct ast_category *cat = NULL;
282
283         if (prev && config->last_browse && (config->last_browse->name == prev))
284                 cat = config->last_browse->next;
285         else if (!prev && config->root)
286                         cat = config->root;
287         else if (prev) {
288                 for (cat = config->root; cat; cat = cat->next) {
289                         if (cat->name == prev) {
290                                 cat = cat->next;
291                                 break;
292                         }
293                 }
294                 if (!cat) {
295                         for (cat = config->root; cat; cat = cat->next) {
296                                 if (!strcasecmp(cat->name, prev)) {
297                                         cat = cat->next;
298                                         break;
299                                 }
300                         }
301                 }
302         }
303         
304         if (cat)
305                 cat = next_available_category(cat);
306
307         config->last_browse = cat;
308         if (cat)
309                 return cat->name;
310         else
311                 return NULL;
312 }
313
314 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
315 {
316         struct ast_variable *v;
317
318         v = cat->root;
319         cat->root = NULL;
320
321         return v;
322 }
323
324 void ast_category_rename(struct ast_category *cat, const char *name)
325 {
326         strncpy(cat->name, name, sizeof(cat->name) - 1);
327 }
328
329 static void inherit_category(struct ast_category *new, const struct ast_category *base)
330 {
331         struct ast_variable *var;
332
333         for (var = base->root; var; var = var->next) {
334                 struct ast_variable *v;
335                 
336                 v = variable_clone(var);
337                 if (v)
338                         ast_variable_append(new, v);
339         }
340 }
341
342 struct ast_config *ast_config_new(void) 
343 {
344         struct ast_config *config;
345
346         config = malloc(sizeof(*config));
347         if (config) {
348                 memset(config, 0, sizeof(*config));
349                 config->max_include_level = MAX_INCLUDE_LEVEL;
350         }
351
352         return config;
353 }
354
355 void ast_config_destroy(struct ast_config *cfg)
356 {
357         struct ast_category *cat, *catn;
358
359         if (!cfg)
360                 return;
361
362         cat = cfg->root;
363         while(cat) {
364                 ast_variables_destroy(cat->root);
365                 catn = cat;
366                 cat = cat->next;
367                 free(catn);
368         }
369         free(cfg);
370 }
371
372 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
373 {
374         return cfg->current;
375 }
376
377 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
378 {
379         /* cast below is just to silence compiler warning about dropping "const" */
380         cfg->current = (struct ast_category *) cat;
381 }
382
383 static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile)
384 {
385         char *c;
386         char *cur = buf;
387         struct ast_variable *v;
388         char cmd[512], exec_file[512];
389         int object, do_exec, do_include;
390
391         /* Actually parse the entry */
392         if (cur[0] == '[') {
393                 struct ast_category *newcat = NULL;
394                 char *catname;
395
396                 /* A category header */
397                 c = strchr(cur, ']');
398                 if (!c) {
399                         ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
400                         return -1;
401                 }
402                 *c++ = '\0';
403                 cur++;
404                 if (*c++ != '(')
405                         c = NULL;
406                 catname = cur;
407                 *cat = newcat = ast_category_new(catname);
408                 if (!newcat) {
409                         ast_log(LOG_WARNING, "Out of memory, line %d of %s\n", lineno, configfile);
410                         return -1;
411                 }
412                 /* If there are options or categories to inherit from, process them now */
413                 if (c) {
414                         if (!(cur = strchr(c, ')'))) {
415                                 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
416                                 return -1;
417                         }
418                         *cur = '\0';
419                         while ((cur = strsep(&c, ","))) {
420                                 if (!strcasecmp(cur, "!")) {
421                                         (*cat)->ignored = 1;
422                                 } else if (!strcasecmp(cur, "+")) {
423                                         *cat = category_get(cfg, catname, 1);
424                                         if (!*cat) {
425                                                 ast_destroy(cfg);
426                                                 if (newcat)
427                                                         ast_category_destroy(newcat);
428                                                 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
429                                                 return -1;
430                                         }
431                                         if (newcat) {
432                                                 move_variables(newcat, *cat);
433                                                 ast_category_destroy(newcat);
434                                                 newcat = NULL;
435                                         }
436                                 } else {
437                                         struct ast_category *base;
438                                 
439                                         base = category_get(cfg, cur, 1);
440                                         if (!base) {
441                                                 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
442                                                 return -1;
443                                         }
444                                         inherit_category(*cat, base);
445                                 }
446                         }
447                 }
448                 if (newcat)
449                         ast_category_append(cfg, *cat);
450         } else if (cur[0] == '#') {
451                 /* A directive */
452                 cur++;
453                 c = cur;
454                 while(*c && (*c > 32)) c++;
455                 if (*c) {
456                         *c = '\0';
457                         c++;
458                         /* Find real argument */
459                         while(*c  && (*c < 33)) c++;
460                         if (!*c)
461                                 c = NULL;
462                 } else 
463                         c = NULL;
464                 do_include = !strcasecmp(cur, "include");
465                 if(!do_include)
466                         do_exec = !strcasecmp(cur, "exec");
467                 else
468                         do_exec = 0;
469                 if (do_exec && !option_exec_includes) {
470                         ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
471                         do_exec = 0;
472                 }
473                 if (do_include || do_exec) {
474                         if (c) {
475                                 /* Strip off leading and trailing "'s and <>'s */
476                                 while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
477                                 /* Get rid of leading mess */
478                                 cur = c;
479                                 while (!ast_strlen_zero(cur)) {
480                                         c = cur + strlen(cur) - 1;
481                                         if ((*c == '>') || (*c == '<') || (*c == '\"'))
482                                                 *c = '\0';
483                                         else
484                                                 break;
485                                 }
486                                 /* #exec </path/to/executable>
487                                    We create a tmp file, then we #include it, then we delete it. */
488                                 if (do_exec) { 
489                                         snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%ld.%ld", time(NULL), pthread_self());
490                                         snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
491                                         ast_safe_system(cmd);
492                                         cur = exec_file;
493                                 } else
494                                         exec_file[0] = '\0';
495                                 /* A #include */
496                                 do_include = ast_config_internal_load(cur, cfg) ? 1 : 0;
497                                 if(!ast_strlen_zero(exec_file))
498                                         unlink(exec_file);
499                                 if(!do_include)
500                                         return -1;
501
502                         } else {
503                                 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", 
504                                                 do_exec ? "exec" : "include",
505                                                 do_exec ? "/path/to/executable" : "filename",
506                                                 lineno,
507                                                 configfile);
508                         }
509                 }
510                 else 
511                         ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
512         } else {
513                 /* Just a line (variable = value) */
514                 if (!*cat) {
515                         ast_log(LOG_WARNING,
516                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
517                         return -1;
518                 }
519                 c = strchr(cur, '=');
520                 if (c) {
521                         *c = 0;
522                         c++;
523                         /* Ignore > in => */
524                         if (*c== '>') {
525                                 object = 1;
526                                 c++;
527                         } else
528                                 object = 0;
529                         v = ast_variable_new(ast_strip(cur), ast_strip(c));
530                         if (v) {
531                                 v->lineno = lineno;
532                                 v->object = object;
533                                 /* Put and reset comments */
534                                 v->blanklines = 0;
535                                 ast_variable_append(*cat, v);
536                         } else {
537                                 ast_log(LOG_WARNING, "Out of memory, line %d\n", lineno);
538                                 return -1;
539                         }
540                 } else {
541                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
542                 }
543
544         }
545         return 0;
546 }
547
548 static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg)
549 {
550         char fn[256];
551         char buf[8192];
552         char *new_buf, *comment_p, *process_buf;
553         FILE *f;
554         int lineno=0;
555         int comment = 0, nest[MAX_NESTED_COMMENTS];
556         struct ast_category *cat = NULL;
557         
558         cat = ast_config_get_current_category(cfg);
559
560         if (filename[0] == '/') {
561                 strncpy(fn, filename, sizeof(fn)-1);
562         } else {
563                 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, filename);
564         }
565
566 #ifdef AST_INCLUDE_GLOB
567         {
568                 int glob_ret;
569                 glob_t globbuf;
570                 globbuf.gl_offs = 0;    /* initialize it to silence gcc */
571 #ifdef SOLARIS
572                 glob_ret = glob(fn, GLOB_NOCHECK, NULL, &globbuf);
573 #else
574                 glob_ret = glob(fn, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
575 #endif
576                 if (glob_ret == GLOB_NOSPACE)
577                         ast_log(LOG_WARNING,
578                                 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
579                 else if (glob_ret  == GLOB_ABORTED)
580                         ast_log(LOG_WARNING,
581                                 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
582                 else  {
583                         /* loop over expanded files */
584                         int i;
585                         for (i=0; i<globbuf.gl_pathc; i++) {
586                                 strncpy(fn, globbuf.gl_pathv[i], sizeof(fn)-1);
587 #endif
588         if ((option_verbose > 1) && !option_debug) {
589                 ast_verbose(  VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
590                 fflush(stdout);
591         }
592         if ((f = fopen(fn, "r"))) {
593                 if (option_debug)
594                         ast_log(LOG_DEBUG, "Parsing %s\n", fn);
595                 else if (option_verbose > 1)
596                         ast_verbose("Found\n");
597                 while(!feof(f)) {
598                         lineno++;
599                         if (fgets(buf, sizeof(buf), f)) {
600                                 new_buf = buf;
601                                 if (comment)
602                                         process_buf = NULL;
603                                 else
604                                         process_buf = buf;
605                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
606                                         if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
607                                                 /* Yuck, gotta memmove */
608                                                 memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
609                                                 new_buf = comment_p;
610                                         } else if(comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
611                                                 /* Meta-Comment start detected ";--" */
612                                                 if (comment < MAX_NESTED_COMMENTS) {
613                                                         *comment_p = '\0';
614                                                         new_buf = comment_p + 3;
615                                                         comment++;
616                                                         nest[comment-1] = lineno;
617                                                 } else {
618                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
619                                                 }
620                                         } else if ((comment_p >= new_buf + 2) &&
621                                                    (*(comment_p - 1) == COMMENT_TAG) &&
622                                                    (*(comment_p - 2) == COMMENT_TAG)) {
623                                                 /* Meta-Comment end detected */
624                                                 comment--;
625                                                 new_buf = comment_p + 1;
626                                                 if (!comment) {
627                                                         /* Back to non-comment now */
628                                                         if (process_buf) {
629                                                                 /* Actually have to move what's left over the top, then continue */
630                                                                 char *oldptr;
631                                                                 oldptr = process_buf + strlen(process_buf);
632                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
633                                                                 new_buf = oldptr;
634                                                         } else
635                                                                 process_buf = new_buf;
636                                                 }
637                                         } else {
638                                                 if (!comment) {
639                                                         /* If ; is found, and we are not nested in a comment, 
640                                                            we immediately stop all comment processing */
641                                                         *comment_p = '\0'; 
642                                                         new_buf = comment_p;
643                                                 } else
644                                                         new_buf = comment_p + 1;
645                                         }
646                                 }
647                                 if (process_buf) {
648                                         char *buf = ast_strip(process_buf);
649                                         if (!ast_strlen_zero(buf))
650                                                 if (process_text_line(cfg, &cat, buf, lineno, filename)) {
651                                                         cfg = NULL;
652                                                         break;
653                                                 }
654                                 }
655                         }
656                 }
657                 fclose(f);              
658         } else { /* can't open file */
659                 if (option_debug)
660                         ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
661                 else if (option_verbose > 1)
662                         ast_verbose( "Not found (%s)\n", strerror(errno));
663         }
664         if (comment) {
665                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment]);
666         }
667 #ifdef AST_INCLUDE_GLOB
668                                         if (!cfg)
669                                                 break;
670                                 }
671                                 globfree(&globbuf);
672                         }
673                 }
674 #endif
675
676         return cfg;
677 }
678
679 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
680 {
681         FILE *f;
682         char fn[256];
683         char date[256]="";
684         time_t t;
685         struct ast_variable *var;
686         struct ast_category *cat;
687         int blanklines = 0;
688
689         if (configfile[0] == '/') {
690                 strncpy(fn, configfile, sizeof(fn)-1);
691         } else {
692                 snprintf(fn, sizeof(fn), "%s/%s", AST_CONFIG_DIR, configfile);
693         }
694         time(&t);
695         strncpy(date, ctime(&t), sizeof(date) - 1);
696         if ((f = fopen(fn, "w"))) {
697                 if ((option_verbose > 1) && !option_debug)
698                         ast_verbose(  VERBOSE_PREFIX_2 "Saving '%s': ", fn);
699                 fprintf(f, ";!\n");
700                 fprintf(f, ";! Automatically generated configuration file\n");
701                 fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
702                 fprintf(f, ";! Generator: %s\n", generator);
703                 fprintf(f, ";! Creation Date: %s", date);
704                 fprintf(f, ";!\n");
705                 cat = cfg->root;
706                 while(cat) {
707                         /* Dump section with any appropriate comment */
708                         fprintf(f, "[%s]\n", cat->name);
709                         var = cat->root;
710                         while(var) {
711                                 if (var->sameline) 
712                                         fprintf(f, "%s %s %s  ; %s\n", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
713                                 else    
714                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
715                                 if (var->blanklines) {
716                                         blanklines = var->blanklines;
717                                         while (blanklines--)
718                                                 fprintf(f, "\n");
719                                 }
720                                         
721                                 var = var->next;
722                         }
723 #if 0
724                         /* Put an empty line */
725                         fprintf(f, "\n");
726 #endif
727                         cat = cat->next;
728                 }
729         } else {
730                 if (option_debug)
731                         printf("Unable to open for writing: %s\n", fn);
732                 else if (option_verbose > 1)
733                         printf( "Unable to write (%s)", strerror(errno));
734                 return -1;
735         }
736         fclose(f);
737         return 0;
738 }
739
740 static void clear_config_maps(void) 
741 {
742         struct ast_config_map *map;
743
744         ast_mutex_lock(&config_lock);
745
746         while (config_maps) {
747                 map = config_maps;
748                 config_maps = config_maps->next;
749                 free(map);
750         }
751                 
752         ast_mutex_unlock(&config_lock);
753 }
754
755 static int append_mapping(char *name, char *driver, char *database, char *table)
756 {
757         struct ast_config_map *map;
758         int length;
759
760         length = sizeof(*map);
761         length += strlen(name) + 1;
762         length += strlen(driver) + 1;
763         length += strlen(database) + 1;
764         if (table)
765                 length += strlen(table) + 1;
766         map = malloc(length);
767
768         if (!map)
769                 return -1;
770
771         memset(map, 0, length);
772         map->name = map->stuff;
773         strcpy(map->name, name);
774         map->driver = map->name + strlen(map->name) + 1;
775         strcpy(map->driver, driver);
776         map->database = map->driver + strlen(map->driver) + 1;
777         strcpy(map->database, database);
778         if (table) {
779                 map->table = map->database + strlen(map->database) + 1;
780                 strcpy(map->table, table);
781         }
782         map->next = config_maps;
783
784         if (option_verbose > 1)
785                 ast_verbose(VERBOSE_PREFIX_2 "Binding %s to %s/%s/%s\n",
786                             map->name, map->driver, map->database, map->table ? map->table : map->name);
787
788         config_maps = map;
789         return 0;
790 }
791
792 void read_config_maps(void) 
793 {
794         struct ast_config *config;
795         struct ast_variable *v;
796         char *driver, *table, *database, *stringp;
797
798         clear_config_maps();
799
800         config = ast_config_new();
801         config->max_include_level = 1;
802         config = ast_config_internal_load(extconfig_conf, config);
803         if (!config)
804                 return;
805
806         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
807                 stringp = v->value;
808                 driver = strsep(&stringp, ",");
809                 database = strsep(&stringp, ",");
810                 table = strsep(&stringp, ",");
811                         
812                 if (!strcmp(v->name, extconfig_conf) || !strcmp(v->name, "asterisk.conf")) {
813                         ast_log(LOG_WARNING, "Cannot bind asterisk.conf or extconfig.conf!\n");
814                         continue;
815                 }
816
817                 if (!driver || !database)
818                         continue;
819                 if (!strcasecmp(v->name, "sipfriends")) {
820                         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");
821                         append_mapping("sipusers", driver, database, table ? table : "sipfriends");
822                         append_mapping("sippeers", driver, database, table ? table : "sipfriends");
823                 } else if (!strcasecmp(v->name, "iaxfriends")) {
824                         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");
825                         append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
826                         append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
827                 } else 
828                         append_mapping(v->name, driver, database, table);
829         }
830                 
831         ast_config_destroy(config);
832 }
833
834 int ast_config_engine_register(struct ast_config_engine *new) 
835 {
836         struct ast_config_engine *ptr;
837
838         ast_mutex_lock(&config_lock);
839
840         if (!config_engine_list) {
841                 config_engine_list = new;
842         } else {
843                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
844                 ptr->next = new;
845         }
846
847         ast_mutex_unlock(&config_lock);
848         ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
849
850         return 1;
851 }
852
853 int ast_config_engine_deregister(struct ast_config_engine *del) 
854 {
855         struct ast_config_engine *ptr, *last=NULL;
856
857         ast_mutex_lock(&config_lock);
858
859         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
860                 if (ptr == del) {
861                         if (last)
862                                 last->next = ptr->next;
863                         else
864                                 config_engine_list = ptr->next;
865                         break;
866                 }
867                 last = ptr;
868         }
869
870         ast_mutex_unlock(&config_lock);
871
872         return 0;
873 }
874
875 static struct ast_config_engine *find_engine(const char *filename, char *database, int dbsiz, char *table, int tabsiz) 
876 {
877         struct ast_config_engine *eng, *ret=NULL;
878         struct ast_config_map *map;
879
880         ast_mutex_lock(&config_lock);
881
882         map = config_maps;
883         while (map) {
884                 if (!strcasecmp(filename, map->name)) {
885                         strncpy(database, map->database, dbsiz-1);
886                         if (map->table)
887                                 strncpy(table, map->table, tabsiz-1);
888                         else
889                                 strncpy(table, filename, tabsiz-1);
890                         break;
891                 }
892                 map = map->next;
893         }
894         if (map) {
895                 for (eng = config_engine_list; eng; eng = eng->next) {
896                         if (!strcmp(eng->name, map->driver)) {
897                                 ret = eng;
898                                 break;
899                         }
900                 }
901         }
902         ast_mutex_unlock(&config_lock);
903
904         return ret;
905 }
906
907 static struct ast_config_engine text_file_engine = {
908         .name = "text",
909         .load_func = config_text_file_load,
910 };
911
912 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg)
913 {
914         char db[256];
915         char table[256];
916         struct ast_config_engine *loader = &text_file_engine;
917         struct ast_config *result;
918
919         if (cfg->include_level == cfg->max_include_level) {
920                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
921                 return NULL;
922         }
923
924         cfg->include_level++;
925
926         if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
927                 struct ast_config_engine *eng;
928
929                 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
930
931
932                 if (eng && eng->load_func) {
933                         loader = eng;
934                 } else {
935                         eng = find_engine("global", db, sizeof(db), table, sizeof(table));
936                         if (eng && eng->load_func)
937                                 loader = eng;
938                 }
939         }
940
941         result = loader->load_func(db, table, filename, cfg);
942
943         if (result)
944                 result->include_level--;
945
946         return result;
947 }
948
949 struct ast_config *ast_config_load(const char *filename)
950 {
951         struct ast_config *cfg;
952         struct ast_config *result;
953
954         cfg = ast_config_new();
955         if (!cfg)
956                 return NULL;
957
958         result = ast_config_internal_load(filename, cfg);
959         if (!result)
960                 ast_config_destroy(cfg);
961
962         return result;
963 }
964
965 struct ast_variable *ast_load_realtime(const char *family, ...)
966 {
967         struct ast_config_engine *eng;
968         char db[256]="";
969         char table[256]="";
970         struct ast_variable *res=NULL;
971         va_list ap;
972
973         va_start(ap, family);
974         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
975         if (eng && eng->realtime_func) 
976                 res = eng->realtime_func(db, table, ap);
977         va_end(ap);
978
979         return res;
980 }
981
982 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
983 {
984         struct ast_config_engine *eng;
985         char db[256]="";
986         char table[256]="";
987         struct ast_config *res=NULL;
988         va_list ap;
989
990         va_start(ap, family);
991         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
992         if (eng && eng->realtime_multi_func) 
993                 res = eng->realtime_multi_func(db, table, ap);
994         va_end(ap);
995
996         return res;
997 }
998
999 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
1000 {
1001         struct ast_config_engine *eng;
1002         int res = -1;
1003         char db[256]="";
1004         char table[256]="";
1005         va_list ap;
1006
1007         va_start(ap, lookup);
1008         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1009         if (eng && eng->update_func) 
1010                 res = eng->update_func(db, table, keyfield, lookup, ap);
1011         va_end(ap);
1012
1013         return res;
1014 }
1015
1016 static int config_command(int fd, int argc, char **argv) 
1017 {
1018         struct ast_config_engine *eng;
1019         struct ast_config_map *map;
1020         
1021         ast_mutex_lock(&config_lock);
1022
1023         ast_cli(fd, "\n\n");
1024         for (eng = config_engine_list; eng; eng = eng->next) {
1025                 ast_cli(fd, "\nConfig Engine: %s\n", eng->name);
1026                 for (map = config_maps; map; map = map->next)
1027                         if (!strcasecmp(map->driver, eng->name)) {
1028                                 ast_cli(fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
1029                                         map->table ? map->table : map->name);
1030                                 break;
1031                         }
1032         }
1033         ast_cli(fd,"\n\n");
1034         
1035         ast_mutex_unlock(&config_lock);
1036
1037         return 0;
1038 }
1039
1040 static char show_config_help[] =
1041         "Usage: show config mappings\n"
1042         "       Shows the filenames to config engines.\n";
1043
1044 static struct ast_cli_entry config_command_struct = {
1045         { "show", "config", "mappings", NULL }, config_command, "Show Config mappings (file names to config engines)", show_config_help, NULL
1046 };
1047
1048 int register_config_cli() 
1049 {
1050         return ast_cli_register(&config_command_struct);
1051 }