/* 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); }