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
|