/*
* apply filter file to all files in a newsgroup
*
* Written by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>.
* Copyright 1999.
*
* Modified and copyright of the modifications 2002 by Matthias Andree
* <matthias.andree@gmx.de> and Ralf Wildenhues <ralf.wildenhues@gmx.de>
*
* See file COPYING for restrictions on the use of this software.
*/
#include "leafnode.h"
#include "ln_log.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <ctype.h>
#include "system.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <utime.h>
#define MAXHEADERSIZE 2047
int debug = 0;
int verbose;
/* read from file f into malloced buffer *bufp of size *size
* up to delim or EOF. Buffer is adjusted to fit input.
* return pointer to match or end of file, NULL in case of error
*/
static /*@null@*/ /*@dependent@*/ char *
readtodelim(FILE *f, const char *name, /*@unique@*/ /*@observer@*/ const char *delim,
char **bufp, size_t *size)
{
size_t dlen = strlen(delim) - 1;
size_t nread, res;
char *k;
nread = 0;
if (*size < 1 || *bufp == NULL)
*bufp = critmalloc((*size = MAXHEADERSIZE), "readtodelim");
/*@+loopexec@*/
for (;;) {
res = fread(*bufp + nread, 1, *size - nread - 1, f);
(*bufp)[nread + res] = '\0';
/* skip as much as possible */
k = strstr(nread > dlen ? *bufp + nread - dlen : *bufp, delim);
if (ferror(f)) {
printf("error reading %s\n", name);
clearerr(f);
return k;
}
nread += res;
if (feof(f)) {
clearerr(f);
return k != NULL ? k : *bufp + nread;
}
if (k != NULL) {
return k;
}
/* must read more */
*bufp = critrealloc(*bufp, (*size)*=2, "readtodelim");
}
/*@=loopexec@*/
}
/** unfold a header string \a t in-place.
* CRLF are converted to LF. */
static void unfold(char *t)
{
char *i;
for (i = t; *i ; i++) {
if (i[0] == '\r' && i[1] == '\n')
continue;
if (i[0] == '\n'
&& (i[1] == ' ' || i[1] == '\t'))
continue;
*t = *i;
t++;
}
*t = '\0';
}
/* read article headers, cut off body
* return 0 for success, -1 for error, -2 for article without body
*/
static int
readheaders(FILE *f, /*@unique@*/ const char *name, char **bufp, size_t *size)
{
char *k = readtodelim(f, name, "\n\n", bufp, size);
if (k != NULL) {
/* unfold lines */
unfold(*bufp);
if (*k == '\0')
return -2;
else {
k[1] = '\0';
return 0;
}
} else {
return -1;
}
}
int
main(int argc, char *argv[])
{
const char c[] = "-\\|/";
int i, score, option, deleted, kept;
unsigned long n;
char *msgid;
char *l;
size_t lsize;
const char *msgidpath = "";
FILE *f;
DIR *d;
struct dirent *de;
struct stat st;
struct utimbuf u;
struct newsgroup *g;
myopenlog("applyfilter");
if (!initvars(argv[0]))
exit(1);
while ((option = getopt(argc, argv, "v")) != -1) {
if (option == 'v')
verbose++;
else {
printf("Usage: %s newsgroup\n", argv[0]);
exit(1);
}
}
if (argv[optind] == NULL) {
printf("Usage: %s newsgroup\n", argv[0]);
exit(1);
}
if (!readconfig(0)) {
printf("Reading configuration failed, exiting "
"(see syslog for more information).\n");
exit(2);
}
freeservers();
if (filterfile)
readfilter(filterfile);
else {
printf("Nothing to filter -- no filterfile found.\n");
freeconfig();
exit(0);
}
if (try_lock(timeout_lock)) {
printf("Cannot obtain lock file, aborting.\n");
exit(1);
}
readactive();
if (!active) {
printf("Problem reading active file.\n");
freeconfig();
exit(1);
}
g = findgroup(argv[optind]);
if (!g) {
printf("Newsgroups %s not found in active file.\n", argv[optind]);
unlink(lockfile);
exit(1);
}
/* to automatically rise the low water mark, we reset g->first to
* ULONG_MAX. */
g->first = ULONG_MAX;
/* We used to do g->last = 0; but that can severely confuse news
* readers, we don't ever want to decrease the high water mark. */
if (!chdirgroup(g->name, FALSE)) {
printf("No such newsgroup: %s\n", g->name);
unlink(lockfile);
exit(1);
}
if (!(d = opendir("."))) {
printf("Unable to open directory for newsgroup %s\n", g->name);
unlink(lockfile);
exit(1);
}
i = 0;
deleted = 0;
kept = 0;
lsize = MAXHEADERSIZE + 1;
l = critmalloc(lsize, "Space for article");
while ((de = readdir(d)) != NULL) {
if (!isdigit((unsigned char)de->d_name[0])) {
/* no need to stat file */
continue;
}
switch (verbose) {
case 1:{
printf("%c\b", c[i % 4]);
fflush(stdout);
i++;
break;
}
case 2:{
printf("%s\n", de->d_name);
}
}
if ((f = fopen(de->d_name, "r")) != NULL
&& fstat(fileno(f), &st) == 0
&& S_ISREG(st.st_mode))
{
switch (readheaders(f, de->d_name, &l, &lsize)) {
case 0:
case -2:
score = dofilter(l);
break;
case -1:
score = -1; /* FIXME: how to handle read error? */
break;
default:
/* this branch will never be executed, but
* eliminates a compiler warning */
score = 0;
break;
}
if (score) {
msgid = fgetheader(f, "Message-ID:");
fclose(f);
unlink(de->d_name);
/* delete stuff in message.id directory as well */
if (msgid) {
msgidpath = lookup(msgid);
if ((stat(msgidpath, &st) == 0) /* RATS: ignore */
&& (st.st_nlink < 2)) {
if (unlink(msgidpath) == 0)
deleted++;
}
free(msgid);
}
if (verbose)
printf("%s %s deleted\n", de->d_name, msgidpath);
} else {
fclose(f);
n = strtoul(de->d_name, NULL, 10);
if (n) {
if (n < g->first)
g->first = n;
if (n > g->last)
g->last = n;
}
u.actime = st.st_atime;
u.modtime = st.st_mtime;
utime(de->d_name, &u);
kept++;
}
} else {
if (f) {
ln_log(LNLOG_SERR, LNLOG_CARTICLE,
"could not stat %s or not a regular file\n",
de->d_name);
(void)fclose(f);
} else {
ln_log(LNLOG_SERR, LNLOG_CARTICLE, "could not open %s\n",
de->d_name);
}
}
}
closedir(d);
free(l);
if (g->first > g->last) {
/* group is empty */
g->first = g->last + 1;
}
if (writeactive()) ln_log(LNLOG_SERR, LNLOG_CTOP, "Error writing groupinfo.");
freeactive(active);
unlink(lockfile);
printf("%d article%s deleted, %d kept.\n", deleted, PLURAL(deleted), kept);
if (verbose)
printf("Updating .overview file\n");
getxover();
freexover();
freeconfig();
exit(0);
}
syntax highlighted by Code2HTML, v. 0.9.1