Mercurial > noffle
annotate src/filter.c @ 288:c02c4eb95f95 noffle
[svn] * src/configfile.h,src/configfile.c,docs/noffle.conf.5: Add noffle-user
and noffle-group configs.
* src/configfile.c,src/fetch.c,src/fetchlist.c,src/protocol.c,
src/server.c: Replace strcpy() with Utl_cpyStr() where appropriate.
See Debian bug 168128.
* src/control.c,src/configfile.c,src/noffle.c: Replace [s]scanf("%s")
with [s]scanf(MAXCHAR_FMT).
* src/noffle.c: Log warning if noffle.conf is world readable.
* src/noffle.c: Restrict most options to news admins; i.e. those who
are root or news on running Noffle.
* Makefile.in,acconfig.h,aclocal.m4,config.h.in,configure,configure.in,
docs/Makefile.in,docs/noffle.conf.5,packages/Makefile.in,
packages/redhat/Makefile.in,src/Makefile.am,src/Makefile.in,
src/authenticate.c,src/authenticate.h,src/noffle.c,src/server.c:
Add basic authentication using either Noffle-specific user file
or authenticating via PAM (service 'noffle'). PAM authentication
needs to run as root, so a Noffle server that needs PAM
must be started by root. Helpful (?) error messages will be logged
if not. Noffle will switch ruid and euid to 'news' (or whatever
is configured) ASAP.
* src/noffle.c: Add uid checking.
author | bears |
---|---|
date | Fri, 10 Jan 2003 23:25:45 +0000 |
parents | 5eece4dfd945 |
children | 21300895412f |
rev | line source |
---|---|
128 | 1 /* |
2 filter.c | |
3 | |
4 Article filtering. | |
5 | |
281
5eece4dfd945
[svn] * src/log.c,src/log.h: Add Log_fatal() for reporting fatal errors
bears
parents:
249
diff
changeset
|
6 $Id: filter.c 413 2002-12-27 21:48:25Z bears $ |
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 ) | |
281
5eece4dfd945
[svn] * src/log.c,src/log.h: Add Log_fatal() for reporting fatal errors
bears
parents:
249
diff
changeset
|
223 Log_fatal( "Could not realloc filter list" ); |
128 | 224 filter.maxFilters += 5; |
225 } | |
226 filter.filters[ filter.nFilters++ ] = f; | |
227 } | |
228 | |
249 | 229 |
230 /* | |
231 * Called by Fetch_init(). | |
232 * Must be called before | |
233 * Fetch_getNewGrps(), Client_getNewgrps(), client.c:processGrps() | |
234 * because processGrps() sets the stampfile needed by lastupdate. | |
235 */ | |
236 void | |
237 Flt_init( const char * server ) | |
238 { | |
239 int index1, index2; | |
240 time_t now, lastupdate; | |
241 FilterRule * thisRule ; | |
242 Str filename; | |
243 | |
244 time ( &now ); | |
245 lastupdate = (time_t) 0; /* defaults to start of epoch */ | |
246 | |
247 snprintf( filename, MAXCHAR, "%s/lastupdate.%s", | |
248 Cfg_spoolDir(), server ); | |
249 if ( !Utl_getStamp( &lastupdate , filename ) ) | |
250 /* There's no stamp file if server has never been queried. | |
251 * | |
252 */ | |
253 Log_dbg( LOG_DBG_FILTER, | |
254 "Filter unable to get stamp file %s . Please query server.", filename ); | |
255 | |
256 /* traverse all rules of all filters */ | |
257 | |
258 for ( index1 = 0; index1 < filter.nFilters; index1++ ) | |
259 { | |
260 for ( index2 = 0; index2 < filter.filters[ index1 ] -> nRules; index2++ ) | |
261 { | |
262 thisRule = & ( filter.filters[ index1 ] -> rules[ index2 ] ); | |
263 switch ( thisRule -> type ) | |
264 { | |
265 /* evaluate variable date specs */ | |
266 case RULE_DATE_LT: | |
267 case RULE_DATE_EQ: | |
268 case RULE_DATE_GT: | |
269 thisRule -> data.reftime.calctime = | |
270 thisRule ->data.reftime.timeoffset; | |
271 switch ( thisRule ->data.reftime.vartime ) | |
272 { | |
273 case NOW: | |
274 thisRule -> data.reftime.calctime += now; | |
275 break; | |
276 case LASTUPDATE: | |
277 thisRule -> data.reftime.calctime += lastupdate; | |
278 break; | |
279 default: | |
280 break; | |
281 } /* end switch( ... vartime) */ | |
282 | |
283 /* Silently fix absolute dates before the epoch. | |
284 * This is not the place to mock about strange dates. | |
285 */ | |
286 if ( thisRule -> data.reftime.calctime < (time_t) 0 ) | |
287 thisRule -> data.reftime.calctime = (time_t) 0 ; | |
288 | |
289 /* Log_dbg( LOG_DBG_FILTER, "%d: %dl = %dl + %d", | |
290 * thisRule -> type, | |
291 * (long) thisRule -> data.reftime.calctime, | |
292 * (long) thisRule ->data.reftime.timeoffset, | |
293 * (int) ( thisRule ->data.reftime.vartime == NOW ? | |
294 * now : | |
295 * ( thisRule ->data.reftime.vartime == LASTUPDATE ? | |
296 * lastupdate : thisRule ->data.reftime.vartime ) ) ); | |
297 */ break; | |
298 default: | |
299 break; | |
300 } /* end switch( ... -> type) */ | |
301 } /* end for() */ | |
302 } /* end for() */ | |
303 return ; | |
304 } | |
305 | |
128 | 306 /* |
307 * 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
|
308 * 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
|
309 * action fires, return the default read mode. |
128 | 310 */ |
311 FilterAction | |
312 Flt_checkFilters( const char *thisGrp, const char *newsgroups, | |
313 const Over *ov, FetchMode mode ) | |
314 { | |
315 int i; | |
316 | |
317 for ( i = 0; i < filter.nFilters; i++ ) | |
318 if ( checkFilter( thisGrp, newsgroups, ov, filter.filters[ i ] ) ) | |
319 { | |
194
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
320 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
|
321 |
185
fed1334d766b
[svn] * src/client.c: Change variable only used on constant to 'const'.
bears
parents:
128
diff
changeset
|
322 Log_dbg( LOG_DBG_FILTER, |
fed1334d766b
[svn] * src/client.c: Change variable only used on constant to 'const'.
bears
parents:
128
diff
changeset
|
323 "Filter %d fired on message %s", |
fed1334d766b
[svn] * src/client.c: Change variable only used on constant to 'const'.
bears
parents:
128
diff
changeset
|
324 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
|
325 if ( action == FILTER_DEFAULT ) |
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
326 break; |
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
327 else |
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
328 return action; |
128 | 329 } |
330 | |
331 switch( mode ) | |
332 { | |
333 case FULL: return FILTER_FULL; | |
334 case THREAD: return FILTER_THREAD; | |
335 case OVER: return FILTER_XOVER; | |
336 } | |
337 | |
338 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
|
339 return FILTER_FULL; /* Keep compiler quiet */ |
128 | 340 } |
341 | |
342 Filter * | |
343 new_Filter( void ) | |
344 { | |
345 Filter *f; | |
346 | |
347 if ( ! ( f = ( Filter * ) malloc( sizeof( Filter ) ) ) ) | |
281
5eece4dfd945
[svn] * src/log.c,src/log.h: Add Log_fatal() for reporting fatal errors
bears
parents:
249
diff
changeset
|
348 Log_fatal( "Cannot allocate Filter" ); |
128 | 349 f->nRules = 0; |
350 f->maxRules = 0; | |
351 f->rules = NULL; | |
194
a4e9a20e50e5
[svn] * docs/noffle.conf.5,src/configfile.c,src/filter.h,src/filter.c:
bears
parents:
185
diff
changeset
|
352 f->action = FILTER_DEFAULT; |
128 | 353 return f; |
354 } | |
355 | |
356 void | |
357 del_Filter( Filter *f ) | |
358 { | |
359 if ( f == NULL ) | |
360 return; | |
361 | |
362 if ( f->rules != NULL ) | |
363 free( f->rules ); | |
364 free( f ); | |
365 } | |
366 | |
367 FilterAction | |
368 Flt_action( const Filter *f ) | |
369 { | |
370 return f->action; | |
371 } | |
372 | |
373 int | |
374 Flt_nRules( const Filter *f ) | |
375 { | |
376 return f->nRules; | |
377 } | |
378 | |
379 /* | |
380 * Do we have a rule requiring us to fetch the Newsgroups: headers of | |
381 * articles? | |
382 */ | |
383 Bool | |
384 Flt_getNewsgroups( void ) | |
385 { | |
386 return filter.needGroups; | |
387 } | |
388 | |
389 FilterRule | |
390 Flt_rule( const Filter *f, int ruleNo ) | |
391 { | |
392 ASSERT( ruleNo < f->nRules ); | |
393 return f->rules[ ruleNo ]; | |
394 } | |
395 | |
396 void | |
397 Flt_setAction( Filter *f, FilterAction action ) | |
398 { | |
399 f->action = action; | |
400 } | |
401 | |
402 void | |
403 Flt_addRule( Filter *f, FilterRule rule ) | |
404 { | |
405 /* Does the rule require Newsgroups: headers to be fetched? */ | |
406 if ( rule.type == RULE_NEWSGROUP || | |
407 ( rule.type >= RULE_XPOSTS_LT && rule.type <= RULE_XPOSTS_GT ) ) | |
408 filter.needGroups = TRUE; | |
409 | |
410 if ( f->nRules + 1 > f->maxRules ) | |
411 { | |
412 f->rules = | |
413 ( FilterRule * ) realloc( f->rules, | |
414 ( f->maxRules + 5 ) | |
415 * sizeof( FilterRule ) ); | |
416 | |
417 if ( f->rules == NULL ) | |
281
5eece4dfd945
[svn] * src/log.c,src/log.h: Add Log_fatal() for reporting fatal errors
bears
parents:
249
diff
changeset
|
418 Log_fatal( "Could not realloc rule list" ); |
128 | 419 f->maxRules += 5; |
420 } | |
421 f->rules[ f->nRules++ ] = rule; | |
422 } | |
423 | |
424 |