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