Brian Antione's INN Filter Patch
Standard warning:

Some browsers interpret the



">"
and



"<"
symbols in the text below as HTML tag delimiters, and will not display this correctly. Saving the page as text (or viewing as HTML source) and cutting/pasting from that text will solve this problem




#
# This is a Shell Archive.
# Remove everything above and including the cut line.
# Then run the rest of the file through #! /bin/sh.
# -----cut here-----cut here-----cut here-----cut here-----
#! /bin/sh
# Execute the file with #! /bin/sh (not csh) to create the files:
#	Makefile
#	Readme
#	cancelchan.ctl
#	cancelchan.c
# This Archive created: Tue Aug 19 13:26:49 1997
#
export PATH; PATH=/bin:$PATH
if test -f 'Makefile'
then
echo shar: will not over-write existing file "'Makefile'"
else
cat > 'Makefile' << \SHAR_EOF
#
# Simple makefile for the INN cancelchan
#
# You need to specify where the INN sources are kept on your system
#
CC = /usr/ucb/cc
CFLAGS = -O -I/usr/src/inn-1.5.1/include
LDFLAGS = -L/usr/src/inn-1.5.1
LIBS = -linn
SRCS = cancelchan.c
HDRS =
OBJS = cancelchan.c
MANS =
OTHERS = Makefile Readme cancelchan.ctl

all: cancelchan

cancelchan: cancelchan.o
	$(CC) $(LDFLAGS) -o cancelchan cancelchan.o $(LIBS)

install: cancelchan
	/usr/ucb/install -c -s -o news -g news -m 700 cancelchan /usr/lib/news/bin
clean:
	rm -f *.o core a.out cancelchan

shar:
	shar $(OTHERS) $(SRCS) $(HDRS) $(MANS) > cancelchan.shar
SHAR_EOF
chmod +x 'Makefile'
fi # end of overwriting check
if test -f 'Readme'
then
echo shar: will not over-write existing file "'Readme'"
else
cat > 'Readme' << \SHAR_EOF
  This is the second public release of the anti-spam tool I developed to
help clean up the news servers I admin.  After a little tinkering and
then spending a few days adding criteria, I found I was throwing away
about 20% of my news feed.  Suffice to say that I'm quite pleased with
the savings in disk space that resulted.

  To install this package, modify the Makefile to point it at your INN
source tree.  Cancelchan needs a couple of the INN header files and the
INN library to utilize the direct cancel feature.  Compile the program
and install it in your INN/bin directory.  Then install the sample .ctl
file in the INN/etc directory and edit the initial Criteria list.

  To enable the program, add the following code to your newsfeeds file.

## Cancel some articles on sight
cancelchan!\
	:!*,\
	alt.*,!alt.nocem.misc,!alt.retromod,\
	:Tc,Ap,Wn:/usr/lib/news/bin/cancelchan

  Then [ ctlinnd reload all "cancelchan" ] and you should begin to see
traffic in the log files (if you enabled them).

Notes:

  In the above newsfeeds example, you'll notice that I excluded a couple
of groups from the feed to cancelchan.  If you add other parts of your
news feed to the filter, you'll want to make sure that abuse reporting
groups get excluded so that you don't end up missing reports of abuse
that happened to include your search criteria.

  The code itself could probably be speeded up a bit.  If you end up
adding large numbers of Body Criteria searches, the filter could start
falling behind.

  I wrote cancelchan as a channel feed instead of using the Perl filter
feature for a couple of reasons.  The first is that the Perl filter path
through INN doesn't allow you to see the body of the message.  The second
is that I'm not real comfortable with Perl. *grin*

  One of the concerns with the channel feed though is that cancelchan has
to open the article that just arrived to process it.  Currently I'm of the
opinion that the article is still in the local disk cache since INN just
finished with it and that the open and reading will not cause much actual
disk activity.  This may not be a safe bet.

  In any case, feel free to experiment with the code and the basic idea.
