jabberd2  2.2.16
util/config.c
Go to the documentation of this file.
00001 /*
00002  * jabberd - Jabber Open Source Server
00003  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
00004  *                    Ryan Eatmon, Robert Norris
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
00019  */
00020 
00021 #include "util.h"
00022 #include "expat.h"
00023 
00025 config_t config_new(void)
00026 {
00027     config_t c;
00028 
00029     c = (config_t) calloc(1, sizeof(struct config_st));
00030 
00031     c->hash = xhash_new(501);
00032 
00033     return c;
00034 }
00035 
00036 struct build_data
00037 {
00038     nad_t               nad;
00039     int                 depth;
00040 };
00041 
00042 static void _config_startElement(void *arg, const char *name, const char **atts)
00043 {
00044     struct build_data *bd = (struct build_data *) arg;
00045     int i = 0;
00046 
00047     nad_append_elem(bd->nad, -1, (char *) name, bd->depth);
00048     while(atts[i] != NULL)
00049     {
00050         nad_append_attr(bd->nad, -1, (char *) atts[i], (char *) atts[i + 1]);
00051         i += 2;
00052     }
00053 
00054     bd->depth++;
00055 }
00056 
00057 static void _config_endElement(void *arg, const char *name)
00058 {
00059     struct build_data *bd = (struct build_data *) arg;
00060 
00061     bd->depth--;
00062 }
00063 
00064 static void _config_charData(void *arg, const char *str, int len)
00065 {
00066     struct build_data *bd = (struct build_data *) arg;
00067 
00068     nad_append_cdata(bd->nad, (char *) str, len, bd->depth);
00069 }
00070 
00071 static char *_config_expandx(config_t c, const char *value, int l);
00072 
00074 int config_load(config_t c, const char *file)
00075 {
00076     return config_load_with_id(c, file, 0);
00077 }
00078 
00080 int config_load_with_id(config_t c, const char *file, const char *id)
00081 {
00082     struct build_data bd;
00083     FILE *f;
00084     XML_Parser p;
00085     int done, len, end, i, j, attr;
00086     char buf[1024], *next;
00087     struct nad_elem_st **path;
00088     config_elem_t elem;
00089     int rv = 0;
00090     
00091     /* open the file */
00092     f = fopen(file, "r");
00093     if(f == NULL)
00094     {
00095         fprintf(stderr, "config_load: couldn't open %s for reading: %s\n", file, strerror(errno));
00096         return 1;
00097     }
00098 
00099     /* new parser */
00100     p = XML_ParserCreate(NULL);
00101     if(p == NULL)
00102     {
00103         fprintf(stderr, "config_load: couldn't allocate XML parser\n");
00104         fclose(f);
00105         return 1;
00106     }
00107 
00108     /* nice new nad to parse it into */
00109     bd.nad = nad_new();
00110     bd.depth = 0;
00111 
00112     /* setup the parser */
00113     XML_SetUserData(p, (void *) &bd);
00114     XML_SetElementHandler(p, _config_startElement, _config_endElement);
00115     XML_SetCharacterDataHandler(p, _config_charData);
00116 
00117     for(;;)
00118     {
00119         /* read that file */
00120         len = fread(buf, 1, 1024, f);
00121         if(ferror(f))
00122         {
00123             fprintf(stderr, "config_load: read error: %s\n", strerror(errno));
00124             XML_ParserFree(p);
00125             fclose(f);
00126             nad_free(bd.nad);
00127             return 1;
00128         }
00129         done = feof(f);
00130 
00131         /* parse it */
00132         if(!XML_Parse(p, buf, len, done))
00133         {
00134             fprintf(stderr, "config_load: parse error at line %llu: %s\n", (unsigned long long) XML_GetCurrentLineNumber(p), XML_ErrorString(XML_GetErrorCode(p)));
00135             XML_ParserFree(p);
00136             fclose(f);
00137             nad_free(bd.nad);
00138             return 1;
00139         }
00140 
00141         if(done)
00142             break;
00143     }
00144 
00145     /* done reading */
00146     XML_ParserFree(p);
00147     fclose(f);
00148 
00149     // Put id if specified
00150     if (id) {
00151         elem = pmalloco(xhash_pool(c->hash), sizeof(struct config_elem_st));
00152         xhash_put(c->hash, pstrdup(xhash_pool(c->hash), "id"), elem);
00153         elem->values = calloc(1, sizeof(char *));
00154         elem->values[0] = pstrdup(xhash_pool(c->hash), id);
00155         elem->nvalues = 1;
00156     }
00157 
00158     /* now, turn the nad into a config hash */
00159     path = NULL;
00160     len = 0, end = 0;
00161     /* start at 1, so we skip the root element */
00162     for(i = 1; i < bd.nad->ecur && rv == 0; i++)
00163     {
00164         /* make sure we have enough room to add this element to our path */
00165         if(end <= bd.nad->elems[i].depth)
00166         {
00167             end = bd.nad->elems[i].depth + 1;
00168             path = (struct nad_elem_st **) realloc((void *) path, sizeof(struct nad_elem_st *) * end);
00169         }
00170 
00171         /* save this path element */
00172         path[bd.nad->elems[i].depth] = &bd.nad->elems[i];
00173         len = bd.nad->elems[i].depth + 1;
00174 
00175         /* construct the key from the current path */
00176         next = buf;
00177         for(j = 1; j < len; j++)
00178         {
00179             strncpy(next, bd.nad->cdata + path[j]->iname, path[j]->lname);
00180             next = next + path[j]->lname;
00181             *next = '.';
00182             next++;
00183         }
00184         next--;
00185         *next = '\0';
00186 
00187         /* find the config element for this key */
00188         elem = xhash_get(c->hash, buf);
00189         if(elem == NULL)
00190         {
00191             /* haven't seen it before, so create it */
00192             elem = pmalloco(xhash_pool(c->hash), sizeof(struct config_elem_st));
00193             xhash_put(c->hash, pstrdup(xhash_pool(c->hash), buf), elem);
00194         }
00195 
00196         /* make room for this value .. can't easily realloc off a pool, so
00197          * we do it this way and let _config_reaper clean up */
00198         elem->values = realloc((void *) elem->values, sizeof(char *) * (elem->nvalues + 1));
00199 
00200         /* and copy it in */
00201         if(NAD_CDATA_L(bd.nad, i) > 0) {
00202             // Expand values
00203 
00204             const char *val = _config_expandx(c, NAD_CDATA(bd.nad, i), NAD_CDATA_L(bd.nad, i));
00205 
00206             if (!val) {
00207                 rv = 1;
00208                 break;
00209             }
00210             // Make a copy
00211             elem->values[elem->nvalues] = val;
00212         } else {
00213             elem->values[elem->nvalues] = "1";
00214         }
00215 
00216         /* make room for the attribute lists */
00217         elem->attrs = realloc((void *) elem->attrs, sizeof(char **) * (elem->nvalues + 1));
00218         elem->attrs[elem->nvalues] = NULL;
00219 
00220         /* count the attributes */
00221         for(attr = bd.nad->elems[i].attr, j = 0; attr >= 0; attr = bd.nad->attrs[attr].next, j++);
00222 
00223         /* make space */
00224         elem->attrs[elem->nvalues] = pmalloc(xhash_pool(c->hash), sizeof(char *) * (j * 2 + 2));
00225 
00226         /* if we have some */
00227         if(j > 0)
00228         {
00229             /* copy them in */
00230             j = 0;
00231             attr = bd.nad->elems[i].attr;
00232             while(attr >= 0)
00233             {
00234                 elem->attrs[elem->nvalues][j] = pstrdupx(xhash_pool(c->hash), NAD_ANAME(bd.nad, attr), NAD_ANAME_L(bd.nad, attr));
00235                 elem->attrs[elem->nvalues][j + 1] = pstrdupx(xhash_pool(c->hash), NAD_AVAL(bd.nad, attr), NAD_AVAL_L(bd.nad, attr));
00236 
00237         /*
00238          * pstrdupx(blob, 0) returns NULL - which means that later
00239          * there's no way of telling whether an attribute is defined
00240          * as empty, or just not defined. This fixes that by creating
00241          * an empty string for attributes which are defined empty
00242          */
00243                 if (NAD_AVAL_L(bd.nad, attr)==0) {
00244                     elem->attrs[elem->nvalues][j + 1] = pstrdup(xhash_pool(c->hash), "");
00245                 } else {
00246                     elem->attrs[elem->nvalues][j + 1] = pstrdupx(xhash_pool(c->hash), NAD_AVAL(bd.nad, attr), NAD_AVAL_L(bd.nad, attr));
00247                 }
00248                 j += 2;
00249                 attr = bd.nad->attrs[attr].next;
00250             }
00251         }
00252 
00253         /* do this and we can use j_attr */
00254         elem->attrs[elem->nvalues][j] = NULL;
00255         elem->attrs[elem->nvalues][j + 1] = NULL;
00256 
00257         elem->nvalues++;
00258     }
00259 
00260     if(path != NULL)
00261         free(path);
00262 
00263     if(c->nad != NULL)
00264         nad_free(c->nad);
00265     c->nad = bd.nad;
00266 
00267     return rv;
00268 }
00269 
00271 config_elem_t config_get(config_t c, const char *key)
00272 {
00273     return xhash_get(c->hash, key);
00274 }
00275 
00277 const char *config_get_one(config_t c, const char* key, int num)
00278 {
00279     config_elem_t elem = xhash_get(c->hash, key);
00280 
00281     if(elem == NULL)
00282         return NULL;
00283 
00284     if(num >= elem->nvalues)
00285         return NULL;
00286 
00287     return elem->values[num];
00288 }
00289 
00291 const char *config_get_one_default(config_t c, const char *key, int num, const char *default_value)
00292 {
00293     const char *rv = config_get_one(c, key, num);
00294 
00295     if (!rv)
00296         rv = default_value;
00297 
00298     return rv;
00299 };
00300 
00301 
00303 int config_count(config_t c, const char *key)
00304 {
00305     config_elem_t elem = xhash_get(c->hash, key);
00306 
00307     if(elem == NULL)
00308         return 0;
00309 
00310     return elem->nvalues;
00311 }
00312 
00314 char *config_get_attr(config_t c, const char *key, int num, const char *attr)
00315 {
00316     config_elem_t elem = xhash_get(c->hash, key);
00317 
00318     if(num >= elem->nvalues || elem->attrs == NULL || elem->attrs[num] == NULL)
00319         return NULL;
00320 
00321     return j_attr((const char **) elem->attrs[num], attr);
00322 }
00323 
00325 static void _config_reaper(const char *key, int keylen, void *val, void *arg)
00326 {
00327     config_elem_t elem = (config_elem_t) val;
00328 
00329     free(elem->values);
00330     free(elem->attrs);
00331 }
00332 
00333 char *config_expand(config_t c, const char *value)
00334 {
00335     return _config_expandx(c, value, strlen(value));
00336 }
00337 
00338 static char *_config_expandx(config_t c, const char *value, int l)
00339 {
00340 #ifdef CONFIGEXPAND_GUARDED
00341     static char guard[] = "deadbeaf";
00342 #endif
00343 
00344 //     fprintf(stderr, "config_expand: Expanding '%s'\n", value);
00345     char *s = strndup(value, l);
00346 
00347     char *var_start, *var_end;
00348 
00349     while (var_start = strstr(s, "${")) {
00350 //         fprintf(stderr, "config_expand: processing '%s'\n", s);
00351         var_end = strstr(var_start + 2, "}");
00352 
00353         if (var_end) {
00354             char *tail = var_end + 1;
00355             char *var = var_start + 2;
00356             *var_end = 0;
00357 
00358 //             fprintf(stderr, "config_expand: Var '%s', tail is '%s'\n", var, tail);
00359 
00360             const char *var_value = config_get_one(c, var, 0);
00361 
00362             if (var_value) {
00363                 int len = (var_start - s) + strlen(tail) + strlen(var_value) + 1;
00364 
00365 #ifdef CONFIGEXPAND_GUARDED
00366                 len += sizeof(guard);
00367 #endif
00368                 char *expanded_str = calloc(len, 1);
00369 
00370 #ifdef CONFIGEXPAND_GUARDED
00371                 char *p_guard = expanded_str + len - sizeof(guard);
00372                 strncpy(p_guard, guard, sizeof(guard));
00373 #endif
00374 
00375                 char *p = expanded_str;
00376                 strncpy(expanded_str, s, var_start - s);
00377                 p += var_start - s;
00378 
00379                 strcpy(p, var_value);
00380                 p += strlen(var_value);
00381 
00382                 strcpy(p, tail);
00383 
00384                 free(s);
00385                 s = expanded_str;
00386             } else {
00387                 fprintf(stderr, "config_expand: Have no '%s' defined\n", var);
00388                 free(s);
00389                 s = 0;
00390                 break;
00391             }
00392         } else {
00393             fprintf(stderr, "config_expand: } missmatch\n");
00394             free(s);
00395             s = 0;
00396             break;
00397         }
00398     }
00399 
00400     if (s) {
00401         char *retval = pstrdup(xhash_pool(c->hash), s);
00402         free(s);
00403         return retval;
00404     } else {
00405         return 0;
00406     }
00407 }
00408 
00410 void config_free(config_t c)
00411 {
00412     xhash_walk(c->hash, _config_reaper, NULL);
00413 
00414     xhash_free(c->hash);
00415 
00416     nad_free(c->nad);
00417 
00418     free(c);
00419 }