/* * apply filter file to all files in a newsgroup * * Written by Cornelius Krasel . * Copyright 1999. * * Modified and copyright of the modifications 2002 by Matthias Andree * and Ralf Wildenhues * * See file COPYING for restrictions on the use of this software. */ #include "leafnode.h" #include "ln_log.h" #include #include #include #include "system.h" #include #include #include #include #include #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); }