PageRenderTime 48ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/tags/release-1-2-0-rc1/noffle/src/configfile.c

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