Mercurial > noffle
view src/configfile.c @ 287:01755687c565 noffle
[svn] * src/configfile.c: Change snprintf() to Utl_cpyStr();
* src/configfile.c,src/fetch.c,src/fetchlist.c,src/protocol.c,
src/server.c: Replace strcpy() with Utl_cpyStr() where appropriate.
See Debian bug 168128.
* src/client.c. Replace strcpy() here too.
* src/configfile.h,src/configfile.c,docs/noffle.conf.5: Add noffle-user
and noffle-group configs.
* src/control.c,src/configfile.c,src/noffle.c: Replace [s]scanf("%s")
with [s]scanf(MAXCHAR_FMT).
author | bears |
---|---|
date | Fri, 10 Jan 2003 23:11:43 +0000 |
parents | 18d6c61ed4e7 |
children | f35a7d45efd5 |
line wrap: on
line source
/* configfile.c The following macros must be set, when compiling this file: CONFIGFILE SPOOLDIR VERSION $Id: configfile.c 419 2003-01-10 23:11:43Z bears $ */ #if HAVE_CONFIG_H #include <config.h> #endif #include <ctype.h> #include <limits.h> #include <sys/types.h> #include <regex.h> #include "configfile.h" #include "filter.h" #include "itemlist.h" #include "log.h" #include "util.h" #include "portable.h" #include "wildmat.h" typedef struct { int numGroup; int maxGroup; char **groups; } GroupEntry; struct GroupEnum { GroupEntry *groupEntry; int groupIdx; }; typedef struct { char *name; char *user; char *pass; GroupEntry getgroups; GroupEntry omitgroups; } ServEntry; typedef struct { char *pattern; int days; } ExpireEntry; typedef struct { char *pattern; char *mode; } AutoSubscribeModeEntry; struct { /* Compile time options */ const char *spoolDir; const char *version; /* Options from the config file */ int maxFetch; int autoUnsubscribeDays; int threadFollowTime; int connectTimeout; Bool autoSubscribe; Bool autoUnsubscribe; Bool infoAlways; Bool replaceMsgId; Str hostnameMsgId; Bool postLocal; Bool clientAuth; Str defaultAutoSubscribeMode; Str mailTo; int defaultExpire; int numServ; int maxServ; ServEntry *serv; int servIdx; /* for server enumeration */ int numExpire; int maxExpire; ExpireEntry *expire; int numAutoSubscribeMode; int maxAutoSubscribeMode; AutoSubscribeModeEntry *autoSubscribeMode; Str pathHeader; Str fromDomain; Str organization; Str noffleUser; Str noffleGroup; } config = { SPOOLDIR, /* spoolDir */ VERSION, /* version */ 300, /* maxFetch */ 30, /* autoUnsubscribeDays */ 7, /* threadFollowTime */ 30, /* connectTimeout */ FALSE, /* autoSubscribe */ FALSE, /* autoUnsubscribe */ TRUE, /* infoAlways */ FALSE, /* replaceMsgId */ "", /* hostnameMsgId */ FALSE, /* postLocal */ FALSE, /* clientAuth */ "over", /* defaultAutoSubscribeMode */ "", /* mailTo */ 14, /* defaultExpire */ 0, /* numServ */ 0, /* maxServ */ NULL, /* serv */ 0, /* servIdx */ 0, /* numExpire */ 0, /* maxExpire */ NULL, /* expire */ 0, /* numAutoSubscribeMode */ 0, /* maxAutoSubscribeMode */ NULL, /* autoSubscribeMode */ "", /* pathHeader */ "", /* fromDomain */ "", /* organization */ "news", /* user Noffle runs as */ "news" /* group Noffle runs as */ }; const char * Cfg_spoolDir( void ) { return config.spoolDir; } const char * Cfg_version( void ) { return config.version; } int Cfg_maxFetch( void ) { return config.maxFetch; } int Cfg_autoUnsubscribeDays( void ) { return config.autoUnsubscribeDays; } int Cfg_threadFollowTime( void ) { return config.threadFollowTime; } int Cfg_connectTimeout( void ) { return config.connectTimeout; } Bool Cfg_autoUnsubscribe( void ) { return config.autoUnsubscribe; } Bool Cfg_autoSubscribe( void ) { return config.autoSubscribe; } Bool Cfg_infoAlways( void ) { return config.infoAlways; } Bool Cfg_replaceMsgId( void ) { return config.replaceMsgId; } const char * Cfg_hostnameMsgId( void ) { return config.hostnameMsgId; } Bool Cfg_postLocal( void ) { return config.postLocal; } Bool Cfg_needClientAuth( void ) { return config.clientAuth; } const char * Cfg_defaultAutoSubscribeMode( void ) { return config.defaultAutoSubscribeMode; } const char * Cfg_mailTo( void ) { return config.mailTo; } int Cfg_defaultExpire( void ) { return config.defaultExpire; } const char * Cfg_pathHeader( void ) { return config.pathHeader; } const char * Cfg_fromDomain( void ) { return config.fromDomain; } const char * Cfg_organization( void ) { return config.organization; } const char * Cfg_noffleUser( void ) { return config.noffleUser; } const char * Cfg_noffleGroup( void ) { return config.noffleGroup; } void Cfg_beginServEnum( void ) { config.servIdx = 0; } Bool Cfg_nextServ( Str name ) { if ( config.servIdx >= config.numServ ) return FALSE; Utl_cpyStr( name, config.serv[ config.servIdx ].name ); ++config.servIdx; return TRUE; } static Bool searchServ( const char *name, int *idx ) { int i; for ( i = 0; i < config.numServ; ++i ) if ( strcmp( name, config.serv[ i ].name ) == 0 ) { *idx = i; return TRUE; } return FALSE; } Bool Cfg_servListContains( const char *name ) { int idx; return searchServ( name, &idx ); } Bool Cfg_servIsPreferential( const char *name1, const char *name2 ) { Bool exists1, exists2; int idx1, idx2; exists1 = searchServ( name1, &idx1 ); exists2 = searchServ( name2, &idx2 ); if ( exists1 && exists2 ) return ( idx1 < idx2 ); if ( exists1 && ! exists2 ) return TRUE; /* ( ! exists1 && exists2 ) || ( ! exists1 && ! exists2 ) */ return FALSE; } void Cfg_authInfo( const char *name, Str user, Str pass ) { int idx; if ( searchServ( name, &idx ) ) { Utl_cpyStr( user, config.serv[ idx ].user ); Utl_cpyStr( pass, config.serv[ idx ].pass ); } else { user[ 0 ] = '\0'; pass[ 0 ] = '\0'; } } int Cfg_expire( const char *grp ) { int i, res; for ( i = 0; i < config.numExpire; i++ ) if ( Wld_match( grp, config.expire[ i ].pattern ) ) { res = config.expire[ i ].days; Log_dbg( LOG_DBG_CONFIG, "Custom expire period %d for group %s", res, grp ); return res; } return Cfg_defaultExpire(); } const char * Cfg_autoSubscribeMode( const char *grp ) { int i; const char *res; for ( i = 0; i < config.numAutoSubscribeMode; i++ ) if ( Wld_match( grp, config.autoSubscribeMode[ i ].pattern ) ) { res = config.autoSubscribeMode[ i ].mode; Log_dbg( LOG_DBG_CONFIG, "Custom auto subscribe mode %s for group %s", res, grp ); return res; } return Cfg_defaultAutoSubscribeMode(); } GroupEnum * new_GetGrEn( const char *name ) { GroupEnum *res; int servIdx; res = (GroupEnum *) malloc( sizeof( GroupEnum ) ); if ( res == NULL ) Log_fatal( "Malloc of GroupEnum failed." ); if ( ! searchServ( name, &servIdx ) ) res->groupEntry = NULL; else res->groupEntry = &config.serv[ servIdx ].getgroups; GrEn_first( res ); return res; } GroupEnum * new_OmitGrEn( const char *name ) { GroupEnum *res; int servIdx; res = (GroupEnum *) malloc( sizeof( GroupEnum ) ); if ( res == NULL ) Log_fatal( "Malloc of GroupEnum failed." ); if ( ! searchServ( name, &servIdx ) ) res->groupEntry = NULL; else res->groupEntry = &config.serv[ servIdx ].omitgroups; GrEn_first( res ); return res; } void del_GrEn( GroupEnum *ge ) { free(ge); } void GrEn_first( GroupEnum *ge ) { ge->groupIdx = 0; } const char * GrEn_next( GroupEnum *ge ) { if ( ge->groupEntry == NULL || ge->groupIdx >= ge->groupEntry->numGroup ) return NULL; return ge->groupEntry->groups[ ge->groupIdx++ ]; } static void logSyntaxErr( const char *line ) { Log_err( "Syntax error in config file: %s", line ); } static void getBool( Bool *variable, const char *line ) { Str value, name, lowerLn; Utl_cpyStr( lowerLn, line ); Utl_toLower( lowerLn ); if ( sscanf( lowerLn, MAXCHAR_FMT " " MAXCHAR_FMT, name, value ) != 2 ) { logSyntaxErr( line ); return; } if ( strcmp( value, "yes" ) == 0 ) *variable = TRUE; else if ( strcmp( value, "no" ) == 0 ) *variable = FALSE; else Log_err( "Error in config file %s must be yes or no", name ); } static void getInt( int *variable, int min, int max, const char *line ) { int value; Str name; if ( sscanf( line, MAXCHAR_FMT " %d", name, &value ) != 2 ) { logSyntaxErr( line ); return; } if ( value < min || value > max ) { Log_err( "Range error in config file %s [%d,%d]", name, min, max ); return; } *variable = value; } static void getStr( char *variable, const char *line ) { Str dummy; if ( sscanf( line, MAXCHAR_FMT " " MAXCHAR_FMT, dummy, variable ) != 2 ) { logSyntaxErr( line ); return; } } static void getText( Str variable, const char *line ) { const char *l; /* Skip command */ l = Utl_restOfLn( line, 1 ); Utl_cpyStr( variable, l ); } static void getServ( const char *line ) { Str dummy, name, user, pass; int r, len; ServEntry entry; memset( &entry, 0, sizeof( entry ) ); user[ 0 ] = pass[ 0 ] = '\0'; r = sscanf( line, MAXCHAR_FMT " " MAXCHAR_FMT " " MAXCHAR_FMT " " MAXCHAR_FMT, dummy, name, user, pass ); if ( r < 2 ) { logSyntaxErr( line ); return; } len = strlen( name ); /* To make server name more definit, it is made lowercase and port is removed, if it is the default port */ if ( len > 4 && strcmp( name + len - 4, ":119" ) == 0 ) name[ len - 4 ] = '\0'; Utl_toLower( name ); Utl_allocAndCpy( &entry.name, name ); Utl_allocAndCpy( &entry.user, user ); Utl_allocAndCpy( &entry.pass, pass ); if ( config.maxServ < config.numServ + 1 ) { if ( ! ( config.serv = realloc( config.serv, ( config.maxServ + 5 ) * sizeof( ServEntry ) ) ) ) Log_fatal( "Could not realloc server list" ); config.maxServ += 5; } config.serv[ config.numServ++ ] = entry; } static void getExpire( const char *line ) { Str dummy, pattern; ExpireEntry entry; int days; if ( sscanf( line, MAXCHAR_FMT " " MAXCHAR_FMT " %d", dummy, pattern, &days ) != 3 ) { logSyntaxErr( line ); return; } else { if ( days < 0 ) { Log_err( "Expire days error in '%s': must be integer > 0", line, days ); return; } Utl_toLower( pattern ); Utl_allocAndCpy( &entry.pattern, pattern ); entry.days = days; if ( config.maxExpire < config.numExpire + 1 ) { if ( ! ( config.expire = realloc( config.expire, ( config.maxExpire + 5 ) * sizeof( ExpireEntry ) ) ) ) Log_fatal( "Could not realloc expire list" ); config.maxExpire += 5; } config.expire[ config.numExpire++ ] = entry; } } static void getGroups( char *line, Bool isGet ) { const char *name; ItemList *patterns; const char *pattern; if ( config.numServ == 0 ) { Log_err( "No current server in %s", line ); return; } name = line; /* Skip over name and terminate it */ while ( line[ 0 ] != '\0' && ! isspace( line[ 0 ] ) ) line++; if ( line[ 0 ] == '\0' ) { logSyntaxErr( name ); return; } line[ 0 ] = '\0'; line++; patterns = new_Itl( line, " ," ); for( pattern = Itl_first( patterns ); pattern != NULL; pattern = Itl_next( patterns ) ) { GroupEntry *g; if ( isGet ) g = &config.serv[ config.numServ - 1 ].getgroups; else g = &config.serv[ config.numServ - 1 ].omitgroups; if ( g->maxGroup < g->numGroup + 1 ) { if ( ! ( g->groups = realloc( g->groups, ( g->maxGroup + 5 ) * sizeof( char * ) ) ) ) Log_fatal( "Could not realloc group list" ); g->maxGroup += 5; } Utl_allocAndCpy( &g->groups[ g->numGroup++ ], pattern ); } del_Itl( patterns) ; } static void getDebugMask( char *line ) { const char *name; ItemList *maskNames; const char *maskName; unsigned mask; name = line; /* Skip over name and terminate it */ while ( line[ 0 ] != '\0' && ! isspace( line[ 0 ] ) ) line++; if ( line[ 0 ] == '\0' ) { logSyntaxErr( name ); return; } line[ 0 ] = '\0'; line++; mask = LOG_DBG_NONE; maskNames = new_Itl( line, " ," ); for( maskName = Itl_first( maskNames ); maskName != NULL; maskName = Itl_next( maskNames ) ) { if ( strcmp( maskName, "all" ) == 0 ) mask = LOG_DBG_ALL; else if ( strcmp( maskName, "none" ) == 0 ) mask = LOG_DBG_NONE; else if ( strcmp( maskName, "config" ) == 0 ) mask |= LOG_DBG_CONFIG; else if ( strcmp( maskName, "control" ) == 0 ) mask |= LOG_DBG_CONTROL; else if ( strcmp( maskName, "expire" ) == 0 ) mask |= LOG_DBG_EXPIRE; else if ( strcmp( maskName, "fetch" ) == 0 ) mask |= LOG_DBG_FETCH; else if ( strcmp( maskName, "filter" ) == 0 ) mask |= LOG_DBG_FILTER; else if ( strcmp( maskName, "newsbase" ) == 0 ) mask |= LOG_DBG_NEWSBASE; else if ( strcmp( maskName, "noffle" ) == 0 ) mask |= LOG_DBG_NOFFLE; else if ( strcmp( maskName, "post" ) == 0 ) mask |= LOG_DBG_POST; else if ( strcmp( maskName, "protocol" ) == 0 ) mask |= LOG_DBG_PROTOCOL; else if ( strcmp( maskName, "requests" ) == 0 ) mask |= LOG_DBG_REQUESTS; else if ( strcmp( maskName, "server" ) == 0 ) mask |= LOG_DBG_SERVER; else if ( strcmp( maskName, "auth" ) == 0 ) mask |= LOG_DBG_AUTH; else logSyntaxErr( line ); } del_Itl( maskNames) ; Log_setDbgMask( mask ); } static Bool isValidAutoSubscribeMode( const char *mode ) { return strcmp( mode, "full" ) == 0 || strcmp( mode, "thread" ) == 0 || strcmp( mode, "over" ) == 0 || strcmp( mode, "off" ) == 0; } static void getAutoSubscribeMode( const char *line ) { Str dummy, pattern, mode; AutoSubscribeModeEntry entry; int items; items = sscanf( line, MAXCHAR_FMT " " MAXCHAR_FMT " " MAXCHAR_FMT, dummy, pattern, mode ); if ( items == 2 ) { /* Backwards compat. default-auto-subscribe-mode */ Utl_cpyStr( mode, pattern ); Utl_toLower( mode ); if ( ! isValidAutoSubscribeMode( mode ) ) { logSyntaxErr( line ); return; } Utl_cpyStr( config.defaultAutoSubscribeMode, mode ); return; } else if ( items != 3 ) { logSyntaxErr( line ); return; } Utl_toLower( mode ); if ( ! isValidAutoSubscribeMode( mode ) ) { logSyntaxErr( line ); return; } Utl_toLower( pattern ); Utl_allocAndCpy( &entry.pattern, pattern ); Utl_allocAndCpy( &entry.mode, mode ); if ( config.maxAutoSubscribeMode < config.numAutoSubscribeMode + 1 ) { if ( ! ( config.autoSubscribeMode = realloc( config.autoSubscribeMode, ( config.maxAutoSubscribeMode + 5 ) * sizeof( AutoSubscribeModeEntry ) ) ) ) Log_fatal( "Could not realloc auto subscribe mode list" ); config.maxAutoSubscribeMode += 5; } config.autoSubscribeMode[ config.numAutoSubscribeMode++ ] = entry; } static const char * getToken( const char *line, Str value ) { Bool isQuoted; char quoteChar; Bool seenEscape; char *maxVal; while ( *line != '\0' && isspace( *line ) ) line++; if ( *line == '\0' ) return NULL; maxVal = &value[ MAXCHAR ]; isQuoted = ( *line == '\'' || *line == '"' ); if ( isQuoted ) { quoteChar = *line; line++; seenEscape = FALSE; while ( *line != '\0' && ( *line != quoteChar || seenEscape ) && value < maxVal ) { if ( seenEscape ) { *value++ = *line; seenEscape = FALSE; } else { if ( *line == '\\' ) seenEscape = TRUE; else *value++ = *line; } line++; } if ( *line == quoteChar ) line++; } else { while ( *line != '\0' && ! isspace( *line ) && value < maxVal ) *value++ = *line++; } *value = '\0'; return line; } /* very simple date parser. * examples: * now+ */ static Bool get_simpledate( time_t *timeoffsetp, FilterRuleDateEnumType *vartimep, const char *val) { float timef; if ( ! strncasecmp( val, "invalid", 7 ) ) { *vartimep = INVALID; return TRUE; } else if ( ! strncasecmp( val, "now", 3 ) ) { val += 3; *vartimep = NOW; } else if ( ! strncasecmp( val, "lastupdate", 10 ) ) { val += 10; *vartimep = LASTUPDATE; } else { *vartimep = FIXED; *timeoffsetp = Utl_parseNewsDate( val ); if ( *timeoffsetp == (time_t) -1 ) return FALSE; else return TRUE; } /* NOW, LASTUPDATE +/- number of days. */ timef = atof( val ) * 86400.0 ; /* 24 * 60 * 60 == 86400 */ /* let's assume more than 10 years of timeoffset are a mistake. */ if ( timef > 31536000.0 || timef < -31536000.0 ) return FALSE; *timeoffsetp = (time_t) timef; /* Todo: check if any garbage follows. */ return TRUE; } static void getFilter( const char *line ) { Str ruleBuf, value; const char *l; char *p, *ruleName; Filter *f; FilterRule rule; Bool seenAction; f = new_Filter(); /* Skip "filter" */ l = Utl_restOfLn( line, 1 ); seenAction = FALSE; for(;;) { while ( *l != '\0' && isspace( *l ) ) l++; if ( *l == '\0' ) break; /* Get the rule title */ p = ruleBuf; while ( *l != '\0' && *l != '=' && *l != '<' && *l != '>' ) *p++ = *l++; *p = '\0'; ruleName = Utl_stripWhiteSpace( ruleBuf ); Utl_toLower( ruleName ); if ( *ruleName == '\0' ) goto synErr; /* Do we know this rule? */ if ( strcmp( ruleName, "group" ) == 0 ) rule.type = RULE_NEWSGROUP; else if ( strcmp( ruleName, "subject" ) == 0 ) rule.type = RULE_SUBJECT; else if ( strcmp( ruleName, "reference" ) == 0 ) rule.type = RULE_REFERENCE; else if ( strcmp( ruleName, "from" ) == 0 ) rule.type = RULE_FROM; else if ( strcmp( ruleName, "msgid" ) == 0 ) rule.type = RULE_MSGID; else if ( strcmp( ruleName, "bytes" ) == 0 ) rule.type = RULE_BYTES_EQ; else if ( strcmp( ruleName, "lines" ) == 0 ) rule.type = RULE_LINES_EQ; else if ( strcmp( ruleName, "refs" ) == 0 ) rule.type = RULE_NOREFS_EQ; else if ( strcmp( ruleName, "xposts" ) == 0 ) rule.type = RULE_XPOSTS_EQ; else if ( strcmp( ruleName, "post-status" ) == 0 ) rule.type = RULE_POST_STATUS; else if ( strcmp( ruleName, "date" ) == 0 ) rule.type = RULE_DATE_EQ; /* date<lastupdate-12 equals older=lastupdate-12 * date>now+1.5 equals newer=now+1.5 * date=now equals older=now+1 AND newer=now-1 * Stupid people like Mirko keep making mistakes * if they're forced using date< or date>. */ else if ( strcmp( ruleName, "older" ) == 0 ) rule.type = RULE_DATE_LT; else if ( strcmp( ruleName, "newer" ) == 0 ) rule.type = RULE_DATE_GT; else if ( strcmp( ruleName, "action" ) != 0 ) goto synErr; if ( rule.type == RULE_BYTES_EQ || rule.type == RULE_LINES_EQ || rule.type == RULE_NOREFS_EQ || rule.type == RULE_XPOSTS_EQ || rule.type == RULE_DATE_EQ ) { if ( *l == '<' ) rule.type--; else if ( *l == '>' ) rule.type++; else if ( *l != '=' ) goto synErr; } else if ( *l != '=' ) goto synErr; /* Skip past '=' (or '>' or '<') */ l++; /* OK, we now have a valid rule. What value? */ l = getToken( l, value ); if ( l == NULL ) goto synErr; if ( strcmp( ruleName, "action" ) == 0 ) { if ( seenAction ) goto synErr; Utl_toLower( value ); if ( strcmp( value, "full" ) == 0 ) f->action = FILTER_FULL; else if ( strcmp( value, "over" ) == 0 ) f->action = FILTER_XOVER; else if ( strcmp( value, "thread" ) == 0 ) f->action = FILTER_THREAD; else if ( strcmp( value, "discard" ) == 0 ) f->action = FILTER_DISCARD; else if ( strcmp( value, "default" ) == 0 ) f->action = FILTER_DEFAULT; seenAction = TRUE; } else if ( rule.type == RULE_NEWSGROUP ) Utl_allocAndCpy( &rule.data.grp, value ); else if ( rule.type >= RULE_SUBJECT && rule.type <= RULE_MSGID ) { if ( regcomp( &rule.data.regex, value, REG_EXTENDED ) != 0 ) goto synErr; } else if (rule.type == RULE_POST_STATUS ) { if ( ( strcmp( value, "yes" ) == 0 ) || \ ( strcmp( value, "no" ) == 0 ) || \ ( strncmp( value, "mod", 3 ) == 0 ) ) /* no need to type out "moderated" */ rule.data.postAllow = value[0]; /* 'y','n' or 'm' */ else goto synErr; } else if ( rule.type == RULE_DATE_LT || rule.type == RULE_DATE_EQ || rule.type == RULE_DATE_GT ) { if ( !get_simpledate( &rule.data.reftime.timeoffset, &rule.data.reftime.vartime, value ) ) goto synErr; if ( rule.type != RULE_DATE_EQ && rule.data.reftime.vartime == INVALID ) goto synErr; } else { char * endVal; int suffix; rule.data.amount = strtoul( value, &endVal, 0 ); suffix = tolower( *endVal ); if ( suffix == 'k' || suffix == 'm' ) { rule.data.amount *= 1024; if ( suffix == 'm' ) rule.data.amount *= 1024; endVal++; } if ( *endVal != '\0' && ! isspace( *endVal ) ) goto synErr; } if ( strcmp( ruleName, "action" ) != 0 ) { Log_dbg( LOG_DBG_CONFIG, "Adding rule type %d value %s", rule.type, value ); Flt_addRule( f, rule ); } } Log_dbg( LOG_DBG_CONFIG, "Adding filter, action %d", f->action ); Flt_addFilter( f ); return; synErr: logSyntaxErr( line ); return; } void Cfg_read( void ) { char *p; FILE *f; Str file, line, lowerLine, name, s; Utl_cpyStr( file, CONFIGFILE ); if ( ! ( f = fopen( file, "r" ) ) ) { Log_err( "Cannot read %s", file ); return; } while ( fgets( line, MAXCHAR, f ) ) { p = Utl_stripWhiteSpace( line ); Utl_stripComment( p ); Utl_cpyStr( lowerLine, p ); Utl_toLower( lowerLine ); p = lowerLine; if ( *p == '\0' ) continue; if ( sscanf( p, MAXCHAR_FMT, name ) != 1 ) Log_err( "Syntax error in %s: %s", file, line ); else if ( strcmp( "max-fetch", name ) == 0 ) getInt( &config.maxFetch, 0, INT_MAX, p ); else if ( strcmp( "auto-unsubscribe-days", name ) == 0 ) getInt( &config.autoUnsubscribe, -1, INT_MAX, p ); else if ( strcmp( "thread-follow-time", name ) == 0 ) getInt( &config.threadFollowTime, 0, INT_MAX, p ); else if ( strcmp( "connect-timeout", name ) == 0 ) getInt( &config.connectTimeout, 0, INT_MAX, p ); else if ( strcmp( "default-expire", name ) == 0 ) getInt( &config.defaultExpire, 0, INT_MAX, p ); else if ( strcmp( "auto-subscribe", name ) == 0 ) getBool( &config.autoSubscribe, p ); else if ( strcmp( "auto-unsubscribe", name ) == 0 ) getBool( &config.autoUnsubscribe, p ); else if ( strcmp( "info-always-unread", name ) == 0 ) getBool( &config.infoAlways, p ); else if ( strcmp( "replace-messageid", name ) == 0 ) getBool( &config.replaceMsgId, p ); else if ( strcmp( "hostname", name ) == 0 ) /* use line, do not change to lowercase */ getStr( config.hostnameMsgId, line ); else if ( strcmp( "post-locally", name ) == 0 ) getBool( &config.postLocal, p ); else if ( strcmp( "authenticate-client", name ) == 0 ) getBool( &config.clientAuth, p ); else if ( strcmp( "default-auto-subscribe-mode", name ) == 0 ) { getStr( s, p ); if ( ! isValidAutoSubscribeMode( s ) ) { logSyntaxErr( line ); return; } else Utl_cpyStr( config.defaultAutoSubscribeMode, s ); } else if ( strcmp( "mail-to", name ) == 0 ) getStr( config.mailTo, p ); else if ( strcmp( "expire", name ) == 0 ) getExpire( p ); else if ( strcmp( "auto-subscribe-mode", name ) == 0 ) getAutoSubscribeMode( p ); else if ( strcmp( "log-debug", name ) == 0 ) getDebugMask( p ); else if ( strcmp( "getgroups", name ) == 0 ) getGroups( p, TRUE ); else if ( strcmp( "omitgroups", name ) == 0 ) getGroups( p, FALSE ); else if ( strcmp( "path-header", name ) == 0 ) getStr( config.pathHeader, p ); else if ( strcmp( "from-domain", name ) == 0 ) getStr( config.fromDomain, p ); /* The following need line because they may have uppercase data */ else if ( strcmp( "organization", name ) == 0 ) getText( config.organization, line ); else if ( strcmp( "noffle-user", name ) == 0 ) getText( config.noffleUser, line ); else if ( strcmp( "noffle-group", name ) == 0 ) getText( config.noffleUser, line ); else if ( strcmp( "server", name ) == 0 ) getServ( line ); else if ( strcmp( "filter", name ) == 0 ) getFilter( line ); else Log_err( "Unknown config option: %s", name ); } fclose( f ); if ( ! config.numServ ) Log_fatal( "Config file contains no server" ); }