PageRenderTime 50ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/start/www/src/filter.c

#
C | 323 lines | 250 code | 53 blank | 20 comment | 54 complexity | 5cf9ddb825d124b28a34c3e90bf3bbe5 MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. filter.c
  3. Article filtering.
  4. $Id: filter.c 346 2001-12-12 13:32:38Z bears $
  5. */
  6. #if HAVE_CONFIG_H
  7. #include <config.h>
  8. #endif
  9. #include <ctype.h>
  10. #include "common.h"
  11. #include "filter.h"
  12. #include "itemlist.h"
  13. #include "log.h"
  14. #include "wildmat.h"
  15. #include "group.h"
  16. struct
  17. {
  18. int nFilters;
  19. int maxFilters;
  20. const Filter **filters;
  21. Bool needGroups;
  22. } filter = { 0, 0, NULL, FALSE };
  23. static unsigned long
  24. countGroups( const char *grps )
  25. {
  26. unsigned long res;
  27. res = 1;
  28. while ( *grps != '\0' )
  29. {
  30. if ( *grps == ',' )
  31. res++;
  32. grps++;
  33. }
  34. return res;
  35. }
  36. static unsigned long
  37. countRefs( const char *refs )
  38. {
  39. unsigned long res;
  40. Bool inRef;
  41. res = 0;
  42. inRef = FALSE;
  43. while ( *refs != '\0' )
  44. {
  45. if ( inRef )
  46. {
  47. if ( *refs == '>' )
  48. {
  49. inRef = FALSE;
  50. res++;
  51. }
  52. }
  53. else if ( *refs == '<' )
  54. inRef = TRUE;
  55. refs++;
  56. }
  57. return res;
  58. }
  59. /* Check a single rule to see if it passes. */
  60. static Bool
  61. checkRule( const char *thisGrp, const char *newsgroups,
  62. const Over *ov, const FilterRule *r )
  63. {
  64. unsigned long ul;
  65. ItemList *grps;
  66. const char *p;
  67. switch( r->type )
  68. {
  69. case RULE_NEWSGROUP:
  70. if ( Wld_match( thisGrp, r->data.grp ) )
  71. return TRUE;
  72. if ( newsgroups != NULL )
  73. {
  74. grps = new_Itl( newsgroups, " ,\t" );
  75. for ( p = Itl_first( grps ); p != NULL; p = Itl_next( grps ) )
  76. if ( Wld_match( p, r->data.grp ) )
  77. return TRUE;
  78. del_Itl( grps );
  79. }
  80. return FALSE;
  81. case RULE_SUBJECT:
  82. return ( regexec( &r->data.regex, Ov_subj( ov ), 0, NULL, 0 ) == 0 );
  83. case RULE_FROM:
  84. return ( regexec( &r->data.regex, Ov_from( ov ), 0, NULL, 0 ) == 0 );
  85. case RULE_BYTES_LT:
  86. return ( Ov_bytes( ov ) < r->data.amount );
  87. case RULE_BYTES_EQ:
  88. return ( Ov_bytes( ov ) == r->data.amount );
  89. case RULE_BYTES_GT:
  90. return ( Ov_bytes( ov ) > r->data.amount );
  91. case RULE_LINES_LT:
  92. return ( Ov_lines( ov ) < r->data.amount );
  93. case RULE_LINES_EQ:
  94. return ( Ov_lines( ov ) == r->data.amount );
  95. case RULE_LINES_GT:
  96. return ( Ov_lines( ov ) > r->data.amount );
  97. case RULE_MSGID:
  98. return ( regexec( &r->data.regex, Ov_msgId( ov ), 0, NULL, 0 ) == 0 );
  99. case RULE_NOREFS_LT:
  100. ul = countRefs( Ov_ref( ov ) );
  101. return ( ul < r->data.amount );
  102. case RULE_NOREFS_EQ:
  103. ul = countRefs( Ov_ref( ov ) );
  104. return ( ul == r->data.amount );
  105. case RULE_NOREFS_GT:
  106. ul = countRefs( Ov_ref( ov ) );
  107. return ( ul > r->data.amount );
  108. case RULE_XPOSTS_LT:
  109. if ( newsgroups == NULL )
  110. return FALSE;
  111. ul = countGroups( newsgroups );
  112. return ( ul < r->data.amount );
  113. case RULE_XPOSTS_EQ:
  114. if ( newsgroups == NULL )
  115. return FALSE;
  116. ul = countGroups( newsgroups );
  117. return ( ul == r->data.amount );
  118. case RULE_XPOSTS_GT:
  119. if ( newsgroups == NULL )
  120. return FALSE;
  121. ul = countGroups( newsgroups );
  122. return ( ul > r->data.amount );
  123. case RULE_POST_STATUS:
  124. if ( Grp_postAllow( thisGrp ) == r->data.postAllow )
  125. return TRUE;
  126. return FALSE;
  127. }
  128. ASSERT( FALSE ); /* Shouldn't get here */
  129. return 0; /* Keep compiler quiet */
  130. }
  131. /* Check a single filter to see if it fires. */
  132. static Bool
  133. checkFilter( const char *thisGrp, const char *newsgroups,
  134. const Over *ov, const Filter *f )
  135. {
  136. int i;
  137. for ( i = 0; i < f->nRules; i++ )
  138. if ( ! checkRule( thisGrp, newsgroups, ov, &f->rules[i] ) )
  139. return FALSE;
  140. return TRUE;
  141. }
  142. /* Add a filter to the list of filters. */
  143. void
  144. Flt_addFilter( const Filter *f )
  145. {
  146. ASSERT( f != NULL );
  147. if ( ( filter.nFilters + 1 ) > filter.maxFilters )
  148. {
  149. filter.filters =
  150. ( const Filter ** ) realloc( filter.filters,
  151. ( filter.maxFilters + 5 )
  152. * sizeof( Filter * ) );
  153. if ( filter.filters == NULL )
  154. {
  155. Log_err( "Could not realloc filter list" );
  156. exit( EXIT_FAILURE );
  157. }
  158. filter.maxFilters += 5;
  159. }
  160. filter.filters[ filter.nFilters++ ] = f;
  161. }
  162. /*
  163. * Run the rules over the supplied overview. If a specific rule fires,
  164. * returns its action. If no rule fires, or a rule specifying the default
  165. * action fires, return the default read mode.
  166. */
  167. FilterAction
  168. Flt_checkFilters( const char *thisGrp, const char *newsgroups,
  169. const Over *ov, FetchMode mode )
  170. {
  171. int i;
  172. for ( i = 0; i < filter.nFilters; i++ )
  173. if ( checkFilter( thisGrp, newsgroups, ov, filter.filters[ i ] ) )
  174. {
  175. FilterAction action = filter.filters[ i ]->action;
  176. Log_dbg( LOG_DBG_FILTER,
  177. "Filter %d fired on message %s",
  178. i, Ov_msgId( ov ) );
  179. if ( action == FILTER_DEFAULT )
  180. break;
  181. else
  182. return action;
  183. }
  184. switch( mode )
  185. {
  186. case FULL: return FILTER_FULL;
  187. case THREAD: return FILTER_THREAD;
  188. case OVER: return FILTER_XOVER;
  189. }
  190. ASSERT( FALSE ); /* Shouldn't get here */
  191. return FILTER_FULL; /* Keep compiler quiet */
  192. }
  193. Filter *
  194. new_Filter( void )
  195. {
  196. Filter *f;
  197. if ( ! ( f = ( Filter * ) malloc( sizeof( Filter ) ) ) )
  198. {
  199. Log_err( "Cannot allocate Filter" );
  200. exit( EXIT_FAILURE );
  201. }
  202. f->nRules = 0;
  203. f->maxRules = 0;
  204. f->rules = NULL;
  205. f->action = FILTER_DEFAULT;
  206. return f;
  207. }
  208. void
  209. del_Filter( Filter *f )
  210. {
  211. if ( f == NULL )
  212. return;
  213. if ( f->rules != NULL )
  214. free( f->rules );
  215. free( f );
  216. }
  217. FilterAction
  218. Flt_action( const Filter *f )
  219. {
  220. return f->action;
  221. }
  222. int
  223. Flt_nRules( const Filter *f )
  224. {
  225. return f->nRules;
  226. }
  227. /*
  228. * Do we have a rule requiring us to fetch the Newsgroups: headers of
  229. * articles?
  230. */
  231. Bool
  232. Flt_getNewsgroups( void )
  233. {
  234. return filter.needGroups;
  235. }
  236. FilterRule
  237. Flt_rule( const Filter *f, int ruleNo )
  238. {
  239. ASSERT( ruleNo < f->nRules );
  240. return f->rules[ ruleNo ];
  241. }
  242. void
  243. Flt_setAction( Filter *f, FilterAction action )
  244. {
  245. f->action = action;
  246. }
  247. void
  248. Flt_addRule( Filter *f, FilterRule rule )
  249. {
  250. /* Does the rule require Newsgroups: headers to be fetched? */
  251. if ( rule.type == RULE_NEWSGROUP ||
  252. ( rule.type >= RULE_XPOSTS_LT && rule.type <= RULE_XPOSTS_GT ) )
  253. filter.needGroups = TRUE;
  254. if ( f->nRules + 1 > f->maxRules )
  255. {
  256. f->rules =
  257. ( FilterRule * ) realloc( f->rules,
  258. ( f->maxRules + 5 )
  259. * sizeof( FilterRule ) );
  260. if ( f->rules == NULL )
  261. {
  262. Log_err( "Could not realloc rule list" );
  263. exit( EXIT_FAILURE );
  264. }
  265. f->maxRules += 5;
  266. }
  267. f->rules[ f->nRules++ ] = rule;
  268. }