Mercurial > noffle
view src/filter.c @ 331:ff7a2dc6023e noffle
[svn] * src/client.c,src/group.c: Reunite forbidden group comment with code, and
revise forbidden rules to allow single component names except for some
specific ones.
author | bears |
---|---|
date | Wed, 26 Feb 2003 11:30:41 +0000 |
parents | 5eece4dfd945 |
children | 21300895412f |
line wrap: on
line source
/* filter.c Article filtering. $Id: filter.c 413 2002-12-27 21:48:25Z bears $ */ #if HAVE_CONFIG_H #include <config.h> #endif #include <ctype.h> #include "common.h" #include "configfile.h" #include "itemlist.h" #include "log.h" #include "wildmat.h" #include "group.h" #include "util.h" #include "filter.h" struct { int nFilters; int maxFilters; const Filter **filters; Bool needGroups; } filter = { 0, 0, NULL, FALSE }; static unsigned long countGroups( const char *grps ) { unsigned long res; res = 1; while ( *grps != '\0' ) { if ( *grps == ',' ) res++; grps++; } return res; } static unsigned long countRefs( const char *refs ) { unsigned long res; Bool inRef; res = 0; inRef = FALSE; while ( *refs != '\0' ) { if ( inRef ) { if ( *refs == '>' ) { inRef = FALSE; res++; } } else if ( *refs == '<' ) inRef = TRUE; refs++; } return res; } /* Check a single rule to see if it passes. */ static Bool checkRule( const char *thisGrp, const char *newsgroups, const Over *ov, const FilterRule *r ) { unsigned long ul; ItemList *grps; const char *p; time_t articletime; switch( r->type ) { case RULE_NEWSGROUP: if ( Wld_match( thisGrp, r->data.grp ) ) return TRUE; if ( newsgroups != NULL ) { grps = new_Itl( newsgroups, " ,\t" ); for ( p = Itl_first( grps ); p != NULL; p = Itl_next( grps ) ) if ( Wld_match( p, r->data.grp ) ) return TRUE; del_Itl( grps ); } return FALSE; case RULE_SUBJECT: return ( regexec( &r->data.regex, Ov_subj( ov ), 0, NULL, 0 ) == 0 ); case RULE_REFERENCE: /* kill thread by Msg-Id in References: */ return ( regexec( &r->data.regex, Ov_ref( ov ), 0, NULL, 0 ) == 0 ); case RULE_FROM: return ( regexec( &r->data.regex, Ov_from( ov ), 0, NULL, 0 ) == 0 ); case RULE_BYTES_LT: return ( Ov_bytes( ov ) < r->data.amount ); case RULE_BYTES_EQ: return ( Ov_bytes( ov ) == r->data.amount ); case RULE_BYTES_GT: return ( Ov_bytes( ov ) > r->data.amount ); case RULE_LINES_LT: return ( Ov_lines( ov ) < r->data.amount ); case RULE_LINES_EQ: return ( Ov_lines( ov ) == r->data.amount ); case RULE_LINES_GT: return ( Ov_lines( ov ) > r->data.amount ); case RULE_MSGID: return ( regexec( &r->data.regex, Ov_msgId( ov ), 0, NULL, 0 ) == 0 ); case RULE_DATE_LT: /* Utl_parseNewsDate() is quite picky. I'm not entirely happy about this, but I won't implement a relaxed date parser. */ articletime = Utl_parseNewsDate( Ov_date( ov ) ); if ( articletime == (time_t) -1 ) return FALSE; return ( articletime < r->data.reftime.calctime ); case RULE_DATE_EQ: articletime = Utl_parseNewsDate( Ov_date( ov ) ); if ( ( articletime == (time_t) -1) && ( r->data.reftime.vartime == INVALID )) return TRUE; if ( ( articletime == (time_t) -1) != ( r->data.reftime.vartime == INVALID )) return FALSE; return ( ( articletime <= r->data.reftime.calctime + RULE_DATE_EQ_PRECISION ) && ( articletime >= r->data.reftime.calctime - RULE_DATE_EQ_PRECISION ) ); case RULE_DATE_GT: articletime = Utl_parseNewsDate( Ov_date( ov ) ); if ( articletime == (time_t) -1 ) return FALSE; return ( articletime > r->data.reftime.calctime ); case RULE_NOREFS_LT: ul = countRefs( Ov_ref( ov ) ); return ( ul < r->data.amount ); case RULE_NOREFS_EQ: ul = countRefs( Ov_ref( ov ) ); return ( ul == r->data.amount ); case RULE_NOREFS_GT: ul = countRefs( Ov_ref( ov ) ); return ( ul > r->data.amount ); case RULE_XPOSTS_LT: if ( newsgroups == NULL ) return FALSE; ul = countGroups( newsgroups ); return ( ul < r->data.amount ); case RULE_XPOSTS_EQ: if ( newsgroups == NULL ) return FALSE; ul = countGroups( newsgroups ); return ( ul == r->data.amount ); case RULE_XPOSTS_GT: if ( newsgroups == NULL ) return FALSE; ul = countGroups( newsgroups ); return ( ul > r->data.amount ); case RULE_POST_STATUS: if ( Grp_postAllow( thisGrp ) == r->data.postAllow ) return TRUE; return FALSE; } ASSERT( FALSE ); /* Shouldn't get here */ return 0; /* Keep compiler quiet */ } /* Check a single filter to see if it fires. */ static Bool checkFilter( const char *thisGrp, const char *newsgroups, const Over *ov, const Filter *f ) { int i; for ( i = 0; i < f->nRules; i++ ) if ( ! checkRule( thisGrp, newsgroups, ov, &f->rules[i] ) ) return FALSE; return TRUE; } /* Add a filter to the list of filters. */ void Flt_addFilter( const Filter *f ) { ASSERT( f != NULL ); if ( ( filter.nFilters + 1 ) > filter.maxFilters ) { filter.filters = ( const Filter ** ) realloc( filter.filters, ( filter.maxFilters + 5 ) * sizeof( Filter * ) ); if ( filter.filters == NULL ) Log_fatal( "Could not realloc filter list" ); filter.maxFilters += 5; } filter.filters[ filter.nFilters++ ] = f; } /* * Called by Fetch_init(). * Must be called before * Fetch_getNewGrps(), Client_getNewgrps(), client.c:processGrps() * because processGrps() sets the stampfile needed by lastupdate. */ void Flt_init( const char * server ) { int index1, index2; time_t now, lastupdate; FilterRule * thisRule ; Str filename; time ( &now ); lastupdate = (time_t) 0; /* defaults to start of epoch */ snprintf( filename, MAXCHAR, "%s/lastupdate.%s", Cfg_spoolDir(), server ); if ( !Utl_getStamp( &lastupdate , filename ) ) /* There's no stamp file if server has never been queried. * */ Log_dbg( LOG_DBG_FILTER, "Filter unable to get stamp file %s . Please query server.", filename ); /* traverse all rules of all filters */ for ( index1 = 0; index1 < filter.nFilters; index1++ ) { for ( index2 = 0; index2 < filter.filters[ index1 ] -> nRules; index2++ ) { thisRule = & ( filter.filters[ index1 ] -> rules[ index2 ] ); switch ( thisRule -> type ) { /* evaluate variable date specs */ case RULE_DATE_LT: case RULE_DATE_EQ: case RULE_DATE_GT: thisRule -> data.reftime.calctime = thisRule ->data.reftime.timeoffset; switch ( thisRule ->data.reftime.vartime ) { case NOW: thisRule -> data.reftime.calctime += now; break; case LASTUPDATE: thisRule -> data.reftime.calctime += lastupdate; break; default: break; } /* end switch( ... vartime) */ /* Silently fix absolute dates before the epoch. * This is not the place to mock about strange dates. */ if ( thisRule -> data.reftime.calctime < (time_t) 0 ) thisRule -> data.reftime.calctime = (time_t) 0 ; /* Log_dbg( LOG_DBG_FILTER, "%d: %dl = %dl + %d", * thisRule -> type, * (long) thisRule -> data.reftime.calctime, * (long) thisRule ->data.reftime.timeoffset, * (int) ( thisRule ->data.reftime.vartime == NOW ? * now : * ( thisRule ->data.reftime.vartime == LASTUPDATE ? * lastupdate : thisRule ->data.reftime.vartime ) ) ); */ break; default: break; } /* end switch( ... -> type) */ } /* end for() */ } /* end for() */ return ; } /* * Run the rules over the supplied overview. If a specific rule fires, * returns its action. If no rule fires, or a rule specifying the default * action fires, return the default read mode. */ FilterAction Flt_checkFilters( const char *thisGrp, const char *newsgroups, const Over *ov, FetchMode mode ) { int i; for ( i = 0; i < filter.nFilters; i++ ) if ( checkFilter( thisGrp, newsgroups, ov, filter.filters[ i ] ) ) { FilterAction action = filter.filters[ i ]->action; Log_dbg( LOG_DBG_FILTER, "Filter %d fired on message %s", i, Ov_msgId( ov ) ); if ( action == FILTER_DEFAULT ) break; else return action; } switch( mode ) { case FULL: return FILTER_FULL; case THREAD: return FILTER_THREAD; case OVER: return FILTER_XOVER; } ASSERT( FALSE ); /* Shouldn't get here */ return FILTER_FULL; /* Keep compiler quiet */ } Filter * new_Filter( void ) { Filter *f; if ( ! ( f = ( Filter * ) malloc( sizeof( Filter ) ) ) ) Log_fatal( "Cannot allocate Filter" ); f->nRules = 0; f->maxRules = 0; f->rules = NULL; f->action = FILTER_DEFAULT; return f; } void del_Filter( Filter *f ) { if ( f == NULL ) return; if ( f->rules != NULL ) free( f->rules ); free( f ); } FilterAction Flt_action( const Filter *f ) { return f->action; } int Flt_nRules( const Filter *f ) { return f->nRules; } /* * Do we have a rule requiring us to fetch the Newsgroups: headers of * articles? */ Bool Flt_getNewsgroups( void ) { return filter.needGroups; } FilterRule Flt_rule( const Filter *f, int ruleNo ) { ASSERT( ruleNo < f->nRules ); return f->rules[ ruleNo ]; } void Flt_setAction( Filter *f, FilterAction action ) { f->action = action; } void Flt_addRule( Filter *f, FilterRule rule ) { /* Does the rule require Newsgroups: headers to be fetched? */ if ( rule.type == RULE_NEWSGROUP || ( rule.type >= RULE_XPOSTS_LT && rule.type <= RULE_XPOSTS_GT ) ) filter.needGroups = TRUE; if ( f->nRules + 1 > f->maxRules ) { f->rules = ( FilterRule * ) realloc( f->rules, ( f->maxRules + 5 ) * sizeof( FilterRule ) ); if ( f->rules == NULL ) Log_fatal( "Could not realloc rule list" ); f->maxRules += 5; } f->rules[ f->nRules++ ] = rule; }