PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/release-1-1-4/noffle/src/configfile.c

#
C | 1010 lines | 923 code | 68 blank | 19 comment | 129 complexity | 0dfbf4be26309fc397f28d4f981fc58f MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. configfile.c
  3. The following macros must be set, when compiling this file:
  4. CONFIGFILE
  5. SPOOLDIR
  6. VERSION
  7. $Id: configfile.c 422 2003-01-11 09:30:05Z bears $
  8. */
  9. #if HAVE_CONFIG_H
  10. #include <config.h>
  11. #endif
  12. #include <ctype.h>
  13. #include <limits.h>
  14. #include <sys/types.h>
  15. #include <regex.h>
  16. #include "configfile.h"
  17. #include "filter.h"
  18. #include "itemlist.h"
  19. #include "log.h"
  20. #include "util.h"
  21. #include "portable.h"
  22. #include "wildmat.h"
  23. typedef struct
  24. {
  25. int numGroup;
  26. int maxGroup;
  27. char **groups;
  28. }
  29. GroupEntry;
  30. struct GroupEnum
  31. {
  32. GroupEntry *groupEntry;
  33. int groupIdx;
  34. };
  35. typedef struct
  36. {
  37. char *name;
  38. char *user;
  39. char *pass;
  40. GroupEntry getgroups;
  41. GroupEntry omitgroups;
  42. }
  43. ServEntry;
  44. typedef struct
  45. {
  46. char *pattern;
  47. int days;
  48. }
  49. ExpireEntry;
  50. typedef struct
  51. {
  52. char *pattern;
  53. char *mode;
  54. }
  55. AutoSubscribeModeEntry;
  56. struct
  57. {
  58. /* Compile time options */
  59. const char *spoolDir;
  60. const char *version;
  61. /* Options from the config file */
  62. int maxFetch;
  63. int autoUnsubscribeDays;
  64. int threadFollowTime;
  65. int connectTimeout;
  66. Bool autoSubscribe;
  67. Bool autoUnsubscribe;
  68. Bool infoAlways;
  69. Bool replaceMsgId;
  70. Str hostnameMsgId;
  71. Bool postLocal;
  72. Bool clientAuth;
  73. Str defaultAutoSubscribeMode;
  74. Str mailTo;
  75. int defaultExpire;
  76. int numServ;
  77. int maxServ;
  78. ServEntry *serv;
  79. int servIdx; /* for server enumeration */
  80. int numExpire;
  81. int maxExpire;
  82. ExpireEntry *expire;
  83. int numAutoSubscribeMode;
  84. int maxAutoSubscribeMode;
  85. AutoSubscribeModeEntry *autoSubscribeMode;
  86. Str pathHeader;
  87. Str fromDomain;
  88. Str organization;
  89. Str noffleUser;
  90. Str noffleGroup;
  91. } config =
  92. {
  93. SPOOLDIR, /* spoolDir */
  94. VERSION, /* version */
  95. 300, /* maxFetch */
  96. 30, /* autoUnsubscribeDays */
  97. 7, /* threadFollowTime */
  98. 30, /* connectTimeout */
  99. FALSE, /* autoSubscribe */
  100. FALSE, /* autoUnsubscribe */
  101. TRUE, /* infoAlways */
  102. FALSE, /* replaceMsgId */
  103. "", /* hostnameMsgId */
  104. FALSE, /* postLocal */
  105. FALSE, /* clientAuth */
  106. "over", /* defaultAutoSubscribeMode */
  107. "", /* mailTo */
  108. 14, /* defaultExpire */
  109. 0, /* numServ */
  110. 0, /* maxServ */
  111. NULL, /* serv */
  112. 0, /* servIdx */
  113. 0, /* numExpire */
  114. 0, /* maxExpire */
  115. NULL, /* expire */
  116. 0, /* numAutoSubscribeMode */
  117. 0, /* maxAutoSubscribeMode */
  118. NULL, /* autoSubscribeMode */
  119. "", /* pathHeader */
  120. "", /* fromDomain */
  121. "", /* organization */
  122. "news", /* user Noffle runs as */
  123. "news" /* group Noffle runs as */
  124. };
  125. const char * Cfg_spoolDir( void ) { return config.spoolDir; }
  126. const char * Cfg_version( void ) { return config.version; }
  127. int Cfg_maxFetch( void ) { return config.maxFetch; }
  128. int Cfg_autoUnsubscribeDays( void ) { return config.autoUnsubscribeDays; }
  129. int Cfg_threadFollowTime( void ) { return config.threadFollowTime; }
  130. int Cfg_connectTimeout( void ) { return config.connectTimeout; }
  131. Bool Cfg_autoUnsubscribe( void ) { return config.autoUnsubscribe; }
  132. Bool Cfg_autoSubscribe( void ) { return config.autoSubscribe; }
  133. Bool Cfg_infoAlways( void ) { return config.infoAlways; }
  134. Bool Cfg_replaceMsgId( void ) { return config.replaceMsgId; }
  135. const char * Cfg_hostnameMsgId( void ) { return config.hostnameMsgId; }
  136. Bool Cfg_postLocal( void ) { return config.postLocal; }
  137. Bool Cfg_needClientAuth( void ) { return config.clientAuth; }
  138. const char * Cfg_defaultAutoSubscribeMode( void ) {
  139. return config.defaultAutoSubscribeMode; }
  140. const char * Cfg_mailTo( void ) { return config.mailTo; }
  141. int Cfg_defaultExpire( void ) { return config.defaultExpire; }
  142. const char * Cfg_pathHeader( void ) { return config.pathHeader; }
  143. const char * Cfg_fromDomain( void ) { return config.fromDomain; }
  144. const char * Cfg_organization( void ) { return config.organization; }
  145. const char * Cfg_noffleUser( void ) { return config.noffleUser; }
  146. const char * Cfg_noffleGroup( void ) { return config.noffleGroup; }
  147. void
  148. Cfg_beginServEnum( void )
  149. {
  150. config.servIdx = 0;
  151. }
  152. Bool
  153. Cfg_nextServ( Str name )
  154. {
  155. if ( config.servIdx >= config.numServ )
  156. return FALSE;
  157. Utl_cpyStr( name, config.serv[ config.servIdx ].name );
  158. ++config.servIdx;
  159. return TRUE;
  160. }
  161. static Bool
  162. searchServ( const char *name, int *idx )
  163. {
  164. int i;
  165. for ( i = 0; i < config.numServ; ++i )
  166. if ( strcmp( name, config.serv[ i ].name ) == 0 )
  167. {
  168. *idx = i;
  169. return TRUE;
  170. }
  171. return FALSE;
  172. }
  173. Bool
  174. Cfg_servListContains( const char *name )
  175. {
  176. int idx;
  177. return searchServ( name, &idx );
  178. }
  179. Bool
  180. Cfg_servIsPreferential( const char *name1, const char *name2 )
  181. {
  182. Bool exists1, exists2;
  183. int idx1, idx2;
  184. exists1 = searchServ( name1, &idx1 );
  185. exists2 = searchServ( name2, &idx2 );
  186. if ( exists1 && exists2 )
  187. return ( idx1 < idx2 );
  188. if ( exists1 && ! exists2 )
  189. return TRUE;
  190. /* ( ! exists1 && exists2 ) || ( ! exists1 && ! exists2 ) */
  191. return FALSE;
  192. }
  193. void
  194. Cfg_authInfo( const char *name, Str user, Str pass )
  195. {
  196. int idx;
  197. if ( searchServ( name, &idx ) )
  198. {
  199. Utl_cpyStr( user, config.serv[ idx ].user );
  200. Utl_cpyStr( pass, config.serv[ idx ].pass );
  201. }
  202. else
  203. {
  204. user[ 0 ] = '\0';
  205. pass[ 0 ] = '\0';
  206. }
  207. }
  208. int
  209. Cfg_expire( const char *grp )
  210. {
  211. int i, res;
  212. for ( i = 0; i < config.numExpire; i++ )
  213. if ( Wld_match( grp, config.expire[ i ].pattern ) )
  214. {
  215. res = config.expire[ i ].days;
  216. Log_dbg( LOG_DBG_CONFIG,
  217. "Custom expire period %d for group %s",
  218. res, grp );
  219. return res;
  220. }
  221. return Cfg_defaultExpire();
  222. }
  223. const char *
  224. Cfg_autoSubscribeMode( const char *grp )
  225. {
  226. int i;
  227. const char *res;
  228. for ( i = 0; i < config.numAutoSubscribeMode; i++ )
  229. if ( Wld_match( grp, config.autoSubscribeMode[ i ].pattern ) )
  230. {
  231. res = config.autoSubscribeMode[ i ].mode;
  232. Log_dbg( LOG_DBG_CONFIG,
  233. "Custom auto subscribe mode %s for group %s",
  234. res, grp );
  235. return res;
  236. }
  237. return Cfg_defaultAutoSubscribeMode();
  238. }
  239. GroupEnum *
  240. new_GetGrEn( const char *name )
  241. {
  242. GroupEnum *res;
  243. int servIdx;
  244. res = (GroupEnum *) malloc( sizeof( GroupEnum ) );
  245. if ( res == NULL )
  246. Log_fatal( "Malloc of GroupEnum failed." );
  247. if ( ! searchServ( name, &servIdx ) )
  248. res->groupEntry = NULL;
  249. else
  250. res->groupEntry = &config.serv[ servIdx ].getgroups;
  251. GrEn_first( res );
  252. return res;
  253. }
  254. GroupEnum *
  255. new_OmitGrEn( const char *name )
  256. {
  257. GroupEnum *res;
  258. int servIdx;
  259. res = (GroupEnum *) malloc( sizeof( GroupEnum ) );
  260. if ( res == NULL )
  261. Log_fatal( "Malloc of GroupEnum failed." );
  262. if ( ! searchServ( name, &servIdx ) )
  263. res->groupEntry = NULL;
  264. else
  265. res->groupEntry = &config.serv[ servIdx ].omitgroups;
  266. GrEn_first( res );
  267. return res;
  268. }
  269. void
  270. del_GrEn( GroupEnum *ge )
  271. {
  272. free(ge);
  273. }
  274. void
  275. GrEn_first( GroupEnum *ge )
  276. {
  277. ge->groupIdx = 0;
  278. }
  279. const char *
  280. GrEn_next( GroupEnum *ge )
  281. {
  282. if ( ge->groupEntry == NULL ||
  283. ge->groupIdx >= ge->groupEntry->numGroup )
  284. return NULL;
  285. return ge->groupEntry->groups[ ge->groupIdx++ ];
  286. }
  287. static void
  288. logSyntaxErr( const char *line )
  289. {
  290. Log_err( "Syntax error in config file: %s", line );
  291. }
  292. static void
  293. getBool( Bool *variable, const char *line )
  294. {
  295. Str value, name, lowerLn;
  296. Utl_cpyStr( lowerLn, line );
  297. Utl_toLower( lowerLn );
  298. if ( sscanf( lowerLn, MAXCHAR_FMT " " MAXCHAR_FMT, name, value ) != 2 )
  299. {
  300. logSyntaxErr( line );
  301. return;
  302. }
  303. if ( strcmp( value, "yes" ) == 0 )
  304. *variable = TRUE;
  305. else if ( strcmp( value, "no" ) == 0 )
  306. *variable = FALSE;
  307. else
  308. Log_err( "Error in config file %s must be yes or no", name );
  309. }
  310. static void
  311. getInt( int *variable, int min, int max, const char *line )
  312. {
  313. int value;
  314. Str name;
  315. if ( sscanf( line, MAXCHAR_FMT " %d", name, &value ) != 2 )
  316. {
  317. logSyntaxErr( line );
  318. return;
  319. }
  320. if ( value < min || value > max )
  321. {
  322. Log_err( "Range error in config file %s [%d,%d]", name, min, max );
  323. return;
  324. }
  325. *variable = value;
  326. }
  327. static void
  328. getStr( char *variable, const char *line )
  329. {
  330. Str dummy;
  331. if ( sscanf( line, MAXCHAR_FMT " " MAXCHAR_FMT, dummy, variable ) != 2 )
  332. {
  333. logSyntaxErr( line );
  334. return;
  335. }
  336. }
  337. static void
  338. getText( Str variable, const char *line )
  339. {
  340. const char *l;
  341. /* Skip command */
  342. l = Utl_restOfLn( line, 1 );
  343. Utl_cpyStr( variable, l );
  344. }
  345. static void
  346. getServ( const char *line )
  347. {
  348. Str dummy, name, user, pass;
  349. int r, len;
  350. ServEntry entry;
  351. memset( &entry, 0, sizeof( entry ) );
  352. user[ 0 ] = pass[ 0 ] = '\0';
  353. r = sscanf( line,
  354. MAXCHAR_FMT " " MAXCHAR_FMT " " MAXCHAR_FMT " " MAXCHAR_FMT,
  355. dummy, name, user, pass );
  356. if ( r < 2 )
  357. {
  358. logSyntaxErr( line );
  359. return;
  360. }
  361. len = strlen( name );
  362. /* To make server name more definit, it is made lowercase and
  363. port is removed, if it is the default port */
  364. if ( len > 4 && strcmp( name + len - 4, ":119" ) == 0 )
  365. name[ len - 4 ] = '\0';
  366. Utl_toLower( name );
  367. Utl_allocAndCpy( &entry.name, name );
  368. Utl_allocAndCpy( &entry.user, user );
  369. Utl_allocAndCpy( &entry.pass, pass );
  370. if ( config.maxServ < config.numServ + 1 )
  371. {
  372. if ( ! ( config.serv = realloc( config.serv,
  373. ( config.maxServ + 5 )
  374. * sizeof( ServEntry ) ) ) )
  375. Log_fatal( "Could not realloc server list" );
  376. config.maxServ += 5;
  377. }
  378. config.serv[ config.numServ++ ] = entry;
  379. }
  380. static void
  381. getExpire( const char *line )
  382. {
  383. Str dummy, pattern;
  384. ExpireEntry entry;
  385. int days;
  386. if ( sscanf( line, MAXCHAR_FMT " " MAXCHAR_FMT " %d",
  387. dummy, pattern, &days ) != 3 )
  388. {
  389. logSyntaxErr( line );
  390. return;
  391. }
  392. else
  393. {
  394. if ( days < 0 )
  395. {
  396. Log_err( "Expire days error in '%s': must be integer > 0",
  397. line, days );
  398. return;
  399. }
  400. Utl_toLower( pattern );
  401. Utl_allocAndCpy( &entry.pattern, pattern );
  402. entry.days = days;
  403. if ( config.maxExpire < config.numExpire + 1 )
  404. {
  405. if ( ! ( config.expire = realloc( config.expire,
  406. ( config.maxExpire + 5 )
  407. * sizeof( ExpireEntry ) ) ) )
  408. Log_fatal( "Could not realloc expire list" );
  409. config.maxExpire += 5;
  410. }
  411. config.expire[ config.numExpire++ ] = entry;
  412. }
  413. }
  414. static void
  415. getGroups( char *line, Bool isGet )
  416. {
  417. const char *name;
  418. ItemList *patterns;
  419. const char *pattern;
  420. if ( config.numServ == 0 )
  421. {
  422. Log_err( "No current server in %s", line );
  423. return;
  424. }
  425. name = line;
  426. /* Skip over name and terminate it */
  427. while ( line[ 0 ] != '\0' && ! isspace( line[ 0 ] ) )
  428. line++;
  429. if ( line[ 0 ] == '\0' )
  430. {
  431. logSyntaxErr( name );
  432. return;
  433. }
  434. line[ 0 ] = '\0';
  435. line++;
  436. patterns = new_Itl( line, " ," );
  437. for( pattern = Itl_first( patterns );
  438. pattern != NULL;
  439. pattern = Itl_next( patterns ) )
  440. {
  441. GroupEntry *g;
  442. if ( isGet )
  443. g = &config.serv[ config.numServ - 1 ].getgroups;
  444. else
  445. g = &config.serv[ config.numServ - 1 ].omitgroups;
  446. if ( g->maxGroup < g->numGroup + 1 )
  447. {
  448. if ( ! ( g->groups = realloc( g->groups,
  449. ( g->maxGroup + 5 )
  450. * sizeof( char * ) ) ) )
  451. Log_fatal( "Could not realloc group list" );
  452. g->maxGroup += 5;
  453. }
  454. Utl_allocAndCpy( &g->groups[ g->numGroup++ ], pattern );
  455. }
  456. del_Itl( patterns) ;
  457. }
  458. static void
  459. getDebugMask( char *line )
  460. {
  461. const char *name;
  462. ItemList *maskNames;
  463. const char *maskName;
  464. unsigned mask;
  465. name = line;
  466. /* Skip over name and terminate it */
  467. while ( line[ 0 ] != '\0' && ! isspace( line[ 0 ] ) )
  468. line++;
  469. if ( line[ 0 ] == '\0' )
  470. {
  471. logSyntaxErr( name );
  472. return;
  473. }
  474. line[ 0 ] = '\0';
  475. line++;
  476. mask = LOG_DBG_NONE;
  477. maskNames = new_Itl( line, " ," );
  478. for( maskName = Itl_first( maskNames );
  479. maskName != NULL;
  480. maskName = Itl_next( maskNames ) )
  481. {
  482. if ( strcmp( maskName, "all" ) == 0 )
  483. mask = LOG_DBG_ALL;
  484. else if ( strcmp( maskName, "none" ) == 0 )
  485. mask = LOG_DBG_NONE;
  486. else if ( strcmp( maskName, "config" ) == 0 )
  487. mask |= LOG_DBG_CONFIG;
  488. else if ( strcmp( maskName, "control" ) == 0 )
  489. mask |= LOG_DBG_CONTROL;
  490. else if ( strcmp( maskName, "expire" ) == 0 )
  491. mask |= LOG_DBG_EXPIRE;
  492. else if ( strcmp( maskName, "fetch" ) == 0 )
  493. mask |= LOG_DBG_FETCH;
  494. else if ( strcmp( maskName, "filter" ) == 0 )
  495. mask |= LOG_DBG_FILTER;
  496. else if ( strcmp( maskName, "newsbase" ) == 0 )
  497. mask |= LOG_DBG_NEWSBASE;
  498. else if ( strcmp( maskName, "noffle" ) == 0 )
  499. mask |= LOG_DBG_NOFFLE;
  500. else if ( strcmp( maskName, "post" ) == 0 )
  501. mask |= LOG_DBG_POST;
  502. else if ( strcmp( maskName, "protocol" ) == 0 )
  503. mask |= LOG_DBG_PROTOCOL;
  504. else if ( strcmp( maskName, "requests" ) == 0 )
  505. mask |= LOG_DBG_REQUESTS;
  506. else if ( strcmp( maskName, "server" ) == 0 )
  507. mask |= LOG_DBG_SERVER;
  508. else if ( strcmp( maskName, "auth" ) == 0 )
  509. mask |= LOG_DBG_AUTH;
  510. else
  511. logSyntaxErr( line );
  512. }
  513. del_Itl( maskNames) ;
  514. Log_setDbgMask( mask );
  515. }
  516. static Bool
  517. isValidAutoSubscribeMode( const char *mode )
  518. {
  519. return strcmp( mode, "full" ) == 0
  520. || strcmp( mode, "thread" ) == 0
  521. || strcmp( mode, "over" ) == 0
  522. || strcmp( mode, "off" ) == 0;
  523. }
  524. static void
  525. getAutoSubscribeMode( const char *line )
  526. {
  527. Str dummy, pattern, mode;
  528. AutoSubscribeModeEntry entry;
  529. int items;
  530. items = sscanf( line, MAXCHAR_FMT " " MAXCHAR_FMT " " MAXCHAR_FMT,
  531. dummy, pattern, mode );
  532. if ( items == 2 )
  533. {
  534. /* Backwards compat. default-auto-subscribe-mode */
  535. Utl_cpyStr( mode, pattern );
  536. Utl_toLower( mode );
  537. if ( ! isValidAutoSubscribeMode( mode ) )
  538. {
  539. logSyntaxErr( line );
  540. return;
  541. }
  542. Utl_cpyStr( config.defaultAutoSubscribeMode, mode );
  543. return;
  544. }
  545. else if ( items != 3 )
  546. {
  547. logSyntaxErr( line );
  548. return;
  549. }
  550. Utl_toLower( mode );
  551. if ( ! isValidAutoSubscribeMode( mode ) )
  552. {
  553. logSyntaxErr( line );
  554. return;
  555. }
  556. Utl_toLower( pattern );
  557. Utl_allocAndCpy( &entry.pattern, pattern );
  558. Utl_allocAndCpy( &entry.mode, mode );
  559. if ( config.maxAutoSubscribeMode < config.numAutoSubscribeMode + 1 )
  560. {
  561. if ( ! ( config.autoSubscribeMode =
  562. realloc( config.autoSubscribeMode,
  563. ( config.maxAutoSubscribeMode + 5 )
  564. * sizeof( AutoSubscribeModeEntry ) ) ) )
  565. Log_fatal( "Could not realloc auto subscribe mode list" );
  566. config.maxAutoSubscribeMode += 5;
  567. }
  568. config.autoSubscribeMode[ config.numAutoSubscribeMode++ ] = entry;
  569. }
  570. static const char *
  571. getToken( const char *line, Str value )
  572. {
  573. Bool isQuoted;
  574. char quoteChar;
  575. Bool seenEscape;
  576. char *maxVal;
  577. while ( *line != '\0' && isspace( *line ) )
  578. line++;
  579. if ( *line == '\0' )
  580. return NULL;
  581. maxVal = &value[ MAXCHAR ];
  582. isQuoted = ( *line == '\'' || *line == '"' );
  583. if ( isQuoted )
  584. {
  585. quoteChar = *line;
  586. line++;
  587. seenEscape = FALSE;
  588. while ( *line != '\0'
  589. && ( *line != quoteChar || seenEscape )
  590. && value < maxVal )
  591. {
  592. if ( seenEscape )
  593. {
  594. *value++ = *line;
  595. seenEscape = FALSE;
  596. }
  597. else
  598. {
  599. if ( *line == '\\' )
  600. seenEscape = TRUE;
  601. else
  602. *value++ = *line;
  603. }
  604. line++;
  605. }
  606. if ( *line == quoteChar )
  607. line++;
  608. }
  609. else
  610. {
  611. while ( *line != '\0' && ! isspace( *line ) && value < maxVal )
  612. *value++ = *line++;
  613. }
  614. *value = '\0';
  615. return line;
  616. }
  617. /* very simple date parser.
  618. * examples:
  619. * now+
  620. */
  621. static Bool
  622. get_simpledate( time_t *timeoffsetp, FilterRuleDateEnumType *vartimep, const char *val)
  623. {
  624. float timef;
  625. if ( ! strncasecmp( val, "invalid", 7 ) )
  626. {
  627. *vartimep = INVALID;
  628. return TRUE;
  629. }
  630. else if ( ! strncasecmp( val, "now", 3 ) )
  631. {
  632. val += 3;
  633. *vartimep = NOW;
  634. }
  635. else if ( ! strncasecmp( val, "lastupdate", 10 ) )
  636. {
  637. val += 10;
  638. *vartimep = LASTUPDATE;
  639. }
  640. else
  641. {
  642. *vartimep = FIXED;
  643. *timeoffsetp = Utl_parseNewsDate( val );
  644. if ( *timeoffsetp == (time_t) -1 )
  645. return FALSE;
  646. else
  647. return TRUE;
  648. }
  649. /* NOW, LASTUPDATE +/- number of days. */
  650. timef = atof( val ) * 86400.0 ; /* 24 * 60 * 60 == 86400 */
  651. /* let's assume more than 10 years of timeoffset are a mistake. */
  652. if ( timef > 31536000.0 || timef < -31536000.0 )
  653. return FALSE;
  654. *timeoffsetp = (time_t) timef;
  655. /* Todo: check if any garbage follows. */
  656. return TRUE;
  657. }
  658. static void
  659. getFilter( const char *line )
  660. {
  661. Str ruleBuf, value;
  662. const char *l;
  663. char *p, *ruleName;
  664. Filter *f;
  665. FilterRule rule;
  666. Bool seenAction;
  667. f = new_Filter();
  668. /* Skip "filter" */
  669. l = Utl_restOfLn( line, 1 );
  670. seenAction = FALSE;
  671. for(;;)
  672. {
  673. while ( *l != '\0' && isspace( *l ) )
  674. l++;
  675. if ( *l == '\0' )
  676. break;
  677. /* Get the rule title */
  678. p = ruleBuf;
  679. while ( *l != '\0' && *l != '=' && *l != '<' && *l != '>' )
  680. *p++ = *l++;
  681. *p = '\0';
  682. ruleName = Utl_stripWhiteSpace( ruleBuf );
  683. Utl_toLower( ruleName );
  684. if ( *ruleName == '\0' )
  685. goto synErr;
  686. /* Do we know this rule? */
  687. if ( strcmp( ruleName, "group" ) == 0 )
  688. rule.type = RULE_NEWSGROUP;
  689. else if ( strcmp( ruleName, "subject" ) == 0 )
  690. rule.type = RULE_SUBJECT;
  691. else if ( strcmp( ruleName, "reference" ) == 0 )
  692. rule.type = RULE_REFERENCE;
  693. else if ( strcmp( ruleName, "from" ) == 0 )
  694. rule.type = RULE_FROM;
  695. else if ( strcmp( ruleName, "msgid" ) == 0 )
  696. rule.type = RULE_MSGID;
  697. else if ( strcmp( ruleName, "bytes" ) == 0 )
  698. rule.type = RULE_BYTES_EQ;
  699. else if ( strcmp( ruleName, "lines" ) == 0 )
  700. rule.type = RULE_LINES_EQ;
  701. else if ( strcmp( ruleName, "refs" ) == 0 )
  702. rule.type = RULE_NOREFS_EQ;
  703. else if ( strcmp( ruleName, "xposts" ) == 0 )
  704. rule.type = RULE_XPOSTS_EQ;
  705. else if ( strcmp( ruleName, "post-status" ) == 0 )
  706. rule.type = RULE_POST_STATUS;
  707. else if ( strcmp( ruleName, "date" ) == 0 )
  708. rule.type = RULE_DATE_EQ;
  709. /* date<lastupdate-12 equals older=lastupdate-12
  710. * date>now+1.5 equals newer=now+1.5
  711. * date=now equals older=now+1 AND newer=now-1
  712. * Stupid people like Mirko keep making mistakes
  713. * if they're forced using date< or date>.
  714. */
  715. else if ( strcmp( ruleName, "older" ) == 0 )
  716. rule.type = RULE_DATE_LT;
  717. else if ( strcmp( ruleName, "newer" ) == 0 )
  718. rule.type = RULE_DATE_GT;
  719. else if ( strcmp( ruleName, "action" ) != 0 )
  720. goto synErr;
  721. if ( rule.type == RULE_BYTES_EQ ||
  722. rule.type == RULE_LINES_EQ ||
  723. rule.type == RULE_NOREFS_EQ ||
  724. rule.type == RULE_XPOSTS_EQ ||
  725. rule.type == RULE_DATE_EQ )
  726. {
  727. if ( *l == '<' )
  728. rule.type--;
  729. else if ( *l == '>' )
  730. rule.type++;
  731. else if ( *l != '=' )
  732. goto synErr;
  733. }
  734. else if ( *l != '=' )
  735. goto synErr;
  736. /* Skip past '=' (or '>' or '<') */
  737. l++;
  738. /* OK, we now have a valid rule. What value? */
  739. l = getToken( l, value );
  740. if ( l == NULL )
  741. goto synErr;
  742. if ( strcmp( ruleName, "action" ) == 0 )
  743. {
  744. if ( seenAction )
  745. goto synErr;
  746. Utl_toLower( value );
  747. if ( strcmp( value, "full" ) == 0 )
  748. f->action = FILTER_FULL;
  749. else if ( strcmp( value, "over" ) == 0 )
  750. f->action = FILTER_XOVER;
  751. else if ( strcmp( value, "thread" ) == 0 )
  752. f->action = FILTER_THREAD;
  753. else if ( strcmp( value, "discard" ) == 0 )
  754. f->action = FILTER_DISCARD;
  755. else if ( strcmp( value, "default" ) == 0 )
  756. f->action = FILTER_DEFAULT;
  757. seenAction = TRUE;
  758. }
  759. else if ( rule.type == RULE_NEWSGROUP )
  760. Utl_allocAndCpy( &rule.data.grp, value );
  761. else if ( rule.type >= RULE_SUBJECT && rule.type <= RULE_MSGID )
  762. {
  763. if ( regcomp( &rule.data.regex, value, REG_EXTENDED ) != 0 )
  764. goto synErr;
  765. }
  766. else if (rule.type == RULE_POST_STATUS )
  767. {
  768. if ( ( strcmp( value, "yes" ) == 0 ) || \
  769. ( strcmp( value, "no" ) == 0 ) || \
  770. ( strncmp( value, "mod", 3 ) == 0 ) )
  771. /* no need to type out "moderated" */
  772. rule.data.postAllow = value[0]; /* 'y','n' or 'm' */
  773. else
  774. goto synErr;
  775. }
  776. else if ( rule.type == RULE_DATE_LT ||
  777. rule.type == RULE_DATE_EQ ||
  778. rule.type == RULE_DATE_GT )
  779. {
  780. if ( !get_simpledate( &rule.data.reftime.timeoffset, &rule.data.reftime.vartime, value ) )
  781. goto synErr;
  782. if ( rule.type != RULE_DATE_EQ &&
  783. rule.data.reftime.vartime == INVALID )
  784. goto synErr;
  785. }
  786. else
  787. {
  788. char * endVal;
  789. int suffix;
  790. rule.data.amount = strtoul( value, &endVal, 0 );
  791. suffix = tolower( *endVal );
  792. if ( suffix == 'k' || suffix == 'm' )
  793. {
  794. rule.data.amount *= 1024;
  795. if ( suffix == 'm' )
  796. rule.data.amount *= 1024;
  797. endVal++;
  798. }
  799. if ( *endVal != '\0' && ! isspace( *endVal ) )
  800. goto synErr;
  801. }
  802. if ( strcmp( ruleName, "action" ) != 0 )
  803. {
  804. Log_dbg( LOG_DBG_CONFIG,
  805. "Adding rule type %d value %s",
  806. rule.type, value );
  807. Flt_addRule( f, rule );
  808. }
  809. }
  810. Log_dbg( LOG_DBG_CONFIG, "Adding filter, action %d", f->action );
  811. Flt_addFilter( f );
  812. return;
  813. synErr:
  814. logSyntaxErr( line );
  815. return;
  816. }
  817. void
  818. Cfg_read( void )
  819. {
  820. char *p;
  821. FILE *f;
  822. Str file, line, lowerLine, name, s;
  823. Utl_cpyStr( file, CONFIGFILE );
  824. if ( ! ( f = fopen( file, "r" ) ) )
  825. {
  826. Log_err( "Cannot read %s", file );
  827. return;
  828. }
  829. while ( fgets( line, MAXCHAR, f ) )
  830. {
  831. p = Utl_stripWhiteSpace( line );
  832. Utl_stripComment( p );
  833. Utl_cpyStr( lowerLine, p );
  834. Utl_toLower( lowerLine );
  835. p = lowerLine;
  836. if ( *p == '\0' )
  837. continue;
  838. if ( sscanf( p, MAXCHAR_FMT, name ) != 1 )
  839. Log_err( "Syntax error in %s: %s", file, line );
  840. else if ( strcmp( "max-fetch", name ) == 0 )
  841. getInt( &config.maxFetch, 0, INT_MAX, p );
  842. else if ( strcmp( "auto-unsubscribe-days", name ) == 0 )
  843. getInt( &config.autoUnsubscribe, -1, INT_MAX, p );
  844. else if ( strcmp( "thread-follow-time", name ) == 0 )
  845. getInt( &config.threadFollowTime, 0, INT_MAX, p );
  846. else if ( strcmp( "connect-timeout", name ) == 0 )
  847. getInt( &config.connectTimeout, 0, INT_MAX, p );
  848. else if ( strcmp( "default-expire", name ) == 0 )
  849. getInt( &config.defaultExpire, 0, INT_MAX, p );
  850. else if ( strcmp( "auto-subscribe", name ) == 0 )
  851. getBool( &config.autoSubscribe, p );
  852. else if ( strcmp( "auto-unsubscribe", name ) == 0 )
  853. getBool( &config.autoUnsubscribe, p );
  854. else if ( strcmp( "info-always-unread", name ) == 0 )
  855. getBool( &config.infoAlways, p );
  856. else if ( strcmp( "replace-messageid", name ) == 0 )
  857. getBool( &config.replaceMsgId, p );
  858. else if ( strcmp( "hostname", name ) == 0 )
  859. /* use line, do not change to lowercase */
  860. getStr( config.hostnameMsgId, line );
  861. else if ( strcmp( "post-locally", name ) == 0 )
  862. getBool( &config.postLocal, p );
  863. #if USE_AUTH
  864. /*
  865. * Don't recognise this unless we have some sort of auth
  866. * built in. A small sanity check on the config.
  867. */
  868. else if ( strcmp( "authenticate-client", name ) == 0 )
  869. getBool( &config.clientAuth, p );
  870. #endif
  871. else if ( strcmp( "default-auto-subscribe-mode", name ) == 0 )
  872. {
  873. getStr( s, p );
  874. if ( ! isValidAutoSubscribeMode( s ) )
  875. {
  876. logSyntaxErr( line );
  877. return;
  878. }
  879. else
  880. Utl_cpyStr( config.defaultAutoSubscribeMode, s );
  881. }
  882. else if ( strcmp( "mail-to", name ) == 0 )
  883. getStr( config.mailTo, p );
  884. else if ( strcmp( "expire", name ) == 0 )
  885. getExpire( p );
  886. else if ( strcmp( "auto-subscribe-mode", name ) == 0 )
  887. getAutoSubscribeMode( p );
  888. else if ( strcmp( "log-debug", name ) == 0 )
  889. getDebugMask( p );
  890. else if ( strcmp( "getgroups", name ) == 0 )
  891. getGroups( p, TRUE );
  892. else if ( strcmp( "omitgroups", name ) == 0 )
  893. getGroups( p, FALSE );
  894. else if ( strcmp( "path-header", name ) == 0 )
  895. getStr( config.pathHeader, p );
  896. else if ( strcmp( "from-domain", name ) == 0 )
  897. getStr( config.fromDomain, p );
  898. /* The following need line because they may have uppercase data */
  899. else if ( strcmp( "organization", name ) == 0 )
  900. getText( config.organization, line );
  901. else if ( strcmp( "noffle-user", name ) == 0 )
  902. getText( config.noffleUser, line );
  903. else if ( strcmp( "noffle-group", name ) == 0 )
  904. getText( config.noffleUser, line );
  905. else if ( strcmp( "server", name ) == 0 )
  906. getServ( line );
  907. else if ( strcmp( "filter", name ) == 0 )
  908. getFilter( line );
  909. else
  910. Log_err( "Unknown config option: %s", name );
  911. }
  912. fclose( f );
  913. if ( ! config.numServ )
  914. Log_fatal( "Config file contains no server" );
  915. }