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