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