Mercurial > noffle
comparison src/filter.c @ 128:8897b7e3b108 noffle
[svn] Add article filtering
author | bears |
---|---|
date | Wed, 09 Aug 2000 22:19:17 +0100 |
parents | |
children | fed1334d766b |
comparison
equal
deleted
inserted
replaced
127:3c71e28c8eef | 128:8897b7e3b108 |
---|---|
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 |