home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume19
/
cnews2
/
part15
< prev
next >
Wrap
Text File
|
1989-06-29
|
51KB
|
1,770 lines
Subject: v19i092: Cnews production release, Part15/19
Newsgroups: comp.sources.unix
Sender: sources
Approved: rsalz@uunet.UU.NET
Submitted-by: utzoo!henry
Posting-number: Volume 19, Issue 92
Archive-name: cnews2/part15
: ---CUT HERE---
echo 'relay/fileart.c':
sed 's/^X//' >'relay/fileart.c' <<'!'
X/*
X * fileart - file an article, given its temporary file name and its headers
X *
X * It may be desirable to, some day, prevent cross-postings across
X * "universes", where a universe might be "alt" or "comp,news".
X *
X * There are three classes of newsgroup for the purposes of filing:
X * "wanted" (in the active file and missing the "x" flag);
X * "not wanted" ("x"ed in active, or not in active and not matched by sys
X * file's subscription list for this machine), so ignore it; or
X * "don't know it" (not in active and matched by subscription list,
X * so file the article in junk once, iff there are no good groups).
X * junk *must* be in the active file or it's an error (ST_DROPPED),
X * but junk may have an "x" flag to prevent filing.
X *
X * Use the active file 'x' flag to snuff groups quietly, even when your
X * subscription list permits them, without filing in junk.
X */
X
X#include <stdio.h>
X#include <errno.h>
X#include <sys/types.h>
X
X#include "libc.h"
X#include "news.h"
X#include "config.h"
X#include "active.h"
X#include "mkdirs.h"
X#include "headers.h"
X#include "article.h"
X#include "history.h"
X#include "system.h"
X
X#define JUNK "junk" /* lost+found pseudo-ng. */
X#define CONTROL "control" /* control message pseudo-ng. */
X
Xstatic long artnum; /* asgnartnum sets artnum */
Xstatic int goodngs; /* asgnartnum reads goodngs */
X
Xstatic boolean debug = NO;
X
X/* imports from news */
Xextern void prefuse();
X
X/* forwards */
XFORWARD void asgnartnum(), gotgoodng(), mkjunklink(), mklinks();
XFORWARD boolean openorlink(), mkonelink(), tryartnum();
X
Xvoid
Xfiledebug(state) /* set debugging state */
Xboolean state;
X{
X debug = state;
X}
X
X/*
X * File in the spool directory the article in art & fill in art->a_files.
X * Generate Xref: header if needed (successfully cross-posted).
X *
X * If a_unlink is true, there is a temp file, so openfirst should
X * be false, and vice versa.
X *
X * If openfirst (!art->a_unlink) is true, fill in a_tmpf with the name of
X * the first link, fopen it (into art->a_artf), and make any remaining links.
X * If openfirst is false, just make links to a_tmpf, which is already
X * open as art->a_artf. openfirst means "Newsgroups:" was seen in time.
X */
Xvoid
Xfileart(art)
Xregister struct article *art;
X{
X register boolean openfirst = !art->a_unlink;
X int junkgroups = 0; /* count "junked" groups */
X char artnumstr[MAXCOMP]; /* article number in ascii */
X
X if (art->a_filed)
X return; /* don't file twice */
X artnum = 0;
X goodngs = 0;
X mklinks(art, openfirst, artnumstr, &junkgroups);
X mkjunklink(art, openfirst, artnumstr, &junkgroups);
X if (goodngs > 1 && art->a_artf != NULL) /* cross-posted? */
X emitxref(art);
X}
X
X/*
X * Store in spooldir. Link temp file to spooldir/ng/article-number
X * for each ng. Control messages go in CONTROL, never in all.all.ctl.
X */
XSTATIC void
Xmklinks(art, openfirst, artnumstr, junkgroupsp)
Xregister struct article *art;
Xboolean openfirst;
Xchar *artnumstr;
Xint *junkgroupsp;
X{
X register char *ngs, *ng;
X register char *comma;
X
X ngs = (art->h.h_ctlcmd != NULL? CONTROL: art->h.h_ngs);
X if (art->a_status&ST_REFUSED)
X (void) fprintf(stderr,
X "%s: mklinks called with ST_REFUSED set (can't happen)\n",
X progname);
X for (; ngs != NULL; ngs = comma) {
X comma = index(ngs, NGSEP);
X if (comma != NULL)
X *comma = '\0'; /* will be restored below */
X ng = realngname(ngs);
X if (ng == NULL)
X ng = strsave(ngs);
X asgnartnum(art, openfirst, ng, artnumstr);
X /*
X * If no such group in active or link failed, and the group
X * wasn't 'x'ed in active, but our subscription list permits
X * this group, then set flag to file it under "junk" later.
X */
X if ((artnum < 1 || art->a_status != ST_OKAY) &&
X art->a_status != ST_REFUSED &&
X ngmatch(oursys()->sy_ngs, ng))
X ++*junkgroupsp;
X /*
X * If article # was assigned & link succeeded,
X * update art->a_files list for history.
X */
X if (artnum >= 1 && art->a_status == ST_OKAY)
X gotgoodng(art, ng, artnumstr);
X free(ng);
X
X if (comma != NULL)
X *comma++ = NGSEP; /* step past comma */
X
X /* asgnartnum refused just this ng */
X art->a_status &= ~ST_REFUSED;
X }
X}
X
X/*
X * File once in "junk" iff no ngs were filed due to absence from
X * active, but some were permitted by sys. This will make one junk
X * link, no matter how many bad groups, and only if all are bad
X * (e.g. rec.drugs,talk.chew-the-fat).
X */
XSTATIC void
Xmkjunklink(art, openfirst, artnumstr, junkgroupsp)
Xregister struct article *art;
Xboolean openfirst;
Xchar *artnumstr;
Xint *junkgroupsp;
X{
X if (*junkgroupsp > 0 && goodngs == 0) { /* all groups were "junked"? */
X asgnartnum(art, openfirst, JUNK, artnumstr);
X if (artnum >= 1 && art->a_status == ST_OKAY) {
X gotgoodng(art, JUNK, artnumstr);
X art->a_status |= ST_JUNKED;
X } else {
X /*
X * couldn't file article in junk.
X * was JUNK not 'x'ed (i.e. doesn't exist)?
X */
X if (art->a_status != ST_REFUSED) {
X static boolean warned = NO;
X
X art->a_status |= ST_REFUSED|ST_DROPPED;
X if (!warned) {
X warned = YES;
X (void) fprintf(stderr, "%s: no %s group\n",
X progname, JUNK);
X }
X }
X prefuse(art);
X (void) printf("no known groups in `%s' and no %s group\n",
X art->h.h_ngs, JUNK);
X }
X } else if (goodngs == 0) {
X extern boolean histreject;
X
X /*
X * Groups were permitted by subscription list, but all
X * were 'x'ed in active, or otherwise refused.
X */
X if (histreject)
X history(art, NOLOG);
X prefuse(art);
X (void) printf("all groups `%s' excluded in active\n", art->h.h_ngs);
X art->a_status |= ST_REFUSED;
X }
X}
X
X/*
X * Append ng/artnumstr to art's list of files, and bump goodngs.
X */
XSTATIC void
Xgotgoodng(art, ng, artnumstr)
Xstruct article *art;
Xchar *ng, *artnumstr;
X{
X ++goodngs;
X histupdfiles(art, ng, artnumstr);
X}
X
X/*
X * Assign a permanent name and article number to the temporary name
X * art->a_tmpf in newsgroup "ng" & store the ascii form of the article
X * number into "artnumstr", returning the article number in "artnum".
X *
X * If openfirst is true and goodngs is zero, set inname to artname,
X * fopen artname and store the result in art->a_artf.
X */
XSTATIC void
Xasgnartnum(art, openfirst, ng, artnumstr)
Xstruct article *art;
Xboolean openfirst; /* open first link? */
Xregister char *ng; /* read-only */
Xchar *artnumstr;
X{
X register char *slashng; /* a group, slashed */
X
X /* field active 'x' flag: don't file this group, quietly */
X if (unwanted(ng)) {
X artnum = -1;
X art->a_status |= ST_REFUSED;
X return;
X }
X
X slashng = strsave(ng);
X mkfilenm(slashng); /* relative to spooldir */
X while ((artnum = nxtartnum(ng)) >= 1)
X if (tryartnum(art, openfirst, slashng, artnumstr))
X break;
X free(slashng);
X}
X
X/*
X * Construct a link name (slashng/artnum) for this article,
X * and try to link art to it.
X *
X * We changed directory to spooldir in main(), so the generated name
X * is relative to spooldir, therefore artname can be used as is.
X *
X * Return value is identical to mkonelink's.
X */
XSTATIC boolean
Xtryartnum(art, openfirst, slashng, artnumstr)
Xregister struct article *art;
Xboolean openfirst;
Xregister char *slashng;
Xchar *artnumstr; /* side-effect returned here */
X{
X register char *artname; /* article file name */
X register boolean ret;
X
X (void) sprintf(artnumstr, "%ld", artnum);
X artname = nemalloc((unsigned) (strlen(slashng) +
X STRLEN(SFNDELIM) + strlen(artnumstr) + 1));
X (void) strcpy(artname, slashng);
X (void) strcat(artname, SFNDELIM);
X (void) strcat(artname, artnumstr);
X#ifdef notdef
X char *tartname = strsave(artfile(artname));
X free(artname);
X artname = tartname;
X#endif
X ret = mkonelink(art, artname, openfirst);
X free(artname);
X return ret;
X}
X
X/*
X * Try to link art to artname.
X * If the attempt fails, maybe some intermediate directories are missing,
X * so create any missing directories and try again. If the second attempt
X * also fails, look at errno; if it is EEXIST, artname already exists
X * (presumably because the active file is out of date, or the existing
X * file is a directory such as net/micro/432), so indicate that higher
X * levels should keep trying, otherwise we are unable to create the
X * necessary directories, so complain and set bad status in art.
X *
X * Returns YES iff there is no point in trying to file this article again,
X * usually because it has been successfully filed, but sometimes because
X * the necessary directories cannot be made.
X */
XSTATIC boolean
Xmkonelink(art, artname, openfirst)
Xregister struct article *art;
Xregister char *artname;
Xboolean openfirst;
X{
X if (openorlink(artname, art, openfirst, goodngs))
X return YES;
X else {
X (void) mkdirs(artname, getuid(), getgid());
X if (openorlink(artname, art, openfirst, goodngs))
X return YES;
X else if (errno != EEXIST) {
X warning("can't link to `%s'", artname);
X art->a_status |= ST_DROPPED;
X return YES; /* hopeless - give up */
X } else
X return NO;
X }
X}
X
X/*
X * Try to make a link of art (actually art->a_tmpf) to artname.
X * If no links have been made yet, record and open artname iff no
X * link yet exists to that name (by any file, to avoid overwriting
X * existing articles, e.g. due to an out-of-date active file).
X * If links already exist, try to make artname a link to the first link
X * (art->a_tmpf).
X *
X * Liberally sprinkled with debugging output, as this is the heart
X * of article filing.
X */
XSTATIC boolean
Xopenorlink(artname, art, openfirst, goodngcnt)
Xregister char *artname;
Xregister struct article *art;
Xboolean openfirst; /* open art's first link? */
Xint goodngcnt; /* count of good news groups */
X{
X register boolean worked; /* open or link worked? */
X
X if (openfirst && goodngcnt == 0) {
X if (debug)
X (void) fprintf(stderr, "opening `%s'... ", artname);
X nnfree(&art->a_tmpf);
X art->a_tmpf = strsave(artname);
X art->a_artf = fopenexcl(art->a_tmpf);
X worked = art->a_artf != NULL;
X } else {
X if (debug)
X (void) fprintf(stderr, "linking `%s' to `%s'... ",
X art->a_tmpf, artname);
X worked = link(art->a_tmpf, artname) == 0;
X if (!worked)
X /*
X * If art->a_tmpf really *is* a temporary name (because
X * the whole header didn't fit in core), then this will
X * produce a symbolic link to a non-existent name when
X * art->a_tmpf is unlinked, which will be soon.
X * Moral: don't run Eunice on your PDP-11.
X */
X worked = symlink(art->a_tmpf, artname) == 0;
X }
X if (debug)
X if (worked)
X (void) fprintf(stderr, "success.\n");
X else
X warning("failed.", "");
X return worked;
X}
!
echo 'relay/fileart.h':
sed 's/^X//' >'relay/fileart.h' <<'!'
X/* imports from fileart.c */
Xextern void filedebug();
Xextern void fileart();
!
echo 'relay/altctl/rmgroup.auto':
sed 's/^X//' >'relay/altctl/rmgroup.auto' <<'!'
X#! /bin/sh
X# rmgroup group - snuff group. active file is locked at entry
X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
X. ${NEWSCONFIG-/usr/lib/news/bin/config}
Xexport NEWSCTL NEWSBIN NEWSARTS
XPATH=$NEWSCTL/bin:$NEWSBIN:$NEWSPATH ; export PATH
Xumask $NEWSUMASK
X
XF=/tmp/nc$$
X
Xcat >$F
Xgrep -s '^Approved:' $F || { rm -f $F; exit 1; } # unapproved ctl msg? then quit
XSENDER="`grep '^Sender:' $F | sed 's/^[^:]*: *//'`"
Xcase "$SENDER" in
X"")
X SENDER="`grep '^From:' $F | sed 's/^[^:]*: *//'`"
X ;;
Xesac
X
X# remove active entry
Xsed "/^`echo $1 | sed 's/\./\\\\./g'` /d" $NEWSCTL/active >$F.act
Xcp $NEWSCTL/active $NEWSCTL/active.old
Xcp $F.act $NEWSCTL/active
X
X# rm -rf $NEWSARTS/`echo $1 | tr . / ` # remove the directory
Xdir=$NEWSARTS/`echo $1 | tr . / ` # name the directory
Xexport dir # for sub-shell below
X(
X if test -x $dir; then
X cd $dir # go there
X rm -f *
X cd ..
X rmdir `basename "$dir" '' ` # remove the empty directory
X fi
X)
X
X# tell the local usenet administrator the bad news
Xecho "rmgrouped $1 cuz $SENDER said to" | mail $NEWSMASTER
X
Xrm -f $F*
!
echo 'relay/altctl/checkgroups.new':
sed 's/^X//' >'relay/altctl/checkgroups.new' <<'!'
X#! /bin/sh
X# checkgroups - check active file for missing or extra newsgroups.
X# stdin must a checkgroups news article, sends mail to $NEWSMASTER
X# after updating $nl/newsgroups from $nl/localgroups
X# v1.4 of 9/4/84, adapted to C news
X# added handling of changes in moderated for groups. Dan
X
X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
X. ${NEWSCONFIG-/usr/lib/news/bin/config}
Xexport NEWSCTL NEWSBIN NEWSARTS
XPATH=$NEWSCTL/bin:$NEWSBIN:$NEWSPATH ; export PATH
Xumask $NEWSUMASK
X
X# generate newsgroups from localgroups & stdin
Xcp $NEWSCTL/localgroups $NEWSCTL/newsgroups
Xsed '1,/^$/d' >>$NEWSCTL/newsgroups # behead the article (snuff headers)
X
X# generate list of approved newsgroups from $nl/newsgroups
Xecho junk >/tmp/$$a
Xecho control >>/tmp/$$a
X# [^.]*\. in next two egreps was net.|mod.|fa., which is inadequate - geoff
Xsed 's/[ \ ].*//' $NEWSCTL/newsgroups |
X egrep "^([^.]*\.|general)" >>/tmp/$$a
Xsort -u /tmp/$$a -o /tmp/$$a
X
X# generate list of locally-present newsgroups from $nl/active
Xegrep "^([^.]*\.|general|junk|control)" $NEWSCTL/active |
X sed 's/ .*//' | sort -u >/tmp/$$b
X
Xcomm -13 /tmp/$$a /tmp/$$b >/tmp/$$missing
Xcomm -23 /tmp/$$a /tmp/$$b >/tmp/$$remove
X
Xegrep "^([^.]*\.|general|junk|control)" $NEWSCTL/active | sed -n "/m\$/s/ .*//p" |
X sort -u > /tmp/$$amod.all
Xegrep "^([^.]*\.|general)" $NEWSCTL/newsgroups |
Xsed -n "/Moderated/s/[ ][ ]*.*//p" | sort -u > /tmp/$$ng.mod
X
Xcomm -12 /tmp/$$missing /tmp/$$ng.mod >/tmp/$$add.mod
Xcomm -23 /tmp/$$missing /tmp/$$ng.mod >/tmp/$$add.unmod
Xcat /tmp/$$add.mod /tmp/$$add.unmod >>/tmp/$$add
X
Xcomm -23 /tmp/$$amod.all /tmp/$$remove >/tmp/$$amod
Xcomm -13 /tmp/$$ng.mod /tmp/$$amod >/tmp/$$ismod
Xcomm -23 /tmp/$$ng.mod /tmp/$$amod >/tmp/$$nm.all
Xcomm -23 /tmp/$$nm.all /tmp/$$add >/tmp/$$notmod
X
X
Xif test -s /tmp/$$remove; then
X echo "The following newsgroups are not valid and should be removed."
X sed "s/^/ /" /tmp/$$remove
X echo ""
X echo "You can do this by executing the command:"
X echo " $NEWSCTL/ctl/rmgroup.auto \\"
X sed 's;.*; & \\;' /tmp/$$remove
X echo ""
Xfi 2>&1 >/tmp/$$out
X
Xif test -s /tmp/$$add; then
X echo "The following newsgroups were missing." # "and were added."
X sed "s/^/ /" /tmp/$$add
X echo ""
X echo "You can add them by executing the command(s):"
X for i in `cat /tmp/$$add.unmod`
X do
X echo "$NEWSBIN/ctl/newgroup $i </dev/null"
X done
X for i in `cat /tmp/$$add.mod`
X do
X echo "$NEWSBIN/ctl/newgroup $i moderated </dev/null"
X done
X echo ""
X
X# for i in `cat /tmp/$$add`
X# do
X# *** "Subject: cmsg " is a hideous botch of a kludge-hack; avoid it!
X# inews -h <<!
X#Control: newgroup $i
X#Newsgroups: control
X#Subject: newgroup $i
X#Distribution: general
X#
X#Create $i locally.
X#!
X# done
X
Xfi 2>&1 >>/tmp/$$out
X
Xif test -s /tmp/$$ismod;then
X echo "The following newsgroups are not moderated and are marked moderated."
X sed "s/^/ /" /tmp/$$ismod
X echo ""
X echo "You can correct this by executing the command(s):"
X for i in `cat /tmp/$$ismod`
X do
X echo "$NEWSBIN/ctl/newgroup $i </dev/null"
X done
X echo ""
Xfi 2>&1 >>/tmp/$$out
X
X
Xif test -s /tmp/$$notmod; then
X echo "The following newsgroups are moderated and not marked so."
X sed "s/^/ /" /tmp/$$notmod
X echo ""
X echo "You can correct this by executing the command(s):"
X for i in `cat /tmp/$$notmod`
X do
X echo "$NEWSBIN/ctl/newgroup $i moderated </dev/null"
X done
X echo ""
Xfi 2>&1 >>/tmp/$$out
X
X
Xif test -s /tmp/$$out; then
X (echo "Subject: Problems with your active file"; echo "";
X cat /tmp/$$out) | mail $NEWSMASTER
Xfi
X
Xrm -f /tmp/$$* # clean up temporaries
!
echo 'relay/altctl/README':
sed 's/^X//' >'relay/altctl/README' <<'!'
XThis is a rmgroup that will do automatic deletion, and a checkgroups
Xthat ties into same. WE DO NOT RECOMMEND INSTALLATION OF THESE.
!
echo 'relay/hdrcommon.c':
sed 's/^X//' >'relay/hdrcommon.c' <<'!'
X/*
X * Usenet header common code.
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include "libc.h"
X#include "news.h"
X#include "headers.h"
X#include "hdrint.h"
X
Xvoid
Xhdrdebug(state)
Xint state;
X{
X headdebug = state;
X}
X
Xvoid
Xhdrinit(hdrs) /* zero all elements of hdrs */
Xregister struct headers *hdrs;
X{
X hdrs->h_subj = NULL;
X hdrs->h_ngs = NULL;
X hdrs->h_distr = NULL;
X hdrs->h_ctlcmd = NULL;
X hdrs->h_approved = NULL;
X hdrs->h_msgid = NULL;
X hdrs->h_artid = NULL;
X hdrs->h_expiry = NULL;
X hdrs->h_path = NULL;
X hdrs->h_sender = NULL;
X}
X
Xboolean
Xoldctl(hdrs) /* true iff ngs are OLDCNTRL (cache in hdrs) */
Xregister struct headers *hdrs;
X{
X#ifdef SLOWCTLMATCH
X return ngmatch(OLDCNTRL, hdrs->h_ngs);
X#else
X register int ngslen = strlen(hdrs->h_ngs);
X
X if (ngslen < STRLEN(SFXOLDCNTRL)) /* ngs too short */
X return NO;
X else /* check for .ctl suffix */
X /*
X * This is more general than RFC 850 specifies, but this
X * generality seems harmless. This doesn't work for e.g.
X * x.y.ctl,z.q, which is a darn shame, but that's a violation
X * of common sense.
X */
X return STREQ(&hdrs->h_ngs[ngslen-STRLEN(SFXOLDCNTRL)],
X SFXOLDCNTRL);
X#endif /* SLOWCTLMATCH */
X}
X
Xvoid
Xfreeheaders(hdrs) /* free (assumed) malloced storage */
Xregister struct headers *hdrs;
X{
X nnfree(&hdrs->h_subj);
X nnfree(&hdrs->h_ngs);
X nnfree(&hdrs->h_distr);
X nnfree(&hdrs->h_ctlcmd);
X nnfree(&hdrs->h_approved);
X nnfree(&hdrs->h_msgid);
X nnfree(&hdrs->h_artid);
X nnfree(&hdrs->h_expiry);
X nnfree(&hdrs->h_path);
X nnfree(&hdrs->h_sender);
X}
!
echo 'relay/hdrdefs.c':
sed 's/^X//' >'relay/hdrdefs.c' <<'!'
X/*
X * Usenet header definitions (see ARPA Internet RFCs 1036 nee 850 & 822;
X * for a second opinion, see The Hideous Name by Pike & Weinberger).
X *
X * Headers are parsed and modified and copied in one pass.
X * Nevertheless, the code is in pieces: hdrdefs.c, hdrcommon.c,
X * hdrparse.c, hdrmunge.c.
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#ifdef REALSTDC
X#include <stdlib.h>
X#endif /* REALSTDC */
X#include "news.h"
X#include "headers.h"
X#include "hdrint.h" /* may define "const" */
X
X#ifndef offsetof
X#define offsetof(type, mem) ((char *)&((type *)NULL)->mem - (char *)NULL)
X#endif
X
X/* "mandatory" headers (also From:, Date:) */
Xstatic const char msgnm[] = "Message-ID:"; /* for rejection */
Xstatic const char ngsnm[] = "Newsgroups:"; /* filing, clone for Xref */
Xstatic const char pathnm[] = "Path:"; /* rejection, extend (damn) */
Xstatic const char subjnm[] = "Subject:"; /* for ctl. msgs. */
X
X/* optional headers */
Xstatic const char appnm[] = "Approved:"; /* for mod. groups */
Xstatic const char ctlnm[] = "Control:"; /* ctl. msg. */
Xstatic const char expnm[] = "Expires:"; /* for history */
Xstatic const char distrnm[] = "Distribution:"; /* for transmission */
Xstatic const char sendnm[] = "Sender:"; /* for mod. groups */
Xstatic const char xrefnm[] = "Xref:"; /* to *replace* (damn!)*/
X
X/* obsolete "useful" headers */
Xstatic const char artnm[] = "Article-I.D.:"; /* obs. Message-ID: */
X
X/* obsolete useless headers: delete them all on contact */
Xstatic const char datercvnm[] = "Date-Received:";
Xstatic const char rcvnm[] = "Received:"; /* obsolete Date-Received: */
Xstatic const char postnm[] = "Posted:"; /* obsolete Date: */
Xstatic const char postversnm[] = "Posting-Version:";
Xstatic const char rlyversnm[] = "Relay-Version:";
Xstatic const char illobjnm[] = "Illegal-Object:"; /* zmailer bitching */
X
Xstatic const struct hdrdef msghdr = {
X msgnm, STRLEN(msgnm), offsetof(struct headers, h_msgid) };
Xstatic const struct hdrdef ngshdr = {
X ngsnm, STRLEN(ngsnm), offsetof(struct headers, h_ngs) };
Xconst struct hdrdef pathhdr = {
X pathnm, STRLEN(pathnm), offsetof(struct headers, h_path) };
Xstatic const struct hdrdef subjhdr = {
X subjnm, STRLEN(subjnm), offsetof(struct headers, h_subj) };
X
Xstatic const struct hdrdef apphdr = {
X appnm, STRLEN(appnm), offsetof(struct headers, h_approved) };
Xstatic const struct hdrdef ctlhdr = {
X ctlnm, STRLEN(ctlnm), offsetof(struct headers, h_ctlcmd) };
Xstatic const struct hdrdef exphdr = {
X expnm, STRLEN(expnm), offsetof(struct headers, h_expiry) };
Xstatic const struct hdrdef distrhdr = {
X distrnm, STRLEN(distrnm), offsetof(struct headers, h_distr) };
Xstatic const struct hdrdef sendhdr = {
X sendnm, STRLEN(sendnm), offsetof(struct headers, h_sender) };
Xconst struct hdrdef xrefhdr = { xrefnm, STRLEN(xrefnm), -1 };
X
Xstatic const struct hdrdef arthdr = {
X artnm, STRLEN(artnm), offsetof(struct headers, h_artid) };
X
Xstatic const struct hdrdef datrcvhdr = { datercvnm, STRLEN(datercvnm), -1 };
Xstatic const struct hdrdef rcvhdr = { rcvnm, STRLEN(rcvnm), -1 };
Xstatic const struct hdrdef psthdr = { postnm, STRLEN(postnm), -1 };
Xstatic const struct hdrdef pstvrshdr = { postversnm, STRLEN(postversnm), -1 };
Xstatic const struct hdrdef rlyvrshdr = { rlyversnm, STRLEN(rlyversnm), -1 };
Xstatic const struct hdrdef illobjhdr = { illobjnm, STRLEN(illobjnm), -1 };
X
Xconst hdrlist parsehdrs = { /* these are parsed into a struct headers */
X &msghdr,
X &arthdr, /* obsolete */
X &ngshdr,
X &pathhdr, /* modified by hdrmunge.c (emithdr()) */
X &subjhdr,
X /* start optional headers */
X &apphdr,
X &ctlhdr,
X &distrhdr,
X &exphdr,
X &sendhdr,
X NULL
X};
X/*
X * the following noxious headers are deleted on contact because neighbours
X * still send them and they are big. in an ideal world, they wouldn't be
X * sent and thus we wouldn't need to delete them.
X * It is tempting to delete Article-I.D.: too, but it may be too soon for that.
X */
Xconst hdrlist hdrvilest = {
X &xrefhdr, /* regenerated by fileart() if needed */
X &datrcvhdr,
X &rcvhdr,
X &psthdr,
X &pstvrshdr,
X &rlyvrshdr,
X &illobjhdr,
X NULL,
X};
X
Xboolean headdebug = NO;
!
echo 'relay/hdrint.h':
sed 's/^X//' >'relay/hdrint.h' <<'!'
X/*
X * definitions internal to the header modules (hdr*.c)
X */
X#ifdef REALSTDC
X#undef REALSTDC
X#endif
X#ifdef __STDC__
X#if __STDC__ >= 1 /* microsoft, etc. brain-damage */
X#define REALSTDC /* truth in advertising: really ANSI */
X#endif /* __STDC__ < 1 */
X#endif /* __STDC__ */
X
X#ifndef REALSTDC
X#define const
X#endif /* REALSTDC */
X
X#ifndef DEFDIST
X#define DEFDIST "world" /* default Distribution: */
X#endif
X#ifndef DEFMSGID
X#define DEFMSGID "<message-id@absent>"
X#endif
X
X#define JUNK "junk"
X#define ALL "all"
X
X#ifdef SLOWCTLMATCH
X#define OLDCNTRL "all.all.ctl"
X#endif
X#define SFXOLDCNTRL ".ctl"
X
Xstruct hdrdef {
X const char *hdrnm; /* ascii name */
X unsigned hdrlen; /* STRLEN(hdrnm) */
X int hdroff; /* offset into struct header */
X};
Xtypedef const struct hdrdef *hdrlist[];
X
Xextern const struct hdrdef pathhdr, xrefhdr;
Xextern const hdrlist parsehdrs, hdrvilest;
X
Xextern boolean headdebug;
!
echo 'relay/hdrmunge.c':
sed 's/^X//' >'relay/hdrmunge.c' <<'!'
X/*
X * Usenet header modification & generation
X *
X * Ideally, headers should never be modified; message text, including
X * headers, should be passed untouched. Path: and Xref: demand that this
X * rule be violated, and we delete huge obsolete headers to save space.
X *
X * Delete obsolete & large headers and Xref (can't be right),
X * as headers are read.
X * Recognise Newsgroups: and if more than one group, generate Xref: &
X * leave holes for the article numbers - fileart will fill them in.
X * (Make rn look in history instead?)
X *
X * Pile up headers into a static buffer until end of buffer (i.e. next
X * line might not fit) [checked in hdrsave()], end of headers, end of
X * file, or byte count is exhausted [checked in cparttofp]. Then write
X * them to disk. Prepend hostname! to Path: value, during output.
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include "libc.h"
X#include "news.h"
X#include "fileart.h"
X#include "headers.h"
X#include "article.h"
X#include "hdrint.h"
X#include "msgs.h"
X
X/*
X * HDRMEMSIZ is the length of a header-stashing buffer, which is used
X * only during article-header copying.
X * HDRMEMSIZ can be too small if memory is tight & will only hurt performance.
X * Derivation: 630 bytes is a large header (after discarding *-Version:, etc.).
X */
X#ifndef HDRMEMSIZ
X#ifdef SMALLMEM
X#define HDRMEMSIZ 630
X#else
X#define HDRMEMSIZ 8192 /* # bytes for saving headers in core */
X#endif /* SMALLMEM */
X#endif /* HDRMEMSIZ */
X
X/* private */
Xstatic char **hptrs = NULL; /* saved-headers-ptrs array; allocated once */
X
X/* forwards */
XFORWARD void emithdr(), hdrsave();
X
X/*
X * Generate an Xref: header from art->a_files.
X * Turn slashes in art->a_files into colons in Xref:.
X */
Xvoid
Xemitxref(art)
Xregister struct article *art;
X{
X register char *slashp, *xrefs;
X
X if (!art->a_xref) {
X art->a_xref = YES;
X xrefs = strsave(art->a_files);
X for (slashp = xrefs; (slashp = index(slashp, FNDELIM)) != NULL; )
X *slashp++ = ':';
X if (fprintf(art->a_artf, "%s %s %s\n",
X xrefhdr.hdrnm, hostname(), xrefs) == EOF)
X fulldisk(art, spoolnm(art));
X free(xrefs);
X }
X}
X
X/*
X * --- header copying starts here ---
X */
X
X/*
X * Copy headers and delete or modify a few. Assumes hdrparse has been called.
X * Delete obsolete & large headers and Xref.
X * Pile up other headers for later output (Path: is changed during output).
X *
X * art->a_artf may be NULL, and may get set by hdrsave.
X */
XSTATIC void
Xhdrmunge(art, buffer, hdrlen, hdrlst)
Xregister struct article *art;
Xregister char *buffer;
Xint hdrlen; /* optimisation only */
Xhdrlist hdrlst; /* headers of negative utility: snuff 'em */
X{
X register struct hdrdef **vhp;
X
X if (headdebug)
X (void) fputs(buffer, stderr);
X for (vhp = hdrlst; *vhp != NULL; vhp++)
X if (STREQN(buffer, (*vhp)->hdrnm, (int)(*vhp)->hdrlen))
X return; /* don't save this header */
X hdrsave(art, buffer, hdrlen);
X}
X
X/*
X * If headers already dumped, just write to art->a_artf.
X * Else if there is room, stash "hdr" away until end of headers is seen
X * (could just wait until Newsgroups: and Control: are seen, if seen)
X * or there is no room left in the header buffer, then open the first
X * article link (on art->a_artf) and dump the saved headers and the current
X * header to it.
X *
X * Copy into art->a_haccum (in future, could read in directly,
X * iff copying is high on the profile).
X *
X * hdrstore is static because it is used repeatedly, it only makes sense
X * to have one active at a time, and there is no memory saving in allocating
X * and deallocating it, particularly since copyart's (header) buffer must
X * coexist with hdrstore.
X */
XSTATIC void
Xhdrsave(art, hdr, hdrlen)
Xregister struct article *art;
Xchar *hdr;
Xregister int hdrlen; /* optimisation only */
X{
X if (art->a_artf != NULL) {
X emithdr(art, hdr, hdrlen);
X return;
X }
X if (art->a_haccum == NULL) {
X static char hdrstore[HDRMEMSIZ];
X
X art->a_haccum = hdrstore;
X art->a_haccum[0] = '\0';
X art->a_hnext = art->a_haccum;
X art->a_hbytesleft = HDRMEMSIZ;
X }
X if (art->a_hbytesleft > hdrlen) {
X /* add new ptr.-to-this-header to tail of saved-hdr-ptr.-list */
X if (art->a_hptrs == NULL) {
X art->a_hpused = 0;
X art->a_hpalloced = MINSHPTRS;
X if (hptrs == NULL) /* once only */
X hptrs = (char **) nemalloc((unsigned)
X (art->a_hpalloced * sizeof(char *)));
X art->a_hptrs = hptrs;
X }
X while (art->a_hpused >= art->a_hpalloced) {
X art->a_hpalloced += MINSHPTRS;
X art->a_hptrs = hptrs = (char **)
X realloc((char *)art->a_hptrs, (unsigned)
X (art->a_hpalloced * sizeof(char *)));
X if (art->a_hptrs == NULL)
X errunlock("out of memory (for art->a_hptrs)", "");
X }
X art->a_hptrs[art->a_hpused++] = art->a_hnext;
X
X /* (void) strcat(art->a_haccum, hdr); */
X (void) strcpy(art->a_hnext, hdr);
X art->a_hnext += hdrlen; /* points at NUL byte */
X art->a_hbytesleft -= hdrlen;
X } else {
X hdrdump(art, NOTALLHDRS); /* don't file */
X if (art->a_artf != NULL)
X emithdr(art, hdr, hdrlen);
X }
X}
X
X/*
X * Change Path: while writing it out, just dump other headers (hdr) verbatim.
X */
XSTATIC void
Xemithdr(art, hdr, hdrlen)
Xregister struct article *art;
Xchar *hdr;
Xregister int hdrlen;
X{
X if (STREQN(hdr, pathhdr.hdrnm, (int)pathhdr.hdrlen)) {
X register char *oldpath, *hostnm = hostname();
X
X oldpath = skipsp(&hdr[pathhdr.hdrlen]);
X /*
X * V7 f?printf return 0 or EOF, not a byte count, so it is
X * not portable to use fprintf's return value as a byte count.
X */
X if (fprintf(art->a_artf, "%s %s!", pathhdr.hdrnm, hostnm) ==
X EOF || fputs(oldpath, art->a_artf) == EOF)
X fulldisk(art, spoolnm(art));
X else {
X static unsigned hostlen = 0;
X
X if (hostlen == 0)
X hostlen = strlen(hostnm);
X art->a_charswritten += pathhdr.hdrlen + STRLEN(" ") +
X hostlen + STRLEN("!") + strlen(oldpath);
X }
X } else {
X if (fwrite(hdr, hdrlen, 1, art->a_artf) != 1)
X fulldisk(art, spoolnm(art));
X else
X art->a_charswritten += hdrlen;
X }
X}
X
X/*
X * Write out saved headers after opening on art->a_artf either a temporary
X * file (using mktemp(3)) or the first article link, based on art->h.h_ngs &
X * nxtartnum(); set a_tmpf to which ever name is opened.
X * Modify Path: value on the way.
X *
X * If all headers were seen, then open the first link, link to the rest,
X * and generate Xref:, else open a temporary name and write the article
X * there (it will get filed later by hdrdump(...,ALLHDRS) or in insart()).
X */
Xvoid
Xhdrdump(art, allhdrsseen)
Xregister struct article *art;
Xboolean allhdrsseen; /* all headers seen & hdrdeflt() called? */
X{
X if (art->a_filed)
X return;
X if (allhdrsseen)
X fileart(art); /* set a_tmpf */
X else if (art->a_artf == NULL) {
X nnfree(&art->a_tmpf);
X art->a_tmpf = strsave(SPOOLTMP);
X (void) mktemp(art->a_tmpf);
X art->a_unlink = YES;
X art->a_artf = fopenwclex(art->a_tmpf, "w");
X if (art->a_artf == NULL)
X art->a_status |= ST_DROPPED;
X }
X if (art->a_artf != NULL &&
X art->a_haccum != NULL && art->a_haccum[0] != '\0') {
X register int i;
X register char **pp;
X register char *nxtln;
X register int saved;
X
X for (i = 0, pp = art->a_hptrs; i < art->a_hpused; ++i, ++pp) {
X if (i >= art->a_hpused-1) /* last in-core hdr? */
X nxtln = art->a_hnext;
X else
X nxtln = pp[1];
X saved = *nxtln;
X *nxtln = '\0';
X emithdr(art, *pp, nxtln - *pp);
X *nxtln = saved; /* restore */
X }
X /* art->a_haccum could be freed and zeroed here, if malloced */
X art->a_haccum[0] = '\0'; /* paranoia */
X art->a_hnext = art->a_haccum; /* for next article */
X art->a_hpused = 0; /* trash saved-header-ptr-list */
X /* reduce saved-header-ptr-list to original size */
X if (art->a_hpalloced > MINSHPTRS) {
X art->a_hpalloced = MINSHPTRS;
X art->a_hptrs = hptrs = (char **)
X realloc((char *)art->a_hptrs, (unsigned)
X (art->a_hpalloced * sizeof(char *)));
X if (hptrs == NULL)
X errunlock("can't free a_hptrs memory", "");
X }
X }
X}
X
X/*
X * hdrparse remembers this header if it's interesting.
X * hdrmunge needs art->h.h_ngs to be set, so it is called second.
X * hdrmunge saves or writes this header, unless it's deemed a waste of bytes.
X * hdrmunge may call hdrdump(art, NOTALLHDRS).
X * hdrdump counts art->a_charswritten.
X */
Xvoid
Xhdrdigest(art, line, hdrlen)
Xregister struct article *art;
Xregister char *line;
Xint hdrlen;
X{
X hdrparse(&art->h, line, parsehdrs);
X hdrmunge(art, line, hdrlen, hdrvilest);
X}
!
echo 'relay/hdrparse.c':
sed 's/^X//' >'relay/hdrparse.c' <<'!'
X/*
X * Usenet header parsing and remembering.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include "libc.h"
X#include "news.h"
X#include "headers.h"
X#include "hdrint.h"
X
X/*
X * Reset internal state of header parser.
X * (Empty the stomach of partially-digested headers.)
X */
Xvoid
Xhdrwretch()
X{
X /* historical stub */
X}
X
X/*
X * Parse RFC822/850/1036 header into "hdrs". Retain significant values.
X * Assumes ishdr has been called first.
X *
X * If a keyword matches one in hdrlst, store the value in *malloc'ed memory*
X * (N.B.). freeheader() will free this memory.
X */
Xvoid
Xhdrparse(hdrs, line, hdrlst)
Xregister struct headers *hdrs;
Xregister char *line;
Xhdrlist hdrlst; /* headers of positive utility */
X{
X register struct hdrdef **hpp;
X
X for (hpp = hdrlst; *hpp != NULL; hpp++)
X if (STREQN(line, (*hpp)->hdrnm, (int)(*hpp)->hdrlen) &&
X (*hpp)->hdroff >= 0) { /* paranoia */
X register char **ptrp =
X (char **)((char *)hdrs+(*hpp)->hdroff);
X
X nnfree(ptrp); /* free prev. val. in this art. */
X *ptrp = strsave(skipsp(&line[(*hpp)->hdrlen]));
X if (*ptrp != NULL)
X trim(*ptrp); /* cut trailing \n */
X break;
X }
X}
X
X/*
X * default missing header values
X *
X * If strsave ever returns NULL on failure, instead of exiting,
X * then the strsave calls need to check for failure.
X *
X * We support control message *backwards* compatibility: if no Control:
X * header exists and the newsgroup matches all.all.ctl, use the Subject:
X * as the control message. Ugh.
X */
Xvoid
Xhdrdeflt(hdrs)
Xregister struct headers *hdrs;
X{
X if (hdrs->h_ngs == NULL)
X hdrs->h_ngs = strsave(JUNK);
X if (hdrs->h_distr == NULL)
X hdrs->h_distr = strsave(DEFDIST);
X if (hdrs->h_msgid == NULL && hdrs->h_artid != NULL) /* obs. art.id. */
X hdrs->h_msgid = strsave(hdrs->h_artid);
X if (hdrs->h_msgid == NULL)
X hdrs->h_msgid = strsave(DEFMSGID);
X if (hdrs->h_msgid[0] == '\0') {
X free(hdrs->h_msgid);
X hdrs->h_msgid = strsave(DEFMSGID);
X }
X if (hdrs->h_expiry == NULL)
X hdrs->h_expiry = strsave(DEFEXP);
X if (hdrs->h_expiry[0] == '\0') {
X free(hdrs->h_expiry);
X hdrs->h_expiry = strsave(DEFEXP);
X }
X if (hdrs->h_subj == NULL)
X hdrs->h_subj = strsave("");
X
X if (hdrs->h_ctlcmd == NULL && oldctl(hdrs))
X hdrs->h_ctlcmd = strsave(hdrs->h_subj);
X}
!
echo 'relay/headers.h':
sed 's/^X//' >'relay/headers.h' <<'!'
X/*
X * All the article header values worth retaining.
X * (strictly from headers in input.)
X *
X * All members of struct headers must point at malloced memory so that
X * freeheaders() can free it without having to keep track of what's
X * malloced and what's static.
X *
X * Furthermore, each member of headers must point at its own private copy
X * of its value string, for the above reason, and no code outside hdr*.c
X * may copy any member nor a modified copy of any member, though it may
X * copy the string pointed to by a (possibly modified) member.
X *
X * Perhaps C++ will allow this to be enforced by a strings class.
X * See section 6.9 of The C++ Programming Language for a candidate.
X */
Xstruct headers {
X char *h_subj; /* subject: only needed for controls, -> h_ctlcmd */
X char *h_ngs; /* newsgroups: used in filing, sys matching & all.all.ctl matching */
X char *h_distr; /* distribution for transmit */
X char *h_ctlcmd; /* control command */
X char *h_approved; /* needed for acceptance in moderated groups */
X char *h_msgid; /* needed for history & rejection */
X char *h_artid; /* needed for history & rejection (obs.) */
X char *h_expiry; /* needed for history */
X char *h_path; /* needed for transmit - must munge */
X char *h_sender; /* needed for transmit in case of moderation */
X};
X
X/* common */
Xextern void hdrdebug(), hdrinit(), freeheaders();
Xextern boolean oldctl();
X
X/* munge */
Xextern void emitxref(), hdrdump(), hdrdigest();
X
X/* parse */
Xextern void hdrwretch(), hdrparse(), hdrdeflt();
Xextern boolean ishdr(), contin();
!
echo 'relay/history.c':
sed 's/^X//' >'relay/history.c' <<'!'
X/*
X * history file bashing
X *
X * B 2.10+ news pulls a dirty (and undocumented) trick and converts
X * message-id's to lower case before using them as keys in the dbm file.
X * We don't; you'll want to fix your news readers accordingly.
X *
X * B 2.10.3+ rnews puts out a leading space before received
X * time if the article contains an Expires: header; tough.
X * C news does this right instead of compatibly.
X *
X * The second history field is really two: time-received and Expires: value,
X * separated by a tilde. This is an attempt at partial compatibility with
X * B news, in that C expire can cope with B news history files.
X *
X * There is no point to storing seek offsets in network byte order in the
X * dbm file, since dbm files are machine-dependent and so can't be shared
X * by dissimilar machines anyway.
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include "libc.h"
X#include "news.h"
X#include "config.h"
X#include "fgetmfs.h"
X#include "headers.h"
X#include "article.h"
X#include "history.h"
X#include "msgs.h"
X
X#define HISTNAME "history" /* name of the history file in $NEWSCTL */
X#define FIELDSEP '\t'
X#define SUBFIELDSEP '~'
X
X/* give 0 & 2 pretty, SVIDish names */
X#ifndef SEEK_SET
X#define SEEK_SET 0
X#define SEEK_END 2
X#endif
X
Xtypedef struct {
X char *dptr;
X int dsize;
X} datum;
X
X/* private data */
Xstatic FILE *fp = NULL;
Xstatic char *filename; /* absolute name of the ascii history file */
Xstatic boolean writable;
X
X/* libdbm imports */
Xextern int dbminit(), store();
Xextern datum fetch();
X
X/* forward decls */
XFORWARD datum getposhist();
XFORWARD void mkhistent(), sanitise(), subsanitise();
X
XSTATIC void
Xhistname()
X{
X if (filename == NULL)
X filename = strsave(ctlfile(HISTNAME));
X}
X
X/*
X * open the history files: ascii first, then dbm.
X * Try a+ mode first, then r mode, as dbm(3) does nowadays,
X * so that this routine can be used by any user to read history files.
X */
XSTATIC boolean
Xopenhist()
X{
X histname();
X if (fp == NULL) {
X if ((fp = fopenclex(filename, "a+")) != NULL)
X writable = YES;
X else if ((fp = fopenwclex(filename, "r")) != NULL)
X writable = NO;
X /* else fp==NULL and fopenwclex just complained */
X
X if (fp != NULL && dbminit(filename) < 0) {
X /* no luck. dbminit will have just honked */
X (void) nfclose(fp); /* close ascii file */
X fp = NULL; /* and mark it */
X }
X }
X return fp != NULL;
X}
X
XSTATIC datum
Xgetposhist(msgid) /* return seek offset of history entry */
Xchar *msgid;
X{
X register char *clnmsgid;
X datum msgidkey, keypos;
X
X msgidkey.dptr = NULL;
X msgidkey.dsize = 0;
X if (!openhist())
X return msgidkey;
X clnmsgid = strsave(msgid);
X sanitise(clnmsgid);
X msgidkey.dptr = clnmsgid;
X msgidkey.dsize = strlen(clnmsgid) + 1; /* include NUL */
X keypos = fetch(msgidkey); /* offset into ascii file */
X free(clnmsgid);
X return keypos;
X}
X
Xboolean
Xalreadyseen(msgid) /* return true if found in the data base */
Xchar *msgid;
X{
X datum posdatum;
X
X posdatum = getposhist(msgid);
X return posdatum.dptr != NULL;
X}
X
Xchar * /* NULL if no history entry */
Xgethistory(msgid) /* return existing history entry, if any */
Xchar *msgid;
X{
X long pos = 0;
X datum posdatum;
X
X posdatum = getposhist(msgid);
X if (posdatum.dptr != NULL && posdatum.dsize == sizeof pos) {
X static char *histent = NULL;
X
X memcpy((char *)&pos, posdatum.dptr, sizeof pos); /* align */
X nnfree(&histent);
X if (fseek(fp, pos, SEEK_SET) != -1 &&
X (histent = fgetms(fp)) != NULL)
X return histent;
X }
X return NULL;
X}
X
X/*
X * Return a pointer to the "files" field of a history entry.
X * Side-effect: trims \n from the history entry.
X */
Xchar *
Xfindfiles(histent)
Xchar *histent;
X{
X register char *tabp;
X
X trim(histent);
X /* find start of 2nd field (arrival~expiry) */
X tabp = index(histent, FIELDSEP);
X if (tabp == NULL)
X return NULL; /* mangled entry */
X /* find start of 3rd field (files list) */
X else if ((tabp = index(tabp + 1, FIELDSEP)) == NULL)
X return NULL; /* cancelled or expired art. */
X else
X return tabp + 1;
X}
X
X/*
X * Generate a history entry from art.
X * The history entry will have tabs and newlines deleted from the
X * interior of fields, to keep the file format sane.
X * Optionally print the start of an "accepted" log file line (no \n)
X * (transmit() prints site names).
X */
Xvoid
Xhistory(art, startlog)
Xregister struct article *art;
Xboolean startlog;
X{
X register char *msgid, *expiry;
X time_t now;
X
X msgid = strsave(nullify(art->h.h_msgid));
X sanitise(msgid); /* RFC 1036 forbids whitespace in msg-ids */
X expiry = strsave(nullify(art->h.h_expiry));
X sanitise(expiry);
X subsanitise(expiry);
X
X if (startlog) {
X timestamp(stdout, &now);
X if (printf(" %s + %s", sendersite(nullify(art->h.h_path)),
X msgid) == EOF)
X fulldisk(art, "stdout");
X } else
X now = time(&now);
X if (!openhist())
X art->a_status |= ST_DROPPED; /* fall through and return */
X else if (!writable) {
X (void) fprintf(stderr, "%s: no write permission on `%s'\n",
X progname, filename);
X art->a_status |= ST_DROPPED;
X } else if (fseek(fp, 0L, SEEK_END) == -1) {
X warning("can't seek to end of `%s'", filename);
X art->a_status |= ST_DROPPED;
X } else
X mkhistent(art, msgid, now, expiry);
X free(msgid);
X free(expiry);
X}
X
X/*
X * Internal interface to generate a history file entry,
X * assuming all sanity checking has been done already.
X * Record the (msgid, position) pair in the data base.
X *
X * The fflush is crash-proofing.
X */
XSTATIC void
Xmkhistent(art, msgid, now, expiry)
Xregister struct article *art;
Xchar *msgid, *expiry;
Xtime_t now;
X{
X long pos;
X datum msgidkey, posdatum;
X
X pos = ftell(fp); /* get seek ptr for dbm */
X if (fprintf(fp, "%s%c%ld%c%s", msgid, FIELDSEP, now, SUBFIELDSEP, expiry)
X == EOF)
X fulldisk(art, filename);
X /* don't write 3rd field for cancelled but unseen articles */
X if (art->a_files != NULL && art->a_files[0] != '\0')
X if (fprintf(fp, "%c%s", FIELDSEP, art->a_files) == EOF)
X fulldisk(art, filename);
X (void) putc('\n', fp);
X if (fflush(fp) == EOF)
X fulldisk(art, filename);
X
X msgidkey.dptr = msgid;
X msgidkey.dsize = strlen(msgid) + 1; /* include NUL */
X posdatum.dptr = (char *)&pos;
X posdatum.dsize = sizeof pos;
X#ifdef NOSTOREVAL
X /* original v7 dbm store() returned no value */
X (void) store(msgidkey, posdatum);
X#else
X if (store(msgidkey, posdatum) < 0)
X fulldisk(art, filename);
X#endif
X}
X
X/*
X * Turn \n & FIELDSEP into ' ' in s.
X */
XSTATIC void
Xsanitise(s)
Xregister char *s;
X{
X for (; *s != '\0'; ++s)
X if (*s == FIELDSEP || *s == '\n')
X *s = ' ';
X}
X
X/*
X * Turn SUBFIELDSEP into ' ' in s.
X */
XSTATIC void
Xsubsanitise(s)
Xregister char *s;
X{
X for (; *s != '\0'; ++s)
X if (*s == SUBFIELDSEP)
X *s = ' ';
X}
X
X/*
X * Generate a fake history file entry, given a message-id, an Expires:
X * value, and a "file" list ("net.foo/123").
X */
Xstatust
Xfakehist(fkmsgid, fkexpiry, fkfiles)
Xchar *fkmsgid, *fkexpiry, *fkfiles;
X{
X struct article art;
X
X artinit(&art);
X art.h.h_msgid = fkmsgid;
X art.h.h_expiry = fkexpiry;
X art.a_files = fkfiles;
X history(&art, STARTLOG);
X return art.a_status;
X}
X
X/*
X * Append "group/artnumstr" to the file list in *art.
X */
Xvoid
Xhistupdfiles(art, group, artnumstr)
Xregister struct article *art;
Xregister char *group;
Xregister char *artnumstr;
X{
X unsigned addlen = strlen(group)+STRLEN(SFNDELIM)+strlen(artnumstr)+1;
X
X art->a_filed = YES; /* make a note */
X if (art->a_files == NULL) {
X art->a_files = nemalloc(addlen);
X art->a_files[0] = '\0';
X } else {
X art->a_files = realloc(art->a_files, (unsigned)
X strlen(art->a_files) + STRLEN(" ") + addlen);
X if (art->a_files == NULL)
X errunlock("can't grow a_files", "");
X (void) strcat(art->a_files, " ");
X }
X (void) strcat(art->a_files, group); /* normal case */
X (void) strcat(art->a_files, SFNDELIM);
X (void) strcat(art->a_files, artnumstr);
X}
!
echo 'relay/history.h':
sed 's/^X//' >'relay/history.h' <<'!'
X/* imports from history.c */
Xextern char *findfiles(), *gethistory();
Xextern boolean alreadyseen();
Xextern statust fakehist();
Xextern void history(), histupdfiles();
X
X#define STARTLOG YES
X#define NOLOG NO
!
echo 'relay/ihave.c':
sed 's/^X//' >'relay/ihave.c' <<'!'
X/*
X * Implement the Usenet ihave/sendme control messages,
X * as per RFC 1036 (nee 850).
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X
X#include "libc.h"
X#include "news.h"
X#include "config.h"
X#include "headers.h"
X#include "article.h"
X#include "history.h"
X#include "fgetmfs.h"
X#include "msgs.h"
X#include "transmit.h"
X
X#ifndef SENDMEDISTR
X#define SENDMEDISTR "sendme" /* kludge: distinguished distribution for sendmes */
X#endif
X#ifndef IHAVEDISTR
X#define IHAVEDISTR "ihave" /* kludge: distinguished distribution for ihaves */
X#endif
X#ifndef AVEARTSIZE
X#define AVEARTSIZE 3000
X#endif
X
X#define PROTO_IHAVE 0
X#define PROTO_SENDME 1
X
X/* static forwards */
XFORWARD void doproto(), procmsgids(), procbodymsgids();
XFORWARD statust faketrans();
X
X/*
X * Read message-IDs from args or control message body,
X * look them up in history, post a sendme to to.remotesys (via the batcher
X * to avoid deadlock) consisting of the message-IDs not in history.
X * The "posting" consists of transmitting to a system matching
X * "to.remotesys" in sys, which had better have the I flag on.
X * ihave message-ID-list remotesys generate a sendme from message-ID-list
X */
Xvoid
Xihave(args, art)
Xchar *args;
Xstruct article *art;
X{
X doproto(args, art, IHAVEDISTR, PROTO_IHAVE);
X}
X
X/*
X * Read message-IDs from args or control message body,
X * transmit the corresponding articles to a system matching
X * "to.remotesys/sendme" in sys, which will typically name a batch file.
X * sendme message-ID-list remotesys send articles named to remotesys
X */
Xvoid
Xsendme(args, art)
Xchar *args;
Xstruct article *art;
X{
X doproto(args, art, SENDMEDISTR, PROTO_SENDME);
X}
X
Xstatic void
Xdoproto(args, art, distr, proto)
Xchar *args;
Xregister struct article *art;
Xchar *distr;
Xint proto;
X{
X register char *argscp = skipsp(args), *remotesys;
X
X if (*argscp == '\n' || *argscp == '\0') /* no args */
X return;
X
X argscp = strsave(argscp);
X
X /* dig out the remote system name */
X remotesys = rindex(argscp, ' ');
X if (remotesys == NULL) /* no msg-ids in command */
X remotesys = argscp;
X else {
X remotesys = argscp + strlen(argscp) - 1; /* last byte */
X while (isascii(*remotesys) && isspace(*remotesys))
X *remotesys-- = '\0'; /* back up to non-whitespace */
X remotesys = rindex(argscp, ' ');
X if (remotesys == NULL) /* no msg-ids in command */
X remotesys = argscp;
X else
X *remotesys++ = '\0'; /* separate msg-ids & sys name */
X }
X if (strcmp(remotesys, hostname()) != 0) /* remotesys may not be me */
X if (remotesys != argscp) /* msg-ids in command */
X procmsgids(art, argscp, remotesys, distr, proto);
X else
X procbodymsgids(art, remotesys, distr, proto);
X free(argscp);
X}
X
X/*
X * Process a list of message-ids in msgidln: look them up
X * and "transmit" the articles to to.remotesys.
X */
Xstatic void
Xprocmsgids(art, msgidln, remotesys, distr, proto)
Xstruct article *art;
Xchar *msgidln, *remotesys, *distr;
Xint proto;
X{
X char *cpmsgid = strsave(skipsp(msgidln));
X register char *msgid = cpmsgid, *endmsgid;
X register int save, sendit;
X
X for (; *msgid != '\n' && *msgid != '\0'; msgid = skipsp(endmsgid)) {
X for (endmsgid = msgid; *endmsgid != '\0' &&
X isascii(*endmsgid) && !isspace(*endmsgid); ++endmsgid)
X ; /* skip msgid */
X
X save = *endmsgid;
X *endmsgid = '\0'; /* terminate msgid at whitespace */
X if (proto == PROTO_IHAVE)
X sendit = !alreadyseen(msgid); /* sendme from remotesys */
X else
X sendit = alreadyseen(msgid); /* sendme to remotesys */
X if (sendit)
X art->a_status |= faketrans(msgid, remotesys, distr, proto);
X *endmsgid = save;
X }
X free(cpmsgid);
X}
X
Xstatic void
Xprocbodymsgids(art, remotesys, distr, proto)
Xregister struct article *art;
Xchar *remotesys, *distr;
Xint proto;
X{
X register FILE *arttext;
X
X arttext = fopenwclex(art->a_tmpf, "r");
X if (arttext != NULL) {
X char *line;
X
X while ((line = fgetms(arttext)) != NULL && *line != '\n')
X nnfree(&line); /* skip header */
X if (line != NULL) { /* article body exists */
X nnfree(&line); /* toss blank separating line */
X while ((line = fgetms(arttext)) != NULL) {
X procmsgids(art, line, remotesys, distr, proto);
X nnfree(&line);
X }
X }
X (void) nfclose(arttext);
X }
X}
X
X/*
X * Fake up a minimal article struct for msgid using group to.remotesys and
X * distribution distr, then invoke transmit(). If there is a history
X * entry for msgid, supply the list of file names too.
X * Generate a log entry for each message-id transmitted.
X */
Xstatic statust
Xfaketrans(msgid, remotesys, distr, proto)
Xregister char *msgid, *remotesys;
Xchar *distr;
Xint proto;
X{
X struct article fakeart;
X register char *ng;
X register char *histent;
X register struct article *fap = &fakeart;
X time_t now;
X statust status = ST_OKAY;
X
X ng = nemalloc((unsigned)(STRLEN("to.") + strlen(remotesys) + 1));
X (void) strcpy(ng, "to.");
X (void) strcat(ng, remotesys);
X
X artinit(fap);
X
X fap->h.h_ngs = ng;
X fap->h.h_distr = (distr != NULL? distr: ng);
X fap->h.h_msgid = msgid;
X fap->h.h_path = hostname();
X fap->a_charswritten = AVEARTSIZE;
X histent = gethistory(msgid);
X if (histent == NULL || (fap->a_files = findfiles(histent)) == NULL)
X fap->a_files = "no.such.article!";
X
X timestamp(stdout, &now); /* start log line */
X if (printf(" %s %c %s", sendersite(nullify(fap->h.h_path)),
X (proto == PROTO_IHAVE? 'i': 's'), fap->h.h_msgid) == EOF)
X fulldisk(fap, "stdout");
X transmit(fap, ""); /* write on batch file; write sys name on stdout */
X (void) putchar('\n'); /* end log line */
X status |= fap->a_status; /* pass back failure writing batch file */
X
X fap->h.h_ngs = NULL;
X fap->h.h_distr = NULL;
X fap->h.h_msgid = NULL;
X fap->h.h_path = NULL;
X fap->a_files = NULL;
X artfree(fap);
X
X free(ng);
X return status;
X}
!
echo 'relay/io.c':
sed 's/^X//' >'relay/io.c' <<'!'
X/*
X * common i/o operations
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include "news.h"
X#include "headers.h"
X#include "article.h"
X#include "msgs.h"
X
X/*
X * If *fpp is non-null, fclose it and check for errors.
X * On error, call fulldisk(art, name).
X */
Xvoid
Xnnfclose(art, fpp, name)
Xstruct article *art;
Xregister FILE **fpp;
Xchar *name;
X{
X if (*fpp != NULL) {
X if (nfclose(*fpp) == EOF)
X fulldisk(art, name);
X *fpp = NULL; /* mark the stream closed */
X }
X}
!
echo 'relay/io.h':
sed 's/^X//' >'relay/io.h' <<'!'
X/* imports from io.c */
Xextern void nnfclose();
!
echo done