If you add neat features or bug fixes (what bugs?), you might pass them
back to me for inclusion in the next version.  Until then, happy filtering
and may your free space never read empty. *grin*

Brian W. Antoine
briana@tau-ceti.isc-br.com
SHAR_EOF
chmod +x 'Readme'
fi # end of overwriting check
if test -f 'cancelchan.ctl'
then
echo shar: will not over-write existing file "'cancelchan.ctl'"
else
cat > 'cancelchan.ctl' << \SHAR_EOF
#
# How much of the Body do we scan (default 0)
#
BodySize 2048

#
# How often should we check to see if the config file has changed?
#
CheckLimit 50

#
# Where do we look for articles?
#
SpoolDir /var/spool/news

#
# Do you wish to log the articles processed?
#
LogFile /var/log/news/cancelchan.log

#
# Do you want to be able to dump the internal stats?
#
StatFile /var/log/news/cancelchan.stats

#
# We can specify Subject keywords that groups use to battle spam.
# The presense of the keyword aborts all other tests.
#
Keyword alt.sex.cthulhu POD:

#
# We can filter based on the number of groups posted to.
# The format is: CrossPosts count
# 
# Note: this check occurs after all Criteria checks have been done.
#
CrossPosts 12

#
# Criteria lines are the actual search criteria used to detect spam.
# The format is: Criteria keyword search-string
#
# Current allowed keywords (article header lines) are:
#   Path, From, Subject, Message-Id, Newsgroups,
#   NNTP-posting-host, Organization, Body
#
# The Body keyword searchs the first N bytes of the message body,
# where N is a configurable option.  Also note that a criteria
# that starts with ":/" is an optimized search.
#

#
# sample Criteria
#
Criteria From @spammer.com
Criteria Message-Id .spammer.com>
Criteria Body http://spammer.com
Criteria Body Make Money Fast
SHAR_EOF
chmod +x 'cancelchan.ctl'
fi # end of overwriting check
if test -f 'cancelchan.c'
then
echo shar: will not over-write existing file "'cancelchan.c'"
else
cat > 'cancelchan.c' << \SHAR_EOF
/*
** CancelFilter channel feed for INN sites
**
** Version History:
**
** Version 1.0  - Initial Version
**
** Version 1.1  - Optimize the search operations.  We don't need to
**                malloc/free everything and we can speed up the case
**                where we are searching for a URL that begins with ://
**                Also, add the ability to keyword protect individual
**                newsgroups.
*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "paths.h"
#include "inndcomm.h"

#define FALSE (0)
#define TRUE (1)
#define DEFAULTCHECK (250)		/* default Config file check count */

/* char *HeaderNames[] should match these definitions */
#define HT_BODY (0)				/* Body search */
#define HT_PATH (1)				/* Path: */
#define HT_FROM (2)				/* From: */
#define HT_MSGID (3)			/* Message-Id: */
#define HT_NGROUPS (4)			/* Newsgroups: */
#define HT_SUBJECT (5)			/* Subject: */
#define HT_SENDER (6)			/* Sender: */
#define HT_NNTPHOST (7)			/* NNTP-posting-host: */
#define HT_ORG (8)				/* Organization: */
#define HT_XPOST (9)			/* CrossPosts */

/*
** external references
*/
extern int optind, opterr;
extern char *optarg;

/*
** local structures
*/
struct criteria {
	struct criteria *next;		/* forward link */
	struct criteria *prev;		/* backward link */
	char *string;				/* the search string */
	int header;					/* the header line to match */
	int count;					/* how many times have we matched this? */
};

struct keyword {
	struct keyword *next;		/* forward link */
	char *ngroup;				/* Newsgroup */
	char *keyword;				/* Keyword */
};

/*
** local functions
*/
void StatDump();

/*
** local variables
*/
char *Version = "cancelchan for INN 1.1";

