--- rnews.c.orig Sat May 18 22:21:26 2002 +++ rnews.c Wed Aug 4 03:43:51 2004 @@ -2,6 +2,8 @@ * S-News version 0.1.9 - A Simple News Server * Copyright (C) 1998 Christopher John Purnell * cjp@lost.org.uk + * Supersedes/cancel modifications (C) 2002 Tony Houghton + * tony@realh.co.uk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,6 +52,8 @@ #include "snews.h" +#define DEBLOG(a) /* fprintf a */ + struct header { struct header *next; @@ -78,14 +82,23 @@ static FILE *open_lock(char *); static int file_name(struct header *,char *,unsigned); static GDBM_FILE open_history(char *,int); +static int read_string(FILE *,char *,unsigned); +static void delete_msg(const char *); +static int whole_string(struct header_pointer *,char *,unsigned); +static int read_line(FILE *,char *,unsigned); +static int isolate_addr(char *); static char *progname; static char fqdn[BUFLEN]; static char msgid[BUFLEN]; static char article[BUFLEN]; +static char vsender[BUFLEN]; +static char cancel[BUFLEN]; static char queue=0,approved=0; +static int cancel_only; struct header junk = { 0, "junk", 0 }; +struct header control_cancel = { 0, "control.cancel", 0 }; int main(int argc,char **argv) { @@ -95,6 +108,16 @@ set_ug_id(); + /* For logging, remove this in release */ + /* + i = open("/var/log/news/rnews", O_WRONLY|O_CREAT|O_APPEND, 0644); + if (i) + { + close(2); + dup2(i, 2); + } + */ + progname = argv[0]; if (get_fqdn()) @@ -116,12 +139,18 @@ return (1); } + cancel[0] = 0; + vsender[0] = 0; + cancel_only = 0; + if (!(hdr = read_header())) return (1); if (!(ngrp = parse_header(hdr))) return (1); + DEBLOG((stderr, "Message-ID: %s\n", msgid)); + if (!(fp = open_lock(CONFDIR"/active.n"))) { perror(progname); @@ -130,8 +159,16 @@ if (!(i = chk_article(msgid))) { - if (!(ngrp = update_active(CONFDIR"/active",CONFDIR"/active.n", - fp,ngrp))) + DEBLOG((stderr, "chk_article OK\n")); + if (cancel_only) + { + ngrp = update_active(CONFDIR"/active", + CONFDIR"/active.n", + fp,&control_cancel); + DEBLOG((stderr, "cancel_only, ngrp = %d\n", ngrp)); + } + else if (!(ngrp = update_active(CONFDIR"/active", + CONFDIR"/active.n",fp,ngrp))) { rewind(fp); if (ftruncate(fileno(fp),0)) @@ -144,8 +181,11 @@ i = !ngrp; } + DEBLOG((stderr, "After chk_article and update_active, result = %d (0 is good)\n", i)); + if (fclose(fp) || i || write_article(hdr,ngrp)) { + DEBLOG((stderr, "fclose || i || write_article failed\n")); unlink(CONFDIR"/active.n"); return (1); } @@ -158,6 +198,9 @@ return (1); } + if (cancel[0]) + delete_msg(cancel); + if (!queue) return (0); execl(QNEWSPATH,QNEWSARG0,article,0); @@ -301,11 +344,78 @@ { approved=1; } + else if (!strncasecmp(hdr->line,"Sender:",7)) + { + hp.hdr = hdr; + hp.ptr = hdr->line+7; + if (whole_string(&hp,vsender,BUFLEN)<=0) + { + fprintf(stderr,"%s: bad Sender\n", + progname); + return (0); + } + DEBLOG((stderr, "Found Sender: %s\n", vsender)); + } + else if (!vsender[0] && !strncasecmp(hdr->line,"From:",5)) + { + hp.hdr = hdr; + hp.ptr = hdr->line+5; + if (whole_string(&hp,vsender,BUFLEN)<=0) + { + fprintf(stderr,"%s: bad From\n", + progname); + return (0); + } + DEBLOG((stderr, "Found From: %s\n", vsender)); + } + else if (!strncasecmp(hdr->line,"Supersedes:",11)) + { + hp.hdr = hdr; + hp.ptr = hdr->line+11; + if (whole_string(&hp,cancel,BUFLEN)<=0) + { + fprintf(stderr,"%s: bad Supersedes\n", + progname); + return (0); + } + DEBLOG((stderr, "Found Supersedes: %s\n", cancel)); + } + else if (!strncasecmp(hdr->line,"Control: cancel",15)) + { + hp.hdr = hdr; + hp.ptr = hdr->line+15; + if (whole_string(&hp,cancel,BUFLEN)<=0) + { + fprintf(stderr,"%s: bad cancel\n", + progname); + return (0); + } + DEBLOG((stderr, "Found cancel: %s\n", cancel)); + cancel_only = 1; + } + else if (!strncasecmp(hdr->line,"Also-Control: cancel",20)) + { + hp.hdr = hdr; + hp.ptr = hdr->line+20; + if (whole_string(&hp,cancel,BUFLEN)<=0) + { + fprintf(stderr,"%s: bad cancel\n", + progname); + return (0); + } + DEBLOG((stderr, "Found also-cancel: %s\n", cancel)); + } } hdr = hdr->next; } + if (vsender[0]) + { + DEBLOG((stderr,"vsender address = %s (result %d)\n", + vsender,isolate_addr(vsender))); + } + return (*msgid?ret:0); } @@ -597,7 +707,10 @@ int ret; if (!(dbf = open_history(CONFDIR"/history",GDBM_READER))) + { + DEBLOG((stderr, "chk_article: Couldn't open history\n")); return (1); + } key.dsize = strlen(key.dptr = id) + 1; @@ -605,6 +718,7 @@ gdbm_close(dbf); + DEBLOG((stderr, "chk_article: %s exists: %d\n", id, ret)); return (ret); } @@ -642,3 +756,232 @@ } return (dbf); } + +/* Added by Tony Houghton , mostly copied from expire.c */ + +static void delete_msg(const char *msgid) +{ + char buf[BUFLEN]; + char sender[BUFLEN]; + struct stat st; + FILE *fp; + GDBM_FILE dbf; + datum key,value; + char *file; + + DEBLOG((stderr, "Deleting %s\n", msgid)); + if (!(dbf = open_history(CONFDIR"/history",GDBM_READER))) + return; + key.dsize = strlen(key.dptr = (char *) msgid) + 1; + value = gdbm_fetch(dbf, key); + if ((file = value.dptr) == NULL) + return; + DEBLOG((stderr, "Filename is %s\n", file)); + + if (!(fp = fopen(file,"r"))) + { + if (errno != ENOENT) + perror(file); + free(file); + return; + } + + if (fstat(fileno(fp),&st)) + { + perror(file); + free(file); + return; + } + + sender[0] = 0; + while (fgets(buf,6,fp)) + { + if (!sender[0] && !strcasecmp(buf,"From:")) + { + if (read_line(fp,sender,BUFLEN) <= 0) + { + fclose(fp); + fprintf(stderr,"%s: bad From header\n",file); + free(file); + return; + } + DEBLOG((stderr, "From: %s\n", sender)); + } + else if (!strcasecmp(buf,"Sende") && fgetc(fp) == 'r' && + fgetc(fp) == ':') + { + if (read_line(fp,sender,BUFLEN) <= 0) + { + fclose(fp); + fprintf(stderr,"%s: bad Sender\n",file); + free(file); + return; + } + DEBLOG((stderr, "Sender: %s\n", sender)); + } + else if (buf[0] == '\n') + break; + } + rewind(fp); + if (isolate_addr(sender) == -1) + { + fprintf(stderr,"Invalid sender, can't cancel\n"); + free(file); + fclose(fp); + return; + } + DEBLOG((stderr,"Sender address = %s\n",sender)); + + if (strcasecmp(sender,vsender)) + { + fprintf(stderr,"Wrong sender, can't cancel\n"); + free(file); + fclose(fp); + return; + } + + while (fgets(buf,6,fp)) + { + char *cp; + int c; + + if (strcasecmp(buf,"Xref:")) + { + if ((cp = strchr(buf,'\n'))) + { + if (cp == buf) break; + } + else + { + while ((c = fgetc(fp)) != EOF && c != '\n'); + if (c == EOF) break; + } + } + else + { + if ((read_string(fp,buf,BUFLEN) <= 0) || + strcasecmp(buf,fqdn)) + { + fclose(fp); + fprintf(stderr,"%s: bad Xref line\n",file); + free(file); + return; + } + + while ((c = read_string(fp,buf,BUFLEN))) + { + if (c < 0) + { + fclose(fp); + fprintf(stderr,"%s: bad Xref line\n", + file); + free(file); + return; + } + + cp = buf; + while ((c = *cp)) + { + if (c == '.' || c ==':') + *cp = '/'; + ++cp; + } + + if (strcmp(file,buf) && + unlink(buf) && errno != ENOENT) + { + perror(buf); + fclose(fp); + free(file); + return; + } + } + + break; + } + } + if (ferror(fp)) + { + perror(file); + free(file); + fclose(fp); + return; + } + + fclose(fp); + if (unlink(file)) + perror(file); + free(file); +} + +static int read_string(FILE *fp,char *str,unsigned len) +{ + int c; + unsigned i=0; + + while ((c = fgetc(fp)) != EOF && c != '\n' && isspace(c)); + + while (c != EOF && !isspace(c)) + { + str[i]=c; + if (++i>=len) return (-1); + c = fgetc(fp); + } + str[i]='\0'; + + return (i); +} + +static int whole_string(struct header_pointer *hp,char *str,unsigned len) +{ + unsigned i=0; + unsigned char c; + + while ((c = next_char(hp)) && isspace(c)); + + while (c && c != '\n') + { + str[i]=c; + if (++i>=len) return (-1); + c = next_char(hp); + } + str[i]='\0'; + + return (i); +} + +static int read_line(FILE *fp,char *str,unsigned len) +{ + int c; + unsigned i=0; + + while ((c = fgetc(fp)) != EOF && c != '\n' && isspace(c)); + + while (c != EOF && c != '\n') + { + str[i]=c; + if (++i>=len) return (-1); + c = fgetc(fp); + } + str[i]='\0'; + + return (i); +} + +static int isolate_addr(char *str) +{ + char *at; + unsigned len; + + if (!(at=strchr(str,'@'))) + return -1; + while (at != str && !isspace(*(at-1)) && *(at-1) != '<') + --at; + for (len = 0; + len <= strlen(at) && !isspace(at[len]) && at[len] !='>'; + ++len); + memmove(str,at,len); + str[len]=0; + return 0; +} +