/* config.c --- gereric config file loader
Copyright (C) 1999 Beau Kuiper
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "ftpd.h"
char *getconfigdata_r(CONFIGFILECACHE *cache, int section, char *cmd,
int *occur, int depth, int *error);
/* this, rather messyly, splits an input line into the command and data
where the command is the first word on a line, and the data is the
second.
*/
char *strsplit(char *in, char *cmd, char *data)
{
int pos = 0;
int pos2 = 0;
if (strlen(in) >= 1)
if (in[strlen(in) - 1] == 10)
in[strlen(in) - 1] = 0;
while(in[pos] != 0) /* scan entire string resolving \ and
remove comments */
{
if ((in[pos] == '\\') && (in[pos+1] != 0))
pos++;
else if (in[pos] == '#')
{
in[pos2] = 0;
break;
}
in[pos2] = in[pos];
pos++;
pos2++;
}
in[pos2] = 0;
pos = 0;
pos2 = 0;
while((in[pos] <= 32) && (in[pos] != 0))
pos++;
if (in[pos] == 0)
return(NULL);
while(((unsigned char)(in[pos]) > 32) && (in[pos] != 0))
{
cmd[pos2] = in[pos];
pos2++;
pos++;
}
if (in[pos] == 0)
return(NULL);
cmd[pos2] = 0;
while((in[pos] <= 32) && (in[pos] != 0))
pos++;
if (in[pos] == 0)
return(NULL);
pos2 = 0;
strcpy(data, in + pos);
pos = strlen(data);
/* now trace back spaces and tabes at end of line */
while(data[--pos] <= 32);
data[pos + 1] = 0;
return(data);
}
/* This will open and load each line in the specified config file (filename)
and then split it up into constitute parts before sending them off to
a handler you specify with a void pointer (to be used as the structure
that the config file will load each item into) */
int loadconfigfile(char *filename, int (* confighandler)(char *, char *, int, void *),
void *configdata, int besecure, int *handlererror, int *linenum)
{
NEWFILE *configfile;
char *inputline;
char *cmd = NULL;
char *data = NULL;
int result;
*linenum = 0;
configfile = nfopen(filename);
if (configfile == NULL)
return(CONFIG_FILE_ERROR);
if (besecure)
if (!isfilesafe(configfile->fd))
{
nfclose(configfile);
return(CONFIG_FILE_UNSAFE);
}
result = CONFIG_OK;
while((inputline = nfgetcs(configfile, '\n')) != NULL)
{
int a = strlen(inputline) + 1;
reallocwrapper(a, (void **)&cmd);
reallocwrapper(a, (void **)&data);
(*linenum)++;
/* This is a fix for config files written with DOS
based software */
a = strlen(inputline);
if (a >= 2)
if (inputline[a-2] == '\r')
inputline[a-2] = 0;
if (strsplit(inputline, cmd, data) != NULL)
{
*handlererror = confighandler(cmd, data, *linenum, configdata);
if (*handlererror)
{
result = CONFIG_HANDLER_ERROR;
freewrapper(inputline);
break;
}
}
freewrapper(inputline);
}
freeifnotnull(cmd);
freeifnotnull(data);
nfclose(configfile);
return(result);
}
/* this code is quite tricky, but it saves much memory */
int cachemaker(char *cmd, char *data, int linenum, void *c)
{
char newcmd[32];
CONFIGFILECACHE *cache = (CONFIGFILECACHE *)c;
int count;
if (strcasecmp(cmd, "[section]") == 0)
{
/* add new section */
if (strcasecmp(data, "none") == 0)
return(CFC_SECTION_NONE);
/* check to see if section already is there */
for (count = 0; count < cache->sectioncount; count++)
if (strcasecmp(data, cache->sectionlist + cache->sectionindex[count]) == 0)
return(CFC_SECTION_EXISTS);
/* nupe, add it, may be a little slow, but is efficient enough */
cache->sectioncount++;
reallocwrapper(sizeof(int) * cache->sectioncount, (void *)&cache->sectionindex);
reallocwrapper(sizeof(int) * cache->sectioncount, (void *)&cache->sectionlinenum);
cache->sectionindex[cache->sectioncount - 1] = cache->sectionlen;
cache->sectionlinenum[cache->sectioncount - 1] = linenum;
cache->sectionlen += strlen(data) + 1;
reallocwrapper(cache->sectionlen, (void *)&(cache->sectionlist));
strcpy(cache->sectionlist + cache->sectionindex[cache->sectioncount - 1], data);
reallocwrapper(sizeof(int) * cache->sectioncount, (void **)&(cache->index));
cache->index[cache->sectioncount - 1] = cache->datacount;
}
else
{
if (cache->sectioncount == 0)
return(CFC_NO_SECTION);
/* change an include to a special character for quick
recognision. Puts this special character in a new
string that is long enough for the machine type
muddleftpd is running on */
if (strcasecmp(cmd, "include") == 0)
{
memset(newcmd, 1, sizeof(int) + 2);
newcmd[sizeof(int)+1] = 0;
cmd = newcmd;
}
/* add it to the data list */
cache->datacount++;
reallocwrapper(sizeof(char *) * cache->datacount, (void *)&(cache->dataindex));
reallocwrapper(sizeof(char *) * cache->datacount, (void *)&(cache->cmdindex));
reallocwrapper(sizeof(int) * cache->datacount, (void *)&cache->datalinenum);
cache->dataindex[cache->datacount - 1] = cache->datalen;
cache->cmdindex[cache->datacount - 1] = cache->cmdlen;
cache->datalinenum[cache->datacount - 1] = linenum;
cache->datalen += strlen(data) + 1;
cache->cmdlen += strlen(cmd) + 1;
reallocwrapper(cache->datalen, (void *)&(cache->datalist));
reallocwrapper(cache->cmdlen, (void *)&(cache->cmdlist));
strcpy(cache->datalist + cache->dataindex[cache->datacount - 1], data);
strcpy(cache->cmdlist + cache->cmdindex[cache->datacount - 1], cmd);
}
return(FALSE);
}
CONFIGFILECACHE *loadconfigcache(char *filename, int *linenum, int *error)
{
CONFIGFILECACHE *newcache = NULL;
int result, count;
newcache = mallocwrapper(sizeof(CONFIGFILECACHE));
newcache->sectioncount = 0;
newcache->datacount = 0;
newcache->sectionlen = 0;
newcache->datalen = 0;
newcache->cmdlen = 0;
newcache->sectionindex = NULL;
newcache->dataindex = NULL;
newcache->cmdindex = NULL;
newcache->sectionlist = NULL;
newcache->datalist = NULL;
newcache->cmdlist = NULL;
newcache->index = NULL;
newcache->sectionlinenum = NULL;
newcache->datalinenum = NULL;
result = loadconfigfile(filename, cachemaker, newcache, TRUE, error, linenum);
if (result != CONFIG_OK)
{
if (result != CONFIG_HANDLER_ERROR)
*error = result;
freeconfigcache(newcache);
return(NULL);
}
/* resolve all include references! */
for (count = 0; count < newcache->datacount; count++)
{
if (*(newcache->cmdlist + newcache->cmdindex[count]) == 1)
{
/* found an include item, resolve the include reference */
int section = getsectionid(newcache, newcache->datalist + newcache->dataindex[count]);
if (section == -1)
{
*error = CFC_INCLUDE_ERROR;
*linenum = newcache->datalinenum[count];
freeconfigcache(newcache);
return(NULL);
}
memcpy((char *)(newcache->cmdlist + newcache->cmdindex[count] + 1), §ion, sizeof(section));
}
}
/* check for recursive includes! */
for (count = 0; count < newcache->sectioncount; count++)
{
int deptherror = FALSE;
int occur = 1;
char *data;
/* search for something to check for looping */
data = getconfigdata_r(newcache, count, "[section]", &occur, 1, &deptherror);
if (deptherror)
{
*error = CFC_INCLUDE_LOOP;
*linenum = newcache->sectionlinenum[count];
freeconfigcache(newcache);
return(NULL);
}
}
freeifnotnull(newcache->sectionlinenum);
newcache->sectionlinenum = NULL;
freeifnotnull(newcache->datalinenum);
newcache->datalinenum = NULL;
return(newcache);
}
char *config_errorstr(int result)
{
static char longerror[256];
switch(result)
{
case CONFIG_OK:
return("No errors.");
case CONFIG_HANDLER_ERROR:
return("Config handler returned error result.");
case CONFIG_FILE_UNSAFE:
return("File was not deemed safe. Must not be group/world writable and must be owned by process owner.");
case CONFIG_FILE_ERROR:
snprintf(longerror, 256, "Error opening file (%s).", strerror(errno));
return(longerror);
case CFC_INCLUDE_ERROR:
return("Included section could not be found.");
case CFC_SECTION_NONE:
return("Section cannot be named 'none'.");
case CFC_SECTION_EXISTS:
return("Section already exists.");
case CFC_NO_SECTION:
return("No section has been decleared for config data.");
case CFC_INCLUDE_LOOP:
return("A possible recursive loop was found in section.");
default:
return("Unknown error!");
}
}
int getsectionid(CONFIGFILECACHE *cache, char *section)
{
int count;
for(count = 0; count < cache->sectioncount; count++)
if (strcasecmp(section, cache->sectionlist + cache->sectionindex[count]) == 0)
return(count);
return(-1);
}
char *getconfigdata_r(CONFIGFILECACHE *cache, int section, char *cmd, int *occur, int depth, int *error)
{
int count, last;
int first;
int num;
char *res;
assert(section < cache->sectioncount);
assert(section >= 0);
assert(*occur > 0);
if (depth > MAXINCLUDEDEPTH)
{
*error = TRUE;
return(NULL);
}
first = cache->index[section];
/* find the last datalist element to look at */
if (section == cache->sectioncount - 1)
last = cache->datacount;
else
last = cache->index[section + 1];
/* find the occurance wanted */
for(count = first; count < last; count++)
{
if (*(cache->cmdlist + cache->cmdindex[count]) == 1)
{
memcpy(&num, (char *)(cache->cmdlist + cache->cmdindex[count] + 1), sizeof(num));
res = getconfigdata_r(cache, num, cmd,
occur, depth + 1, error);
if (res)
return(res);
}
if (strcasecmp(cmd, cache->cmdlist + cache->cmdindex[count]) == 0)
(*occur)--;
if (*occur == 0)
return(cache->datalist + cache->dataindex[count]);
}
/* if not found, return NULL */
return(NULL);
}
char *getconfigdata(CONFIGFILECACHE *cache, int section, char *cmd, int occur)
{
int error;
return(getconfigdata_r(cache, section, cmd, &occur, 1, &error));
}
char **makeconfiglist(CONFIGFILECACHE *cache, char *section, char *label)
{
int sectnum;
char **out;
char *setting;
int occur;
sectnum = getsectionid(cache, section);
if (sectnum == -1)
return(NULL);
out = mallocwrapper(sizeof(char *));
occur = 1;
while ((setting = getconfigdata(cache, sectnum, label, occur)))
{
out[occur - 1] = setting;
occur++;
reallocwrapper(sizeof(char *) * occur, (void *)&out);
}
out[occur - 1] = NULL;
return(out);
}
void loadintfromconfig(CONFIGFILECACHE *cache, int section, char *setting,
int *to, int def)
{
char *set = getconfigdata(cache, section, setting, 1);
if (set)
sscanf(set, "%d", to);
else
*to = def;
}
void loadstrfromconfig(CONFIGFILECACHE *cache, int section, char *setting,
char **to, char *def)
{
char *set = getconfigdata(cache, section, setting, 1);
if (set)
*to = set;
else
*to = def;
}
void freeconfigcache(CONFIGFILECACHE *cache)
{
freeifnotnull(cache->sectionindex);
freeifnotnull(cache->dataindex);
freeifnotnull(cache->cmdindex);
freeifnotnull(cache->sectionlist);
freeifnotnull(cache->index);
freeifnotnull(cache->datalist);
freeifnotnull(cache->cmdlist);
freeifnotnull(cache->sectionlinenum);
freeifnotnull(cache->datalinenum);
freewrapper(cache);
}
syntax highlighted by Code2HTML, v. 0.9.1