Mercurial > noffle
view src/configfile.c @ 288:c02c4eb95f95 noffle
[svn] * src/configfile.h,src/configfile.c,docs/noffle.conf.5: Add noffle-user
and noffle-group configs.
* 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/control.c,src/configfile.c,src/noffle.c: Replace [s]scanf("%s")
with [s]scanf(MAXCHAR_FMT).
* src/noffle.c: Log warning if noffle.conf is world readable.
* src/noffle.c: Restrict most options to news admins; i.e. those who
are root or news on running Noffle.
* Makefile.in,acconfig.h,aclocal.m4,config.h.in,configure,configure.in,
docs/Makefile.in,docs/noffle.conf.5,packages/Makefile.in,
packages/redhat/Makefile.in,src/Makefile.am,src/Makefile.in,
src/authenticate.c,src/authenticate.h,src/noffle.c,src/server.c:
Add basic authentication using either Noffle-specific user file
or authenticating via PAM (service 'noffle'). PAM authentication
needs to run as root, so a Noffle server that needs PAM
must be started by root. Helpful (?) error messages will be logged
if not. Noffle will switch ruid and euid to 'news' (or whatever
is configured) ASAP.
* src/noffle.c: Add uid checking.
author | bears |
---|---|
date | Fri, 10 Jan 2003 23:25:45 +0000 |
parents | 01755687c565 |
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" ); }