Mercurial > noffle
annotate src/filter.c @ 279:49b452b667a6 noffle
[svn] * src/util.c: localTimeDiff() cached its value and recalculated it every
hour of clock time, regardless of the time the calculated was based on.
This is potentially dangerous at daylight saving changes. So instead
use the cached last result only when the new request is to be based on
a time in the same hour as the cached result.
* src/util.c: Replace the alternate Utl_mktimeGMT() implementation used when
timegm() is not available. The previous version, as suggested by the
glibc timegm() man page, used setenv() and unsetenv() for changing the
environment. These aren't POSIX function, and the POSIX putenv()
(a) is tricky to manage if the same var is being constantly update and
memory isn't to leak, and (b) provides no way to remove an environment
entry. So change to an implementation Wget uses. This should compile on
not glibc systems - the previous version failed to build on Solaris.
author | bears |
---|---|
date | Sun, 17 Nov 2002 15:18:19 +0000 |
parents | 0340b9c17edc |
children | 5eece4dfd945 |
rev | line source |
---|---|
128 | 1 /* |
2 filter.c | |
3 | |
4 Article filtering. | |
5 | |
249 | 6 $Id: filter.c 381 2002-05-14 14:25:45Z mirkol $ |
128 | 7 */ |
8 | |
9 #if HAVE_CONFIG_H | |
10 #include <config.h> | |
11 #endif | |
12 | |
13 #include <ctype.h> | |
14 #include "common.h" | |
249 | 15 #include "configfile.h" |
128 | 16 #include "itemlist.h" |
17 #include "log.h" | |
18 #include "wildmat.h" | |
212 | 19 #include "group.h" |
249 | 20 #include "util.h" |
21 #include "filter.h" | |
128 | 22 |
23 struct | |
24 { | |
25 int nFilters; | |
26 int maxFilters; | |
27 const Filter **filters; | |
28 Bool needGroups; | |
29 } filter = { 0, 0, NULL, FALSE }; | |
30 | |
31 static unsigned long | |
32 countGroups( const char *grps ) | |
33 { | |
34 unsigned long res; | |
35 | |
36 res = 1; | |
37 while ( *grps != '\0' ) | |
38 { | |
39 if ( *grps == ',' ) | |
40 res++; | |
41 grps++; | |
42 } | |
43 | |
44 return res; | |
45 } | |
46 static unsigned long | |
47 countRefs( const char *refs ) | |
48 { | |
49 unsigned long res; | |
50 Bool inRef; | |
51 | |
52 res = 0; | |
53 inRef = FALSE; | |
54 | |
55 while ( *refs != '\0' ) | |
56 { | |
57 if ( inRef ) | |
58 { | |
59 if ( *refs == '>' ) | |
60 { | |
61 inRef = FALSE; | |
62 res++; | |
63 } | |
64 } | |
65 else if ( *refs == '<' ) | |
66 inRef = TRUE; | |
67 refs++; | |
68 } | |
69 | |
70 return res; | |
71 } | |
72 | |
73 /* Check a single rule to see if it passes. */ | |
74 static Bool | |
75 checkRule( const char *thisGrp, const char *newsgroups, | |
76 const Over *ov, const FilterRule *r ) | |
77 { | |
78 unsigned long ul; | |
79 ItemList *grps; | |
80 const char *p; | |
249 | 81 time_t articletime; |
128 | 82 |
83 switch( r->type ) | |
84 { | |
85 case RULE_NEWSGROUP: | |
86 if ( Wld_match( thisGrp, r->data.grp ) ) | |
87 return TRUE; | |
88 if ( newsgroups != NULL ) | |
89 { | |
90 grps = new_Itl( newsgroups, " ,\t" ); | |
91 for ( p = Itl_first( grps ); p != NULL; p = Itl_next( grps ) ) | |
92 if ( Wld_match( p, r->data.grp ) ) | |
93 return TRUE; | |
94 del_Itl( grps ); | |
95 } | |
96 return FALSE; | |
97 | |
98 case RULE_SUBJECT: | |
99 return ( regexec( &r->data.regex, Ov_subj( ov ), 0, NULL, 0 ) == 0 ); | |
100 | |
249 | 101 case RULE_REFERENCE: /* kill thread by Msg-Id in References: */ |
102 return ( regexec( &r->data.regex, Ov_ref( ov ), 0, NULL, 0 ) == 0 ); | |
103 | |
128 | 104 case RULE_FROM: |
105 return ( regexec( &r->data.regex, Ov_from( ov ), 0, NULL, 0 ) == 0 ); | |
106 | |
107 case RULE_BYTES_LT: | |
108 return ( Ov_bytes( ov ) < r->data.amount ); | |
109 | |
110 case RULE_BYTES_EQ: | |
111 return ( Ov_bytes( ov ) == r->data.amount ); | |
112 | |
113 case RULE_BYTES_GT: | |
114 return ( Ov_bytes( ov ) > r->data.amount ); | |
115 | |
116 case RULE_LINES_LT: | |
117 return ( Ov_lines( ov ) < r->data.amount ); | |
118 | |
119 case RULE_LINES_EQ: | |
120 return ( Ov_lines( ov ) == r->data.amount ); | |
121 | |
122 case RULE_LINES_GT: | |
123 return ( Ov_lines( ov ) > r->data.amount ); | |
124 | |
125 case RULE_MSGID: | |
126 return ( regexec( &r->data.regex, Ov_msgId( ov ), 0, NULL, 0 ) == 0 ); | |
127 | |
249 | 128 case RULE_DATE_LT: |
129 /* Utl_parseNewsDate() is quite picky. I'm not entirely happy | |
130 about this, but I won't implement a relaxed date parser. */ | |
131 articletime = Utl_parseNewsDate( Ov_date( ov ) ); | |
132 if ( articletime == (time_t) -1 ) | |
133 return FALSE; | |
134 return ( articletime < r->data.reftime.calctime ); | |
135 | |
136 case RULE_DATE_EQ: | |
137 articletime = Utl_parseNewsDate( Ov_date( ov ) ); | |
138 if ( ( articletime == (time_t) -1) | |
139 && ( r->data.reftime.vartime == INVALID )) | |
140 return TRUE; | |
141 if ( ( articletime == (time_t) -1) | |
142 != ( r->data.reftime.vartime == INVALID )) | |
143 return FALSE; | |
144 return ( ( articletime <= r->data.reftime.calctime | |
145 + RULE_DATE_EQ_PRECISION ) | |
146 && ( articletime >= r->data.reftime.calctime | |
147 - RULE_DATE_EQ_PRECISION ) ); | |
148 | |
149 case RULE_DATE_GT: | |
150 articletime = Utl_parseNewsDate( Ov_date( ov ) ); | |
151 if ( articletime == (time_t) -1 ) | |
152 return FALSE; | |
153 return ( articletime > r->data.reftime.calctime ); | |
154 | |
128 | 155 case RULE_NOREFS_LT: |
156 ul = countRefs( Ov_ref( ov ) ); | |
157 return ( ul < r->data.amount ); | |
158 | |
159 case RULE_NOREFS_EQ: | |
160 ul = countRefs( Ov_ref( ov ) ); | |
161 return ( ul == r->data.amount ); | |
162 | |
163 case RULE_NOREFS_GT: | |
164 ul = countRefs( Ov_ref( ov ) ); | |
165 return ( ul > r->data.amount ); | |
166 | |
167 case RULE_XPOSTS_LT: | |
168 if ( newsgroups == NULL ) | |
169 return FALSE; | |
170 ul = countGroups( newsgroups ); | |
171 return ( ul < r->data.amount ); | |
172 | |
173 case RULE_XPOSTS_EQ: | |
174 if ( newsgroups == NULL ) | |
175 return FALSE; | |
176 ul = countGroups( newsgroups ); | |
177 return ( ul == r->data.amount ); | |
178 | |
179 case RULE_XPOSTS_GT: | |
180 if ( newsgroups == NULL ) | |
181 return FALSE; | |
182 ul = countGroups( newsgroups ); | |
183 return ( ul > r->data.amount ); | |
212 | 184 |
185 case RULE_POST_STATUS: | |
186 if ( Grp_postAllow( thisGrp ) == r->data.postAllow ) | |
187 return TRUE; | |
188 return FALSE; | |
189 | |
128 | 190 } |
191 | |
192 ASSERT( FALSE ); /* Shouldn't get here */ | |
185
fed1334d766b
[svn] * src/client.c: Change variable only used on constant to 'const'.
bears
parents:
128
diff
changeset
|
193 return 0; /* Keep compiler quiet */ |
128 | 194 } |
195 | |
196 /* Check a single filter to see if it fires. */ | |
197 static Bool | |
198 checkFilter( const char *thisGrp, const char *newsgroups, | |
199 const Over *ov, const Filter *f ) | |
200 { | |
201 int i; | |
202 | |
203 for ( i = 0; i < f->nRules; i++ ) | |
204 if ( ! checkRule( thisGrp, newsgroups, ov, &f->rules[i] ) ) | |
205 return FALSE; | |
206 | |
207 return TRUE; | |
208 } | |
209 | |
210 /* Add a filter to the list of filters. */ | |
211 void | |
212 Flt_addFilter( const Filter *f ) | |
213 { | |
214 ASSERT( f != NULL ); | |
215 | |
216 if ( ( filter.nFilters + 1 ) > filter.maxFilters ) | |
217 { | |
218 filter.filters = | |
219 ( const Filter ** ) realloc( filter.filters, | |
220 ( filter.maxFilters + 5 ) | |
221 * sizeof( Filter * ) ); | |
222 if ( filter.filters == NULL ) | |
223 { | |
224 Log_err( "Could not realloc filter list" ); | |
225 exit( EXIT_FAILURE ); | |
226 } | |
227 filter.maxFilters += 5; | |
228 } | |
229 filter.filters[ filter.nFilters++ ] = f; | |
230 } | |
231 | |
249 | 232 |
233 /* | |
234 * Called by Fetch_init(). | |
235 * Must be called before | |
236 * Fetch_getNewGrps(), Client_getNewgrps(), client.c:processGrps() | |
237 * because processGrps() sets the stampfile needed by lastupdate. | |
238 */ | |
239 void | |
240 Flt_init( const char * server ) | |
241 { | |
242 int index1, index2; | |
243 time_t now, lastupdate; | |
244 FilterRule * thisRule ; | |
245 Str filename; | |
246 | |
247 time ( &now ); | |
248 lastupdate = (time_t) 0; /* defaults to start of epoch */ | |
249 | |
250 snprintf( filename, MAXCHAR, "%s/lastupdate.%s", | |
251 Cfg_spoolDir(), server ); | |
252 if ( !Utl_getStamp( &lastupdate , filename ) ) | |
253 /* There's no stamp file if server has never been queried. | |
254 * | |
255 */ | |
256 Log_dbg( LOG_DBG_FILTER, | |
257 "Filter unable to get stamp file %s . Please query server.", filename ); | |
258 | |
259 /* traverse all rules of all filters */ | |
260 | |
261 for ( index1 = 0; index1 < filter.nFilters; index1++ ) | |
262 { | |
263 for ( index2 = 0; index2 < filter.filters[ index1 ] -> nRules; index2++ ) | |
264 { | |
265 thisRule = & ( filter.filters[ index1 ] -> rules[ index2 ] ); | |
266 switch ( thisRule -> type ) | |
267 { | |
268 /* evaluate variable date specs */ | |
269 case RULE_DATE_LT: | |
270 case RULE_DATE_EQ: | |
271 case RULE_DATE_GT: | |
272 thisRule -> data.reftime.calctime = | |
273 thisRule ->data.reftime.timeoffset; | |
274 switch ( thisRule ->data.reftime.vartime ) | |
275 { | |
276 case NOW: | |
277 thisRule -> data.reftime.calctime += now; | |
278 break; | |
279 case LASTUPDATE: | |
280 thisRule -> data.reftime.calctime += lastupdate; | |
281 break; | |
282 default: | |
283 break; | |
284 } /* end switch( ... vartime) */ | |
285 | |
286 /* Silently fix absolute dates before the epoch. | |
287 * This is not the place to mock about strange dates. | |
288 */ | |
289 if ( thisRule -> data.reftime.calctime < (time_t) 0 ) | |
290 thisRule -> data.reftime.calctime = (time_t) 0 ; | |
291 | |
292 /* Log_dbg( LOG_DBG_FILTER, "%d: %dl = %dl + %d", | |
293 * thisRule -> type, | |
294 * (long) thisRule -> data.reftime.calctime, | |
295 * (long) thisRule ->data.reftime.timeoffset, | |
296 * (int) ( thisRule ->data.reftime.vartime == NOW ? | |
297 * now : | |
298 * ( thisRule ->data.reftime.vartime == LASTUPDATE ? | |
299 * lastupdate : thisRule ->data.reftime.vartime ) ) ); | |
300 */ break; | |
301 default: | |
302 break; | |
303 } /* end switch( ... -> type) */ | |
304 } /* end for() */ | |
305 } /* end for() */ | |
306 return ; | |
307 } | |
308 | |
128 | 309 /* |
310 * Run the rules over the supplied overview. If a specific rule fires, | |
194
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
311 * returns its action. If no rule fires, or a rule specifying the default |
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
312 * action fires, return the default read mode. |
128 | 313 */ |
314 FilterAction | |
315 Flt_checkFilters( const char *thisGrp, const char *newsgroups, | |
316 const Over *ov, FetchMode mode ) | |
317 { | |
318 int i; | |
319 | |
320 for ( i = 0; i < filter.nFilters; i++ ) | |
321 if ( checkFilter( thisGrp, newsgroups, ov, filter.filters[ i ] ) ) | |
322 { | |
194
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
323 FilterAction action = filter.filters[ i ]->action; |
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
324 |
185
fed1334d766b
[svn] * src/client.c: Change variable only used on constant to 'const'.
bears
parents:
128
diff
changeset
|
325 Log_dbg( LOG_DBG_FILTER, |
fed1334d766b
[svn] * src/client.c: Change variable only used on constant to 'const'.
bears
parents:
128
diff
changeset
|
326 "Filter %d fired on message %s", |
fed1334d766b
[svn] * src/client.c: Change variable only used on constant to 'const'.
bears
parents:
128
diff
changeset
|
327 i, Ov_msgId( ov ) ); |
194
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
328 if ( action == FILTER_DEFAULT ) |
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
329 break; |
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
330 else |
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
331 return action; |
128 | 332 } |
333 | |
334 switch( mode ) | |
335 { | |
336 case FULL: return FILTER_FULL; | |
337 case THREAD: return FILTER_THREAD; | |
338 case OVER: return FILTER_XOVER; | |
339 } | |
340 | |
341 ASSERT( FALSE ); /* Shouldn't get here */ | |
185
fed1334d766b
[svn] * src/client.c: Change variable only used on constant to 'const'.
bears
parents:
128
diff
changeset
|
342 return FILTER_FULL; /* Keep compiler quiet */ |
128 | 343 } |
344 | |
345 Filter * | |
346 new_Filter( void ) | |
347 { | |
348 Filter *f; | |
349 | |
350 if ( ! ( f = ( Filter * ) malloc( sizeof( Filter ) ) ) ) | |
351 { | |
352 Log_err( "Cannot allocate Filter" ); | |
353 exit( EXIT_FAILURE ); | |
354 } | |
355 f->nRules = 0; | |
356 f->maxRules = 0; | |
357 f->rules = NULL; | |
194
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
358 f->action = FILTER_DEFAULT; |
128 | 359 return f; |
360 } | |
361 | |
362 void | |
363 del_Filter( Filter *f ) | |
364 { | |
365 if ( f == NULL ) | |
366 return; | |
367 | |
368 if ( f->rules != NULL ) | |
369 free( f->rules ); | |
370 free( f ); | |
371 } | |
372 | |
373 FilterAction | |
374 Flt_action( const Filter *f ) | |
375 { | |
376 return f->action; | |
377 } | |
378 | |
379 int | |
380 Flt_nRules( const Filter *f ) | |
381 { | |
382 return f->nRules; | |
383 } | |
384 | |
385 /* | |
386 * Do we have a rule requiring us to fetch the Newsgroups: headers of | |
387 * articles? | |
388 */ | |
389 Bool | |
390 Flt_getNewsgroups( void ) | |
391 { | |
392 return filter.needGroups; | |
393 } | |
394 | |
395 FilterRule | |
396 Flt_rule( const Filter *f, int ruleNo ) | |
397 { | |
398 ASSERT( ruleNo < f->nRules ); | |
399 return f->rules[ ruleNo ]; | |
400 } | |
401 | |
402 void | |
403 Flt_setAction( Filter *f, FilterAction action ) | |
404 { | |
405 f->action = action; | |
406 } | |
407 | |
408 void | |
409 Flt_addRule( Filter *f, FilterRule rule ) | |
410 { | |
411 /* Does the rule require Newsgroups: headers to be fetched? */ | |
412 if ( rule.type == RULE_NEWSGROUP || | |
413 ( rule.type >= RULE_XPOSTS_LT && rule.type <= RULE_XPOSTS_GT ) ) | |
414 filter.needGroups = TRUE; | |
415 | |
416 if ( f->nRules + 1 > f->maxRules ) | |
417 { | |
418 f->rules = | |
419 ( FilterRule * ) realloc( f->rules, | |
420 ( f->maxRules + 5 ) | |
421 * sizeof( FilterRule ) ); | |
422 | |
423 if ( f->rules == NULL ) | |
424 { | |
425 Log_err( "Could not realloc rule list" ); | |
426 exit( EXIT_FAILURE ); | |
427 } | |
428 f->maxRules += 5; | |
429 } | |
430 f->rules[ f->nRules++ ] = rule; | |
431 } | |
432 | |
433 |