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