/* ** ** Copyright (c) 2002,2003 Dave McMurtrie ** ** This file is part of imapproxy. ** ** imapproxy 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 of the License, or ** (at your option) any later version. ** ** imapproxy 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 imapproxy; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** ** ** Facility: ** ** config.c ** ** Abstract: ** ** Routines for parsing a config file and setting global configuration ** options. ** ** Authors: ** ** Dave McMurtrie ** ** RCS: ** ** $Source: /afs/pitt.edu/usr12/dgm/work/IMAP_Proxy/src/RCS/config.c,v $ ** $Id: config.c,v 1.15 2005/07/06 12:17:44 dgm Exp $ ** ** Modification History: ** ** $Log: config.c,v $ ** Revision 1.15 2005/07/06 12:17:44 dgm ** Add enable_admin_commands to ConfigTable. ** ** Revision 1.14 2005/06/15 12:12:26 dgm ** Patch by Dave Steinberg and Jarno Huuskonen to add chroot_directory ** config option. ** ** Revision 1.13 2005/01/12 17:49:51 dgm ** Applied patch by David Lancaster to provide force_tls config ** option. ** ** Revision 1.12 2004/11/10 15:26:25 dgm ** Explictly NULL terminate all strings that are the result of an strncpy. ** ** Revision 1.11 2004/10/11 18:01:29 dgm ** Added foreground mode configuration option. ** ** Revision 1.10 2004/02/24 14:57:47 dgm ** Added new config option 'enable_select_cache'. ** ** Revision 1.9 2003/11/14 15:03:58 dgm ** Patch by Geoffrey Hort to allow ** configurable listen address. ** ** Revision 1.8 2003/10/23 06:18:58 dgm ** Fixed bug in SetBooleanValue doing upcase of Value. ** ** Revision 1.7 2003/10/09 12:36:08 dgm ** Added the ability to set boolean configuration options. ** ** Revision 1.6 2003/05/20 18:42:04 dgm ** comment changes only. ** ** Revision 1.5 2003/05/06 12:10:37 dgm ** Applied patches by Ken Murchison to include additional config options ** for SSL support. ** ** Revision 1.4 2003/04/16 12:13:34 dgm ** Added commodore logo ascii-art comment at the end. ** Added support for syslog configuration options. ** ** Revision 1.3 2003/02/17 14:08:42 dgm ** added an fclose() that I forgot. ** ** Revision 1.2 2003/01/27 13:59:15 dgm ** Patch by Gary Mills to allow compilation ** using Sun's cc instead of gcc. ** ** Revision 1.1 2002/12/17 14:26:49 dgm ** Initial revision ** ** */ #define _REENTRANT #define MAX_KEYWORD_LEN 128 #include #include #include #include #include #include #include #include "common.h" #include "imapproxy.h" /* * External globals */ extern ProxyConfig_Struct PC_Struct; /* * Internal prototypes */ static void SetStringValue( char *, char **, unsigned int ); static void SetNumericValue( char *, int *, unsigned int ); static void SetBooleanValue( char *, unsigned int *, unsigned int ); /* * An array of Config_Structs will need to be allocated, one for * every possible keyword/value combination (plus a NULL). Note that * I declare an array of 100, meaning that there's a hard limit of 99 * possible config values right now. Currently, there are only 9 in use. */ struct Config_Struct { char Keyword[MAX_KEYWORD_LEN]; /* The configuration keyword */ void (*(SetFunction))(); /* ptr to function used to set the value */ void *StorageAddress; /* address to store the value */ }; struct Config_Struct ConfigTable[ 100 ]; /* * Macro to populate the above ConfigTable */ #define ADD_TO_TABLE( KEYWORD, SETFUNCTION, STA, INDEX ) \ strncpy( ConfigTable[ INDEX ].Keyword, KEYWORD, MAX_KEYWORD_LEN -1 ); \ ConfigTable[ INDEX ].Keyword[ MAX_KEYWORD_LEN - 1 ] = '\0'; \ ConfigTable[ INDEX ].SetFunction = SETFUNCTION; \ ConfigTable[ INDEX ].StorageAddress = STA; \ INDEX++; /*++ * Function: SetStringValue * * Purpose: Common routine to assign string values in the config * options struct. * * Parameters: ptr to string from config file * ptr to char ptr for dynamically allocated storage for string. * int -- line of config file where the string was read from. * * Returns: nada -- exit()s on errors. * * Authors: Dave McMurtrie * * Notes: *-- */ static void SetStringValue( char *String, char **SavedString, unsigned int linenum ) { char *fn = "SetStringValue()"; unsigned int Size; Size = strlen( String ) + 1; /* * Do some reasonable bounds checking before we malloc() */ if ( ( Size < 1 ) || ( Size > 4096 ) ) { syslog( LOG_ERR, "%s: Length of string value at line %d of config file is not within size boundaries -- Exiting.", fn, linenum ); exit( 1 ); } *SavedString = malloc( Size ); if ( ! *SavedString ) { syslog( LOG_ERR, "%s: malloc() failed: %s -- Exiting.", fn, strerror( errno ) ); exit( 1 ); } memcpy( *SavedString, String, Size ); return; } /*++ * Function: SetNumericValue * * Purpose: Common routine to convert an ascii string to a numeric value * and set a config value accordingly. * * Parameters: ptr to the string value to convert. * ptr to int. (where to store the converted value) * int -- line of config file where the string was read from. * * Returns: nada -- exit()s on failure. * * Authors: Dave McMurtrie * * Notes: *-- */ static void SetNumericValue( char *StringValue, int *Value, unsigned int linenum ) { char *fn = "SetNumericValue()"; /* * Need to generalize this routine. atoi() seems to only set errno * on Solaris, making this very platform specific. */ *Value = atoi( (const char *)StringValue ); if ( ( ! *Value ) && ( errno == EINVAL ) ) { syslog( LOG_ERR, "%s: numeric value specified at line %d of config file is invalid -- Exiting.", fn, linenum ); exit( 1 ); } return; } /*++ * Function: SetBooleanValue * * Purpose: Common routine to assign true/false (or yes/no) values in * the config options struct. * * Parameters: ptr to string value from configfile (yes, no, true, false) * ptr to unsigned int -- where to store the boolean value (1/0) * unsigned int -- Config file line number * * Returns: nada -- exit()s on errors. * * Authors: Dave McMurtrie * * Notes: This function is not case sensitive. * It will parse any of y, yes, true and store it as 1. * It will parse any of n, no, false and store it as 0. * * Major note -- the Value passed into here will be upcased. *-- */ static void SetBooleanValue( char *Value, unsigned int *StoredValue, unsigned int linenum ) { char *fn = "SetBooleanValue()"; char *CP; /* * Upcase for ease of comparison */ CP = Value; while( *CP ) { *CP = toupper( *CP ); CP++; } if ( ( ( Value[0] == 'Y' ) && ( Value[1] == '\0' ) ) || ( ( Value[0] == 'Y' ) && ( Value[1] == 'E' ) && ( Value[2] == 'S' ) ) ) { *StoredValue = 1; return; } if ( ( ( Value[0] == 'N' ) && ( Value[1] == '\0' ) ) || ( ( Value[0] == 'N' ) && ( Value[1] == 'O' ) ) ) { *StoredValue = 0; return; } if ( !strcmp( "TRUE", Value ) ) { *StoredValue = 1; return; } if ( !strcmp( "FALSE", Value ) ) { *StoredValue = 0; return; } syslog( LOG_WARNING, "%s: Invalid boolean value '%s' specified at line %d of config file. Defaulting to FALSE.", fn, Value, linenum ); *StoredValue = 0; return; } /*++ * Function: SetConfigOptions * * Purpose: Set global configuration options by reading and parsing * the configuration file. * * Parameters: char pointer to config filename path. * * Returns: nada. exit()s on any error. * * Authors: Dave McMurtrie * * Notes: Sets values in global ProxyConfig_Struct PC_Struct. *-- */ extern void SetConfigOptions( char *ConfigFile ) { FILE *FP; char *fn = "SetConfigOptions()"; char Buffer[1024]; unsigned int LineNumber; unsigned int index; unsigned int i; char *CP; char *Keyword; char *Value; index = LineNumber = 0; /* * initialize the proxy config struct */ memset( &PC_Struct, 0, sizeof PC_Struct ); /* * Build our config option table. */ ADD_TO_TABLE( "server_hostname", SetStringValue, &PC_Struct.server_hostname, index ); ADD_TO_TABLE( "listen_port", SetNumericValue, &PC_Struct.listen_port, index ); ADD_TO_TABLE( "listen_address", SetStringValue, &PC_Struct.listen_addr, index ); ADD_TO_TABLE( "server_port", SetNumericValue, &PC_Struct.server_port, index ); ADD_TO_TABLE( "cache_size", SetNumericValue, &PC_Struct.cache_size, index ); ADD_TO_TABLE( "cache_expiration_time", SetNumericValue, &PC_Struct.cache_expiration_time, index ); ADD_TO_TABLE( "proc_username", SetStringValue, &PC_Struct.proc_username, index ); ADD_TO_TABLE( "proc_groupname", SetStringValue, &PC_Struct.proc_groupname, index ); ADD_TO_TABLE( "stat_filename", SetStringValue, &PC_Struct.stat_filename, index ); ADD_TO_TABLE( "syslog_facility", SetStringValue, &PC_Struct.syslog_facility, index ); ADD_TO_TABLE( "protocol_log_filename", SetStringValue, &PC_Struct.protocol_log_filename, index ); ADD_TO_TABLE( "syslog_prioritymask", SetStringValue, &PC_Struct.syslog_prioritymask, index ); ADD_TO_TABLE( "tls_ca_file", SetStringValue, &PC_Struct.tls_ca_file, index ); ADD_TO_TABLE( "tls_ca_path", SetStringValue, &PC_Struct.tls_ca_path, index ); ADD_TO_TABLE( "tls_cert_file", SetStringValue, &PC_Struct.tls_cert_file, index ); ADD_TO_TABLE( "tls_key_file", SetStringValue, &PC_Struct.tls_key_file, index ); ADD_TO_TABLE( "send_tcp_keepalives", SetBooleanValue, &PC_Struct.send_tcp_keepalives, index ); ADD_TO_TABLE( "chroot_directory", SetStringValue, &PC_Struct.chroot_directory, index ); ADD_TO_TABLE( "enable_select_cache", SetBooleanValue, &PC_Struct.enable_select_cache, index ); ADD_TO_TABLE( "foreground_mode", SetBooleanValue, &PC_Struct.foreground_mode, index ); ADD_TO_TABLE( "force_tls", SetBooleanValue, &PC_Struct.force_tls, index ); ADD_TO_TABLE( "enable_admin_commands", SetBooleanValue, &PC_Struct.enable_admin_commands, index ); ConfigTable[index].Keyword[0] = '\0'; FP = fopen( ConfigFile, "r" ); if ( !FP ) { syslog(LOG_ERR, "%s: Unable to open config file '%s': %s -- Exiting", fn, ConfigFile, strerror( errno ) ); exit( 1 ); } for ( ;; ) { if ( !fgets( Buffer, sizeof Buffer, FP ) ) break; LineNumber++; /* * Nullify comments, and CRLFs */ CP = strchr( Buffer, '#' ); if ( CP ) *CP = 0; CP = strchr( Buffer, '\n' ); if ( CP ) *CP = 0; CP = strchr( Buffer, '\r' ); if ( CP ) *CP = 0; /* * Any line that started with a comment or CRLF, we'll * skip. */ if ( !strlen( Buffer ) ) continue; CP = strtok( Buffer, " " ); if ( !CP ) { syslog(LOG_ERR, "%s: parse error reading config file at line %d. Exiting.", fn, LineNumber ); exit( 1 ); } Keyword = CP; CP = strtok( NULL, " " ); if ( !CP ) { syslog(LOG_ERR, "%s: parse error reading config file at line %d. Exiting.", fn, LineNumber ); exit( 1 ); } Value = CP; for (i = 0; ConfigTable[i].Keyword[0] != '\0'; i++ ) { if ( ! strcasecmp( (const char *)Keyword, ConfigTable[i].Keyword ) ) { ( ConfigTable[i].SetFunction )( Value, ConfigTable[i].StorageAddress, LineNumber ); break; } } /* * If we get here and we're at our NULL value at the end of our * keyword array, we cycled through the entire array and never * matched any keyword. */ if ( ! ConfigTable[i].Keyword ) { syslog( LOG_ERR, "%s: unknown keyword '%s' found at line %d of config file -- Exiting.", fn, Keyword, LineNumber ); exit( 1 ); } } fclose( FP ); } /* * _________ * / | * / | * / ______| * / / ________ * | | | / * | | |_____/ * | | ______ * | | | \ * | | |______\ * \ \_______ * \ | * \ | * \_________| */