20f99f7230ec358c0ba4345b96a679a49e477510
[asterisk/asterisk.git] / config.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Configuration File Parser
5  * 
6  * Copyright (C) 1999-2004, 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 # include <glob.h>
23 #endif
24 #include <asterisk/config.h>
25 #include <asterisk/config_pvt.h>
26 #include <asterisk/cli.h>
27 #include <asterisk/lock.h>
28 #include <asterisk/options.h>
29 #include <asterisk/logger.h>
30 #include <asterisk/utils.h>
31 #include "asterisk.h"
32 #include "astconf.h"
33
34 #define MAX_NESTED_COMMENTS 128
35 #define COMMENT_START ";--"
36 #define COMMENT_END "--;"
37 #define COMMENT_META ';'
38 #define COMMENT_TAG '-'
39
40 static int ast_cust_config=0;
41 struct ast_config *(*global_load_func)(const char *dbname, const char *table, const char *, struct ast_config *,struct ast_category **,struct ast_variable **,int);
42
43 static struct ast_config_map {
44         struct ast_config_map *next;
45         char *name;
46         char *driver;
47         char *database;
48         char *table;
49         char stuff[0];
50 } *maps = NULL;
51
52 AST_MUTEX_DEFINE_STATIC(ast_cust_config_lock);
53 static struct ast_config_reg *ast_cust_config_list;
54 static char *config_conf_file = "extconfig.conf";
55
56 void ast_destroy_realtime(struct ast_variable *v)
57 {
58         struct ast_variable *vn;
59         while(v) {
60                 vn = v;
61                 v = v->next;
62                 free(vn);
63         }
64 }
65
66 void ast_category_destroy(struct ast_category *cat)
67 {
68         ast_destroy_realtime(cat->root);
69         free(cat);
70 }
71
72 void ast_destroy(struct ast_config *ast)
73 {
74         struct ast_category *cat, *catn;
75
76         if (!ast)
77                 return;
78
79         cat = ast->root;
80         while(cat) {
81                 ast_destroy_realtime(cat->root);
82                 catn = cat;
83                 cat = cat->next;
84                 free(catn);
85         }
86         free(ast);
87 }
88
89 int ast_true(const char *s)
90 {
91         if (!s)
92                 return 0;
93         /* Determine if this is a true value */
94         if (!strcasecmp(s, "yes") ||
95             !strcasecmp(s, "true") ||
96                 !strcasecmp(s, "y") ||
97                 !strcasecmp(s, "t") ||
98                 !strcasecmp(s, "1") ||
99                 !strcasecmp(s, "on"))
100                         return -1;
101         return 0;
102 }
103
104 int ast_false(const char *s)
105 {
106         if (!s)
107                 return 0;
108         /* Determine if this is a false value */
109         if (!strcasecmp(s, "no") ||
110             !strcasecmp(s, "false") ||
111                 !strcasecmp(s, "n") ||
112                 !strcasecmp(s, "f") ||
113                 !strcasecmp(s, "0") ||
114                 !strcasecmp(s, "off"))
115                         return -1;
116         return 0;
117 }
118
119 struct ast_variable *ast_variable_browse(struct ast_config *config, char *category)
120 {
121         struct ast_category *cat;
122         cat = config->root;
123         while(cat) {
124                 if (cat->name == category)
125                         return cat->root;
126                 cat = cat->next;
127         }
128         cat = config->root;
129         while(cat) {
130                 if (!strcasecmp(cat->name, category))
131                         return cat->root;
132                 cat = cat->next;
133         }
134         return NULL;
135 }
136
137 char *ast_variable_retrieve(struct ast_config *config, char *category, char *value)
138 {
139         struct ast_variable *v;
140         if (category) {
141                 v = ast_variable_browse(config, category);
142                 while (v) {
143                         if (value == v->name)
144                                 return v->value;
145                         v=v->next;
146                 }
147                 v = ast_variable_browse(config, category);
148                 while (v) {
149                         if (!strcasecmp(value, v->name))
150                                 return v->value;
151                         v=v->next;
152                 }
153         } else {
154                 struct ast_category *cat;
155                 cat = config->root;
156                 while(cat) {
157                         v = cat->root;
158                         while (v) {
159                                 if (!strcasecmp(value, v->name))
160                                         return v->value;
161                                 v=v->next;
162                         }
163                         cat = cat->next;
164                 }
165         }
166         return NULL;
167 }
168
169 int ast_category_exist(struct ast_config *config, char *category_name)
170 {
171         struct ast_category *category = NULL;
172
173         category = config->root;
174
175         while(category) {
176                 if (!strcasecmp(category->name,category_name)) 
177                         return 1;
178                 category = category->next;
179         } 
180
181         return 0;
182 }
183
184
185 static struct ast_config_reg *get_ast_cust_config_keyword(const char *name, char *database, int dbsiz, char *table, int tabsiz) 
186 {
187         struct ast_config_reg *reg,*ret=NULL;
188         struct ast_config_map *map;
189
190         ast_mutex_lock(&ast_cust_config_lock);
191         map = maps;
192         while(map) {
193                 if (!strcasecmp(name, map->name)) {
194                         strncpy(database, map->database, dbsiz - 1);
195                         if (map->table)
196                                 strncpy(table, map->table, tabsiz - 1);
197                         else
198                                 strncpy(table, name, tabsiz - 1);
199                         break;
200                 }
201                 map = map->next;
202         }
203         if (map) {
204                 for (reg=ast_cust_config_list;reg && !ret;reg=reg->next) {
205                         if (!strcmp(reg->name,map->driver))
206                                 ret=reg;
207                 }
208         }
209         ast_mutex_unlock(&ast_cust_config_lock);
210         return ret;
211 }
212
213 void ast_config_destroy_all(void) 
214 {
215         struct ast_config_reg *key;
216         ast_mutex_lock(&ast_cust_config_lock);
217         for (key=ast_cust_config_list;key;key=key->next) {
218                 ast_config_deregister(key);
219         }
220         ast_cust_config_list = NULL;
221         ast_mutex_unlock(&ast_cust_config_lock);
222 }
223
224 static struct ast_config_reg *get_config_registrations(void) 
225 {
226         return ast_cust_config_list;
227 }
228
229
230 static struct ast_config *__ast_load(const char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel);
231
232 static int cfg_process(struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, char *buf, int lineno, const char *configfile, int includelevel )
233 {
234         char *c;
235         char *cur;
236         char *arg=NULL;
237         struct ast_variable *v;
238         int object;
239         cur = ast_strip(buf);
240         if (!ast_strlen_zero(cur)) {
241                 /* Actually parse the entry */
242                 if (cur[0] == '[') {
243                         /* A category header */
244                         c = strchr(cur, ']');
245                         if (c) {
246                                 *c = 0;
247                                 *_tmpc = malloc(sizeof(struct ast_category));
248                                 if (!*_tmpc) {
249                                         ast_destroy(tmp);
250                                         ast_log(LOG_WARNING,
251                                                 "Out of memory, line %d\n", lineno);
252                                         return -1;
253                                 }
254                                 memset(*_tmpc, 0, sizeof(struct ast_category));
255                                 strncpy((*_tmpc)->name, cur+1, sizeof((*_tmpc)->name) - 1);
256                                 (*_tmpc)->root =  NULL;
257                                 if (!tmp->prev)
258                                         tmp->root = *_tmpc;
259                                 else
260                                         tmp->prev->next = *_tmpc;
261
262                                 tmp->prev = *_tmpc;
263                                 *_last =  NULL;
264                         } else {
265                                 ast_log(LOG_WARNING, 
266                                         "parse error: no closing ']', line %d of %s\n", lineno, configfile);
267                         }
268                 } else if (cur[0] == '#') {
269                         /* A directive */
270                         cur++;
271                         c = cur;
272                         while(*c && (*c > 32)) c++;
273                         if (*c) {
274                                 *c = '\0';
275                                 c++;
276                                 /* Find real argument */
277                                 while(*c  && (*c < 33)) c++;
278                                 if (!*c)
279                                         c = NULL;
280                         } else 
281                                 c = NULL;
282                         if (!strcasecmp(cur, "include")) {
283                                 /* A #include */
284                                 if (c) {
285                                         while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
286                                         /* Get rid of leading mess */
287                                         cur = c;
288                                         while (!ast_strlen_zero(cur)) {
289                                                 c = cur + strlen(cur) - 1;
290                                                 if ((*c == '>') || (*c == '<') || (*c == '\"'))
291                                                         *c = '\0';
292                                                 else
293                                                         break;
294                                         }
295                                         
296                                         if((c = strchr(cur,':'))) {
297                                                 *c = '\0';
298                                                 c++;
299                                                 arg = c;
300                                         }
301                                         
302                                         if (includelevel < MAX_INCLUDE_LEVEL) {
303                                                 if(arg && cur) {
304                                                         ast_log(LOG_WARNING, "Including files with explicit config engine no longer permitted.  Please use extconfig.conf to specify all mappings\n");
305                                                 } else {
306                                                         if (!__ast_load(cur, tmp, _tmpc, _last, includelevel + 1))
307                                                                 return -1;
308                                                 }
309                                         } else
310                                                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", includelevel);
311                                 } else
312                                         ast_log(LOG_WARNING, "Directive '#include' needs an argument (filename) at line %d of %s\n", lineno, configfile);
313                                 /* Strip off leading and trailing "'s and <>'s */
314                         }
315                         else 
316                                 ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
317                 } else {
318                         /* Just a line (variable = value) */
319                         if (!*_tmpc) {
320                                 ast_log(LOG_WARNING,
321                                         "parse error: No category context for line %d of %s\n", lineno, configfile);
322                                 ast_destroy(tmp);
323                                 return -1;
324                         }
325                         c = strchr(cur, '=');
326                         if (c) {
327                                 *c = 0;
328                                 c++;
329                                 /* Ignore > in => */
330                                 if (*c== '>') {
331                                         object = 1;
332                                         c++;
333                                 } else
334                                         object = 0;
335                                 v = ast_new_variable(ast_strip(cur), ast_strip(c));
336                                 if (v) {
337                                         v->next = NULL;
338                                         v->lineno = lineno;
339                                         v->object = object;
340                                         /* Put and reset comments */
341                                         v->blanklines = 0;
342                                         if (*_last)
343                                                 (*_last)->next = v;
344                                         else
345                                                 (*_tmpc)->root = v;
346                                         *_last = v;
347                                 } else {
348                                         ast_destroy(tmp);
349                                         ast_log(LOG_WARNING, "Out of memory, line %d\n", lineno);
350                                         return -1;
351                                 }
352                         } else {
353                                 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
354                         }
355                                                                                                                 
356                 }
357         }
358         return 0;
359 }
360
361 int ast_save(char *configfile, struct ast_config *cfg, char *generator)
362 {
363         FILE *f;
364         char fn[256];
365         char date[256]="";
366         time_t t;
367         struct ast_variable *var;
368         struct ast_category *cat;
369         int blanklines = 0;
370         if (configfile[0] == '/') {
371                 strncpy(fn, configfile, sizeof(fn)-1);
372         } else {
373                 snprintf(fn, sizeof(fn), "%s/%s", AST_CONFIG_DIR, configfile);
374         }
375         time(&t);
376         strncpy(date, ctime(&t), sizeof(date) - 1);
377         if ((f = fopen(fn, "w"))) {
378                 if ((option_verbose > 1) && !option_debug)
379                         ast_verbose(  VERBOSE_PREFIX_2 "Saving '%s': ", fn);
380                 fprintf(f, ";!\n");
381                 fprintf(f, ";! Automatically generated configuration file\n");
382                 fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
383                 fprintf(f, ";! Generator: %s\n", generator);
384                 fprintf(f, ";! Creation Date: %s", date);
385                 fprintf(f, ";!\n");
386                 cat = cfg->root;
387                 while(cat) {
388                         /* Dump section with any appropriate comment */
389                         fprintf(f, "[%s]\n", cat->name);
390                         var = cat->root;
391                         while(var) {
392                                 if (var->sameline) 
393                                         fprintf(f, "%s %s %s  ; %s\n", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
394                                 else    
395                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
396                                 if (var->blanklines) {
397                                         blanklines = var->blanklines;
398                                         while (blanklines) {
399                                                 fprintf(f, "\n");
400                                                 blanklines--;
401                                         }
402                                 }
403                                         
404                                 var = var->next;
405                         }
406 #if 0
407                         /* Put an empty line */
408                         fprintf(f, "\n");
409 #endif
410                         cat = cat->next;
411                 }
412         } else {
413                 if (option_debug)
414                         printf("Unable to open for writing: %s\n", fn);
415                 else if (option_verbose > 1)
416                         printf( "Unable to write (%s)", strerror(errno));
417                 return -1;
418         }
419         fclose(f);
420         return 0;
421 }
422
423
424 struct ast_variable *ast_load_realtime(const char *family, ...)
425 {
426         struct ast_config_reg *reg;
427         char db[256]="";
428         char table[256]="";
429         struct ast_variable *res=NULL;
430         va_list ap;
431         va_start(ap, family);
432         reg = get_ast_cust_config_keyword(family, db, sizeof(db), table, sizeof(table));
433         if (reg && reg->realtime_func) 
434                 res = reg->realtime_func(db, table, ap);
435         va_end(ap);
436         return res;
437 }
438
439 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
440 {
441         struct ast_config_reg *reg;
442         char db[256]="";
443         char table[256]="";
444         struct ast_config *res=NULL;
445         va_list ap;
446         va_start(ap, family);
447         reg = get_ast_cust_config_keyword(family, db, sizeof(db), table, sizeof(table));
448         if (reg && reg->realtime_multi_func) 
449                 res = reg->realtime_multi_func(db, table, ap);
450         va_end(ap);
451         return res;
452 }
453
454 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
455 {
456         struct ast_config_reg *reg;
457         int res = -1;
458         char db[256]="";
459         char table[256]="";
460         va_list ap;
461         va_start(ap, lookup);
462         reg = get_ast_cust_config_keyword(family, db, sizeof(db), table, sizeof(table));
463         if (reg && reg->update_func) 
464                 res = reg->update_func(db, table, keyfield, lookup, ap);
465         va_end(ap);
466         return res;
467 }
468
469 static struct ast_config *__ast_load(const char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel)
470 {
471         char fn[256];
472         char buf[8192];
473         char db[256];
474         char table[256];
475         FILE *f;
476         int lineno=0;
477         int master=0;
478         int comment = 0, nest[MAX_NESTED_COMMENTS];
479         
480         struct ast_config_reg *reg=NULL;
481         struct ast_config *(*load_func)(const char *database, const char *table, const char *, struct ast_config *,struct ast_category **,struct ast_variable **,int);
482         char *comment_p, *process_buf, *new_buf=NULL;
483
484         load_func=NULL;
485         if (strcmp(configfile,config_conf_file) && strcmp(configfile,"asterisk.conf") && ast_cust_config_list) {
486                 if (global_load_func) {
487                         load_func = global_load_func;
488                 } else {
489                         reg = get_ast_cust_config_keyword(configfile, db, sizeof(db), table, sizeof(table));
490                         if (reg && reg->static_func) {
491                                 load_func = reg->static_func;
492                         } else {
493                                 reg = get_ast_cust_config_keyword(configfile, db, sizeof(db), table, sizeof(table));
494                                 if (reg && reg->static_func)
495                                         global_load_func = load_func = reg->static_func;
496                         }
497                 }
498
499                 if (load_func) {
500                         ast_log(LOG_NOTICE,"Loading Config %s via %s engine\n",configfile,reg && reg->name ? reg->name : "global");
501                         if((tmp = load_func(db, table, configfile,tmp, _tmpc, _last, includelevel)))
502                                 return tmp;
503                 }
504         }
505
506         if (configfile[0] == '/') {
507                 strncpy(fn, configfile, sizeof(fn)-1);
508         } else {
509                 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, configfile);
510         }
511 #ifdef AST_INCLUDE_GLOB
512         {
513                 int glob_ret;
514                 glob_t globbuf;
515                 globbuf.gl_offs = 0;    /* initialize it to silence gcc */
516 #ifdef SOLARIS
517                 glob_ret = glob(fn, GLOB_NOMAGIC, NULL, &globbuf);
518 #else
519                 glob_ret = glob(fn, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
520 #endif
521                 if (glob_ret == GLOB_NOSPACE)
522                         ast_log(LOG_WARNING,
523                                 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
524                 else if (glob_ret  == GLOB_ABORTED)
525                         ast_log(LOG_WARNING,
526                                 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
527                 else  {
528                         /* loop over expanded files */
529                         int i;
530                         for (i=0; i<globbuf.gl_pathc; i++) {
531                                 strncpy(fn, globbuf.gl_pathv[i], sizeof(fn)-1);
532 #endif
533         if ((option_verbose > 1) && !option_debug) {
534                 ast_verbose(  VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
535                 fflush(stdout);
536         }
537         if ((f = fopen(fn, "r"))) {
538                 if (option_debug)
539                         ast_log(LOG_DEBUG, "Parsing %s\n", fn);
540                 else if (option_verbose > 2)
541                         ast_verbose( "Found\n");
542                 if (!tmp) {
543                         if((tmp = malloc(sizeof(struct ast_config)))) {
544                                 memset(tmp, 0, sizeof(struct ast_config));
545                                 master = 1;
546                         }
547                 }
548                 if (tmp) {
549                         while(!feof(f)) {
550                                 lineno++;
551                                 if (fgets(buf, sizeof(buf), f)) {
552                                         new_buf = buf;
553                                         if (comment)
554                                                 process_buf = NULL;
555                                         else
556                                                 process_buf = buf;
557                                         while ((comment_p = strchr(new_buf, COMMENT_META))) {
558                                                 if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
559                                                         /* Yuck, gotta memmove */
560                                                         memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
561                                                         new_buf = comment_p;
562                                                 } else if(comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
563                                                         /* Meta-Comment start detected ";--" */
564                                                         if (comment < MAX_NESTED_COMMENTS) {
565                                                                 *comment_p = '\0';
566                                                                 new_buf = comment_p + 3;
567                                                                 comment++;
568                                                                 nest[comment-1] = lineno;
569                                                         } else {
570                                                                 ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
571                                                         }
572                                                 } else if ((comment_p >= new_buf + 2) &&
573                                                                    (*(comment_p - 1) == COMMENT_TAG) &&
574                                                                    (*(comment_p - 2) == COMMENT_TAG)) {
575                                                         /* Meta-Comment end detected */
576                                                         comment--;
577                                                         new_buf = comment_p + 1;
578                                                         if (!comment) {
579                                                                 /* Back to non-comment now */
580                                                                 if (process_buf) {
581                                                                         /* Actually have to move what's left over the top, then continue */
582                                                                         char *oldptr;
583                                                                         oldptr = process_buf + strlen(process_buf);
584                                                                         memmove(oldptr, new_buf, strlen(new_buf) + 1);
585                                                                         new_buf = oldptr;
586                                                                 } else
587                                                                         process_buf = new_buf;
588                                                         }
589                                                 } else {
590                                                         if (!comment) {
591                                                                 /* If ; is found, and we are not nested in a comment, 
592                                                                    we immediately stop all comment processing */
593                                                                 *comment_p = '\0'; 
594                                                                 new_buf = comment_p;
595                                                         } else
596                                                                 new_buf = comment_p + 1;
597                                                 }
598                                         }
599                                         if (process_buf && cfg_process(tmp, _tmpc, _last, process_buf, lineno, fn, includelevel)) {
600                                                 tmp = NULL;
601                                                 break;
602                                         }
603                                 }
604                         }
605                 } else 
606                         ast_log(LOG_WARNING, "Out of memory\n");
607                 
608                 fclose(f);              
609         } else { /* can't open file */
610                 if (option_debug)
611                         ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
612                 else if (option_verbose > 2)
613                         ast_verbose( "Not found (%s)\n", strerror(errno));
614         }
615         if (comment) {
616                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment]);
617         }
618 #ifdef AST_INCLUDE_GLOB
619                                         if (!tmp)
620                                                 break;
621                                 }
622                                 globfree(&globbuf);
623                         }
624                 }
625 #endif
626
627         return tmp;
628 }
629
630 int ast_config_register(struct ast_config_reg *new) 
631 {
632         struct ast_config_reg *ptr;
633         ast_mutex_lock(&ast_cust_config_lock);
634         if (!ast_cust_config_list) {
635                 ast_cust_config_list = new;
636         } else {
637                 for(ptr=ast_cust_config_list;ptr->next;ptr=ptr->next);
638                         ptr->next = new;
639         }
640         ast_mutex_unlock(&ast_cust_config_lock);
641         ast_log(LOG_NOTICE,"Registered Config Engine %s\n",new->name);
642         return 1;
643 }
644
645 int ast_config_deregister(struct ast_config_reg *del) 
646 {
647         struct ast_config_reg *ptr=NULL,*last=NULL;
648         ast_mutex_lock(&ast_cust_config_lock);
649         for (ptr=ast_cust_config_list;ptr;ptr=ptr->next) {
650                 if (ptr == del) {
651                         if (last && ptr->next) {
652                                 last->next = ptr->next;
653                         } else if (last && ! ptr->next) {
654                                 last->next = NULL;
655                         } else if (!last && ptr->next) {
656                                 ast_cust_config_list = ptr->next;
657                         } else if (!last && !ptr->next) {
658                                 ast_cust_config_list = NULL;
659                         }
660                 }
661                 last = ptr;
662         }
663         ast_mutex_unlock(&ast_cust_config_lock);
664         return 0;
665 }
666
667 int ast_cust_config_active(void) {
668         return (ast_cust_config >0) ? 1 : 0;
669 }
670
671 struct ast_config *ast_load(char *configfile)
672 {
673         struct ast_category *tmpc=NULL;
674         struct ast_variable *last = NULL;
675
676         return __ast_load(configfile, NULL, &tmpc, &last, 0);
677 }
678
679 void ast_category_append(struct ast_config *config, struct ast_category *cat)
680 {
681         struct ast_category *prev = NULL;
682         cat->next = NULL;
683         if (config->root) {
684                 prev = config->root;
685                 while(prev->next) prev = prev->next;
686                 prev->next = cat;
687         } else
688                 config->root = cat;
689 }
690
691 char *ast_category_browse(struct ast_config *config, char *prev)
692 {       
693         struct ast_category *cat;
694         if (!prev) {
695                 if (config->root)
696                         return config->root->name;
697                 else
698                         return NULL;
699         }
700         cat = config->root;
701         while(cat) {
702                 if (cat->name == prev) {
703                         if (cat->next)
704                                 return cat->next->name;
705                         else
706                                 return NULL;
707                 }
708                 cat = cat->next;
709         }
710         cat = config->root;
711         while(cat) {
712                 if (!strcasecmp(cat->name, prev)) {
713                         if (cat->next)
714                                 return cat->next->name;
715                         else
716                                 return NULL;
717                 }
718                 cat = cat->next;
719         }
720         return NULL;
721 }
722
723
724 struct ast_config *ast_new_config(void) 
725 {
726         struct ast_config *config;
727         config = malloc(sizeof(struct ast_config));
728         memset(config,0,sizeof(struct ast_config));
729         return config;
730 }
731
732
733
734 struct ast_category *ast_new_category(char *name) 
735 {
736         struct ast_category *category;
737         category = malloc(sizeof(struct ast_category));
738         if (category) {
739                 memset(category,0,sizeof(struct ast_category));
740                 strncpy(category->name,name,sizeof(category->name) - 1);
741         }
742         return category;
743 }
744
745
746 struct ast_variable *ast_new_variable(char *name, char *value) 
747 {
748         struct ast_variable *variable;
749         int length = strlen(name) + strlen(value) + 2 + sizeof(struct ast_variable);
750         variable = malloc(length);
751         if (variable) {
752                 memset(variable, 0, length);
753                 variable->name = variable->stuff;
754                 variable->value = variable->stuff + strlen(name) + 1;           
755                 variable->object=0;
756                 strcpy(variable->name,name);
757                 strcpy(variable->value,value);
758         }
759         return variable;
760 }
761
762 int ast_cust_config_register(struct ast_config_reg *new) 
763 {
764         ast_config_register(new);
765         read_ast_cust_config();
766         return 1;
767 }
768 int ast_cust_config_deregister(struct ast_config_reg *new) 
769 {
770         ast_config_deregister(new);
771         read_ast_cust_config();
772         return 1;
773 }
774
775 static void clear_cust_keywords(void) 
776 {
777         struct ast_config_map *map, *prev;
778         ast_mutex_lock(&ast_cust_config_lock);
779         map = maps;
780         while(map) {
781                 prev = map;
782                 map = map->next;
783                 free(prev);
784         }
785         maps = NULL;
786         ast_mutex_unlock(&ast_cust_config_lock);
787 }
788
789 static int config_command(int fd, int argc, char **argv) 
790 {
791         struct ast_config_reg *key;
792         struct ast_config_map *map;
793         
794         ast_cli(fd,"\n\n");
795         ast_mutex_lock(&ast_cust_config_lock);
796         for (key=get_config_registrations();key;key=key->next) {
797                 ast_cli(fd,"\nConfig Engine: %s\n",key->name);
798                 map = maps;
799                 while(map) {
800                         if (!strcasecmp(map->driver, key->name))
801                                 ast_cli(fd,"===> %s (db=%s, table=%s)\n",map->name, map->database, map->table ? map->table : map->name);
802                         map = map->next;
803                 }
804         }
805         ast_mutex_unlock(&ast_cust_config_lock);
806         ast_cli(fd,"\n\n");
807         
808         return 0;
809 }
810
811 static struct ast_cli_entry config_command_struct = {
812   { "show","config","handles", NULL }, config_command,
813   "Show Config Handles", NULL };
814
815 int register_config_cli() 
816 {
817         return ast_cli_register(&config_command_struct);
818 }
819
820 int read_ast_cust_config(void) 
821 {
822         char *cfg = config_conf_file;
823         struct ast_config *config;
824         struct ast_variable *v;
825         struct ast_config_map *map;
826         int length;
827         char *driver, *table, *database, *stringp;
828
829         clear_cust_keywords();
830         config = ast_load(cfg);
831         if (config) {
832                 for (v = ast_variable_browse(config,"settings");v;v=v->next) {
833                         stringp = v->value;
834                         driver = strsep(&stringp, ",");
835                         database = strsep(&stringp, ",");
836                         table = strsep(&stringp, ",");
837                         
838                         if (!strcmp(v->name,config_conf_file) || !strcmp(v->name,"asterisk.conf")) {
839                                 ast_log(LOG_WARNING, "Cannot bind asterisk.conf or extconfig.conf!\n");
840                         } else if (driver && database) {
841                                 length = sizeof(struct ast_config_map);
842                                 length += strlen(v->name) + 1;
843                                 length += strlen(driver) + 1;
844                                 length += strlen(database) + 1;
845                                 if (table)
846                                         length += strlen(table) + 1;
847                                 map = malloc(length);
848                                 if (map) {
849                                         memset(map, 0, length);
850                                         map->name = map->stuff;
851                                         strcpy(map->name, v->name);
852                                         map->driver = map->name + strlen(map->name) + 1;
853                                         strcpy(map->driver, driver);
854                                         map->database = map->driver + strlen(map->driver) + 1;
855                                         strcpy(map->database, database);
856                                         if (table) {
857                                                 map->table = map->database + strlen(map->database) + 1;
858                                                 strcpy(map->table, table);
859                                         } else
860                                                 map->table = NULL;
861                                         map->next = maps;
862                                         if (option_verbose > 1)
863                                                 ast_verbose(VERBOSE_PREFIX_2 "Binding %s to %s/%s/%s\n",map->name,map->driver, map->database, map->table ? map->table : map->name);
864                                         maps = map;
865                                 }
866                         }
867                 }
868                 
869                 ast_destroy(config);
870         }
871
872         return 0;
873 }