aboutsummaryrefslogtreecommitdiff
path: root/src/common/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/config.c')
-rw-r--r--src/common/config.c347
1 files changed, 347 insertions, 0 deletions
diff --git a/src/common/config.c b/src/common/config.c
new file mode 100644
index 000000000..99961f147
--- /dev/null
+++ b/src/common/config.c
@@ -0,0 +1,347 @@
+/*
+ * config.c
+ * Functions for the manipulation of configuration files.
+ *
+ * Matej Pfajfar <mp292@cam.ac.uk>
+ */
+
+/*
+ * Changes :
+ * $Log$
+ * Revision 1.1 2002/06/26 22:45:50 arma
+ * Initial revision
+ *
+ * Revision 1.7 2002/04/02 14:27:11 badbytes
+ * Final finishes.
+ *
+ * Revision 1.6 2002/01/27 19:23:03 mp292
+ * Fixed a bug in parameter checking.
+ *
+ * Revision 1.5 2002/01/26 18:42:15 mp292
+ * Reviewed according to Secure-Programs-HOWTO.
+ *
+ * Revision 1.4 2002/01/21 21:07:56 mp292
+ * Parameter checking was missing in some functions.
+ *
+ * Revision 1.3 2001/12/07 09:38:03 badbytes
+ * Tested.
+ *
+ * Revision 1.2 2001/12/06 15:43:50 badbytes
+ * config.c compiles. Proceeding to test it.
+ *
+ * Revision 1.1 2001/11/22 01:20:27 mp292
+ * Functions for dealing with configuration files.
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "config.h"
+#include "log.h"
+
+/* open configuration file for reading */
+FILE *open_config(const unsigned char *filename)
+{
+ FILE *f;
+
+ if (filename) /* non-NULL filename */
+ {
+ if (strspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) == strlen(filename)) /* filename consists of legal characters only */
+ {
+ f = fopen(filename, "r");
+
+ return f;
+ } /* filename consists of legal characters only */
+ else /* illegal values in filename */
+ {
+ return NULL;
+ } /* illegal values in filename */
+ } /* non-NULL filename */
+ else /* NULL filename */
+ return NULL;
+}
+
+/* close configuration file */
+int close_config(FILE *f)
+{
+ int retval = 0;
+
+ if (f) /* valid file descriptor */
+ {
+ retval = fclose(f);
+
+ return retval;
+ } /* valid file descriptor */
+ else
+ return -1;
+}
+
+/* parse the config file and obtain the required option values */
+int parse_config(FILE *f, config_opt_t *option)
+{
+ unsigned char keyword[CONFIG_KEYWORD_MAXLEN+1]; /* for storing the option keyword */
+
+ unsigned char *buffer = NULL; /* option value */
+ size_t buflen = 0;
+
+ char *errtest = NULL; /* used for testing correctness of strtol() etc. */
+
+ unsigned int i_keyword = 0; /* current position within keyword */
+ unsigned int i_buf = 0; /* current position within buffer */
+
+ char c=0; /* input char */
+
+ unsigned int state=0; /* internal state
+ * 0 - trying to find a keyword
+ * 1 - reading a keyword
+ * 2 - keyword read and recognized, looking for the option value
+ * 3 - reading the option value
+ * 4 - option value read
+ * 5 - inside a comment
+ */
+
+ int retval=0; /* return value */
+
+ int lineno=1; /* current line number */
+ int curopt=-1; /* current option, as an indexed in config_opt_t */
+ int i;
+
+ if ( (f==NULL) || (option==NULL) ) /* invalid parameters */
+ return -1;
+
+ fseek(f,0,SEEK_SET); /* make sure we start at the beginning of file */
+
+ for (;;) /* infinite loop */
+ {
+ c = getc(f);
+
+ if ((c == '\n') || (c == EOF))
+ {
+ if (state == 1) /* reading a keyboard */
+ {
+ log(LOG_ERR,"Error parsing the configuration file on line %d.", lineno);
+ i_keyword = 0;
+ state = 0;
+ retval = -1;
+ break;
+ } /* reading a keyboard */
+ else if (state == 2) /* keyword read and recognized */
+ {
+ log(LOG_ERR,"Error parsing option %s on line %d.",option[curopt].keyword, lineno);
+ i_keyword = 0;
+ state = 0;
+ option[curopt].err=-1;
+ retval = -1;
+ break;
+ } /* keyboard read and recognized */
+ else if (state == 3) /* reading the option value */
+ {
+ buffer[i_buf++] = 0; /* add NULL character to terminate the string */
+ state = 4;
+ /* conversion and copying the value into config_opt_t is done later on */
+ } /* reading the option value */
+ else if (state == 5) /* reached end of comment */
+ state = 0;
+
+ if (c == EOF)
+ {
+ log(LOG_DEBUG,"parse_config() : Reached eof on line %d.",lineno);
+ break;
+ }
+ else
+ {
+ log(LOG_DEBUG,"parse_config() : Reached eol on line %d.", lineno);
+ lineno++;
+ }
+ }
+ else if ( (state==0) && (c == '#') ) /* lines beginning with # are ignored */
+ {
+ log(LOG_DEBUG,"parse_config() : Line %d begins with #.",lineno);
+ state = 5;
+ }
+ else if ( (state==0) && (isspace(c)) ) /* leading whitespace is ignored */
+ ;
+ else if ( (state==1) && (isspace(c)) ) /* have apparently read in all of the keyword */
+ {
+ keyword[i_keyword++] = 0;
+ curopt = -1;
+ for (i=0;option[i].keyword != NULL;i++) /* try and identify the keyword */
+ {
+ if (!strncmp(keyword,option[i].keyword,CONFIG_KEYWORD_MAXLEN))
+ {
+ curopt = i;
+ break;
+ }
+ } /* try and identify the keyword */
+
+ if (curopt == -1) /* can't recognise the keyword */
+ {
+ log(LOG_ERR,"Error parsing the configuration file. Cannot recognize keyword %s on line %d.",keyword,lineno);
+ retval=-1;
+ break;
+ }
+ else
+ state = 2;
+ }
+ else if ( (state==2) && (isspace(c)) ) /* whitespace separating keyword and value is ignored */
+ ;
+ else if ( (state==3) && (isspace(c)) ) /* have apparently finished reading the option value */
+ {
+ buffer[i_buf++]=0;
+ state = 4;
+ }
+ else /* all other characters */
+ {
+ if (state == 0) /* first character of the keyword */
+ {
+ log(LOG_DEBUG, "parse_config() : %c is the start of a keyword on line %d.",c,lineno);
+ state = 1;
+ i_keyword = 0;
+ keyword[i_keyword++] = c;
+ }
+ else if (state == 1) /* keep on reading the keyword */
+ {
+ log(LOG_DEBUG,"parse_config() : %c is a character in the keyword on line %d.",c,lineno);
+ if (i_keyword < CONFIG_KEYWORD_MAXLEN) /* check for buffer overflow */
+ keyword[i_keyword++] = c;
+ else
+ {
+ log(LOG_ERR,"Error parsing the configuration file. Keyword on line %d exceeds %d characters.",lineno,CONFIG_KEYWORD_MAXLEN);
+ retval=-1;
+ break;
+ }
+ }
+ else if (state == 2) /* first character of the value */
+ {
+ log(LOG_DEBUG,"parse_config() : %c is the first character of the option value on line %d.",c,lineno);
+ state = 3;
+ i_buf=0;
+ buflen = CONFIG_VALUE_MAXLEN+1; /* allocate memory for the value buffer */
+ buffer = (char *)malloc(buflen);
+ if (!buffer)
+ {
+ log(LOG_ERR,"Could not allocate memory.");
+ retval=-1;
+ break;
+ } else
+ buffer[i_buf++]=c;
+ }
+ else if (state == 3) /* keep on reading the value */
+ {
+ log(LOG_DEBUG,"parse_config() : %c is a character in the value of the keyword on line %d.",c,lineno);
+ if (i_buf >= buflen)
+ {
+ log(LOG_ERR,"Length of keyword value on line %u exceeds the length limit (%u).",lineno, CONFIG_VALUE_MAXLEN);
+ retval=-1;
+ break;
+ }
+
+ buffer[i_buf++]=c;
+ }
+ else if (state == 5)
+ ; /* character is part of a comment, skip */
+ else /* unexpected error */
+ {
+ log(LOG_ERR,"Unexpected error while parsing the configuration file.");
+ log(LOG_DEBUG,"parse_config() : Encountered a non-delimiter character while not in states 0,1,2 or 3!");
+ break;
+ }
+ }
+
+ if (state==4) /* convert the value of the option to the appropriate type and write into OPT */
+ {
+ switch(option[curopt].r_type) /* consider each type separately */
+ {
+ case CONFIG_TYPE_STRING:
+ /* resize the buffer to fit the data exactly */
+ buffer = (char *)realloc(buffer,i_buf);
+ if (!buffer)
+ {
+ log(LOG_ERR,"Could not allocate memory.");
+ return -1;
+ }
+ option[curopt].r.str = buffer;
+ option[curopt].err = 1;
+ break;
+
+ case CONFIG_TYPE_CHAR:
+ option[curopt].r.c = *buffer;
+ option[curopt].err = 1;
+ break;
+
+ case CONFIG_TYPE_INT:
+ errtest = NULL;
+ option[curopt].r.i = (int)strtol(buffer,&errtest,0);
+ if ((unsigned char *)errtest == buffer)
+ {
+ log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno);
+ option[curopt].err = -1;
+ if (buffer)
+ free(buffer);
+ return -1;
+ }
+ else
+ option[curopt].err = 1;
+ break;
+
+ case CONFIG_TYPE_LONG:
+ errtest = NULL;
+ option[curopt].r.l = strtol(buffer,&errtest,0);
+ if ((unsigned char *)errtest == buffer)
+ {
+ log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno);
+ option[curopt].err = -1;
+ if (buffer)
+ free(buffer);
+ return -1;
+ }
+ else
+ option[curopt].err = 1;
+ break;
+
+ case CONFIG_TYPE_DOUBLE:
+ errtest = NULL;
+ option[curopt].r.d = strtod(buffer,&errtest);
+ if ((unsigned char *)errtest == buffer)
+ {
+ log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno);
+ option[curopt].err = -1;
+ if (buffer)
+ free(buffer);
+ return -1;
+ }
+ else
+ option[curopt].err = 1;
+ break;
+
+ default: /* unexpected type */
+ log(LOG_ERR, "Error parsing configuration file. Unrecognized option type!");
+ if (buffer)
+ free(buffer);
+ return -1;
+ }
+
+ /* clean up */
+ if (option[curopt].r_type != CONFIG_TYPE_STRING)
+ {
+ if (buffer)
+ free(buffer);
+ buflen=0;
+ }
+
+ state = 0;
+ curopt = -1;
+ i_buf=0;
+ i_keyword=0;
+ }
+
+
+ } /* infinite loop */
+
+ return retval;
+}