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
Return to the News Server Patches Page
Home - Usenet Area - General - Email Area