PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

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

#
C | 424 lines | 315 code | 65 blank | 44 comment | 74 complexity | 5bea6f296c8e9ff40d99c5481fbf2106 MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. filter.c
  3. Article filtering.
  4. $Id: filter.c 413 2002-12-27 21:48:25Z bears $
  5. */
  6. #if HAVE_CONFIG_H
  7. #include <config.h>
  8. #endif
  9. #include <ctype.h>
  10. #include "common.h"
  11. #include "configfile.h"
  12. #include "itemlist.h"
  13. #include "log.h"
  14. #include "wildmat.h"
  15. #include "group.h"
  16. #include "util.h"
  17. #include "filter.h"
  18. struct
  19. {
  20. int nFilters;
  21. int maxFilters;
  22. const Filter **filters;
  23. Bool needGroups;
  24. } filter = { 0, 0, NULL, FALSE };
  25. static unsigned long
  26. countGroups( const char *grps )
  27. {
  28. unsigned long res;
  29. res = 1;
  30. while ( *grps != '\0' )
  31. {
  32. if ( *grps == ',' )
  33. res++;
  34. grps++;
  35. }
  36. return res;
  37. }
  38. static unsigned long
  39. countRefs( const char *refs )
  40. {
  41. unsigned long res;
  42. Bool inRef;
  43. res = 0;
  44. inRef = FALSE;
  45. while ( *refs != '\0' )
  46. {
  47. if ( inRef )
  48. {
  49. if ( *refs == '>' )
  50. {
  51. inRef = FALSE;
  52. res++;
  53. }
  54. }
  55. else if ( *refs == '<' )
  56. inRef = TRUE;
  57. refs++;
  58. }
  59. return res;
  60. }
  61. /* Check a single rule to see if it passes. */
  62. static Bool
  63. checkRule( const char *thisGrp, const char *newsgroups,
  64. const Over *ov, const FilterRule *r )
  65. {
  66. unsigned long ul;
  67. ItemList *grps;
  68. const char *p;
  69. time_t articletime;
  70. switch( r->type )
  71. {
  72. case RULE_NEWSGROUP:
  73. if ( Wld_match( thisGrp, r->data.grp ) )
  74. return TRUE;
  75. if ( newsgroups != NULL )
  76. {
  77. grps = new_Itl( newsgroups, " ,\t" );
  78. for ( p = Itl_first( grps ); p != NULL; p = Itl_next( grps ) )
  79. if ( Wld_match( p, r->data.grp ) )
  80. return TRUE;
  81. del_Itl( grps );
  82. }
  83. return FALSE;
  84. case RULE_SUBJECT:
  85. return ( regexec( &r->data.regex, Ov_subj( ov ), 0, NULL, 0 ) == 0 );
  86. case RULE_REFERENCE: /* kill thread by Msg-Id in References: */
  87. return ( regexec( &r->data.regex, Ov_ref( ov ), 0, NULL, 0 ) == 0 );
  88. case RULE_FROM:
  89. return ( regexec( &r->data.regex, Ov_from( ov ), 0, NULL, 0 ) == 0 );
  90. case RULE_BYTES_LT:
  91. return ( Ov_bytes( ov ) < r->data.amount );
  92. case RULE_BYTES_EQ:
  93. return ( Ov_bytes( ov ) == r->data.amount );
  94. case RULE_BYTES_GT:
  95. return ( Ov_bytes( ov ) > r->data.amount );
  96. case RULE_LINES_LT:
  97. return ( Ov_lines( ov ) < r->data.amount );
  98. case RULE_LINES_EQ:
  99. return ( Ov_lines( ov ) == r->data.amount );
  100. case RULE_LINES_GT:
  101. return ( Ov_lines( ov ) > r->data.amount );
  102. case RULE_MSGID:
  103. return ( regexec( &r->data.regex, Ov_msgId( ov ), 0, NULL, 0 ) == 0 );
  104. case RULE_DATE_LT:
  105. /* Utl_parseNewsDate() is quite picky. I'm not entirely happy
  106. about this, but I won't implement a relaxed date parser. */
  107. articletime = Utl_parseNewsDate( Ov_date( ov ) );
  108. if ( articletime == (time_t) -1 )
  109. return FALSE;
  110. return ( articletime < r->data.reftime.calctime );
  111. case RULE_DATE_EQ:
  112. articletime = Utl_parseNewsDate( Ov_date( ov ) );
  113. if ( ( articletime == (time_t) -1)
  114. && ( r->data.reftime.vartime == INVALID ))
  115. return TRUE;
  116. if ( ( articletime == (time_t) -1)
  117. != ( r->data.reftime.vartime == INVALID ))
  118. return FALSE;
  119. return ( ( articletime <= r->data.reftime.calctime
  120. + RULE_DATE_EQ_PRECISION )
  121. && ( articletime >= r->data.reftime.calctime
  122. - RULE_DATE_EQ_PRECISION ) );
  123. case RULE_DATE_GT:
  124. articletime = Utl_parseNewsDate( Ov_date( ov ) );
  125. if ( articletime == (time_t) -1 )
  126. return FALSE;
  127. return ( articletime > r->data.reftime.calctime );
  128. case RULE_NOREFS_LT:
  129. ul = countRefs( Ov_ref( ov ) );
  130. return ( ul < r->data.amount );
  131. case RULE_NOREFS_EQ:
  132. ul = countRefs( Ov_ref( ov ) );
  133. return ( ul == r->data.amount );
  134. case RULE_NOREFS_GT:
  135. ul = countRefs( Ov_ref( ov ) );
  136. return ( ul > r->data.amount );
  137. case RULE_XPOSTS_LT:
  138. if ( newsgroups == NULL )
  139. return FALSE;
  140. ul = countGroups( newsgroups );
  141. return ( ul < r->data.amount );
  142. case RULE_XPOSTS_EQ:
  143. if ( newsgroups == NULL )
  144. return FALSE;
  145. ul = countGroups( newsgroups );
  146. return ( ul == r->data.amount );
  147. case RULE_XPOSTS_GT:
  148. if ( newsgroups == NULL )
  149. return FALSE;
  150. ul = countGroups( newsgroups );
  151. return ( ul > r->data.amount );
  152. case RULE_POST_STATUS:
  153. if ( Grp_postAllow( thisGrp ) == r->data.postAllow )
  154. return TRUE;
  155. return FALSE;
  156. }
  157. ASSERT( FALSE ); /* Shouldn't get here */
  158. return 0; /* Keep compiler quiet */
  159. }
  160. /* Check a single filter to see if it fires. */
  161. static Bool
  162. checkFilter( const char *thisGrp, const char *newsgroups,
  163. const Over *ov, const Filter *f )
  164. {
  165. int i;
  166. for ( i = 0; i < f->nRules; i++ )
  167. if ( ! checkRule( thisGrp, newsgroups, ov, &f->rules[i] ) )
  168. return FALSE;
  169. return TRUE;
  170. }
  171. /* Add a filter to the list of filters. */
  172. void
  173. Flt_addFilter( const Filter *f )
  174. {
  175. ASSERT( f != NULL );
  176. if ( ( filter.nFilters + 1 ) > filter.maxFilters )
  177. {
  178. filter.filters =
  179. ( const Filter ** ) realloc( filter.filters,
  180. ( filter.maxFilters + 5 )
  181. * sizeof( Filter * ) );
  182. if ( filter.filters == NULL )
  183. Log_fatal( "Could not realloc filter list" );
  184. filter.maxFilters += 5;
  185. }
  186. filter.filters[ filter.nFilters++ ] = f;
  187. }
  188. /*
  189. * Called by Fetch_init().
  190. * Must be called before
  191. * Fetch_getNewGrps(), Client_getNewgrps(), client.c:processGrps()
  192. * because processGrps() sets the stampfile needed by lastupdate.
  193. */
  194. void
  195. Flt_init( const char * server )
  196. {
  197. int index1, index2;
  198. time_t now, lastupdate;
  199. FilterRule * thisRule ;
  200. Str filename;
  201. time ( &now );
  202. lastupdate = (time_t) 0; /* defaults to start of epoch */
  203. snprintf( filename, MAXCHAR, "%s/lastupdate.%s",
  204. Cfg_spoolDir(), server );
  205. if ( !Utl_getStamp( &lastupdate , filename ) )
  206. /* There's no stamp file if server has never been queried.
  207. *
  208. */
  209. Log_dbg( LOG_DBG_FILTER,
  210. "Filter unable to get stamp file %s . Please query server.", filename );
  211. /* traverse all rules of all filters */
  212. for ( index1 = 0; index1 < filter.nFilters; index1++ )
  213. {
  214. for ( index2 = 0; index2 < filter.filters[ index1 ] -> nRules; index2++ )
  215. {
  216. thisRule = & ( filter.filters[ index1 ] -> rules[ index2 ] );
  217. switch ( thisRule -> type )
  218. {
  219. /* evaluate variable date specs */
  220. case RULE_DATE_LT:
  221. case RULE_DATE_EQ:
  222. case RULE_DATE_GT:
  223. thisRule -> data.reftime.calctime =
  224. thisRule ->data.reftime.timeoffset;
  225. switch ( thisRule ->data.reftime.vartime )
  226. {
  227. case NOW:
  228. thisRule -> data.reftime.calctime += now;
  229. break;
  230. case LASTUPDATE:
  231. thisRule -> data.reftime.calctime += lastupdate;
  232. break;
  233. default:
  234. break;
  235. } /* end switch( ... vartime) */
  236. /* Silently fix absolute dates before the epoch.
  237. * This is not the place to mock about strange dates.
  238. */
  239. if ( thisRule -> data.reftime.calctime < (time_t) 0 )
  240. thisRule -> data.reftime.calctime = (time_t) 0 ;
  241. /* Log_dbg( LOG_DBG_FILTER, "%d: %dl = %dl + %d",
  242. * thisRule -> type,
  243. * (long) thisRule -> data.reftime.calctime,
  244. * (long) thisRule ->data.reftime.timeoffset,
  245. * (int) ( thisRule ->data.reftime.vartime == NOW ?
  246. * now :
  247. * ( thisRule ->data.reftime.vartime == LASTUPDATE ?
  248. * lastupdate : thisRule ->data.reftime.vartime ) ) );
  249. */ break;
  250. default:
  251. break;
  252. } /* end switch( ... -> type) */
  253. } /* end for() */
  254. } /* end for() */
  255. return ;
  256. }
  257. /*
  258. * Run the rules over the supplied overview. If a specific rule fires,
  259. * returns its action. If no rule fires, or a rule specifying the default
  260. * action fires, return the default read mode.
  261. */
  262. FilterAction
  263. Flt_checkFilters( const char *thisGrp, const char *newsgroups,
  264. const Over *ov, FetchMode mode )
  265. {
  266. int i;
  267. for ( i = 0; i < filter.nFilters; i++ )
  268. if ( checkFilter( thisGrp, newsgroups, ov, filter.filters[ i ] ) )
  269. {
  270. FilterAction action = filter.filters[ i ]->action;
  271. Log_dbg( LOG_DBG_FILTER,
  272. "Filter %d fired on message %s",
  273. i, Ov_msgId( ov ) );
  274. if ( action == FILTER_DEFAULT )
  275. break;
  276. else
  277. return action;
  278. }
  279. switch( mode )
  280. {
  281. case FULL: return FILTER_FULL;
  282. case THREAD: return FILTER_THREAD;
  283. case OVER: return FILTER_XOVER;
  284. }
  285. ASSERT( FALSE ); /* Shouldn't get here */
  286. return FILTER_FULL; /* Keep compiler quiet */
  287. }
  288. Filter *
  289. new_Filter( void )
  290. {
  291. Filter *f;
  292. if ( ! ( f = ( Filter * ) malloc( sizeof( Filter ) ) ) )
  293. Log_fatal( "Cannot allocate Filter" );
  294. f->nRules = 0;
  295. f->maxRules = 0;
  296. f->rules = NULL;
  297. f->action = FILTER_DEFAULT;
  298. return f;
  299. }
  300. void
  301. del_Filter( Filter *f )
  302. {
  303. if ( f == NULL )
  304. return;
  305. if ( f->rules != NULL )
  306. free( f->rules );
  307. free( f );
  308. }
  309. FilterAction
  310. Flt_action( const Filter *f )
  311. {
  312. return f->action;
  313. }
  314. int
  315. Flt_nRules( const Filter *f )
  316. {
  317. return f->nRules;
  318. }
  319. /*
  320. * Do we have a rule requiring us to fetch the Newsgroups: headers of
  321. * articles?
  322. */
  323. Bool
  324. Flt_getNewsgroups( void )
  325. {
  326. return filter.needGroups;
  327. }
  328. FilterRule
  329. Flt_rule( const Filter *f, int ruleNo )
  330. {
  331. ASSERT( ruleNo < f->nRules );
  332. return f->rules[ ruleNo ];
  333. }
  334. void
  335. Flt_setAction( Filter *f, FilterAction action )
  336. {
  337. f->action = action;
  338. }
  339. void
  340. Flt_addRule( Filter *f, FilterRule rule )
  341. {
  342. /* Does the rule require Newsgroups: headers to be fetched? */
  343. if ( rule.type == RULE_NEWSGROUP ||
  344. ( rule.type >= RULE_XPOSTS_LT && rule.type <= RULE_XPOSTS_GT ) )
  345. filter.needGroups = TRUE;
  346. if ( f->nRules + 1 > f->maxRules )
  347. {
  348. f->rules =
  349. ( FilterRule * ) realloc( f->rules,
  350. ( f->maxRules + 5 )
  351. * sizeof( FilterRule ) );
  352. if ( f->rules == NULL )
  353. Log_fatal( "Could not realloc rule list" );
  354. f->maxRules += 5;
  355. }
  356. f->rules[ f->nRules++ ] = rule;
  357. }