FILE *Logfp = NULL;

struct criteria *GlobalCriteria = NULL;
struct criteria *CurrentCriteria = NULL;
struct criteria CrossPostCriteria;
struct keyword *KeyList = NULL;
struct keyword *CurrentKey = NULL;

struct stat configstat;
int CheckCount = 0;
int CheckLimit = DEFAULTCHECK;
char *ConfigFile = NULL;

int ReSort = FALSE;
int BodySize = 0;
int CrossPosts = 0;

char *SpoolDir = _PATH_SPOOL;
char *LogFile = NULL;
char *StatFile = NULL;

char Path[4096], Date[1024], From[2048], Subject[4096], Sender[2048];
char Newsgroups[4096], MessageId[2048], Nntphost[2048], Organization[2048];
char *Body = NULL;
char hostname[256];

char *HeaderNames[] = {
	"Body",
	"Path:",
	"From:",
	"Message-Id:",
	"Newsgroups:",
	"Subject:",
	"Sender:",
	"NNTP-Posting-Host:",
	"Organization:",
	"CrossPosts"
};

char *day[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

unsigned char translate[256] = {
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '.', '/',
	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', 0, 0, 0, 0, 0,
	0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
	'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0,
	0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
	'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

main(argc, argv)
int argc;
char *argv[];
{
	int i, opt;
	char *number, *cp;
	char article[BUFSIZ];

	/* process our command line	*/
	while ( (opt = getopt(argc, argv, "Vc")) != EOF ) {
		switch ( opt ) {
			case 'V':			/* display Version number */
			puts(Version);
			exit(0);
			break;

			case 'c':			/* configfile */
			ConfigFile = optarg;
			break;
		}
	}

	/* initialize things */
	gethostname(hostname, sizeof(hostname));
	if ( ICCopen() < 0 ) {
		fprintf(stderr, "cancelchan: ICCopen failed\n");
		sleep(5);
		exit(1);
	}
	ICCsettimeout(-1);

reload:
	ReadConfigFile();
	if ( LogFile )
		Logfp = fopen(LogFile, "a");
	if ( Logfp )
		chmod(LogFile, 0660);
	if ( StatFile )
		signal(SIGHUP, StatDump);
	else
		signal(SIGHUP, SIG_DFL);
	if ( BodySize )
		Body = malloc(BodySize);
	chdir(SpoolDir);

	/* read in article names from INN */
	while ( fgets(article, BUFSIZ, stdin) ) {
		/* strip the newline */
		cp = strchr(article, '\n');
		if ( cp )
			*cp = 0;

		/* if not posted to the control group */
		if ( !strstr(article, "control") )
			ProcessArticle(article);

		/* should we check the config file? */
		if ( ++CheckCount >= CheckLimit )
			if ( CheckConfigFile() )
				goto reload;
	}

	/* cleanup */
	ICCclose();				/* discard our cancel channel */
	if ( Logfp )
		fclose(Logfp);
	exit(0);
}

/*
** check the config file to see if it has been modified
*/
CheckConfigFile()
{
	struct criteria *critp;
	struct keyword *keyp;
	struct stat statbuf;
	struct tm *tm;
	time_t now;

	/* reset the check count */
	CheckCount = 0;

	/* check the file times */
	if ( stat(ConfigFile, &statbuf) )
		return(FALSE);	/* file missing, continue to run with old info */
	if ( configstat.st_mtime == statbuf.st_mtime )
		return(FALSE);	/* file hasn't been modified */
	configstat = statbuf;

	ReSort = FALSE;
	SpoolDir = _PATH_SPOOL;
	CheckLimit = DEFAULTCHECK;
	CrossPosts = 0;
	BodySize = 0;
	if ( Body ) {
		free(Body);
		Body = NULL;
	}
	if ( Logfp ) {
		fclose(Logfp);
		Logfp = NULL;
	}
	if ( LogFile ) {
		free(LogFile);
		LogFile = NULL;
	}
	if ( StatFile ) {
		free(StatFile);
		StatFile = NULL;
	}

	CurrentCriteria = GlobalCriteria;
	while ( CurrentCriteria ) {
		critp = CurrentCriteria->next;	/* save forward pointer */
		free(CurrentCriteria->string);	/* free members */
		free(CurrentCriteria);			/* free structure */
		CurrentCriteria = critp;
	}
	GlobalCriteria = NULL;
	CurrentCriteria = NULL;

	CurrentKey = KeyList;
	while ( CurrentKey ) {
		keyp = CurrentKey->next;		/* save forward pointer */
		free(CurrentKey->ngroup);		/* free members */
		free(CurrentKey->keyword);
		free(CurrentKey);				/* free structure */
		CurrentKey = keyp;
	}
	KeyList = NULL;
	CurrentKey = NULL;

	now = time(NULL);
	tm = localtime(&now);
	fprintf(stderr, "%s %02d %02d:%02d:%02d", month[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
	fprintf(stderr, " %s cancelchan: reloading %s\n", hostname, ConfigFile);
	return(TRUE);
}

/*
** read the criteria from our config file
*/
ReadConfigFile()
{
	FILE *fp;
	struct criteria *critp;
	struct keyword *keyp;
	int line_num = 0;
	char *command, *arg1, *arg2;
	char buffer[1024];

	if ( !ConfigFile ) {
		sprintf(buffer, "%s/cancelchan.ctl", _PATH_NEWSLIB);
		ConfigFile = strdup(buffer);
	}
	if ( stat(ConfigFile, &configstat) ) {
		fprintf(stderr, "cancelchan: unable to stat %s\n", ConfigFile);
		sleep(5);
		exit(1);
	}
	fp = fopen(ConfigFile, "r");
	if ( !fp ) {
		fprintf(stderr, "cancelchan: unable to open %s\n", ConfigFile);
		sleep(5);
		exit(1);
	}

	/* process each line */
	while ( fgets(buffer, sizeof(buffer), fp) ) {
		/* skip comment lines */
		line_num++;
		if ( buffer[0] == '#' || buffer[0] == '\n' )
			continue;

		/* get our command */
		command = strtok(buffer, " \t\n");
		if ( !command ) {
			fprintf(stderr, "cancelchan: syntax error in config file, line %d\n", line_num);
			sleep(5);
			exit(1);
		}
		arg1 = strtok(NULL, " \t\n");
		arg2 = strtok(NULL, "\n");

		/* process the command */
		if ( !strcasecmp(command, "BodySize") ) {
			BodySize = atoi(arg1);				/* How much of the Body do we scan? */
		} else if ( !strcasecmp(command, "ReSort") ) {
			ReSort = TRUE;						/* ReSort Criteria if matched */
		} else if ( !strcasecmp(command, "Checklimit") ) {
			CheckLimit = atoi(arg1);			/* How often do we check the Config File? */
		} else if ( !strcasecmp(command, "SpoolDir") ) {
			SpoolDir = strdup(arg1);			/* where do we look for articles */
		} else if ( !strcasecmp(command, "LogFile") ) {
			LogFile = strdup(arg1);				/* log our actions */
		} else if ( !strcasecmp(command, "StatFile") ) {
			StatFile = strdup(arg1);			/* Enable statistics dumping */
		} else if ( !strcasecmp(command, "CrossPosts") ) {
			if ( !arg1 ) {
				fprintf(stderr, "cancelchan: No crosspost count found, line %d\n", line_num);
				continue;
			}
			CrossPosts = atoi(arg1);			/* How many newsgroups max? */
			CrossPostCriteria.string = "Excessive";
			CrossPostCriteria.next = NULL;
			CrossPostCriteria.prev = NULL;
			CrossPostCriteria.header = HT_XPOST;
			CrossPostCriteria.count = 0;
		} else if ( !strcasecmp(command, "Criteria") ) {
			CurrentCriteria = (struct criteria *)malloc(sizeof(struct criteria));
			if ( !CurrentCriteria ) {
				fprintf(stderr, "cancelchan: malloc failure building new criteria, line %d\n", line_num);
				continue;
			}
			if ( !arg1 ) {
				fprintf(stderr, "cancelchan: No criteria header name found, line %d\n", line_num);
				continue;
			}
			if ( !strcasecmp(arg1, "Body") )
				CurrentCriteria->header = HT_BODY;
			else if ( !strcasecmp(arg1, "Path") )
				CurrentCriteria->header = HT_PATH;
			else if ( !strcasecmp(arg1, "From") )
				CurrentCriteria->header = HT_FROM;
			else if ( !strcasecmp(arg1, "Message-Id") )
				CurrentCriteria->header = HT_MSGID;
			else if ( !strcasecmp(arg1, "Newsgroups") )
				CurrentCriteria->header = HT_NGROUPS;
			else if ( !strcasecmp(arg1, "Subject") )
				CurrentCriteria->header = HT_SUBJECT;
			else if ( !strcasecmp(arg1, "Sender") )
				CurrentCriteria->header = HT_SENDER;
			else if ( !strcasecmp(arg1, "NNTP-Posting-Host") )
				CurrentCriteria->header = HT_NNTPHOST;
			else if ( !strcasecmp(arg1, "Organization") )
				CurrentCriteria->header = HT_ORG;
			else {
				fprintf(stderr, "cancelchan: Invalid criteria header name found, line %d\n", line_num);
				continue;
			}
			if ( !arg2 ) {
				fprintf(stderr, "cancelchan: No criteria search string found, line %d\n", line_num);
				continue;
			}
			if ( CurrentCriteria->header == HT_BODY )
				normalize(arg2);
			CurrentCriteria->string = strdup(arg2);
			CurrentCriteria->next = NULL;
			CurrentCriteria->prev = NULL;
			CurrentCriteria->count = 0;

			/* insert it into the criteria chain */
			if ( !GlobalCriteria )		/* if first entry */
				GlobalCriteria = CurrentCriteria;
			else {						/* insert on end of chain */
				critp = GlobalCriteria;
				while ( critp->next )
					critp = critp->next;
				critp->next = CurrentCriteria;
				CurrentCriteria->prev = critp;
			}
		} else if ( !strcasecmp(command, "Keyword") ) {
			CurrentKey = (struct keyword *)malloc(sizeof(struct keyword));
			if ( !CurrentKey ) {
				fprintf(stderr, "cancelchan: malloc failure building new keyword, line %d\n", line_num);
				continue;
			}
			if ( !arg1 ) {
				fprintf(stderr, "cancelchan: No keyword group found, line %d\n", line_num);
				continue;
			}
			CurrentKey->ngroup = strdup(arg1);
			if ( !arg2 ) {
				fprintf(stderr, "cancelchan: No keyword found, line %d\n", line_num);
				continue;
			}
			CurrentKey->keyword = strdup(arg2);
			CurrentKey->next = NULL;

			/* insert it into the keyword chain */
			if ( !KeyList )		/* if first entry */
				KeyList = CurrentKey;
			else {						/* insert on end of chain */
				keyp = KeyList;
				while ( keyp->next )
					keyp = keyp->next;
				keyp->next = CurrentKey;
			}
		}
	}
	fclose(fp);
}

/*
** process the current article,
** testing its headers against the
** cancel criteria of the group.
*/
ProcessArticle(article)
char *article;
{
	FILE *fp;
	struct tm *tm;
	time_t now;
	int i, url = FALSE;
	char buffer[2048], *cp, *cp2;

	/* what time is it? */
	now = time(NULL);

	/* cleanup from previous article (if any) */
	Path[0] = 0;
	Date[0] = 0;
	From[0] = 0;
	Subject[0] = 0;
	Sender[0] = 0;
	Newsgroups[0] = 0;
	MessageId[0] = 0;
	Nntphost[0] = 0;
	Organization[0] = 0;

	/* open the article if possible */
	fp = fopen(article, "r");
	if ( !fp )
		return;

	/* scan the article headers, looking for the headers we need */
	while ( fgets(buffer, sizeof(buffer), fp) && buffer[0] != '\n' ) {
		cp = strchr(buffer, '\n');
		if ( cp )
			*cp = 0;
		if ( !Path[0] && !strncasecmp(buffer, "Path:", 5) )
			strcpy(Path, buffer);
		if ( !Date[0] && !strncasecmp(buffer, "Date:", 5) )
			strcpy(Date, buffer);
		if ( !From[0] && !strncasecmp(buffer, "From:", 5) )
			strcpy(From, buffer);
		if ( !MessageId[0] && !strncasecmp(buffer, "Message-Id:", 11) )
			strcpy(MessageId, buffer);
		if ( !Newsgroups[0] && !strncasecmp(buffer, "Newsgroups:", 11) )
			strcpy(Newsgroups, buffer);
		if ( !Subject[0] && !strncasecmp(buffer, "Subject:", 8) )
			strcpy(Subject, buffer);
		if ( !Sender[0] && !strncasecmp(buffer, "Sender:", 7) )
			strcpy(Sender, buffer);
		if ( !Nntphost[0] && !strncasecmp(buffer, "Nntp-posting-host:", 18) )
			strcpy(Nntphost, buffer);
		if ( !Organization[0] && !strncasecmp(buffer, "Organization:", 13) )
			strcpy(Organization, buffer);
	}

	/* do we want to scan the body? */
	if ( Body ) {
		int bytes;

		bytes = fread(Body, 1, BodySize-1, fp);
		Body[bytes] = 0;
		normalize(Body);
		if ( strstr(Body, ":/") )
			url = TRUE;		/* possible URL present */
	}
	fclose(fp);

	/* should we ignore this article because it contains the correct keyword? */
	CurrentKey = KeyList;
	while ( CurrentKey ) {
		/* is this a group that uses a keyword? */
		if ( strstr(Newsgroups, CurrentKey->ngroup) ) {
			/* is the keyword present? */
			if ( strstr(Subject, CurrentKey->keyword) )
				return;		/* yep, ignore this article */
		}
		CurrentKey = CurrentKey->next;
	}

	/* scan the article for a matching criteria */
	CurrentCriteria = GlobalCriteria;
	while ( CurrentCriteria ) {
		switch ( CurrentCriteria->header ) {
			case HT_BODY:
			if ( Body ) {
				if ( CurrentCriteria->string[0] == ':' && CurrentCriteria->string[1] == '/' && !url )
					break;		/* looking for URL and none present */
				if ( strstr(Body, CurrentCriteria->string) )
					goto found;
			}
			break;

			case HT_PATH:
			if ( strstr(Path, CurrentCriteria->string) )
				goto found;
			break;

			case HT_FROM:
			if ( strstr(From, CurrentCriteria->string) )
				goto found;
			break;

			case HT_MSGID:
			if ( strstr(MessageId, CurrentCriteria->string) )
				goto found;
			break;

			case HT_NGROUPS:
			if ( strstr(Newsgroups, CurrentCriteria->string) )
				goto found;
			break;

			case HT_SUBJECT:
			if ( strstr(Subject, CurrentCriteria->string) )
				goto found;
			break;

			case HT_SENDER:
			if ( Sender[0] && strstr(Sender, CurrentCriteria->string) )
				goto found;
			break;

			case HT_NNTPHOST:
			if ( Nntphost[0] && strstr(Nntphost, CurrentCriteria->string) )
				goto found;
			break;

			case HT_ORG:
			if ( Organization[0] && strstr(Organization, CurrentCriteria->string) )
				goto found;
			break;
		}
		CurrentCriteria = CurrentCriteria->next;
	}

	/* if we are checking CrossPosts, do it now */
	if ( CrossPosts ) {
		CurrentCriteria = &CrossPostCriteria;

		/* count the groups this was posted to */
		for ( i = 1, cp = Newsgroups; *cp; cp++ ) {
			if ( *cp == ',' )
				i++;
		}
		if ( i > CrossPosts )
			goto found;
	}

	/* must be a good article */
	return;

found:
	/* sanity check, don't cancel a cancel */
	if ( strstr(Subject, "cmsg cancel") )
		return;

	/* if we wanted to resort Criteria, do it now */
	if ( ReSort ) {
		if ( CurrentCriteria->prev == NULL ) {
			/* we are already the head of the list */
			/* or this is a crosspost check */

			/* do nothing */
		} else if ( CurrentCriteria->next == NULL ) {
			/* we are currently the tail of the list */

			/* stub off the previous criteria */
			if ( CurrentCriteria->prev )
				CurrentCriteria->prev->next = NULL;

			/* put us on the head of the chain */
			CurrentCriteria->prev = NULL;
			CurrentCriteria->next = GlobalCriteria;
			GlobalCriteria = CurrentCriteria;
		} else {
			/* we are in the middle of the list */

			/* link the previous criteria to our next link */
			CurrentCriteria->prev->next = CurrentCriteria->next;

			/* link the next criteria to our previous link */
			CurrentCriteria->next->prev = CurrentCriteria->prev;

			/* put us on the head of the chain */
			CurrentCriteria->prev = NULL;
			CurrentCriteria->next = GlobalCriteria;
			GlobalCriteria = CurrentCriteria;
		}
	}

	/* find the MessageId and tell INN to remove the article */
	CurrentCriteria->count++;
	cp = strchr(MessageId, '<');
	if ( ICCcancel(cp) < 0 )
		return;

	/* if it worked, log the article */
	tm = localtime(&now);
	if ( Logfp ) {
		fprintf(Logfp, "%s %02d %02d:%02d:%02d", month[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
		fprintf(Logfp, " %s cancelchan: %s (%s)\n", hostname, cp, article);
		fflush(Logfp);
	}
}

/*
** attempt to make the string easier to deal with
** by removing all whitespace, newlines and making
** sure everything is in lowercase.
*/
normalize(string)
char *string;
{
	unsigned char *src, *dst;

    src = dst = (unsigned char *)string;
	while ( *src ) {
		if ( translate[*src] )
			*dst++ = translate[*src];
		src++;
	}
	*dst = 0;
}

/*
** Dump our internal stats to a file
*/
void StatDump()
{
	FILE *fp;
	struct criteria *critp;

	fp = fopen(StatFile, "w");
	if ( fp ) {
		fprintf(fp, "Cancelchan Criteria Statistics\n\n");
		if ( CrossPosts )
			fprintf(fp, "%d Articles CrossPosted to more than %d groups\n\n", CrossPostCriteria.count, CrossPosts);
		critp = GlobalCriteria;
		while ( critp ) {
			fprintf(fp, "%d %s %s\n", critp->count, HeaderNames[critp->header], critp->string);
			critp = critp->next;
		}
		fclose(fp);
	}
}
SHAR_EOF
chmod +x 'cancelchan.c'
fi # end of overwriting check
#
# End of shell archive
#
exit 0

-- 
Brian W. Antoine                                   Engineer and News Admin at
briana@tau-ceti.isc-br.com                             Olivetti North America
http://tau-ceti.isc-br.com/                                   Spokane, WA USA
Have YOU nuked a spammer today?

Return to the News Server Patches Page

Home - Usenet Area - General - Email Area

Made With Macintosh  © 1997 Ken Lucke - all rights reserved  Spun With PageSpinner