PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/noffle/src/configfile.c

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