Mon Sep 20 2010 00:22:31

Asterisk developer's documentation


config.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Configuration File Parser
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  *
00025  * Includes the Asterisk Realtime API - ARA
00026  * See doc/realtime.txt and doc/extconfig.txt
00027  */
00028 
00029 #include "asterisk.h"
00030 
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 241019 $")
00032 
00033 #include "asterisk/paths.h"   /* use ast_config_AST_CONFIG_DIR */
00034 #include "asterisk/network.h" /* we do some sockaddr manipulation here */
00035 #include <time.h>
00036 #include <sys/stat.h>
00037 
00038 #include <math.h> /* HUGE_VAL */
00039 
00040 #define AST_INCLUDE_GLOB 1
00041 
00042 #include "asterisk/config.h"
00043 #include "asterisk/cli.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/utils.h"
00046 #include "asterisk/channel.h"
00047 #include "asterisk/app.h"
00048 #include "asterisk/astobj2.h"
00049 #include "asterisk/strings.h" /* for the ast_str_*() API */
00050 
00051 #define MAX_NESTED_COMMENTS 128
00052 #define COMMENT_START ";--"
00053 #define COMMENT_END "--;"
00054 #define COMMENT_META ';'
00055 #define COMMENT_TAG '-'
00056 
00057 static char *extconfig_conf = "extconfig.conf";
00058 
00059 
00060 /*! \brief Structure to keep comments for rewriting configuration files */
00061 struct ast_comment {
00062    struct ast_comment *next;
00063    char cmt[0];
00064 };
00065 
00066 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
00067 struct cache_file_include {
00068    AST_LIST_ENTRY(cache_file_include) list;
00069    char include[0];
00070 };
00071 
00072 struct cache_file_mtime {
00073    AST_LIST_ENTRY(cache_file_mtime) list;
00074    AST_LIST_HEAD(includes, cache_file_include) includes;
00075    unsigned int has_exec:1;
00076    time_t mtime;
00077    char *who_asked;
00078    char filename[0];
00079 };
00080 
00081 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
00082 
00083 static int init_appendbuf(void *data)
00084 {
00085    struct ast_str **str = data;
00086    *str = ast_str_create(16);
00087    return *str ? 0 : -1;
00088 }
00089 
00090 AST_THREADSTORAGE_CUSTOM(appendbuf, init_appendbuf, ast_free_ptr);
00091 
00092 /* comment buffers are better implemented using the ast_str_*() API */
00093 #define CB_SIZE 250  /* initial size of comment buffers */
00094 
00095 static void  CB_ADD(struct ast_str **cb, const char *str)
00096 {
00097    ast_str_append(cb, 0, "%s", str);
00098 }
00099 
00100 static void  CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
00101 {
00102    char *s = alloca(len + 1);
00103    ast_copy_string(s, str, len);
00104    ast_str_append(cb, 0, "%s", str);
00105 }
00106 
00107 static void CB_RESET(struct ast_str *cb, struct ast_str *llb)  
00108 { 
00109    if (cb) {
00110       ast_str_reset(cb);
00111    }
00112    if (llb) {
00113       ast_str_reset(llb);
00114    }
00115 }
00116 
00117 static struct ast_comment *ALLOC_COMMENT(struct ast_str *buffer)
00118 { 
00119    struct ast_comment *x = NULL;
00120    if (!buffer || !ast_str_strlen(buffer)) {
00121       return NULL;
00122    }
00123    if ((x = ast_calloc(1, sizeof(*x) + ast_str_strlen(buffer) + 1))) {
00124       strcpy(x->cmt, ast_str_buffer(buffer)); /* SAFE */
00125    }
00126    return x;
00127 }
00128 
00129 /* I need to keep track of each config file, and all its inclusions,
00130    so that we can track blank lines in each */
00131 
00132 struct inclfile {
00133    char *fname;
00134    int lineno;
00135 };
00136 
00137 static int hash_string(const void *obj, const int flags)
00138 {
00139    char *str = ((struct inclfile *) obj)->fname;
00140    int total;
00141 
00142    for (total = 0; *str; str++) {
00143       unsigned int tmp = total;
00144       total <<= 1; /* multiply by 2 */
00145       total += tmp; /* multiply by 3 */
00146       total <<= 2; /* multiply by 12 */
00147       total += tmp; /* multiply by 13 */
00148 
00149       total += ((unsigned int) (*str));
00150    }
00151    if (total < 0) {
00152       total = -total;
00153    }
00154    return total;
00155 }
00156 
00157 static int hashtab_compare_strings(void *a, void *b, int flags)
00158 {
00159    const struct inclfile *ae = a, *be = b;
00160    return !strcmp(ae->fname, be->fname) ? CMP_MATCH | CMP_STOP : 0;
00161 }
00162 
00163 static struct ast_config_map {
00164    struct ast_config_map *next;
00165    char *name;
00166    char *driver;
00167    char *database;
00168    char *table;
00169    char stuff[0];
00170 } *config_maps = NULL;
00171 
00172 AST_MUTEX_DEFINE_STATIC(config_lock);
00173 static struct ast_config_engine *config_engine_list;
00174 
00175 #define MAX_INCLUDE_LEVEL 10
00176 
00177 struct ast_category_template_instance {
00178    char name[80]; /* redundant? */
00179    const struct ast_category *inst;
00180    AST_LIST_ENTRY(ast_category_template_instance) next;
00181 };
00182 
00183 struct ast_category {
00184    char name[80];
00185    int ignored;         /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
00186    int include_level;
00187    char *file;            /*!< the file name from whence this declaration was read */
00188    int lineno;
00189    AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
00190    struct ast_comment *precomments;
00191    struct ast_comment *sameline;
00192    struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
00193    struct ast_variable *root;
00194    struct ast_variable *last;
00195    struct ast_category *next;
00196 };
00197 
00198 struct ast_config {
00199    struct ast_category *root;
00200    struct ast_category *last;
00201    struct ast_category *current;
00202    struct ast_category *last_browse;     /*!< used to cache the last category supplied via category_browse */
00203    int include_level;
00204    int max_include_level;
00205    struct ast_config_include *includes;  /*!< a list of inclusions, which should describe the entire tree */
00206 };
00207 
00208 struct ast_config_include {
00209    char *include_location_file;     /*!< file name in which the include occurs */
00210    int  include_location_lineno;    /*!< lineno where include occurred */
00211    int  exec;                       /*!< set to non-zero if itsa #exec statement */
00212    char *exec_file;                 /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
00213    char *included_file;             /*!< file name included */
00214    int inclusion_count;             /*!< if the file is included more than once, a running count thereof -- but, worry not,
00215                                          we explode the instances and will include those-- so all entries will be unique */
00216    int output;                      /*!< a flag to indicate if the inclusion has been output */
00217    struct ast_config_include *next; /*!< ptr to next inclusion in the list */
00218 };
00219 
00220 #ifdef MALLOC_DEBUG
00221 struct ast_variable *_ast_variable_new(const char *name, const char *value, const char *filename, const char *file, const char *func, int lineno) 
00222 #else
00223 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename) 
00224 #endif
00225 {
00226    struct ast_variable *variable;
00227    int name_len = strlen(name) + 1; 
00228    int val_len = strlen(value) + 1; 
00229    int fn_len = strlen(filename) + 1;  
00230 
00231 #ifdef MALLOC_DEBUG
00232    if ((variable = __ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable), file, lineno, func))) {
00233 #else
00234    if ((variable = ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable)))) {
00235 #endif
00236       char *dst = variable->stuff;  /* writable space starts here */
00237       variable->name = strcpy(dst, name);
00238       dst += name_len;
00239       variable->value = strcpy(dst, value);
00240       dst += val_len;
00241       variable->file = strcpy(dst, filename);
00242    }
00243    return variable;
00244 }
00245 
00246 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)
00247 {
00248    /* a file should be included ONCE. Otherwise, if one of the instances is changed,
00249     * then all be changed. -- how do we know to include it? -- Handling modified 
00250     * instances is possible, I'd have
00251     * to create a new master for each instance. */
00252    struct ast_config_include *inc;
00253    struct stat statbuf;
00254    
00255    inc = ast_include_find(conf, included_file);
00256    if (inc) {
00257       do {
00258          inc->inclusion_count++;
00259          snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
00260       } while (stat(real_included_file_name, &statbuf) == 0);
00261       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);
00262    } else
00263       *real_included_file_name = 0;
00264    
00265    inc = ast_calloc(1,sizeof(struct ast_config_include));
00266    inc->include_location_file = ast_strdup(from_file);
00267    inc->include_location_lineno = from_lineno;
00268    if (!ast_strlen_zero(real_included_file_name))
00269       inc->included_file = ast_strdup(real_included_file_name);
00270    else
00271       inc->included_file = ast_strdup(included_file);
00272    
00273    inc->exec = is_exec;
00274    if (is_exec)
00275       inc->exec_file = ast_strdup(exec_file);
00276    
00277    /* attach this new struct to the conf struct */
00278    inc->next = conf->includes;
00279    conf->includes = inc;
00280    
00281    return inc;
00282 }
00283 
00284 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
00285 {
00286    struct ast_config_include *incl;
00287    struct ast_category *cat;
00288    struct ast_variable *v;
00289    
00290    int from_len = strlen(from_file);
00291    int to_len = strlen(to_file);
00292    
00293    if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
00294       return;
00295    
00296    /* the manager code allows you to read in one config file, then
00297     * write it back out under a different name. But, the new arrangement
00298     * ties output lines to the file name. So, before you try to write
00299     * the config file to disk, better riffle thru the data and make sure
00300     * the file names are changed.
00301     */
00302    /* file names are on categories, includes (of course), and on variables. So,
00303     * traverse all this and swap names */
00304 
00305    for (incl = conf->includes; incl; incl=incl->next) {
00306       if (strcmp(incl->include_location_file,from_file) == 0) {
00307          if (from_len >= to_len)
00308             strcpy(incl->include_location_file, to_file);
00309          else {
00310             free(incl->include_location_file);
00311             incl->include_location_file = strdup(to_file);
00312          }
00313       }
00314    }
00315    for (cat = conf->root; cat; cat = cat->next) {
00316       if (strcmp(cat->file,from_file) == 0) {
00317          if (from_len >= to_len)
00318             strcpy(cat->file, to_file);
00319          else {
00320             free(cat->file);
00321             cat->file = strdup(to_file);
00322          }
00323       }
00324       for (v = cat->root; v; v = v->next) {
00325          if (strcmp(v->file,from_file) == 0) {
00326             if (from_len >= to_len)
00327                strcpy(v->file, to_file);
00328             else {
00329                free(v->file);
00330                v->file = strdup(to_file);
00331             }
00332          }
00333       }
00334    }
00335 }
00336 
00337 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
00338 {
00339    struct ast_config_include *x;
00340    for (x=conf->includes;x;x=x->next) {
00341       if (strcmp(x->included_file,included_file) == 0)
00342          return x;
00343    }
00344    return 0;
00345 }
00346 
00347 
00348 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
00349 {
00350    if (!variable)
00351       return;
00352    if (category->last)
00353       category->last->next = variable;
00354    else
00355       category->root = variable;
00356    category->last = variable;
00357    while (category->last->next)
00358       category->last = category->last->next;
00359 }
00360 
00361 void ast_variable_insert(struct ast_category *category, struct ast_variable *variable, const char *line)
00362 {
00363    struct ast_variable *cur = category->root;
00364    int lineno;
00365    int insertline;
00366 
00367    if (!variable || sscanf(line, "%30d", &insertline) != 1) {
00368       return;
00369    }
00370    if (!insertline) {
00371       variable->next = category->root;
00372       category->root = variable;
00373    } else {
00374       for (lineno = 1; lineno < insertline; lineno++) {
00375          cur = cur->next;
00376          if (!cur->next) {
00377             break;
00378          }
00379       }
00380       variable->next = cur->next;
00381       cur->next = variable;
00382    }
00383 }
00384 
00385 static void ast_comment_destroy(struct ast_comment **comment)
00386 {
00387    struct ast_comment *n, *p;
00388 
00389    for (p = *comment; p; p = n) {
00390       n = p->next;
00391       ast_free(p);
00392    }
00393 
00394    *comment = NULL;
00395 }
00396 
00397 void ast_variables_destroy(struct ast_variable *v)
00398 {
00399    struct ast_variable *vn;
00400 
00401    while (v) {
00402       vn = v;
00403       v = v->next;
00404       ast_comment_destroy(&vn->precomments);
00405       ast_comment_destroy(&vn->sameline);
00406       ast_comment_destroy(&vn->trailing);
00407       ast_free(vn);
00408    }
00409 }
00410 
00411 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
00412 {
00413    struct ast_category *cat = NULL;
00414 
00415    if (category && config->last_browse && (config->last_browse->name == category)) {
00416       cat = config->last_browse;
00417    } else {
00418       cat = ast_category_get(config, category);
00419    }
00420 
00421    return (cat) ? cat->root : NULL;
00422 }
00423 
00424 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
00425 {
00426    const char *tmp;
00427    tmp = ast_variable_retrieve(cfg, cat, var);
00428    if (!tmp) {
00429       tmp = ast_variable_retrieve(cfg, "general", var);
00430    }
00431    return tmp;
00432 }
00433 
00434 
00435 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
00436 {
00437    struct ast_variable *v;
00438 
00439    if (category) {
00440       for (v = ast_variable_browse(config, category); v; v = v->next) {
00441          if (!strcasecmp(variable, v->name)) {
00442             return v->value;
00443          }
00444       }
00445    } else {
00446       struct ast_category *cat;
00447 
00448       for (cat = config->root; cat; cat = cat->next) {
00449          for (v = cat->root; v; v = v->next) {
00450             if (!strcasecmp(variable, v->name)) {
00451                return v->value;
00452             }
00453          }
00454       }
00455    }
00456 
00457    return NULL;
00458 }
00459 
00460 static struct ast_variable *variable_clone(const struct ast_variable *old)
00461 {
00462    struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
00463 
00464    if (new) {
00465       new->lineno = old->lineno;
00466       new->object = old->object;
00467       new->blanklines = old->blanklines;
00468       /* TODO: clone comments? */
00469    }
00470 
00471    return new;
00472 }
00473  
00474 static void move_variables(struct ast_category *old, struct ast_category *new)
00475 {
00476    struct ast_variable *var = old->root;
00477 
00478    old->root = NULL;
00479    /* we can just move the entire list in a single op */
00480    ast_variable_append(new, var);
00481 }
00482 
00483 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno) 
00484 {
00485    struct ast_category *category;
00486 
00487    if ((category = ast_calloc(1, sizeof(*category))))
00488       ast_copy_string(category->name, name, sizeof(category->name));
00489    category->file = strdup(in_file);
00490    category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
00491    return category;
00492 }
00493 
00494 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
00495 {
00496    struct ast_category *cat;
00497 
00498    /* try exact match first, then case-insensitive match */
00499    for (cat = config->root; cat; cat = cat->next) {
00500       if (cat->name == category_name && (ignored || !cat->ignored))
00501          return cat;
00502    }
00503 
00504    for (cat = config->root; cat; cat = cat->next) {
00505       if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
00506          return cat;
00507    }
00508 
00509    return NULL;
00510 }
00511 
00512 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
00513 {
00514    return category_get(config, category_name, 0);
00515 }
00516 
00517 int ast_category_exist(const struct ast_config *config, const char *category_name)
00518 {
00519    return !!ast_category_get(config, category_name);
00520 }
00521 
00522 void ast_category_append(struct ast_config *config, struct ast_category *category)
00523 {
00524    if (config->last)
00525       config->last->next = category;
00526    else
00527       config->root = category;
00528    category->include_level = config->include_level;
00529    config->last = category;
00530    config->current = category;
00531 }
00532 
00533 void ast_category_insert(struct ast_config *config, struct ast_category *cat, const char *match)
00534 {
00535    struct ast_category *cur_category;
00536 
00537    if (!cat || !match)
00538       return;
00539    if (!strcasecmp(config->root->name, match)) {
00540       cat->next = config->root;
00541       config->root = cat;
00542       return;
00543    } 
00544    for (cur_category = config->root; cur_category; cur_category = cur_category->next) {
00545       if (!strcasecmp(cur_category->next->name, match)) {
00546          cat->next = cur_category->next;
00547          cur_category->next = cat;
00548          break;
00549       }
00550    }
00551 }
00552 
00553 static void ast_destroy_template_list(struct ast_category *cat)
00554 {
00555    struct ast_category_template_instance *x;
00556 
00557    while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
00558       free(x);
00559 }
00560 
00561 void ast_category_destroy(struct ast_category *cat)
00562 {
00563    ast_variables_destroy(cat->root);
00564    if (cat->file) {
00565       free(cat->file);
00566       cat->file = 0;
00567    }
00568    ast_comment_destroy(&cat->precomments);
00569    ast_comment_destroy(&cat->sameline);
00570    ast_comment_destroy(&cat->trailing);
00571    ast_destroy_template_list(cat);
00572    ast_free(cat);
00573 }
00574 
00575 static void ast_includes_destroy(struct ast_config_include *incls)
00576 {
00577    struct ast_config_include *incl,*inclnext;
00578    
00579    for (incl=incls; incl; incl = inclnext) {
00580       inclnext = incl->next;
00581       if (incl->include_location_file)
00582          free(incl->include_location_file);
00583       if (incl->exec_file)
00584          free(incl->exec_file);
00585       if (incl->included_file)
00586          free(incl->included_file);
00587       free(incl);
00588    }
00589 }
00590 
00591 static struct ast_category *next_available_category(struct ast_category *cat)
00592 {
00593    for (; cat && cat->ignored; cat = cat->next);
00594 
00595    return cat;
00596 }
00597 
00598 /*! return the first var of a category */
00599 struct ast_variable *ast_category_first(struct ast_category *cat)
00600 {
00601    return (cat) ? cat->root : NULL;
00602 }
00603 
00604 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
00605 {
00606    struct ast_category *category = ast_category_get(config, cat);
00607 
00608    if (category)
00609       return category->root;
00610    return NULL;
00611 }
00612 
00613 char *ast_category_browse(struct ast_config *config, const char *prev)
00614 {  
00615    struct ast_category *cat = NULL;
00616 
00617    if (prev && config->last_browse && (config->last_browse->name == prev))
00618       cat = config->last_browse->next;
00619    else if (!prev && config->root)
00620       cat = config->root;
00621    else if (prev) {
00622       for (cat = config->root; cat; cat = cat->next) {
00623          if (cat->name == prev) {
00624             cat = cat->next;
00625             break;
00626          }
00627       }
00628       if (!cat) {
00629          for (cat = config->root; cat; cat = cat->next) {
00630             if (!strcasecmp(cat->name, prev)) {
00631                cat = cat->next;
00632                break;
00633             }
00634          }
00635       }
00636    }
00637    
00638    if (cat)
00639       cat = next_available_category(cat);
00640 
00641    config->last_browse = cat;
00642    return (cat) ? cat->name : NULL;
00643 }
00644 
00645 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
00646 {
00647    struct ast_variable *v;
00648 
00649    v = cat->root;
00650    cat->root = NULL;
00651    cat->last = NULL;
00652 
00653    return v;
00654 }
00655 
00656 void ast_category_rename(struct ast_category *cat, const char *name)
00657 {
00658    ast_copy_string(cat->name, name, sizeof(cat->name));
00659 }
00660 
00661 static void inherit_category(struct ast_category *new, const struct ast_category *base)
00662 {
00663    struct ast_variable *var;
00664    struct ast_category_template_instance *x = ast_calloc(1,sizeof(struct ast_category_template_instance));
00665 
00666    strcpy(x->name, base->name);
00667    x->inst = base;
00668    AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
00669    for (var = base->root; var; var = var->next)
00670       ast_variable_append(new, variable_clone(var));
00671 }
00672 
00673 struct ast_config *ast_config_new(void) 
00674 {
00675    struct ast_config *config;
00676 
00677    if ((config = ast_calloc(1, sizeof(*config))))
00678       config->max_include_level = MAX_INCLUDE_LEVEL;
00679    return config;
00680 }
00681 
00682 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match, const char *line)
00683 {
00684    struct ast_variable *cur, *prev=NULL, *curn;
00685    int res = -1;
00686    int lineno = 0;
00687 
00688    cur = category->root;
00689    while (cur) {
00690       if (cur->name == variable) {
00691          if (prev) {
00692             prev->next = cur->next;
00693             if (cur == category->last)
00694                category->last = prev;
00695          } else {
00696             category->root = cur->next;
00697             if (cur == category->last)
00698                category->last = NULL;
00699          }
00700          cur->next = NULL;
00701          ast_variables_destroy(cur);
00702          return 0;
00703       }
00704       prev = cur;
00705       cur = cur->next;
00706    }
00707 
00708    prev = NULL;
00709    cur = category->root;
00710    while (cur) {
00711       curn = cur->next;
00712       if ((!ast_strlen_zero(line) && lineno == atoi(line)) || (ast_strlen_zero(line) && !strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match)))) {
00713          if (prev) {
00714             prev->next = cur->next;
00715             if (cur == category->last)
00716                category->last = prev;
00717          } else {
00718             category->root = cur->next;
00719             if (cur == category->last)
00720                category->last = NULL;
00721          }
00722          cur->next = NULL;
00723          ast_variables_destroy(cur);
00724          res = 0;
00725       } else
00726          prev = cur;
00727 
00728       cur = curn;
00729       lineno++;
00730    }
00731    return res;
00732 }
00733 
00734 int ast_variable_update(struct ast_category *category, const char *variable, 
00735                   const char *value, const char *match, unsigned int object)
00736 {
00737    struct ast_variable *cur, *prev=NULL, *newer=NULL;
00738 
00739    for (cur = category->root; cur; prev = cur, cur = cur->next) {
00740       if (strcasecmp(cur->name, variable) ||
00741          (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
00742          continue;
00743 
00744       if (!(newer = ast_variable_new(variable, value, cur->file)))
00745          return -1;
00746    
00747       newer->next = cur->next;
00748       newer->object = cur->object || object;
00749       if (prev)
00750          prev->next = newer;
00751       else
00752          category->root = newer;
00753       if (category->last == cur)
00754          category->last = newer;
00755 
00756       cur->next = NULL;
00757       ast_variables_destroy(cur);
00758 
00759       return 0;
00760    }
00761 
00762    /* Could not find variable to update */
00763    return -1;
00764 }
00765 
00766 int ast_category_delete(struct ast_config *cfg, const char *category)
00767 {
00768    struct ast_category *prev=NULL, *cat;
00769 
00770    cat = cfg->root;
00771    while (cat) {
00772       if (cat->name == category) {
00773          if (prev) {
00774             prev->next = cat->next;
00775             if (cat == cfg->last)
00776                cfg->last = prev;
00777          } else {
00778             cfg->root = cat->next;
00779             if (cat == cfg->last)
00780                cfg->last = NULL;
00781          }
00782          ast_category_destroy(cat);
00783          return 0;
00784       }
00785       prev = cat;
00786       cat = cat->next;
00787    }
00788 
00789    prev = NULL;
00790    cat = cfg->root;
00791    while (cat) {
00792       if (!strcasecmp(cat->name, category)) {
00793          if (prev) {
00794             prev->next = cat->next;
00795             if (cat == cfg->last)
00796                cfg->last = prev;
00797          } else {
00798             cfg->root = cat->next;
00799             if (cat == cfg->last)
00800                cfg->last = NULL;
00801          }
00802          ast_category_destroy(cat);
00803          return 0;
00804       }
00805       prev = cat;
00806       cat = cat->next;
00807    }
00808    return -1;
00809 }
00810 
00811 int ast_category_empty(struct ast_config *cfg, const char *category)
00812 {
00813    struct ast_category *cat;
00814 
00815    for (cat = cfg->root; cat; cat = cat->next) {
00816       if (!strcasecmp(cat->name, category))
00817          continue;
00818       ast_variables_destroy(cat->root);
00819       cat->root = NULL;
00820       cat->last = NULL;
00821       return 0;
00822    }
00823 
00824    return -1;
00825 }
00826 
00827 void ast_config_destroy(struct ast_config *cfg)
00828 {
00829    struct ast_category *cat, *catn;
00830 
00831    if (!cfg)
00832       return;
00833 
00834    ast_includes_destroy(cfg->includes);
00835 
00836    cat = cfg->root;
00837    while (cat) {
00838       catn = cat;
00839       cat = cat->next;
00840       ast_category_destroy(catn);
00841    }
00842    ast_free(cfg);
00843 }
00844 
00845 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
00846 {
00847    return cfg->current;
00848 }
00849 
00850 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
00851 {
00852    /* cast below is just to silence compiler warning about dropping "const" */
00853    cfg->current = (struct ast_category *) cat;
00854 }
00855 
00856 enum config_cache_attribute_enum {
00857    ATTRIBUTE_INCLUDE = 0,
00858    ATTRIBUTE_EXEC = 1,
00859 };
00860 
00861 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename, const char *who_asked)
00862 {
00863    struct cache_file_mtime *cfmtime;
00864    struct cache_file_include *cfinclude;
00865    struct stat statbuf = { 0, };
00866 
00867    /* Find our cached entry for this configuration file */
00868    AST_LIST_LOCK(&cfmtime_head);
00869    AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
00870       if (!strcmp(cfmtime->filename, configfile) && !strcmp(cfmtime->who_asked, who_asked))
00871          break;
00872    }
00873    if (!cfmtime) {
00874       cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1 + strlen(who_asked) + 1);
00875       if (!cfmtime) {
00876          AST_LIST_UNLOCK(&cfmtime_head);
00877          return;
00878       }
00879       AST_LIST_HEAD_INIT(&cfmtime->includes);
00880       strcpy(cfmtime->filename, configfile);
00881       cfmtime->who_asked = cfmtime->filename + strlen(configfile) + 1;
00882       strcpy(cfmtime->who_asked, who_asked);
00883       /* Note that the file mtime is initialized to 0, i.e. 1970 */
00884       AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
00885    }
00886 
00887    if (!stat(configfile, &statbuf))
00888       cfmtime->mtime = 0;
00889    else
00890       cfmtime->mtime = statbuf.st_mtime;
00891 
00892    switch (attrtype) {
00893    case ATTRIBUTE_INCLUDE:
00894       AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
00895          if (!strcmp(cfinclude->include, filename)) {
00896             AST_LIST_UNLOCK(&cfmtime_head);
00897             return;
00898          }
00899       }
00900       cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
00901       if (!cfinclude) {
00902          AST_LIST_UNLOCK(&cfmtime_head);
00903          return;
00904       }
00905       strcpy(cfinclude->include, filename);
00906       AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
00907       break;
00908    case ATTRIBUTE_EXEC:
00909       cfmtime->has_exec = 1;
00910       break;
00911    }
00912    AST_LIST_UNLOCK(&cfmtime_head);
00913 }
00914 
00915 /*! \brief parse one line in the configuration.
00916  * \verbatim
00917  * We can have a category header [foo](...)
00918  * a directive          #include / #exec
00919  * or a regular line       name = value
00920  * \endverbatim
00921  */
00922 static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
00923    char *buf, int lineno, const char *configfile, struct ast_flags flags,
00924    struct ast_str *comment_buffer,
00925    struct ast_str *lline_buffer,
00926    const char *suggested_include_file,
00927    struct ast_category **last_cat, struct ast_variable **last_var, const char *who_asked)
00928 {
00929    char *c;
00930    char *cur = buf;
00931    struct ast_variable *v;
00932    char cmd[512], exec_file[512];
00933 
00934    /* Actually parse the entry */
00935    if (cur[0] == '[') { /* A category header */
00936       /* format is one of the following:
00937        * [foo] define a new category named 'foo'
00938        * [foo](!) define a new template category named 'foo'
00939        * [foo](+) append to category 'foo', error if foo does not exist.
00940        * [foo](a) define a new category and inherit from template a.
00941        *    You can put a comma-separated list of templates and '!' and '+'
00942        *    between parentheses, with obvious meaning.
00943        */
00944       struct ast_category *newcat = NULL;
00945       char *catname;
00946 
00947       c = strchr(cur, ']');
00948       if (!c) {
00949          ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
00950          return -1;
00951       }
00952       *c++ = '\0';
00953       cur++;
00954       if (*c++ != '(')
00955          c = NULL;
00956       catname = cur;
00957       if (!(*cat = newcat = ast_category_new(catname,
00958             S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile),
00959             lineno))) {
00960          return -1;
00961       }
00962       (*cat)->lineno = lineno;
00963       *last_var = 0;
00964       *last_cat = newcat;
00965       
00966       /* add comments */
00967       if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
00968          newcat->precomments = ALLOC_COMMENT(comment_buffer);
00969       if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
00970          newcat->sameline = ALLOC_COMMENT(lline_buffer);
00971       if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
00972          CB_RESET(comment_buffer, lline_buffer);
00973       
00974       /* If there are options or categories to inherit from, process them now */
00975       if (c) {
00976          if (!(cur = strchr(c, ')'))) {
00977             ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
00978             return -1;
00979          }
00980          *cur = '\0';
00981          while ((cur = strsep(&c, ","))) {
00982             if (!strcasecmp(cur, "!")) {
00983                (*cat)->ignored = 1;
00984             } else if (!strcasecmp(cur, "+")) {
00985                *cat = category_get(cfg, catname, 1);
00986                if (!(*cat)) {
00987                   if (newcat)
00988                      ast_category_destroy(newcat);
00989                   ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
00990                   return -1;
00991                }
00992                if (newcat) {
00993                   move_variables(newcat, *cat);
00994                   ast_category_destroy(newcat);
00995                   newcat = NULL;
00996                }
00997             } else {
00998                struct ast_category *base;
00999             
01000                base = category_get(cfg, cur, 1);
01001                if (!base) {
01002                   ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
01003                   return -1;
01004                }
01005                inherit_category(*cat, base);
01006             }
01007          }
01008       }
01009       if (newcat)
01010          ast_category_append(cfg, *cat);
01011    } else if (cur[0] == '#') { /* A directive - #include or #exec */
01012       char *cur2;
01013       char real_inclusion_name[256];
01014       struct ast_config_include *inclu;
01015       int do_include = 0;  /* otherwise, it is exec */
01016 
01017       cur++;
01018       c = cur;
01019       while (*c && (*c > 32)) {
01020          c++;
01021       }
01022 
01023       if (*c) {
01024          *c = '\0';
01025          /* Find real argument */
01026          c = ast_skip_blanks(c + 1);
01027          if (!(*c)) {
01028             c = NULL;
01029          }
01030       } else 
01031          c = NULL;
01032       if (!strcasecmp(cur, "include")) {
01033          do_include = 1;
01034       } else if (!strcasecmp(cur, "exec")) {
01035          if (!ast_opt_exec_includes) {
01036             ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
01037             return 0;   /* XXX is this correct ? or we should return -1 ? */
01038          }
01039       } else {
01040          ast_log(LOG_WARNING, "Unknown directive '#%s' at line %d of %s\n", cur, lineno, configfile);
01041          return 0;   /* XXX is this correct ? or we should return -1 ? */
01042       }
01043 
01044       if (c == NULL) {
01045          ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", 
01046                do_include ? "include" : "exec",
01047                do_include ? "filename" : "/path/to/executable",
01048                lineno,
01049                configfile);
01050          return 0;   /* XXX is this correct ? or we should return -1 ? */
01051       }
01052 
01053       cur = c;
01054       /* Strip off leading and trailing "'s and <>'s */
01055       if (*c == '"') {
01056          /* Dequote */
01057          while (*c) {
01058             if (*c == '"') {
01059                strcpy(c, c + 1); /* SAFE */
01060                c--;
01061             } else if (*c == '\\') {
01062                strcpy(c, c + 1); /* SAFE */
01063             }
01064             c++;
01065          }
01066       } else if (*c == '<') {
01067          /* C-style include */
01068          if (*(c + strlen(c) - 1) == '>') {
01069             cur++;
01070             *(c + strlen(c) - 1) = '\0';
01071          }
01072       }
01073       cur2 = cur;
01074 
01075       /* #exec </path/to/executable>
01076          We create a tmp file, then we #include it, then we delete it. */
01077       if (!do_include) {
01078          struct timeval now = ast_tvnow();
01079          if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01080             config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL, who_asked);
01081          snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d%d.%ld", (int)now.tv_sec, (int)now.tv_usec, (long)pthread_self());
01082          snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
01083          ast_safe_system(cmd);
01084          cur = exec_file;
01085       } else {
01086          if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01087             config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur, who_asked);
01088          exec_file[0] = '\0';
01089       }
01090       /* A #include */
01091       /* record this inclusion */
01092       inclu = ast_include_new(cfg, cfg->include_level == 1 ? "" : configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
01093 
01094       do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name, who_asked) ? 1 : 0;
01095       if (!ast_strlen_zero(exec_file))
01096          unlink(exec_file);
01097       if (!do_include) {
01098          ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
01099          return -1;
01100       }
01101       /* XXX otherwise what ? the default return is 0 anyways */
01102 
01103    } else {
01104       /* Just a line (variable = value) */
01105       int object = 0;
01106       if (!(*cat)) {
01107          ast_log(LOG_WARNING,
01108             "parse error: No category context for line %d of %s\n", lineno, configfile);
01109          return -1;
01110       }
01111       c = strchr(cur, '=');
01112 
01113       if (c && c > cur && (*(c - 1) == '+')) {
01114          struct ast_variable *var, *replace = NULL;
01115          struct ast_str **str = ast_threadstorage_get(&appendbuf, sizeof(*str));
01116 
01117          if (!str || !*str) {
01118             return -1;
01119          }
01120 
01121          *(c - 1) = '\0';
01122          c++;
01123          cur = ast_strip(cur);
01124 
01125          /* Must iterate through category until we find last variable of same name (since there could be multiple) */
01126          for (var = ast_category_first(*cat); var; var = var->next) {
01127             if (!strcmp(var->name, cur)) {
01128                replace = var;
01129             }
01130          }
01131 
01132          if (!replace) {
01133             /* Nothing to replace; just set a variable normally. */
01134             goto set_new_variable;
01135          }
01136 
01137          ast_str_set(str, 0, "%s", replace->value);
01138          ast_str_append(str, 0, "%s", c);
01139          ast_str_trim_blanks(*str);
01140          ast_variable_update(*cat, replace->name, ast_skip_blanks(ast_str_buffer(*str)), replace->value, object);
01141       } else if (c) {
01142          *c = 0;
01143          c++;
01144          /* Ignore > in => */
01145          if (*c== '>') {
01146             object = 1;
01147             c++;
01148          }
01149 set_new_variable:
01150          if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), S_OR(suggested_include_file, cfg->include_level == 1 ? "" : configfile)))) {
01151             v->lineno = lineno;
01152             v->object = object;
01153             *last_cat = 0;
01154             *last_var = v;
01155             /* Put and reset comments */
01156             v->blanklines = 0;
01157             ast_variable_append(*cat, v);
01158             /* add comments */
01159             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01160                v->precomments = ALLOC_COMMENT(comment_buffer);
01161             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01162                v->sameline = ALLOC_COMMENT(lline_buffer);
01163             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01164                CB_RESET(comment_buffer, lline_buffer);
01165             
01166          } else {
01167             return -1;
01168          }
01169       } else {
01170          ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
01171       }
01172    }
01173    return 0;
01174 }
01175 
01176 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, const char *who_asked)
01177 {
01178    char fn[256];
01179 #if defined(LOW_MEMORY)
01180    char buf[512];
01181 #else
01182    char buf[8192];
01183 #endif
01184    char *new_buf, *comment_p, *process_buf;
01185    FILE *f;
01186    int lineno=0;
01187    int comment = 0, nest[MAX_NESTED_COMMENTS];
01188    struct ast_category *cat = NULL;
01189    int count = 0;
01190    struct stat statbuf;
01191    struct cache_file_mtime *cfmtime = NULL;
01192    struct cache_file_include *cfinclude;
01193    struct ast_variable *last_var = 0;
01194    struct ast_category *last_cat = 0;
01195    /*! Growable string buffer */
01196    struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
01197    struct ast_str *lline_buffer = NULL;   /*!< A buffer for stuff behind the ; */
01198 
01199    if (cfg)
01200       cat = ast_config_get_current_category(cfg);
01201 
01202    if (filename[0] == '/') {
01203       ast_copy_string(fn, filename, sizeof(fn));
01204    } else {
01205       snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
01206    }
01207 
01208    if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01209       comment_buffer = ast_str_create(CB_SIZE);
01210       if (comment_buffer)
01211          lline_buffer = ast_str_create(CB_SIZE);
01212       if (!lline_buffer) {
01213          if (comment_buffer)
01214             ast_free(comment_buffer);
01215          ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
01216          return NULL;
01217       }
01218    }
01219 #ifdef AST_INCLUDE_GLOB
01220    {
01221       int glob_ret;
01222       glob_t globbuf;
01223       globbuf.gl_offs = 0; /* initialize it to silence gcc */
01224       glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
01225       if (glob_ret == GLOB_NOSPACE)
01226          ast_log(LOG_WARNING,
01227             "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
01228       else if (glob_ret  == GLOB_ABORTED)
01229          ast_log(LOG_WARNING,
01230             "Glob Expansion of pattern '%s' failed: Read error\n", fn);
01231       else  {
01232          /* loop over expanded files */
01233          int i;
01234          for (i=0; i<globbuf.gl_pathc; i++) {
01235             ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
01236 #endif
01237    /*
01238     * The following is not a loop, but just a convenient way to define a block
01239     * (using do { } while(0) ), and be able to exit from it with 'continue'
01240     * or 'break' in case of errors. Nice trick.
01241     */
01242    do {
01243       if (stat(fn, &statbuf))
01244          continue;
01245 
01246       if (!S_ISREG(statbuf.st_mode)) {
01247          ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
01248          continue;
01249       }
01250 
01251       if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
01252          /* Find our cached entry for this configuration file */
01253          AST_LIST_LOCK(&cfmtime_head);
01254          AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
01255             if (!strcmp(cfmtime->filename, fn) && !strcmp(cfmtime->who_asked, who_asked))
01256                break;
01257          }
01258          if (!cfmtime) {
01259             cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1 + strlen(who_asked) + 1);
01260             if (!cfmtime)
01261                continue;
01262             AST_LIST_HEAD_INIT(&cfmtime->includes);
01263             strcpy(cfmtime->filename, fn);
01264             cfmtime->who_asked = cfmtime->filename + strlen(fn) + 1;
01265             strcpy(cfmtime->who_asked, who_asked);
01266             /* Note that the file mtime is initialized to 0, i.e. 1970 */
01267             AST_LIST_INSERT_SORTALPHA(&cfmtime_head, cfmtime, list, filename);
01268          }
01269       }
01270 
01271       if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
01272          /* File is unchanged, what about the (cached) includes (if any)? */
01273          int unchanged = 1;
01274          AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
01275             /* We must glob here, because if we did not, then adding a file to globbed directory would
01276              * incorrectly cause no reload to be necessary. */
01277             char fn2[256];
01278 #ifdef AST_INCLUDE_GLOB
01279             int glob_return;
01280             glob_t glob_buf = { .gl_offs = 0 };
01281             glob_return = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &glob_buf);
01282             /* On error, we reparse */
01283             if (glob_return == GLOB_NOSPACE || glob_return  == GLOB_ABORTED)
01284                unchanged = 0;
01285             else  {
01286                /* loop over expanded files */
01287                int j;
01288                for (j = 0; j < glob_buf.gl_pathc; j++) {
01289                   ast_copy_string(fn2, glob_buf.gl_pathv[j], sizeof(fn2));
01290 #else
01291                   ast_copy_string(fn2, cfinclude->include);
01292 #endif
01293                   if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "", who_asked) == NULL) {
01294                      /* that second-to-last field needs to be looked at in this case... TODO */
01295                      unchanged = 0;
01296                      /* One change is enough to short-circuit and reload the whole shebang */
01297                      break;
01298                   }
01299 #ifdef AST_INCLUDE_GLOB
01300                }
01301             }
01302 #endif
01303          }
01304 
01305          if (unchanged) {
01306             AST_LIST_UNLOCK(&cfmtime_head);
01307             return CONFIG_STATUS_FILEUNCHANGED;
01308          }
01309       }
01310       if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
01311          AST_LIST_UNLOCK(&cfmtime_head);
01312 
01313       /* If cfg is NULL, then we just want an answer */
01314       if (cfg == NULL)
01315          return NULL;
01316 
01317       if (cfmtime)
01318          cfmtime->mtime = statbuf.st_mtime;
01319 
01320       ast_verb(2, "Parsing '%s': ", fn);
01321          fflush(stdout);
01322       if (!(f = fopen(fn, "r"))) {
01323          ast_debug(1, "No file to parse: %s\n", fn);
01324          ast_verb(2, "Not found (%s)\n", strerror(errno));
01325          continue;
01326       }
01327       count++;
01328       /* If we get to this point, then we're loading regardless */
01329       ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
01330       ast_debug(1, "Parsing %s\n", fn);
01331       ast_verb(2, "Found\n");
01332       while (!feof(f)) {
01333          lineno++;
01334          if (fgets(buf, sizeof(buf), f)) {
01335             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
01336                CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
01337                ast_str_reset(lline_buffer);        /* erase the lline buffer */
01338             }
01339             
01340             new_buf = buf;
01341             if (comment) 
01342                process_buf = NULL;
01343             else
01344                process_buf = buf;
01345             
01346             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer) && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
01347                /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
01348                CB_ADD(&comment_buffer, "\n");       /* add a newline to the comment buffer */
01349                continue; /* go get a new line, then */
01350             }
01351             
01352             while ((comment_p = strchr(new_buf, COMMENT_META))) {
01353                if ((comment_p > new_buf) && (*(comment_p - 1) == '\\')) {
01354                   /* Escaped semicolons aren't comments. */
01355                   new_buf = comment_p + 1;
01356                } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
01357                   /* Meta-Comment start detected ";--" */
01358                   if (comment < MAX_NESTED_COMMENTS) {
01359                      *comment_p = '\0';
01360                      new_buf = comment_p + 3;
01361                      comment++;
01362                      nest[comment-1] = lineno;
01363                   } else {
01364                      ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
01365                   }
01366                } else if ((comment_p >= new_buf + 2) &&
01367                      (*(comment_p - 1) == COMMENT_TAG) &&
01368                      (*(comment_p - 2) == COMMENT_TAG)) {
01369                   /* Meta-Comment end detected */
01370                   comment--;
01371                   new_buf = comment_p + 1;
01372                   if (!comment) {
01373                      /* Back to non-comment now */
01374                      if (process_buf) {
01375                         /* Actually have to move what's left over the top, then continue */
01376                         char *oldptr;
01377                         oldptr = process_buf + strlen(process_buf);
01378                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01379                            CB_ADD(&comment_buffer, ";");
01380                            CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
01381                         }
01382                         
01383                         memmove(oldptr, new_buf, strlen(new_buf) + 1);
01384                         new_buf = oldptr;
01385                      } else
01386                         process_buf = new_buf;
01387                   }
01388                } else {
01389                   if (!comment) {
01390                      /* If ; is found, and we are not nested in a comment, 
01391                         we immediately stop all comment processing */
01392                      if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01393                         CB_ADD(&lline_buffer, comment_p);
01394                      }
01395                      *comment_p = '\0'; 
01396                      new_buf = comment_p;
01397                   } else
01398                      new_buf = comment_p + 1;
01399                }
01400             }
01401             if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
01402                CB_ADD(&comment_buffer, buf);  /* the whole line is a comment, store it */
01403             }
01404             
01405             if (process_buf) {
01406                char *buffer = ast_strip(process_buf);
01407                if (!ast_strlen_zero(buffer)) {
01408                   if (process_text_line(cfg, &cat, buffer, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var, who_asked)) {
01409                      cfg = CONFIG_STATUS_FILEINVALID;
01410                      break;
01411                   }
01412                }
01413             }
01414          }
01415       }
01416       /* end of file-- anything in a comment buffer? */
01417       if (last_cat) {
01418          if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
01419             if (lline_buffer && ast_str_strlen(lline_buffer)) {
01420                CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
01421                ast_str_reset(lline_buffer);        /* erase the lline buffer */
01422             }
01423             last_cat->trailing = ALLOC_COMMENT(comment_buffer);
01424          }
01425       } else if (last_var) {
01426          if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
01427             if (lline_buffer && ast_str_strlen(lline_buffer)) {
01428                CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer));       /* add the current lline buffer to the comment buffer */
01429                ast_str_reset(lline_buffer);        /* erase the lline buffer */
01430             }
01431             last_var->trailing = ALLOC_COMMENT(comment_buffer);
01432          }
01433       } else {
01434          if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && ast_str_strlen(comment_buffer)) {
01435             ast_debug(1, "Nothing to attach comments to, discarded: %s\n", ast_str_buffer(comment_buffer));
01436          }
01437       }
01438       if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
01439          CB_RESET(comment_buffer, lline_buffer);
01440 
01441       fclose(f);
01442    } while (0);
01443    if (comment) {
01444       ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
01445    }
01446 #ifdef AST_INCLUDE_GLOB
01447                if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
01448                   break;
01449                }
01450             }
01451             globfree(&globbuf);
01452          }
01453       }
01454 #endif
01455 
01456    if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg != CONFIG_STATUS_FILEINVALID && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
01457       if (comment_buffer)
01458          ast_free(comment_buffer);
01459       if (lline_buffer)
01460          ast_free(lline_buffer);
01461       comment_buffer = NULL;
01462       lline_buffer = NULL;
01463    }
01464    
01465    if (count == 0)
01466       return NULL;
01467 
01468    return cfg;
01469 }
01470 
01471 
01472 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
01473    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
01474    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
01475    be shocked and mystified as to why things are not showing up in the files! 
01476 
01477    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
01478    and line number are stored for each include, plus the name of the file included, so that these statements may be
01479    included in the output files on a file_save operation. 
01480 
01481    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
01482    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
01483    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
01484    and a header gets added.
01485 
01486    vars and category heads are output in the order they are stored in the config file. So, if the software
01487    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
01488    file/lineno data probably won't get changed.
01489 
01490 */
01491 
01492 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
01493 {
01494    char date[256]="";
01495    time_t t;
01496 
01497    time(&t);
01498    ast_copy_string(date, ctime(&t), sizeof(date));
01499 
01500    fprintf(f1, ";!\n");
01501    fprintf(f1, ";! Automatically generated configuration file\n");
01502    if (strcmp(configfile, fn))
01503       fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
01504    else
01505       fprintf(f1, ";! Filename: %s\n", configfile);
01506    fprintf(f1, ";! Generator: %s\n", generator);
01507    fprintf(f1, ";! Creation Date: %s", date);
01508    fprintf(f1, ";!\n");
01509 }
01510 
01511 static void   inclfile_destroy(void *obj)
01512 {
01513    const struct inclfile *o = obj;
01514 
01515    if (o->fname)
01516       free(o->fname);
01517 }
01518 
01519 
01520 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
01521 {
01522    struct inclfile lookup;
01523    
01524    if (!file || file[0] == 0) {
01525       if (configfile[0] == '/')
01526          ast_copy_string(fn, configfile, fn_size);
01527       else
01528          snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
01529    } else if (file[0] == '/') 
01530       ast_copy_string(fn, file, fn_size);
01531    else
01532       snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
01533    lookup.fname = fn;
01534    *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
01535    if (!(*fi)) {
01536       /* set up a file scratch pad */
01537       struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
01538       fx->fname = ast_strdup(fn);
01539       fx->lineno = 1;
01540       *fi = fx;
01541       ao2_link(fileset, fx);
01542    }
01543 }
01544 
01545 static int count_linefeeds(char *str)
01546 {
01547    int count = 0;
01548 
01549    while (*str) {
01550       if (*str =='\n')
01551          count++;
01552       str++;
01553    }
01554    return count;
01555 }
01556 
01557 static int count_linefeeds_in_comments(struct ast_comment *x)
01558 {
01559    int count = 0;
01560 
01561    while (x) {
01562       count += count_linefeeds(x->cmt);
01563       x = x->next;
01564    }
01565    return count;
01566 }
01567 
01568 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
01569 {
01570    int precomment_lines = count_linefeeds_in_comments(precomments);
01571    int i;
01572 
01573    /* I don't have to worry about those ;! comments, they are
01574       stored in the precomments, but not printed back out.
01575       I did have to make sure that comments following
01576       the ;! header comments were not also deleted in the process */
01577    if (lineno - precomment_lines - fi->lineno < 0) { /* insertions can mess up the line numbering and produce negative numbers that mess things up */
01578       return;
01579    }
01580    for (i=fi->lineno; i<lineno - precomment_lines; i++) {
01581       fprintf(fp,"\n");
01582    }
01583    fi->lineno = lineno+1; /* Advance the file lineno */
01584 }
01585 
01586 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
01587 {
01588    return ast_config_text_file_save(configfile, cfg, generator);
01589 }
01590 
01591 int ast_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
01592 {
01593    FILE *f;
01594    char fn[256];
01595    struct ast_variable *var;
01596    struct ast_category *cat;
01597    struct ast_comment *cmt;
01598    struct ast_config_include *incl;
01599    int blanklines = 0;
01600    struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
01601    struct inclfile *fi = 0;
01602 
01603    /* reset all the output flags, in case this isn't our first time saving this data */
01604 
01605    for (incl=cfg->includes; incl; incl = incl->next)
01606       incl->output = 0;
01607 
01608    /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
01609       are all truncated to zero bytes and have that nice header*/
01610 
01611    for (incl=cfg->includes; incl; incl = incl->next)
01612    {
01613       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*/
01614          FILE *f1;
01615 
01616          set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset, &fi); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
01617          f1 = fopen(fn,"w");
01618          if (f1) {
01619             gen_header(f1, configfile, fn, generator);
01620             fclose(f1); /* this should zero out the file */
01621          } else {
01622             ast_debug(1, "Unable to open for writing: %s\n", fn);
01623             ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01624          }
01625          ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01626          fi = 0;
01627       }
01628    }
01629 
01630    set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
01631 #ifdef __CYGWIN__ 
01632    if ((f = fopen(fn, "w+"))) {
01633 #else
01634    if ((f = fopen(fn, "w"))) {
01635 #endif       
01636       ast_verb(2, "Saving '%s': ", fn);
01637       gen_header(f, configfile, fn, generator);
01638       cat = cfg->root;
01639       fclose(f);
01640       ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01641       
01642       /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
01643       /* since each var, cat, and associated comments can come from any file, we have to be 
01644          mobile, and open each file, print, and close it on an entry-by-entry basis */
01645 
01646       while (cat) {
01647          set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
01648          f = fopen(fn, "a");
01649          if (!f)
01650          {
01651             ast_debug(1, "Unable to open for writing: %s\n", fn);
01652             ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01653             ao2_ref(fileset, -1);
01654             return -1;
01655          }
01656 
01657          /* dump any includes that happen before this category header */
01658          for (incl=cfg->includes; incl; incl = incl->next) {
01659             if (strcmp(incl->include_location_file, cat->file) == 0){
01660                if (cat->lineno > incl->include_location_lineno && !incl->output) {
01661                   if (incl->exec)
01662                      fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01663                   else
01664                      fprintf(f,"#include \"%s\"\n", incl->included_file);
01665                   incl->output = 1;
01666                }
01667             }
01668          }
01669 
01670          insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
01671          /* Dump section with any appropriate comment */
01672          for (cmt = cat->precomments; cmt; cmt=cmt->next) {
01673             char *cmtp = cmt->cmt;
01674             while (*cmtp == ';' && *(cmtp+1) == '!') {
01675                char *cmtp2 = strchr(cmtp+1, '\n');
01676                if (cmtp2)
01677                   cmtp = cmtp2+1;
01678                else cmtp = 0;
01679             }
01680             if (cmtp)
01681                fprintf(f,"%s", cmtp);
01682          }
01683          fprintf(f, "[%s]", cat->name);
01684          if (cat->ignored || !AST_LIST_EMPTY(&cat->template_instances)) {
01685             fprintf(f, "(");
01686             if (cat->ignored) {
01687                fprintf(f, "!");
01688             }
01689             if (cat->ignored && !AST_LIST_EMPTY(&cat->template_instances)) {
01690                fprintf(f, ",");
01691             }
01692             if (!AST_LIST_EMPTY(&cat->template_instances)) {
01693                struct ast_category_template_instance *x;
01694                AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
01695                   fprintf(f,"%s",x->name);
01696                   if (x != AST_LIST_LAST(&cat->template_instances))
01697                      fprintf(f,",");
01698                }
01699             }
01700             fprintf(f, ")");
01701          }
01702          for(cmt = cat->sameline; cmt; cmt=cmt->next)
01703          {
01704             fprintf(f,"%s", cmt->cmt);
01705          }
01706          if (!cat->sameline)
01707             fprintf(f,"\n");
01708          for (cmt = cat->trailing; cmt; cmt=cmt->next) {
01709             if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01710                fprintf(f,"%s", cmt->cmt);
01711          }
01712          fclose(f);
01713          ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01714          fi = 0;
01715          
01716          var = cat->root;
01717          while (var) {
01718             struct ast_category_template_instance *x;
01719             int found = 0;
01720             AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
01721                struct ast_variable *v;
01722                for (v = x->inst->root; v; v = v->next) {
01723                   if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
01724                      found = 1;
01725                      break;
01726                   }
01727                }
01728                if (found)
01729                   break;
01730             }
01731             if (found) {
01732                var = var->next;
01733                continue;
01734             }
01735             set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
01736             f = fopen(fn, "a");
01737             if (!f)
01738             {
01739                ast_debug(1, "Unable to open for writing: %s\n", fn);
01740                ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01741                ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01742                fi = 0;
01743                ao2_ref(fileset, -1);
01744                return -1;
01745             }
01746             
01747             /* dump any includes that happen before this category header */
01748             for (incl=cfg->includes; incl; incl = incl->next) {
01749                if (strcmp(incl->include_location_file, var->file) == 0){
01750                   if (var->lineno > incl->include_location_lineno && !incl->output) {
01751                      if (incl->exec)
01752                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01753                      else
01754                         fprintf(f,"#include \"%s\"\n", incl->included_file);
01755                      incl->output = 1;
01756                   }
01757                }
01758             }
01759             
01760             insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
01761             for (cmt = var->precomments; cmt; cmt=cmt->next) {
01762                if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01763                   fprintf(f,"%s", cmt->cmt);
01764             }
01765             if (var->sameline) 
01766                fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
01767             else  
01768                fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
01769             for (cmt = var->trailing; cmt; cmt=cmt->next) {
01770                if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
01771                   fprintf(f,"%s", cmt->cmt);
01772             }
01773             if (var->blanklines) {
01774                blanklines = var->blanklines;
01775                while (blanklines--)
01776                   fprintf(f, "\n");
01777             }
01778             
01779             fclose(f);
01780             ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01781             fi = 0;
01782             
01783             var = var->next;
01784          }
01785          cat = cat->next;
01786       }
01787       if (!option_debug)
01788          ast_verb(2, "Saved\n");
01789    } else {
01790       ast_debug(1, "Unable to open for writing: %s\n", fn);
01791       ast_verb(2, "Unable to write (%s)", strerror(errno));
01792       ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01793       ao2_ref(fileset, -1);
01794       return -1;
01795    }
01796 
01797    /* Now, for files with trailing #include/#exec statements,
01798       we have to make sure every entry is output */
01799 
01800    for (incl=cfg->includes; incl; incl = incl->next) {
01801       if (!incl->output) {
01802          /* open the respective file */
01803          set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
01804          f = fopen(fn, "a");
01805          if (!f)
01806          {
01807             ast_debug(1, "Unable to open for writing: %s\n", fn);
01808             ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
01809             ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01810             fi = 0;
01811             ao2_ref(fileset, -1);
01812             return -1;
01813          }
01814          
01815          /* output the respective include */
01816          if (incl->exec)
01817             fprintf(f,"#exec \"%s\"\n", incl->exec_file);
01818          else
01819             fprintf(f,"#include \"%s\"\n", incl->included_file);
01820          fclose(f);
01821          incl->output = 1;
01822          ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
01823          fi = 0;
01824       }
01825    }
01826    ao2_ref(fileset, -1); /* this should destroy the hash container */
01827             
01828    return 0;
01829 }
01830 
01831 static void clear_config_maps(void) 
01832 {
01833    struct ast_config_map *map;
01834 
01835    ast_mutex_lock(&config_lock);
01836 
01837    while (config_maps) {
01838       map = config_maps;
01839       config_maps = config_maps->next;
01840       ast_free(map);
01841    }
01842       
01843    ast_mutex_unlock(&config_lock);
01844 }
01845 
01846 static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
01847 {
01848    struct ast_config_map *map;
01849    int length;
01850 
01851    length = sizeof(*map);
01852    length += strlen(name) + 1;
01853    length += strlen(driver) + 1;
01854    length += strlen(database) + 1;
01855    if (table)
01856       length += strlen(table) + 1;
01857 
01858    if (!(map = ast_calloc(1, length)))
01859       return -1;
01860 
01861    map->name = map->stuff;
01862    strcpy(map->name, name);
01863    map->driver = map->name + strlen(map->name) + 1;
01864    strcpy(map->driver, driver);
01865    map->database = map->driver + strlen(map->driver) + 1;
01866    strcpy(map->database, database);
01867    if (table) {
01868       map->table = map->database + strlen(map->database) + 1;
01869       strcpy(map->table, table);
01870    }
01871    map->next = config_maps;
01872 
01873    ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
01874 
01875    config_maps = map;
01876    return 0;
01877 }
01878 
01879 int read_config_maps(void) 
01880 {
01881    struct ast_config *config, *configtmp;
01882    struct ast_variable *v;
01883    char *driver, *table, *database, *stringp, *tmp;
01884    struct ast_flags flags = { 0 };
01885 
01886    clear_config_maps();
01887 
01888    configtmp = ast_config_new();
01889    configtmp->max_include_level = 1;
01890    config = ast_config_internal_load(extconfig_conf, configtmp, flags, "", "extconfig");
01891    if (!config) {
01892       ast_config_destroy(configtmp);
01893       return 0;
01894    }
01895 
01896    for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
01897       char buf[512];
01898       ast_copy_string(buf, v->value, sizeof(buf));
01899       stringp = buf;
01900       driver = strsep(&stringp, ",");
01901 
01902       if ((tmp = strchr(stringp, '\"')))
01903          stringp = tmp;
01904 
01905       /* check if the database text starts with a double quote */
01906       if (*stringp == '"') {
01907          stringp++;
01908          database = strsep(&stringp, "\"");
01909          strsep(&stringp, ",");
01910       } else {
01911          /* apparently this text has no quotes */
01912          database = strsep(&stringp, ",");
01913       }
01914 
01915       table = strsep(&stringp, ",");
01916 
01917       if (!strcmp(v->name, extconfig_conf)) {
01918          ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
01919          continue;
01920       }
01921 
01922       if (!strcmp(v->name, "asterisk.conf")) {
01923          ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
01924          continue;
01925       }
01926 
01927       if (!strcmp(v->name, "logger.conf")) {
01928          ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
01929          continue;
01930       }
01931 
01932       if (!driver || !database)
01933          continue;
01934       if (!strcasecmp(v->name, "sipfriends")) {
01935          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");
01936          append_mapping("sipusers", driver, database, table ? table : "sipfriends");
01937          append_mapping("sippeers", driver, database, table ? table : "sipfriends");
01938       } else if (!strcasecmp(v->name, "iaxfriends")) {
01939          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");
01940          append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
01941          append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
01942       } else 
01943          append_mapping(v->name, driver, database, table);
01944    }
01945       
01946    ast_config_destroy(config);
01947    return 0;
01948 }
01949 
01950 int ast_config_engine_register(struct ast_config_engine *new) 
01951 {
01952    struct ast_config_engine *ptr;
01953 
01954    ast_mutex_lock(&config_lock);
01955 
01956    if (!config_engine_list) {
01957       config_engine_list = new;
01958    } else {
01959       for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
01960       ptr->next = new;
01961    }
01962 
01963    ast_mutex_unlock(&config_lock);
01964    ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
01965 
01966    return 1;
01967 }
01968 
01969 int ast_config_engine_deregister(struct ast_config_engine *del) 
01970 {
01971    struct ast_config_engine *ptr, *last=NULL;
01972 
01973    ast_mutex_lock(&config_lock);
01974 
01975    for (ptr = config_engine_list; ptr; ptr=ptr->next) {
01976       if (ptr == del) {
01977          if (last)
01978             last->next = ptr->next;
01979          else
01980             config_engine_list = ptr->next;
01981          break;
01982       }
01983       last = ptr;
01984    }
01985 
01986    ast_mutex_unlock(&config_lock);
01987 
01988    return 0;
01989 }
01990 
01991 /*! \brief Find realtime engine for realtime family */
01992 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz) 
01993 {
01994    struct ast_config_engine *eng, *ret = NULL;
01995    struct ast_config_map *map;
01996 
01997    ast_mutex_lock(&config_lock);
01998 
01999    for (map = config_maps; map; map = map->next) {
02000       if (!strcasecmp(family, map->name)) {
02001          if (database)
02002             ast_copy_string(database, map->database, dbsiz);
02003          if (table)
02004             ast_copy_string(table, map->table ? map->table : family, tabsiz);
02005          break;
02006       }
02007    }
02008 
02009    /* Check if the required driver (engine) exist */
02010    if (map) {
02011       for (eng = config_engine_list; !ret && eng; eng = eng->next) {
02012          if (!strcasecmp(eng->name, map->driver))
02013             ret = eng;
02014       }
02015    }
02016 
02017    ast_mutex_unlock(&config_lock);
02018    
02019    /* if we found a mapping, but the engine is not available, then issue a warning */
02020    if (map && !ret)
02021       ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
02022 
02023    return ret;
02024 }
02025 
02026 static struct ast_config_engine text_file_engine = {
02027    .name = "text",
02028    .load_func = config_text_file_load,
02029 };
02030 
02031 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file, const char *who_asked)
02032 {
02033    char db[256];
02034    char table[256];
02035    struct ast_config_engine *loader = &text_file_engine;
02036    struct ast_config *result; 
02037 
02038    /* The config file itself bumps include_level by 1 */
02039    if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
02040       ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
02041       return NULL;
02042    }
02043 
02044    cfg->include_level++;
02045 
02046    if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
02047       struct ast_config_engine *eng;
02048 
02049       eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
02050 
02051 
02052       if (eng && eng->load_func) {
02053          loader = eng;
02054       } else {
02055          eng = find_engine("global", db, sizeof(db), table, sizeof(table));
02056          if (eng && eng->load_func)
02057             loader = eng;
02058       }
02059    }
02060 
02061    result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
02062 
02063    if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED)
02064       result->include_level--;
02065    else if (result != CONFIG_STATUS_FILEINVALID)
02066       cfg->include_level--;
02067 
02068    return result;
02069 }
02070 
02071 struct ast_config *ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
02072 {
02073    struct ast_config *cfg;
02074    struct ast_config *result;
02075 
02076    cfg = ast_config_new();
02077    if (!cfg)
02078       return NULL;
02079 
02080    result = ast_config_internal_load(filename, cfg, flags, "", who_asked);
02081    if (!result || result == CONFIG_STATUS_FILEUNCHANGED || result == CONFIG_STATUS_FILEINVALID)
02082       ast_config_destroy(cfg);
02083 
02084    return result;
02085 }
02086 
02087 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
02088 {
02089    struct ast_config_engine *eng;
02090    char db[256];
02091    char table[256];
02092    struct ast_variable *res=NULL;
02093 
02094    eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02095    if (eng && eng->realtime_func) 
02096       res = eng->realtime_func(db, table, ap);
02097 
02098    return res;
02099 }
02100 
02101 struct ast_variable *ast_load_realtime_all(const char *family, ...)
02102 {
02103    struct ast_variable *res;
02104    va_list ap;
02105 
02106    va_start(ap, family);
02107    res = ast_load_realtime_helper(family, ap);
02108    va_end(ap);
02109 
02110    return res;
02111 }
02112 
02113 struct ast_variable *ast_load_realtime(const char *family, ...)
02114 {
02115    struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
02116    va_list ap;
02117 
02118    va_start(ap, family);
02119    res = ast_load_realtime_helper(family, ap);
02120    va_end(ap);
02121 
02122    /* Eliminate blank entries */
02123    for (cur = res; cur; cur = cur->next) {
02124       if (freeme) {
02125          ast_free(freeme);
02126          freeme = NULL;
02127       }
02128 
02129       if (ast_strlen_zero(cur->value)) {
02130          if (prev)
02131             prev->next = cur->next;
02132          else
02133             res = cur->next;
02134          freeme = cur;
02135       } else if (cur->value[0] == ' ' && cur->value[1] == '\0') {
02136          char *vptr = (char *) cur->value;
02137          vptr[0] = '\0';
02138          prev = cur;
02139       } else {
02140          prev = cur;
02141       }
02142    }
02143    return res;
02144 }
02145 
02146 /*! \brief Check if realtime engine is configured for family */
02147 int ast_check_realtime(const char *family)
02148 {
02149    struct ast_config_engine *eng;
02150    if (!ast_realtime_enabled()) {
02151       return 0;   /* There are no engines at all so fail early */
02152    }
02153 
02154    eng = find_engine(family, NULL, 0, NULL, 0);
02155    if (eng)
02156       return 1;
02157    return 0;
02158 }
02159 
02160 /*! \brief Check if there's any realtime engines loaded */
02161 int ast_realtime_enabled()
02162 {
02163    return config_maps ? 1 : 0;
02164 }
02165 
02166 int ast_realtime_require_field(const char *family, ...)
02167 {
02168    struct ast_config_engine *eng;
02169    char db[256];
02170    char table[256];
02171    va_list ap;
02172    int res = -1;
02173 
02174    va_start(ap, family);
02175    eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02176    if (eng && eng->require_func) {
02177       res = eng->require_func(db, table, ap);
02178    }
02179    va_end(ap);
02180 
02181    return res;
02182 }
02183 
02184 int ast_unload_realtime(const char *family)
02185 {
02186    struct ast_config_engine *eng;
02187    char db[256];
02188    char table[256];
02189    int res = -1;
02190 
02191    eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02192    if (eng && eng->unload_func) {
02193       res = eng->unload_func(db, table);
02194    }
02195    return res;
02196 }
02197 
02198 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
02199 {
02200    struct ast_config_engine *eng;
02201    char db[256];
02202    char table[256];
02203    struct ast_config *res = NULL;
02204    va_list ap;
02205 
02206    va_start(ap, family);
02207    eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02208    if (eng && eng->realtime_multi_func) 
02209       res = eng->realtime_multi_func(db, table, ap);
02210    va_end(ap);
02211 
02212    return res;
02213 }
02214 
02215 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02216 {
02217    struct ast_config_engine *eng;
02218    int res = -1;
02219    char db[256];
02220    char table[256];
02221    va_list ap;
02222 
02223    va_start(ap, lookup);
02224    eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02225    if (eng && eng->update_func) 
02226       res = eng->update_func(db, table, keyfield, lookup, ap);
02227    va_end(ap);
02228 
02229    return res;
02230 }
02231 
02232 int ast_update2_realtime(const char *family, ...)
02233 {
02234    struct ast_config_engine *eng;
02235    int res = -1;
02236    char db[256];
02237    char table[256];
02238    va_list ap;
02239 
02240    va_start(ap, family);
02241    eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02242    if (eng && eng->update2_func) 
02243       res = eng->update2_func(db, table, ap);
02244    va_end(ap);
02245 
02246    return res;
02247 }
02248 
02249 int ast_store_realtime(const char *family, ...)
02250 {
02251    struct ast_config_engine *eng;
02252    int res = -1;
02253    char db[256];
02254    char table[256];
02255    va_list ap;
02256 
02257    va_start(ap, family);
02258    eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02259    if (eng && eng->store_func) 
02260       res = eng->store_func(db, table, ap);
02261    va_end(ap);
02262 
02263    return res;
02264 }
02265 
02266 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
02267 {
02268    struct ast_config_engine *eng;
02269    int res = -1;
02270    char db[256];
02271    char table[256];
02272    va_list ap;
02273 
02274    va_start(ap, lookup);
02275    eng = find_engine(family, db, sizeof(db), table, sizeof(table));
02276    if (eng && eng->destroy_func) 
02277       res = eng->destroy_func(db, table, keyfield, lookup, ap);
02278    va_end(ap);
02279 
02280    return res;
02281 }
02282 
02283 /*! \brief Helper function to parse arguments
02284  * See documentation in config.h
02285  */
02286 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
02287    void *p_result, ...)
02288 {
02289    va_list ap;
02290    int error = 0;
02291 
02292    va_start(ap, p_result);
02293    switch (flags & PARSE_TYPE) {
02294    case PARSE_INT32:
02295        {
02296       int32_t *result = p_result;
02297       int32_t x, def = result ? *result : 0,
02298          high = (int32_t)0x7fffffff,
02299          low  = (int32_t)0x80000000;
02300       /* optional argument: first default value, then range */
02301       if (flags & PARSE_DEFAULT)
02302          def = va_arg(ap, int32_t);
02303       if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02304          /* range requested, update bounds */
02305          low = va_arg(ap, int32_t);
02306          high = va_arg(ap, int32_t);
02307       }
02308       x = strtol(arg, NULL, 0);
02309       error = (x < low) || (x > high);
02310       if (flags & PARSE_OUT_RANGE)
02311          error = !error;
02312       if (result)
02313          *result  = error ? def : x;
02314       ast_debug(3,
02315          "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
02316          arg, low, high,
02317          result ? *result : x, error);
02318       break;
02319        }
02320 
02321    case PARSE_UINT32:
02322        {
02323       uint32_t *result = p_result;
02324       uint32_t x, def = result ? *result : 0,
02325          low = 0, high = (uint32_t)~0;
02326       /* optional argument: first default value, then range */
02327       if (flags & PARSE_DEFAULT)
02328          def = va_arg(ap, uint32_t);
02329       if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02330          /* range requested, update bounds */
02331          low = va_arg(ap, uint32_t);
02332          high = va_arg(ap, uint32_t);
02333       }
02334       x = strtoul(arg, NULL, 0);
02335       error = (x < low) || (x > high);
02336       if (flags & PARSE_OUT_RANGE)
02337          error = !error;
02338       if (result)
02339          *result  = error ? def : x;
02340       ast_debug(3,
02341          "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
02342          arg, low, high,
02343          result ? *result : x, error);
02344       break;
02345        }
02346 
02347    case PARSE_DOUBLE:
02348        {
02349       double *result = p_result;
02350       double x, def = result ? *result : 0,
02351          low = -HUGE_VAL, high = HUGE_VAL;
02352 
02353       /* optional argument: first default value, then range */
02354       if (flags & PARSE_DEFAULT)
02355          def = va_arg(ap, double);
02356       if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
02357          /* range requested, update bounds */
02358          low = va_arg(ap, double);
02359          high = va_arg(ap, double);
02360       }
02361       x = strtod(arg, NULL);
02362       error = (x < low) || (x > high);
02363       if (flags & PARSE_OUT_RANGE)
02364          error = !error;
02365       if (result)
02366          *result  = error ? def : x;
02367       ast_debug(3,
02368          "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
02369          arg, low, high,
02370          result ? *result : x, error);
02371       break;
02372        }
02373    case PARSE_INADDR:
02374        {
02375       char *port, *buf;
02376       struct sockaddr_in _sa_buf;   /* buffer for the result */
02377       struct sockaddr_in *sa = p_result ?
02378          (struct sockaddr_in *)p_result : &_sa_buf;
02379       /* default is either the supplied value or the result itself */
02380       struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
02381          va_arg(ap, struct sockaddr_in *) : sa;
02382       struct hostent *hp;
02383       struct ast_hostent ahp;
02384 
02385       memset(&_sa_buf, '\0', sizeof(_sa_buf)); /* clear buffer */
02386       /* duplicate the string to strip away the :port */
02387       port = ast_strdupa(arg);
02388       buf = strsep(&port, ":");
02389       sa->sin_family = AF_INET;  /* assign family */
02390       /*
02391        * honor the ports flag setting, assign default value
02392        * in case of errors or field unset.
02393        */
02394       flags &= PARSE_PORT_MASK; /* the only flags left to process */
02395       if (port) {
02396          if (flags == PARSE_PORT_FORBID) {
02397             error = 1;  /* port was forbidden */
02398             sa->sin_port = def->sin_port;
02399          } else if (flags == PARSE_PORT_IGNORE)
02400             sa->sin_port = def->sin_port;
02401          else /* accept or require */
02402             sa->sin_port = htons(strtol(port, NULL, 0));
02403       } else {
02404          sa->sin_port = def->sin_port;
02405          if (flags == PARSE_PORT_REQUIRE)
02406             error = 1;
02407       }
02408       /* Now deal with host part, even if we have errors before. */
02409       hp = ast_gethostbyname(buf, &ahp);
02410       if (hp)  /* resolved successfully */
02411          memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
02412       else {
02413          error = 1;
02414          sa->sin_addr = def->sin_addr;
02415       }
02416       ast_debug(3,
02417          "extract inaddr from [%s] gives [%s:%d](%d)\n",
02418          arg, ast_inet_ntoa(sa->sin_addr),
02419          ntohs(sa->sin_port), error);
02420          break;
02421        }
02422    }
02423    va_end(ap);
02424    return error;
02425 }
02426 
02427 static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02428 {
02429    struct ast_config_engine *eng;
02430    struct ast_config_map *map;
02431 
02432    switch (cmd) {
02433    case CLI_INIT:
02434       e->command = "core show config mappings";
02435       e->usage =
02436          "Usage: core show config mappings\n"
02437          "  Shows the filenames to config engines.\n";
02438       return NULL;
02439    case CLI_GENERATE:
02440       return NULL;
02441    }
02442    
02443    ast_mutex_lock(&config_lock);
02444 
02445    if (!config_engine_list) {
02446       ast_cli(a->fd, "No config mappings found.\n");
02447    } else {
02448       ast_cli(a->fd, "\n\n");
02449       for (eng = config_engine_list; eng; eng = eng->next) {
02450          ast_cli(a->fd, "\nConfig Engine: %s\n", eng->name);
02451          for (map = config_maps; map; map = map->next) {
02452             if (!strcasecmp(map->driver, eng->name)) {
02453                ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
02454                      map->table ? map->table : map->name);
02455             }
02456          }
02457       }
02458       ast_cli(a->fd,"\n\n");
02459    }
02460    
02461    ast_mutex_unlock(&config_lock);
02462 
02463    return CLI_SUCCESS;
02464 }
02465 
02466 static char *handle_cli_config_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02467 {
02468    struct cache_file_mtime *cfmtime;
02469    char *prev = "", *completion_value = NULL;
02470    int wordlen, which = 0;
02471 
02472    switch (cmd) {
02473    case CLI_INIT:
02474       e->command = "config reload";
02475       e->usage =
02476          "Usage: config reload <filename.conf>\n"
02477          "   Reloads all modules that reference <filename.conf>\n";
02478       return NULL;
02479    case CLI_GENERATE:
02480       if (a->pos > 2) {
02481          return NULL;
02482       }
02483 
02484       wordlen = strlen(a->word);
02485 
02486       AST_LIST_LOCK(&cfmtime_head);
02487       AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02488          /* Skip duplicates - this only works because the list is sorted by filename */
02489          if (strcmp(cfmtime->filename, prev) == 0) {
02490             continue;
02491          }
02492 
02493          /* Core configs cannot be reloaded */
02494          if (ast_strlen_zero(cfmtime->who_asked)) {
02495             continue;
02496          }
02497 
02498          if (++which > a->n && strncmp(cfmtime->filename, a->word, wordlen) == 0) {
02499             completion_value = ast_strdup(cfmtime->filename);
02500             break;
02501          }
02502 
02503          /* Otherwise save that we've seen this filename */
02504          prev = cfmtime->filename;
02505       }
02506       AST_LIST_UNLOCK(&cfmtime_head);
02507 
02508       return completion_value;
02509    }
02510 
02511    if (a->argc != 3) {
02512       return CLI_SHOWUSAGE;
02513    }
02514 
02515    AST_LIST_LOCK(&cfmtime_head);
02516    AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02517       if (!strcmp(cfmtime->filename, a->argv[2])) {
02518          char *buf = alloca(strlen("module reload ") + strlen(cfmtime->who_asked) + 1);
02519          sprintf(buf, "module reload %s", cfmtime->who_asked);
02520          ast_cli_command(a->fd, buf);
02521       }
02522    }
02523    AST_LIST_UNLOCK(&cfmtime_head);
02524 
02525    return CLI_SUCCESS;
02526 }
02527 
02528 static char *handle_cli_config_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02529 {
02530    struct cache_file_mtime *cfmtime;
02531 
02532    switch (cmd) {
02533    case CLI_INIT:
02534       e->command = "config list";
02535       e->usage =
02536          "Usage: config list\n"
02537          "   Show all modules that have loaded a configuration file\n";
02538       return NULL;
02539    case CLI_GENERATE:
02540       return NULL;
02541    }
02542 
02543    AST_LIST_LOCK(&cfmtime_head);
02544    AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
02545       ast_cli(a->fd, "%-20.20s %-50s\n", S_OR(cfmtime->who_asked, "core"), cfmtime->filename);
02546    }
02547    AST_LIST_UNLOCK(&cfmtime_head);
02548 
02549    return CLI_SUCCESS;
02550 }
02551 
02552 static struct ast_cli_entry cli_config[] = {
02553    AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
02554    AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
02555    AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
02556 };
02557 
02558 int register_config_cli() 
02559 {
02560    ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));
02561    return 0;
02562 }