home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume19
/
cnews2
/
part16
< prev
next >
Wrap
Text File
|
1989-06-29
|
51KB
|
1,742 lines
Subject: v19i093: Cnews production release, Part16/19
Newsgroups: comp.sources.unix
Sender: sources
Approved: rsalz@uunet.UU.NET
Submitted-by: utzoo!henry
Posting-number: Volume 19, Issue 93
Archive-name: cnews2/part16
: ---CUT HERE---
echo 'relay/makefile':
sed 's/^X//' >'relay/makefile' <<'!'
X# makefile for C news relaynews
X
X# =()<NEWSARTS = @<NEWSARTS>@>()=
XNEWSARTS = /usr/spool/news
X# =()<NEWSBIN = @<NEWSBIN>@>()=
XNEWSBIN = /usr/lib/newsbin
X# =()<NEWSCTL = @<NEWSCTL>@>()=
XNEWSCTL = /usr/lib/news
X# workaround for System V make bug
XSHELL = /bin/sh
X
XBIN=/bin
XNPROC=2
X# -DVOID=int for libc.h & old lint libraries
XDEFINES= -I../include -I. -DVOID=int -DFLUSHEVERY=6
X#CC=CC +V
X#CC=gcc -ansi -pedantic -Wall -S
X#CC=redcc
XCOPTS= -O # -pg -g
XCFLAGS=$(DEFINES) $(COPTS)
XDBM = -ldbm
XLIBS= $(DBM)
XLINT=lint
XLINTFLAGS=-haz $(DEFINES)
XLLIBS=-llocal
X# I wish I could make lint shut the fk up about some things. Grrr!
XLINTFILT=egrep -v '(possible pointer|long assign|can.t take|never used|nnfree|getdate|:$$)'
XPROPTS=
XP=stpr
XPP=pp -Tpsc -fR # lazywriter
XPPBACK=dps | stps # lazywriter
X
XLIBOBJS=../libcnews.a
XSRC=relaynews.c active.c article.c caches.c mkdirs.c control.c fileart.c \
X hdrdefs.c hdrcommon.c hdrparse.c hdrmunge.c \
X history.c io.c msgs.c procart.c \
X sys.c transmit.c trbatch.c ihave.c $(LIBSRCS)
XOBJ=relaynews.o active.o article.o caches.o mkdirs.o control.o fileart.o \
X hdrdefs.o hdrcommon.o hdrparse.o hdrmunge.o \
X history.o io.o msgs.o procart.o \
X sys.o transmit.o trbatch.o ihave.o $(LIBOBJS)
XFILES=$(NONCFILES) $(CFILES)
XNONCFILES= TODO* README ads/README ads/[0-9]* \
X sh/inews sh/tear sh/anne.jones sh/defhdrs.awk \
X sh/realrnews sh/serverrnews makefile
XCFILES= ../include/*.h \
X active.h article.h caches.h mkdirs.h control.h cpu.h fileart.h \
X hdrint.h headers.h history.h system.h transmit.h trbatch.h $(SRC)
X
Xall: makefile relaynews
X
Xmkfile: makefile
X sed '/mkfile/d' makefile | mkconv | sed 's/make/mk/g' >$@
X
Xrelaynews: $(OBJ)
X $(CC) $(CFLAGS) $(OBJ) $(LIBS) $(LIBOBJS) -o $@
Xlint: $(SRC)
X $(LINT) $(LINTFLAGS) $(SRC) $(LLIBS) | $(LINTFILT)
Xlint-p: $(SRC)
X $(LINT) $(LINTFLAGS) -p $(SRC) $(LLIBS) | $(LINTFILT)
X
Xnewsinstall:
X : nothing
X
X# bininstall: make directories, install programs
Xbininstall: install
Xinstall: $(NEWSBIN)/relay/relaynews
X$(NEWSBIN)/relay/relaynews: relaynews
X -mkdir $(NEWSBIN)/relay $(NEWSBIN)/inject $(NEWSBIN)/ctl
X rm -f $(NEWSBIN)/relay/relaynews
X cp relaynews $(NEWSBIN)/relay
X : needs to be news-owned, setuid -- build looks after that
X chmod +x sh/* aux/* ctl/*
X cp sh/* $(NEWSBIN)/inject
X cp ctl/* $(NEWSBIN)/ctl
X cp aux/* $(NEWSBIN)/relay
X cp sh/postnews sh/inews $(BIN)
X
XTODO.grep: TODO
X -egrep TODO ../include/*.h *.h *.c sh/* | tr -s " \11" " " >$@
X -egrep TODO ../lib*/*.[ch] | tr -s " \11" " " >>$@
X
Xv7 v8 v9 usg bsd42:
X test -d libos && exit 1
X mv lib$@ libos # or ln -s lib$@ libos
X make
X
Xprint: printc printnonc
X touch $@
Xprintc: $(CFILES)
X $(PP) $? | $(PPBACK)
X touch $@
Xprintnonc: $(NONCFILES)
X pr $(PROPTS) $? | $P
X touch $@
Xdistr: $(FILES)
X (echo relaynews update of `date`; echo ""; bundle $?) | /bin/mail cnews-updates
X touch $@
Xclean:
X rm -f core a.out relaynews *.o
X rm -rf regress/tmp
X
Xr: relaynews
X chmod +x regress/regress
X cd regress; ./regress
X
X# header dependencies follow
Xactive.o: ../include/libc.h ../include/news.h ../include/config.h
Xactive.o: active.h
Xarticle.o: ../include/news.h article.h headers.h
Xcaches.o: ../include/news.h active.h caches.h transmit.h
Xmkdirs.o: ../include/libc.h ../include/news.h
Xcontrol.o: ../include/libc.h ../include/news.h ../include/config.h
Xcontrol.o: headers.h article.h caches.h history.h
Xfileart.o: ../include/libc.h ../include/news.h ../include/config.h
Xfileart.o: active.h mkdirs.h headers.h article.h history.h system.h
Xhdrcommon.o: ../include/news.h headers.h hdrint.h
Xhdrdefs.o: ../include/news.h headers.h hdrint.h
Xhdrmunge.o: ../include/libc.h ../include/news.h fileart.h headers.h
Xhdrmunge.o: article.h hdrint.h
Xhdrparse.o: ../include/libc.h ../include/news.h headers.h hdrint.h
Xhistory.o: ../include/libc.h ../include/news.h ../include/config.h
Xhistory.o: ../include/fgetmfs.h headers.h article.h history.h
Xhostname.o: ../include/news.h ../include/config.h
Xihave.o: ../include/libc.h ../include/news.h ../include/config.h
Xihave.o: headers.h article.h caches.h history.h
Xio.o: ../include/news.h headers.h article.h
Xmsgs.o: ../include/news.h headers.h article.h
Xprocart.o: ../include/libc.h ../include/news.h active.h control.h
Xprocart.o: headers.h article.h history.h system.h
Xrelaynews.o: ../include/libc.h ../include/news.h ../include/config.h
Xrelaynews.o: ../include/fgetmfs.h active.h caches.h cpu.h headers.h
Xrelaynews.o: history.h
Xstring.o: ../include/libc.h ../include/news.h
Xsys.o: ../include/libc.h ../include/fgetmfs.h ../include/news.h
Xsys.o: ../include/config.h system.h
Xtransmit.o: ../include/libc.h ../include/news.h ../include/config.h
Xtransmit.o: headers.h active.h article.h system.h trbatch.h transmit.h
Xtrbatch.o: ../include/libc.h ../include/news.h trbatch.h
!
echo 'relay/mkdirs.c':
sed 's/^X//' >'relay/mkdirs.c' <<'!'
X/*
X * mkdirs - make the directories implied by `name'
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include "libc.h"
X#include "news.h"
X
X/*
X * Given a/b/c/d, try to make any of a, a/b, a/b/c and a/b/c/d which are missing;
X * stop on first failure.
X * Returns success.
X */
Xboolean
Xmkdirs(name, uid, gid)
Xregister char *name;
Xint uid, gid;
X{
X register char *cp;
X register int isthere = YES;
X struct stat stbuf;
X
X for (cp = name; isthere && *cp != '\0'; cp++)
X if (*cp == FNDELIM) {
X *cp = '\0';
X isthere = stat(name, &stbuf) >= 0;
X if (!isthere) {
X isthere = mkdir(name, 0777) >= 0;
X (void) chown(name, uid, gid);
X }
X *cp = FNDELIM;
X }
X return isthere;
X}
!
echo 'relay/mkdirs.h':
sed 's/^X//' >'relay/mkdirs.h' <<'!'
X/* imports from mkdirs.c */
Xextern boolean mkdirs();
!
echo 'relay/mklint':
sed 's/^X//' >'relay/mklint' <<'!'
X#! /bin/sh
Xlint -hazu -I../include -I../include/bsd42 -I../rnews -DSTATIC= $* -llocal |
X egrep -v '(possible pointer|long assign|can.t take|never used|:$)'
!
echo 'relay/msgs.c':
sed 's/^X//' >'relay/msgs.c' <<'!'
X/*
X * print common messages
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
Xvoid
Xfulldisk(art, file) /* complain once & set ST_DISKFULL */
Xregister struct article *art;
Xchar *file;
X{
X if (!(art->a_status&ST_DISKFULL))
X art->a_status |= prfulldisk(file);
X}
X
Xstatust
Xprfulldisk(file) /* complain once & return bad status */
Xchar *file;
X{
X warning("error writing `%s', probably the disk filled", file);
X return ST_DISKFULL|ST_DROPPED;
X}
!
echo 'relay/msgs.h':
sed 's/^X//' >'relay/msgs.h' <<'!'
X/* imports from msgs.c */
Xextern statust prfulldisk();
Xextern void fulldisk();
!
echo 'relay/procart.c':
sed 's/^X//' >'relay/procart.c' <<'!'
X/*
X * process a single incoming article
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include "libc.h"
X#include "news.h"
X#include "active.h"
X#include "control.h"
X#include "headers.h"
X#include "article.h"
X#include "history.h"
X#include "io.h"
X#include "msgs.h"
X#include "system.h"
X#include "transmit.h"
X
X/*
X * COPYSIZE is the length of a bulk-copying buffer: the bigger the better,
X * though fewer than 3% of articles exceed 8192 bytes (may 1988).
X * It holds header lines first, and later holds bytes of the body.
X * This buffer is allocated once at the start and never deallocated.
X */
X#ifndef COPYSIZE
X#ifdef SMALLMEM
X#define COPYSIZE BUFSIZ /* conserve memory at the expense of speed */
X#else
X#define COPYSIZE 8192 /* big enough even for worst-case 4.2bsd blocks */
X#endif /* SMALLMEM */
X#endif /* COPYSIZE */
X
Xextern char *exclude; /* for erik */
Xextern boolean okrefusal; /* flag from command line */
X
X/* forwards */
Xextern void tossorfile(), surveydamage(), reject(), prefuse(), uninsart();
Xextern char *hdrcopy();
XFORWARD void copyart(), cpybody(), insart();
XFORWARD statust snuffmayreturn();
X
X/*
X * Copy the article on "in" to a temporary name in the news spool directory,
X * unlink temp name; *or* copy into the final names, if known early enough.
X * (Sets a_tmpf in or near hdrmunge() or hdrdump().)
X * If the spool file opened, install the article it contains.
X */
Xstatust
Xcpinsart(in, inname, maxima, blvmax)
XFILE *in;
Xregister char *inname;
Xlong maxima;
Xboolean blvmax; /* believe maxima? */
X{
X register struct article *artp;
X register statust status;
X struct article art;
X
X artp = &art;
X artinit(artp);
X artp->a_blvmax = blvmax;
X artp->a_unread = maxima;
X
X /*
X * copyart() may reject() the article, and may fill the disk.
X * it calls fileart and logs rejected articles.
X */
X copyart(artp, in, inname);
X
X if (artp->a_status&ST_REFUSED) {
X /* no good ngs (in fileart) or reject()ed; not serious */
X artp->a_status &= ~ST_REFUSED;
X /* paranoia; shouldn't happen */
X nnfclose(artp, &artp->a_artf, inname);
X } else if (artp->a_artf == NULL) {
X warning("can't open spool file `%s'", artp->a_tmpf);
X artp->a_status |= ST_DROPPED;
X } else {
X nnfclose(artp, &artp->a_artf, inname);
X insart(artp); /* logs accepted art.s during transmission */
X if (artp->a_status&ST_JUNKED) { /* yer welcome, henry */
X artp->a_status &= ~ST_JUNKED;
X timestamp(stdout, (time_t *)NULL);
X (void) printf(" %s j %s junked due to groups `%s'\n",
X sendersite(nullify(artp->h.h_path)),
X artp->h.h_msgid, artp->h.h_ngs);
X }
X }
X status = artp->a_status;
X artfree(artp);
X return status;
X}
X
X/*
X * Copy the next charcnt bytes of "in" (may be not a disk file)
X * to a permanent file under a (possibly) temporary name.
X * After the headers are seen, accept or reject the article.
X * If rejected and the headers fit in core, no files will be opened.
X * Must munge certain headers on the way & remember certain values.
X * hdrmunge() or hdrdump() sets art->a_tmpf & art->a_artf.
X * Unlink art->a_tmpf, if a temporary link.
X */
X/* ARGSUSED inname */
XSTATIC void
Xcopyart(art, in, inname)
Xregister struct article *art;
Xregister FILE *in;
Xchar *inname;
X{
X boolean installed = YES;
X char *body;
X
X body = hdrcopy(art, in);
X hdrdeflt(&art->h);
X tossorfile(art, &installed);
X /* assertion: header values (art->h) can be forgotten here */
X cpybody(art, in, body);
X surveydamage(art, &installed);
X}
X
X/*
X * The loop copies header lines from input to output or a
X * header output cache. On exit, hdr will contain the first
X * non-header line, if any, left over from the end of header copying.
X *
X * Some people think the loop is ugly; I'm not sure why.
X * If the byte count is positive, read a line; if it doesn't return
X * EOF and is a header, then adjust byte count, stash and munge headers.
X * strlen(line) must be computed before hdrstash is called,
X * as hdrstash (and thus hdrdigest) removes newlines.
X */
Xchar * /* first body line, from gethdr */
Xhdrcopy(art, in)
Xregister struct article *art;
XFILE *in;
X{
X register char *hdr = NULL;
X long limit;
X int is_hdr = NO;
X
X hdrwretch(); /* reset the header parser */
X limit = (art->a_blvmax? art->a_unread+1: art->a_unread); /* 1 for NUL */
X /* 1 is again for NUL */
X while (limit > 1 && (hdr = gethdr(in, &limit, &is_hdr)) != NULL && is_hdr) {
X hdrdigest(art, hdr, strlen(hdr));
X hdr = NULL; /* freed inside gethdr */
X }
X /* If we read a body line, gethdr has adjusted limit appropriately. */
X art->a_unread = limit - 1; /* limit updated by gethdr */
X if (is_hdr) /* no body: header fills limit */
X hdr = NULL;
X return hdr;
X}
X
X/*
X * Either reject the article described by art, or accept it and file it.
X * If rejecting it, remove any links and give back assigned #'s
X * (art->a_artf may still be open; arguably uninsart should close it).
X * If accepting it, dump any saved headers and file the article.
X * Unlink art->a_tmpf if it's a temporary link.
X */
Xvoid
Xtossorfile(art, installedp)
Xregister struct article *art;
Xboolean *installedp;
X{
X reject(art); /* duplicate, etc.? */
X if (art->a_status&(ST_DROPPED|ST_REFUSED)) {
X uninsart(art);
X *installedp = NO;
X } else
X hdrdump(art, ALLHDRS); /* ALLHDRS triggers fileart */
X
X if (art->a_unlink) {
X /* a_tmpf has had links made to it, so it can be removed. */
X if (unlink(art->a_tmpf) < 0) {
X warning("copyart can't unlink `%s'", art->a_tmpf);
X art->a_status |= ST_ACCESS;
X }
X art->a_unlink = NO; /* caution */
X }
X}
X
X/*
X * Copy article body.
X * body will contain the first non-header line, if any,
X * left over from the end of header copying. Write it.
X * Copy at most COPYSIZE bytes of body at a time and exactly art->a_unread
X * bytes in total, barring EOF or a full disk. Then "block" is no longer needed.
X * Force the article to disk, mostly for the benefit of control message
X * processing.
X *
X * The copying buffer, block, is static because it is used repeatedly
X * and persists through most of execution, so dynamic allocation
X * and deallocation seems wasteful, but also for the benefit
X * of compilers for odd machines (e.g. PE, 370s) which make
X * implementing "large" automatic arrays difficult.
X */
XSTATIC void
Xcpybody(art, in, body)
Xregister struct article *art;
XFILE *in;
Xregister char *body;
X{
X register int readcnt;
X static char block[COPYSIZE];
X
X if (body != NULL) { /* read too far? */
X register int bodylen = strlen(body);
X
X if (art->a_artf != NULL &&
X fwrite(body, 1, bodylen, art->a_artf) != bodylen)
X fulldisk(art, spoolnm(art));
X art->a_charswritten += bodylen;
X }
X for (; art->a_unread > 0 && !(art->a_status&ST_DISKFULL) &&
X (readcnt=fread(block, 1, (int)min(art->a_unread, COPYSIZE), in)) > 0;
X art->a_unread -= readcnt, art->a_charswritten += readcnt)
X if (art->a_artf != NULL &&
X fwrite(block, 1, readcnt, art->a_artf) != readcnt)
X fulldisk(art, spoolnm(art));
X if (art->a_artf != NULL && fflush(art->a_artf) == EOF)
X fulldisk(art, spoolnm(art));
X}
X
X/*
X * If not yet uninstalled, and the disk filled, uninstall this article
X * to remove any zero-length links and decrement the active article number.
X * The ST_DISKFULL status will prevent a history entry from being generated.
X */
Xvoid
Xsurveydamage(art, installedp)
Xregister struct article *art;
Xregister boolean *installedp;
X{
X if (art->a_unread > 0 && art->a_blvmax) {
X (void) fprintf(stderr, "%s: article %s short by %ld bytes\n",
X progname, (art->h.h_msgid != NULL? art->h.h_msgid: ""),
X (long)art->a_unread);
X art->a_status |= ST_SHORT; /* NB.: don't uninstall this art. */
X }
X if (*installedp && art->a_status&ST_DISKFULL) {
X uninsart(art);
X *installedp = NO;
X }
X#ifdef WATCHCORE
X {
X char stbot;
X extern char *sbrk();
X
X printf("debugging memory use: top of data=%u", (unsigned)sbrk(0));
X printf(", bottom of stack=%u\n", (unsigned)&stbot);
X }
X#endif
X}
X
X/*
X * Install the article on art->a_tmpf or art->a_files:
X * The article should have been accepted and filed in copyart().
X * Add history entries for the article. Log arrival.
X * Transmit the article to our neighbours.
X * Process control mess(age)es. ctlmsg can call transmit(fakeart,x)
X * and generate log lines for cancels and ihave/sendme.
X */
XSTATIC void
Xinsart(art)
Xregister struct article *art;
X{
X if (!(art->a_status&(ST_DROPPED|ST_REFUSED|ST_DISKFULL))) {
X if (!art->a_filed) /* paranoia */
X (void) fprintf(stderr, "%s: %s not filed by copyart!\n",
X progname, art->h.h_msgid);
X history(art, STARTLOG);
X transmit(art, exclude); /* writes systems on stdout */
X (void) putchar('\n'); /* ends the log line */
X if (art->h.h_ctlcmd != NULL)
X ctlmsg(art);
X#ifdef notdef /* it's only a log file! */
X (void) fflush(stdout); /* crash-proofness */
X#endif
X }
X art->a_status &= ~ST_REFUSED; /* refusal is quite casual & common */
X}
X
X/*
X * Reject articles. This can be arbitrarily picky.
X * Only the headers are used to decide, so this can be called before
X * the article is filed.
X * Be sure to put the fastest tests first, especially if they often result
X * in rejections.
X */
Xvoid
Xreject(art)
Xregister struct article *art;
X{
X if (art->h.h_path == NULL) {
X prefuse(art);
X (void) printf("no Path: header\n");
X } else if (alreadyseen(art->h.h_msgid)) {
X prefuse(art);
X (void) printf("duplicate\n");
X } else if (art->h.h_path != NULL && hopcount(art->h.h_path) > 0 &&
X !ngmatch(oursys()->sy_ngs, art->h.h_ngs)) {
X extern boolean histreject;
X
X /*
X * non-local article, with all bad groups.
X * (local articles with bad groups will be bounced
X * by fileart when the groups aren't in active.)
X */
X if (histreject)
X history(art, NOLOG);
X prefuse(art);
X (void) printf("no subscribed groups in `%s'\n", art->h.h_ngs);
X } else if (art->h.h_approved == NULL && moderated(art->h.h_ngs)) {
X prefuse(art);
X (void) printf("unapproved article in moderated group(s) `%s'\n",
X art->h.h_ngs);
X } else
X return; /* art was accepted */
X art->a_status |= ST_REFUSED;
X if (!okrefusal)
X art->a_status |= ST_DROPPED;
X}
X
X/*
X * print the leader of a refusal message about the article in "art".
X */
Xvoid
Xprefuse(art)
Xregister struct article *art;
X{
X timestamp(stdout, (time_t *)NULL);
X (void) printf(" %s - %s ", sendersite(nullify(art->h.h_path)),
X art->h.h_msgid);
X}
X
X/*
X * "Uninstall" an article: remove art->a_files (permanent names) and
X * a_tmpf (temporary name if a_unlink set), and return assigned article #'s.
X * If a_unlink isn't set, a_tmpf is a copy of the first link in art->a_files.
X * Must be called before history() is called, else there will be a
X * history entry for the article, but no spool files.
X */
Xvoid
Xuninsart(art)
Xregister struct article *art;
X{
X if (art->a_unlink && art->a_tmpf != NULL) {
X (void) unlink(art->a_tmpf); /* I don't wanna know... */
X art->a_unlink = NO;
X }
X /* return article numbers (YES) & ignore unlink errors */
X (void) snuffmayreturn(art->a_files, YES);
X}
X
Xstatust
Xsnufffiles(filelist) /* just unlink all files in filelist */
Xchar *filelist;
X{
X /* don't return article numbers (NO) & return unlink errors */
X return snuffmayreturn(filelist, NO);
X}
X
X/*
X * Unlink all files in filelist, and optionally return article numbers.
X * When removing a link, note any failure, but don't issue an error message.
X * For one thing, cancel controls fail routinely because the article has been
X * removed manually or never existed (a previous cancel arrived before its
X * subject and generated a fake history entry).
X */
XSTATIC statust
Xsnuffmayreturn(filelist, artret)
Xchar *filelist;
Xboolean artret; /* return article numbers & note unlink errors? */
X{
X register statust status = ST_OKAY;
X register char *arts, *spacep, *slashp, *artnm;
X
X /* this is a deadly tedious job and I really should automate it */
X for (arts = filelist; arts != NULL && arts[0] != '\0';
X arts = (spacep == NULL? NULL: spacep+1)) {
X spacep = index(arts, ' ');
X if (spacep != NULL)
X spacep[0] = '\0'; /* will be restored below */
X artnm = strsave(arts);
X if (spacep != NULL)
X spacep[0] = ' '; /* restore space */
X
X slashp = index(artnm, FNDELIM);
X if (slashp != NULL)
X slashp[0] = '\0'; /* will be restored below */
X if (artret)
X /* prevartnum will complain on i/o error to active */
X (void) prevartnum(artnm); /* return assigned # */
X if (slashp != NULL)
X slashp[0] = FNDELIM; /* restore slash */
X
X mkfilenm(artnm);
X if (unlink(artnm) < 0)
X status |= ST_ACCESS;
X free(artnm);
X }
X return status;
X}
!
echo 'relay/relaynews.c':
sed 's/^X//' >'relay/relaynews.c' <<'!'
X/*
X * relaynews - relay Usenet news (version C)
X * See the file COPYRIGHT for the copyright notice.
X *
X * relaynews should be setuid-news, setgid-news. You'll need to install
X * setnewsids setuid-root if setuid(geteuid()) doesn't work on your
X * machine (e.g. on V7 and possibly SystemIII).
X *
X * Written by Geoff Collyer, 15-20 November 1985 and revised periodically
X * since.
X *
X * relaynews parses article headers, rejects articles by newsgroup &
X * message-id, files articles, updates the active & history files,
X * transmits articles, and honours (infrequent) control messages, which do
X * all sorts of varied and rococo things. Control messages are implemented
X * by separate programs. relaynews reads a "sys" file to control the
X * transmission of articles but can function as a promiscuous leaf node
X * without one. See ARPA Internet RFC 1036 nee 850 for the whole story.
X *
X * A truly radical notion: people may over-ride via environment variables
X * the compiled-in default directories so IHCC kludges are not needed and
X * testing is possible (and encouraged) in alternate directories. This
X * does cause a loss of privilege, to avoid spoofing.
X *
X * The disused old unbatched ihave/sendme protocol is gone because it was
X * too wasteful; use the batched form instead (see the ihave sys flag
X * ("I") instead).
X *
X * Portability vs SystemV. relaynews uses dbm(3) and makes no apologies
X * for so doing. Imitation UNIX (registered trademark of AT&T in the
X * United States) brand operating systems that lack dbm are going to
X * have to use my incredibly slow dbm simulation, or another.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <signal.h> /* to make locking safe */
X#include <sys/types.h>
X
X#include "libc.h"
X#include "news.h"
X#include "config.h"
X#include "fgetmfs.h"
X#include "active.h"
X#include "caches.h"
X#include "cpu.h"
X#include "fileart.h"
X#include "headers.h"
X#include "history.h"
X#include "transmit.h"
X
X/*
X * setuid-root program to set ids to news/news & rexec rnews with
X * NEWSPERMS in the environment to break loops.
X */
X#ifndef SETNEWSIDS
X#define SETNEWSIDS "setnewsids"
X#endif
X
X/* exports */
Xchar *progname;
Xboolean okrefusal = YES; /* okay to refuse articles? */
Xchar *exclude = NULL; /* site to exclude, for erik */
Xboolean histreject = NO; /* keep history of rejects? */
X
X/* internal */
Xstatic boolean userealids = NO;
X
X/* imports */
Xextern int optind; /* set by getopt */
Xextern char *optarg;
Xextern statust cpinsart(); /* from procart.c */
X
X/* forwards */
Xextern void prelude(), setids(), procopts(), redirectlogs(), logfile();
Xextern void getwdandcd();
Xextern statust procargs(), relnmprocess(), process(), unbatch();
Xextern boolean batchln();
XFORWARD boolean debugon();
X
X/*
X * main - take setuid precautions, switch to "news" ids, ignore signals,
X * handle options, lock news system, process files & unlock news system.
X */
Xint
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X statust status = ST_OKAY;
X int redirlogs = 0; /* redirect n std output streams to logs */
X char *origdir = NULL; /* current directory at start */
X
X progname = argv[0];
X#ifdef CSRIMALLOC
X mal_debug(0); /* was 2; 3 is too slow */
X mal_leaktrace(0); /* was 1 */
X#endif
X prelude(argv); /* various precautions; switch to "news" */
X
X /* ignore signals (for locking). relaynews runs quickly, so don't worry. */
X (void) signal(SIGINT, (sigarg_t)SIG_IGN);
X (void) signal(SIGQUIT, (sigarg_t)SIG_IGN);
X (void) signal(SIGHUP, (sigarg_t)SIG_IGN);
X (void) signal(SIGTERM, (sigarg_t)SIG_IGN);
X
X procopts(argc, argv, &redirlogs, &okrefusal);
X
X newslock(); /* done here due to dbm internal cacheing */
X if (redirlogs > 0) {
X redirectlogs(redirlogs); /* redirect std output streams to logs */
X#ifdef MANYERRORS
X (void) putc('\n', stderr); /* leave a blank line */
X /* prints "Jun 5 12:34:56" */
X timestamp(stderr, (time_t *)NULL);
X (void) putc('\n', stderr);
X#endif
X }
X
X getwdandcd(argc, argv, &origdir);
X status |= procargs(argc, argv, &origdir);
X
X status |= synccaches(); /* being cautious: write & close caches */
X (void) fflush(stdout); /* log file */
X (void) fflush(stderr); /* errlog file */
X
X#ifdef notdef
X#ifdef CSRIMALLOC
X mal_dumpleaktrace(fileno(stderr));
X#endif
X#endif
X newsunlock();
X exit(status);
X /* NOTREACHED */
X}
X
X/*
X * reset various environmental things for safety: umask, alarm,
X * environment variables (PATH, IFS), standard file descriptors,
X * user & group ids.
X */
Xvoid
Xprelude(argv) /* setuid daemon prelude */
Xchar **argv;
X{
X register char *newpath;
X
X (void) umask(2); /* undo silly umasks, ignore newsumask() */
X (void) alarm(0); /* cancel any pending alarm */
X newpath = malloc(STRLEN("PATH=") + strlen(newspath()) + 1);
X if (newpath == NULL)
X exit(1); /* no chatter until stdfdopen */
X (void) strcpy(newpath, "PATH=");
X (void) strcat(newpath, newspath());
X if (putenv(newpath) ||
X putenv("IFS= \t\n"))
X exit(1); /* no chatter until stdfdopen */
X closeall(1); /* closes all but std descriptors */
X stdfdopen(); /* ensure standard descriptors are open */
X setids(argv); /* change of real and effective ids */
X}
X
X/*
X * change real and effective ids to real ids if unprivileged() is called,
X * else to effective ("news") ids. ctlfile((char *)0) will trigger a call
X * to unprivileged() if any environment variables override the default
X * path names. unprivileged() in turn sets userealids.
X *
X * If setuid(geteuid()) fails, try execing a small, setuid-root program
X * to just do "getpwnam(), getgrnam() (with NEWSPERMS set), setgid(),
X * setuid()," and exec this program again. If NEWSPERMS is set,
X * the failure is a fatal error (recursive loop).
X * This program (relaynews) can be setuid-news.
X *
X * The peculiar tests for failure (getuid() != newsuid) are to work
X * around a Xenix bug which returns 0 from setuid() upon failure.
X */
Xvoid
Xsetids(argv)
Xchar **argv;
X{
X int newsuid, newsgid;
X
X (void) ctlfile((char *)NULL);
X if (userealids)
X newsuid = getuid(), newsgid = getgid();
X else
X newsuid = geteuid(), newsgid = getegid();
X if (setgid(newsgid) < 0 || setuid(newsuid) < 0 ||
X getgid() != newsgid || getuid() != newsuid) {
X if (getenv("NEWSPERMS") != 0)
X error("recursive loop setting ids", "");
X execv(ctlfile(SETNEWSIDS), argv);
X error("can't exec `%s' to set ids", ctlfile(SETNEWSIDS));
X /* NOTREACHED */
X }
X /* we are now running as news, so you can all relax */
X}
X
X/*
X * parse options and set flags
X */
Xvoid
Xprocopts(argc, argv, redirlogsp, okrefusalp)
Xint argc;
Xchar **argv;
Xint *redirlogsp;
Xboolean *okrefusalp;
X{
X int c, errflg = 0;
X
X while ((c = getopt(argc, argv, "d:inrsx:")) != EOF)
X switch (c) {
X case 'd': /* -d debug-options; thanks, henry */
X if (!debugon(optarg))
X errflg++; /* debugon has complained */
X break;
X case 'i': /* redirect stdout to log (inews) */
X *redirlogsp = 1; /* just stdout */
X break;
X case 'n': /* nntp mode: keep history of rejects */
X histreject = YES;
X break;
X case 'r': /* redirect std. ostreams to logs (rnews) */
X *redirlogsp = 2; /* stdout & stderr */
X break;
X case 's': /* dropping input is serious (inews) */
X *okrefusalp = NO;
X break;
X case 'x': /* -x site: don't send to site */
X /* you're welcome, erik */
X /* erik says he only needs one -x per inews */
X if (exclude != NULL) {
X (void) fprintf(stderr,
X "%s: more than one -x site (%s)\n",
X progname, optarg);
X errflg++;
X } else
X exclude = optarg;
X break;
X default:
X errflg++;
X break;
X }
X if (errflg) {
X (void) fprintf(stderr, "usage: %s [-inrs][-d fhlmt][-x site]\n",
X progname);
X exit(2);
X }
X}
X
Xvoid
Xunprivileged() /* called if NEWSARTS, NEWSCTL or NEWSBIN present */
X{
X userealids = YES;
X}
X
XSTATIC boolean
Xdebugon(dbopt)
Xregister char *dbopt;
X{
X statust status = YES;
X
X for (; *dbopt != '\0'; dbopt++)
X switch (*dbopt) {
X case 'f':
X filedebug(YES);
X break;
X case 'h':
X hdrdebug(YES);
X break;
X case 'l':
X lockdebug(YES);
X break;
X case 'm':
X matchdebug(YES);
X break;
X case 't':
X transdebug(YES);
X break;
X default:
X status = NO; /* unknown debugging option */
X (void) fprintf(stderr, "%s: bad -d %c\n",
X progname, *dbopt);
X break;
X }
X return status;
X}
X
X/*
X * Redirect stdout or stderr into log files at known locations.
X */
Xvoid
Xredirectlogs(count)
Xint count;
X{
X if (count > 0)
X logfile(stdout, ctlfile("log"));
X if (count > 1)
X logfile(stderr, ctlfile("errlog"));
X}
X
Xvoid
Xlogfile(stream, name) /* redirect stream into name */
XFILE *stream;
Xchar *name;
X{
X if (freopen(name, "a", stream) == NULL)
X errunlock("can't redirect standard stream to `%s'", name);
X}
X
X/*
X * if argv contains relative file name arguments, save current directory name
X * in malloced memory, through origdirp.
X * then change directory to the spool directory ($NEWSARTS).
X */
Xvoid
Xgetwdandcd(argc, argv, origdirp)
Xint argc;
Xchar **argv;
Xchar **origdirp;
X{
X register int argind;
X boolean needpwd = NO;
X char dirtmp[MAXPATH]; /* much bigger than needed */
X
X for (argind = optind; argind < argc; argind++)
X if (argv[argind][0] != FNDELIM)
X needpwd = YES;
X
X *origdirp = "/???"; /* pessimism */
X if (needpwd && getcwd(dirtmp, sizeof dirtmp) != 0)
X *origdirp = dirtmp;
X *origdirp = strsave(*origdirp); /* save a smaller copy */
X cd(fullartfile((char *)NULL)); /* move to spool directory */
X}
X
X/*
X * process files named as arguments (or implied)
X */
Xstatust
Xprocargs(argc, argv, origdirp)
Xint argc;
Xchar **argv;
Xchar **origdirp;
X{
X register statust status = ST_OKAY;
X
X if (optind == argc)
X status |= process(stdin, "stdin");
X else
X for (; optind < argc; optind++)
X status |= relnmprocess(argv[optind], *origdirp);
X nnfree(origdirp);
X return status;
X}
X
Xstatust
Xrelnmprocess(name, origdir) /* process a (relative) file name */
Xchar *name, *origdir;
X{
X register statust status = ST_OKAY;
X register FILE *in;
X register char *fullname;
X
X fullname = nemalloc((unsigned)strlen(origdir) + STRLEN(SFNDELIM) +
X strlen(name) + 1);
X fullname[0] = '\0';
X
X if (name[0] != FNDELIM) { /* relative path */
X (void) strcat(fullname, origdir);
X (void) strcat(fullname, SFNDELIM);
X }
X (void) strcat(fullname, name);
X
X in = fopenwclex(fullname, "r");
X if (in != NULL) {
X status |= process(in, fullname);
X (void) nfclose(in);
X }
X free(fullname);
X return status;
X}
X
X/*
X * process - process input file
X * If it starts with '#', assume it's a batch and unravel it,
X * else it's a single article, so just inject it.
X */
Xstatust
Xprocess(in, inname)
XFILE *in;
Xchar *inname;
X{
X register int c;
X
X if ((c = getc(in)) == EOF)
X return ST_OKAY; /* normal EOF */
X (void) ungetc(c, in);
X if (c == '#')
X return unbatch(in, inname);
X else
X return cpinsart(in, inname, MAXLONG, NO);
X}
X
X/*
X * Unwind "in" and insert each article.
X * For each article, call cpinsart to copy the article from "in" into
X * a (temporary) file in the news spool directory and rename the temp file
X * to the correct final name if it isn't right already.
X *
X * If the unbatcher gets out of sync with the input batch, the unbatcher
X * will print and discard each input line until it gets back in sync.
X */
Xstatust
Xunbatch(in, inname)
Xregister FILE *in;
Xchar *inname;
X{
X register int c;
X /* register */ char *line;
X register statust status = ST_OKAY;
X long charcnt;
X
X while (!(status&ST_DISKFULL) && (c = getc(in)) != EOF) {
X (void) ungetc(c, in);
X while ((line = fgetms(in)) != NULL &&
X !batchln(line, &charcnt)) { /* returns charcnt */
X status |= ST_DROPPED;
X (void) fprintf(stderr,
X "%s: unbatcher out of synch, tossing: ",
X progname);
X (void) fputs(line, stderr);
X free(line);
X }
X nnfree(&line); /* free "#! rnews n" */
X if (!feof(in))
X status |= cpinsart(in, inname, charcnt, YES);
X }
X if (ferror(in))
X errunlock("error reading `%s'", inname);
X return status;
X}
X
X/*
X * Is line a batcher-produced line (#! rnews count)?
X * If so, return the count through charcntp.
X * This is slightly less convenient than sscanf, but a lot smaller.
X */
Xboolean
Xbatchln(line, charcntp)
Xregister char *line;
Xregister long *charcntp;
X{
X register char *countp;
X static char batchtext[] = "#! rnews ";
X
X countp = line + STRLEN(batchtext);
X if (STREQN(line, batchtext, STRLEN(batchtext)) &&
X isascii(*countp) && isdigit(*countp)) {
X *charcntp = atol(countp);
X return YES;
X } else {
X *charcntp = 0;
X return NO;
X }
X}
!
echo 'relay/sh/anne.jones':
sed 's/^X//' >'relay/sh/anne.jones' <<'!'
X#! /bin/sh
X# anne.jones [file...] - censor headers: munge locally-generated headers in
X# files, enforce feeble attempts at Usenet security, generate lots of silly
X# headers.
X# (after the notorious ring-leader of the Ontario Film and Video Review Board
X# (nee Ontario Board of Censors), Ontario's very own Mrs. Mary Whitehouse.)
X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
X. ${NEWSCONFIG-/usr/lib/news/bin/config}
Xexport NEWSCTL NEWSBIN NEWSARTS
XPATH=$NEWSCTL/bin:$NEWSBIN/inject:$NEWSBIN:$NEWSPATH ; export PATH
Xumask $NEWSUMASK
X
X# pass 0 - dredge up defaults
Xcase "$trversion" in
Xv[67]) ;;
X*) echo "$0: trversion is nonsense or missing from environment" >&2
X exit 1 ;;
Xesac
Xif test -r $NEWSCTL/mailname; then
X mailname="`tr -d ' \11' <$NEWSCTL/mailname`"
Xelse
X mailname="`newshostname`"
X case "$mailname" in
X *.*) ;; # not a uucp host name
X *) mailname="$mailname.uucp" ;; # probably a uucp host name
X esac
Xfi
X# badsites="pucc.bitnet!" # tailor, syntax is "host1!host2!...host3!"
Xhost="$mailname"
X
X# dig up user's name (a simple task, you'd think, but you'd be wrong)
Xcase "$LOGNAME" in
X"")
X # "who am i" on many Unixes does "ttyname(0)" and "getpwuid(getuid())"
X # if that fails - it can be confused by empty utmp entries (per jerqs);
X # "who am i </dev/null" yields your userid, not your login name.
X # "tty" does "ttyname(0)"; also fallible.
X # So, emulate a slightly-modified V7 getlogin(3) (actually ttyslot(3)):
X # look for tty on /dev/tty, stdin, stdout, stderr (actually via ttyname(3)).
X for fd in 3 0 1 2 # 3 is /dev/tty on V8
X do
X if test -t $fd; then
X case "$USER" in
X "") USER="`who am i <&$fd |
X sed -e 's/[ ].*//' -e '/!/s/^.*!//' `" ;;
X esac
X fi
X done
X case "$USER" in
X "") USER="`who am i </dev/null | # last resort: use userid
X sed -e 's/[ ].*//' -e '/!/s/^.*!//' `" ;;
X esac
X ;;
X*) USER="$LOGNAME" ;;
Xesac
Xcase "$NAME" in
X"")
X if test -s $HOME/.name; then
X NAME=`cat $HOME/.name`
X else
X NAME=`(grep "^$USER:" /etc/passwd || ypmatch "$USER" passwd) |
X sed 's/^[^:]*:[^:]*:[^:]*:[^:]*:\([^,:]*\).*$/\1/' `
X # tailor: for BTL RJE format, add
X # | sed -e 's/^[^-]*- *//' -e 's/ *(.*$//'
X # otherwise for Berkeley format, use this
X # (courtesy Rayan Zachariassen):
X case "$NAME" in
X *'&'*)
X # generate Capitalised login name
X NM=`echo "$USER" | sed -e 's/^\(.\)\(.*\)/\1:\2/'`
X NM1=`expr "$NM" : '\(.\):.*' |
X case "$trversion" in
X v7) tr a-z A-Z ;;
X v6) tr '[a-z]' '[A-Z]' ;;
X esac
X `
X NMR=`expr "$NM" : '.:\(.*\)'`
X CAPNM="$NM1$NMR"
X # turn & into Capitalised login name
X NAME=`echo "$NAME" | sed "s:&:$CAPNM:"`
X ;;
X esac
X fi
X ;;
Xesac
Xcase "$NAME" in
X"") fullname="" ;; # no full name, leave it off
X*) fullname=" ($NAME)" ;;
Xesac
Xreallyfrom="$USER@$host$fullname"
XFROM="$reallyfrom"
X
X# generate a few defaults.
X# RFC 1036 requests a GMT Date:, despite it being hard to read.
X# Compensate for V6 Uglix date (no -u) tarted up with all that TZ goo.
Xdate="`
X set ''\`TZ=GMT0 date\` # give TZ to see if (Uglix) date responds
X case \"$5\" in
X GMT) echo $* ;; # Uglix date or V7 date with GMT local time
X *) date -u ;; # must be V7 date command, it ignored TZ
X esac
X`" # for defdate, defmsgid
Xcase "$ORGANIZATION" in
X"") deforg="`sed 1q $NEWSCTL/organi[sz]ation`" ;; # look in a file
X*) deforg="$ORGANIZATION" ;; # look in environment
Xesac
X
X# give defaults and headers to awk
Xcat $* |
X # strip invisible chars, a la B news; turn tabs to spaces (RFC1036)
X case "$trversion" in
X v7) tr -d '\1-\7\13\14\16-\37';;
X v6) tr -d '[\1-\7]\13\14[\16-\37]' ;;
X esac |
X sed 's/: /: /' |
X awk -f $NEWSBIN/inject/defhdrs.awk \
Xdefpath="$badsites$USER" \
Xdeffrom="$FROM" deforg="$deforg" \
Xdefdate="` set $date; echo $1, $3 $2 \` echo $6 | sed 's/^..//' \` $4 $5`" \
Xdefmsgid="`set $date; echo \<$6$2$3.\` echo $4 | tr -d : \`.$$@$host\>`" -
!
echo 'relay/sh/ctlrun':
sed 's/^X//' >'relay/sh/ctlrun' <<'!'
X#! /bin/sh
X# ctlrun - run the control messages in control again
X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
X. ${NEWSCONFIG-/usr/lib/news/bin/config}
X# export NEWSCTL NEWSBIN NEWSARTS
XPATH=$NEWSCTL/bin:$NEWSBIN/ctl:$NEWSBIN:$NEWSPATH ; export PATH
Xumask $NEWSUMASK
X
Xcd $NEWSCTL
Xnewslock sys LOCK || exit 1 # lock
X
Xcd $NEWSARTS/control
X
Xfor file in *
Xdo
X grep '^Control:' $file |
X sed 's;^Control:[ ]*;'$NEWSBIN/ctl/';' |
X grep -v '/cancel ' >/tmp/ctl$$ # cancel needs dbm(3)
X sh -x /tmp/ctl$$ <$file
Xdone
X
Xrm -f /tmp/ctl$$
Xrm -f LOCK
!
echo 'relay/sh/defhdrs.awk':
sed 's/^X//' >'relay/sh/defhdrs.awk' <<'!'
X# defhdrs.awk
X# pass 1 - note presence | absence of certain headers
X# a header keyword: remember it and its value
X/^[^\t ]*:/ {
X hdrval[$1] = $0
X keyword=$1
X next
X}
X# a continuation: concatenate this line to the value
X { hdrval[keyword] = hdrval[keyword] "\n" $0 }
X
XEND {
X # pass 2 - cogitate & omit & emit headers
X emptyhdrre = "^[^\t ]*:[\t ]*$"
X subjname = "Subject:"
X ctlname = "Control:"
X ngname = "Newsgroups:"
X msgidname = "Message-ID:"
X typoname = "Message-Id:"
X pathname = "Path:"
X datename = "Date:"
X fromname = "From:"
X orgname = "Organization:"
X distrname = "Distribution:"
X sendername = "Sender:"
X
X # fill in missing headers
X if (hdrval[typoname] != "") { # spelling hack
X hdrval[msgidname] = hdrval[typoname]
X hdrval[typoname] = ""
X # fix spelling: Message-Id: -> Message-ID:
X nf = split(hdrval[msgidname], fields); # bust up
X fields[1] = msgidname; # fix spelling
X hdrval[msgidname] = fields[1]; # reassemble...
X for (i = 2; i <= nf; i++)
X hdrval[msgidname] = hdrval[msgidname] " " fields[i]
X }
X if (hdrval[msgidname] == "")
X hdrval[msgidname] = msgidname " " defmsgid
X if (hdrval[orgname] == "")
X hdrval[orgname] = orgname " " deforg
X
X # replace users headers (if any)
X hdrval[datename] = datename " " defdate
X hdrval[pathname] = pathname " " defpath
X if (hdrval[fromname] == "")
X hdrval[fromname] = fromname " " deffrom
X else if (hdrval[sendername] == "")
X hdrval[sendername] = sendername " " deffrom
X
X # snuff some headers
X distworld = distrname " world"
X if (hdrval[distrname] == distworld)
X hdrval[distrname] = ""
X
X # the vile cmsg hack, for the sake of the news readers *only*
X if (hdrval[ctlname] == "" && \
X substr(hdrval[subjname], 1, 14) == "Subject: cmsg ")
X hdrval[ctlname] = ctlname " " substr(hdrval[subjname], 15)
X
X # warn if no Newsgroups:
X if (hdrval[ngname] == "")
X print "no newsgroups header!" | "cat >&2"
X
X # field the all.all.ctl hack, for the sake of the backward only:
X # clone Subject: to make Control:
X if (hdrval[ctlname] == "" && hdrval[ngname] ~ /\.ctl(,|$)/)
X hdrval[ctlname] = ctlname " " substr(hdrval[subjname], 8)
X
X # reorder & emit headers
X
X # favour Control: & Newsgroups: for future benefit of rnews
X if (hdrval[ctlname] != "") {
X print hdrval[ctlname]
X hdrval[ctlname] = "" # no Control: to print now
X }
X if (hdrval[ngname] != "") {
X print hdrval[ngname]
X hdrval[ngname] = "" # no Newsgroups: to print now
X }
X
X # B inews kludgery: print Path: before From: to avoid confusing it
X if (hdrval[pathname] != "") {
X print hdrval[pathname]
X hdrval[pathname] = "" # no Path: to print now
X }
X if (hdrval[fromname] != "") {
X print hdrval[fromname]
X hdrval[fromname] = "" # no From: to print now
X }
X
X # have pity on readers: put Subject: next
X if (hdrval[subjname] != "") {
X print hdrval[subjname]
X hdrval[subjname] = "" # no Subject: to print now
X }
X
X # print misc. non-empty headers in random order
X for (i in hdrval)
X if (hdrval[i] != "" && hdrval[i] !~ /^[^\t ]*:[\t ]*$/)
X print hdrval[i]
X}
!
echo 'relay/sh/inews':
sed 's/^X//' >'relay/sh/inews' <<'!'
X#! /bin/sh
X# inews [-p] [-debug k] [-x site] [-hMD] [-t subj] [-n ng] [-e exp] [-F ref] \
X# [-d dist] [-a mod] [-f from] [-o org] [-C ng] [file...] - inject news:
X# censor locally-posted article and field the "inews -C" kludge;
X# munge the articles, enforce feeble attempts at Usenet security,
X# generate lots of silly headers.
X#
X# Yes, it's big, slow and awkward. The alternative is casting a lot of
X# local policy in C.
X
X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
X. ${NEWSCONFIG-/usr/lib/news/bin/config}
Xexport NEWSCTL NEWSBIN NEWSARTS NEWSPATH NEWSUMASK NEWSMASTER NEWSCONFIG
XPATH=$NEWSCTL/bin:$NEWSBIN/inject:$NEWSBIN/relay:$NEWSBIN:$NEWSPATH; export PATH
XPASSEDFROM=''; export PASSEDFROM # passed to anne.jones in environ.
X
Xdebug='' # flags
Xexclusion=''
Xhdrspresent=no
Xautopost=no
Xwaitcmd=''
Xrelayopts=-i # redirect stdout to log
X
Xwhoami=/tmp/in$$who # just created to determine effective uid
Xinput=/tmp/in$$in # uncensored input
Xinhdrs=/tmp/in$$hdr # generated by tear: headers
Xinbody=/tmp/in$$body # generated by tear: body
Xcensart=/tmp/in$$cens # censored input
Xnglist=/tmp/in$$ngs # newsgroups: list
Xmodroute=/tmp/in$$route # route to moderator's forwarder
Xexitflag=/tmp/in$$exit # exit status, if present
Xoutfile=/tmp/in$$out # relaynews stdout
Xgrpok=/tmp/in$$grp # flag file: groups okay if present
Xrmlist="$inhdrs $inbody $input $censart $nglist $modroute $exitflag $outfile $grpok"
X
Xumask $NEWSUMASK
X
X# "inews -p": invoke rnews
Xcase "$1" in
X-p)
X shift
X exec rnews $* # rnews, bailing out at or near line 1
X ;;
Xesac
X
X# process arguments: for options, cat headers onto $input; cat files onto $input
X>$input
Xcleanup="test ! -f $HOME/dead.article -o -w $HOME/dead.article &&
X cat $input >>$HOME/dead.article &&
X { echo $0: article in $HOME/dead.article >&2; rm -f $rmlist; }; exit 1"
Xtrap "$cleanup" 0 1 2 3 15
Xwhile :
Xdo
X case $# in
X 0) break ;; # arguments exhausted
X esac
X
X case "$1" in
X # peculiar to C news
X -debug) shift; debug="$1" ;;
X -A) autopost=yes ;; # wait for free space
X -V) relayopts= ;; # verbose: don't redirect stdout (or stderr)
X -W) waitcmd=wait ;; # wait for completion
X # useful standard options
X -h) hdrspresent=yes ;;
X -x) shift; exclusion="-x $1" ;; # you're welcome, erik (2.11)
X # silly options supplied by newsreaders
X -a) shift; echo "Approved: $1" >>$input ;;
X -c) shift; echo "Control: $1" >>$input ;;
X -d) shift; echo "Distribution: $1" >>$input ;;
X -e) shift; echo "Expires: $1" >>$input ;;
X -f) shift; echo "From: $1" >>$input ;;
X -n) shift; echo "Newsgroups: $1" >>$input ;;
X -t) shift; echo "Subject: $1" >>$input ;; # aka Title:
X -D) # obsolete, undocumented: meant "don't check for recordings".
X # last present in B 2.10.1, invoked by readnews for followups.
X ;;
X -F) # undocumented in B 2.10.1, documented in B 2.11.
X shift; echo "References: $1" >>$input ;;
X -M) # this apparently just sets From: to the author of the article
X # instead of the poster (moderator), by leaving the From: line
X # alone (under -h); easy to implement.
X ;;
X
X # pass next options as environment variables to anne.jones
X -o) shift; ORGANIZATION="$1"; export ORGANIZATION ;;
X
X -C) # megakludge-o-rama
X # first, permit only to super-users
X >$whoami
X case "`ls -l $whoami | awk '{print $3}'`" in
X root) : a winner ;;
X *)
X echo "$0: only super-users may create news groups" >&2
X exit 1
X ;;
X esac
X rm -f $whoami
X
X inewsopt="$1" # for use in message body
X shift # skip -C to get ng as $1
X
X cat <<! >>$input # generate a control message
XNewsgroups: $1
XControl: newgroup $1
XSubject: newgroup $1
XApproved: above-user@above-host
X
XThis article generated by inews $inewsopt $1.
X!
X ;;
X -*)
X echo "$0: bad option $1" >&2
X exit 1
X ;;
X *) # is a filename; append file
X # B 2.11 kludge: assume -h if input starts with headers.
X # apparently the B 2.11 newsreaders assume this.
X tear /tmp/in$$ <$1
X if test -s $inhdrs; then
X hdrspresent=yes
X fi
X
X case "$hdrspresent" in
X no) echo "" >>$input; hdrspresent=yes ;;
X esac
X # capture incoming news in case relaynews fails
X if cat $inhdrs $inbody >>$input; then
X : far out
X else
X echo "$0: lost news; cat status $?" >&2
X exit 1
X fi
X fileseen=yes
X ;;
X esac
X shift # pass option or filename (any value was done above)
Xdone
X
X# if no files named, read stdin
Xcase "$fileseen" in
Xyes) ;;
X*)
X # B 2.11 kludge: assume -h if input starts with headers
X # apparently the B 2.11 newsreaders assume this.
X tear /tmp/in$$
X if test -s $inhdrs; then
X hdrspresent=yes
X fi
X
X case "$hdrspresent" in
X no) echo "" >>$input; hdrspresent=yes ;;
X esac
X # capture incoming news in case relaynews fails
X if cat $inhdrs $inbody >>$input; then
X : far out
X else
X echo "$0: lost news; cat status $?" >&2
X exit 1
X fi
X ;;
Xesac
Xtrap '' 1 2 15 # ignore signals to avoid losing articles
X
X# run the remainder in the background for the benefit of impatient people
X# who lack a window system
X(
Xtrap "$cleanup" 0
Xtear /tmp/in$$ <$input # output in $inhdrs and $inbody
X# pad zero-line articles, since old B [ir]news are confused by them
X# and the news readers generate zero-line control messages, alas.
Xif test ! -s $inbody; then
X (echo '';
X echo This article was probably generated by a buggy news reader.) \
X >$inbody
Xfi
X
X# deduce which tr we have: v6 or v7
Xcase "`echo B | tr A-Z a-z `" in
Xb) trversion=v7 ;;
XB) trversion=v6 ;; # or System V
Xesac
Xexport trversion
X
X# post with new headers and .signature
X(anne.jones <$inhdrs # bash headers
X # echo "Lines: ` # sop to msb, just uncomment to use
X # if test -r $HOME/.signature; then
X # (cat $inbody; echo '-- '; sed 4q $HOME/.signature) | wc -l
X # else
X # wc -l <$inbody
X # fi
X # `"
X
X # strip invisible chars from body, a la B news
X case "$trversion" in
X v7) tr -d '\1-\7\13\14\16-\37' ;;
X v6) tr -d '[\1-\7]\13\14[\16-\37]' ;;
X esac <$inbody
X
X if test -r $HOME/.signature; then
X echo "-- "; sed 4q $HOME/.signature # glue on first bit of signature
X fi) >$censart
X
X# to post or to mail? that is the question; whether 'tis nobler in the mind
X# to suffer the slings and arrows of outrageous mailers - Bill Shakespeare
Xif grep -s '^Control:' $inhdrs >/dev/null; then
X echo "control" # a dreadful hack around all.all.ctl
Xelse
X sed -n '
X/^Newsgroups:[ ]/{
Xs/^Newsgroups:[ ]*\(.*\)$/\1/p
Xq
X}
X' <$inhdrs
Xfi >$nglist
X
Xif test ! -s $nglist; then # no Newsgroups:
X exit 1 # anne.jones will have already complained
Xfi
X
X# look up groups in active, to determine disposition of this message.
X# n, x and (unapproved) m flags are dealt with on the spot; if none are
X# seen, the article is posted normally.
X# escape egrep metacharacters. In theory one could add " ' ` \
Xegreppat="^(` sed -e 's/[.+*()|[]/\\\\&/g' -e 's/,/|/g' <$nglist `) "
Xegrep "$egreppat" $NEWSCTL/active >/dev/null || {
X echo "$0: `cat $nglist` matches no groups in $NEWSCTL/active" >&2
X exit 1
X}
Xrm -f $grpok
Xegrep "$egreppat" $NEWSCTL/active |
X (while read ng high low flag junk # look at next group's active entry
X do
X >>$grpok
X case "$flag" in
X [nx])
X echo "$0: sorry, $ng may not be posted to locally." >&2
X echo 1 >$exitflag
X trap 0 # this is a child process - no cleanup
X exit 1 # dregs in /tmp/in$$*
X ;;
X m)
X if grep -s '^Approved:[ ]' $inhdrs >/dev/null; then
X rm -f $modroute # just post normally
X else
X # un-Approved: mail it to the moderator(s).
X echo "%s" >$modroute # in case no route
X # look for route for this group
X cat $NEWSCTL/mailpaths |
X while read ngpat route junk
X do
X # a dreadful B 2.11 hack:
X # backbone == all
X case "$ngpat" in
X backbone) ngpat="all" ;;
X esac
X if gngp -a "$ngpat" $nglist >/dev/null; then
X echo "$route" >$modroute
X break # take only 1st match
X fi
X done
X fi
X # ngpat and route are not set here, damn it!
X if test -s $modroute; then
X # an unapproved article in a mod group:
X # mail the article to this moderator.
X moderator=`
X sed "s/%s/\` echo $ng | tr . - \`/" $modroute
X `
X echo "$0: mailing your article to $moderator" >&2
X mail $moderator <$censart
X rm -f $rmlist
X echo 0 >$exitflag
X trap 0 # this is a child process - did cleanup
X exit 0
X fi
X ;;
X
X # "" matches short active entries,
X # to be backward compatible.
X # * matches garbage flags, to be cautious.
X y|""|*)
X # okay so far, but wait until we see all Newsgroups:.
X ;;
X esac
X done
X trap 0 # paranoia - no clean up
X )
Xif test ! -r $grpok; then
X echo "$0: no active groups in `cat $nglist`" >&2
X exit 1 # abnormal exit - cleans up, makes dead.article
Xfi
Xif test -f $exitflag; then
X exitstatus="`cat $exitflag`"
X case "$exitstatus" in
X 0) trap 0 ;; # normal exit - cleanup done, no dead.article
X esac
X exit $exitstatus # trap 0 will cleanup, make dead.article
Xfi
X
X# deal with inadequate free space
Xcase "$autopost" in
Xno)
X if test "`spacefor 1 articles`" -le 0; then
X echo "$0: too little space free on $NEWSARTS" >&2
X exit 1 # dregs in /tmp/in$$* for trap 0
X fi
X ;;
X*)
X iter=0
X while test "`spacefor 1 articles`" -le 0 -o "`spacefor 1 control`" -le 0
X do
X sleep 30
X iter=`expr $iter + 1`
X case "$iter" in
X 3)
X mail "$NEWSMASTER" <<!
XSubject: free space too low on $NEWSARTS
X
XThere is too little free space on $NEWSARTS for inews to run comfortably.
X!
X ;;
X esac
X done
X ;;
Xesac
X
X# to get here, we must have seen no n, x, nor (unapproved) m flags.
X# <$censart is used rather than a pipe to work around a bug in the 4.2 sh
X# which made it sometimes return the wrong exit status (that of anne.jones).
X# execute relaynews commands on the server, for the sake of locking.
X# may not use "exec" or sh will leave /tmp/sh* files from here docs in /tmp.
Xme="`hostname`"
Xserver=`cat $NEWSCTL/server 2>/dev/null`
Xcase "$server" in
X"") server="$me" ;; # if no server file, assume this is it
Xesac
Xcase "$me" in
X$server)
X relaynews $relayopts -s $exclusion -d "$debug" <$censart
X status=$?
X# echo "status $? from relaynews" >>/tmp/inewsdebug # DEBUG
X ;;
X*)
X status=`rsh $server \
X"PATH=$PATH relaynews $relayopts -s $exclusion -d \"$debug\"; echo status $?" \
X <$censart >$outfile; sed -n '/^status /s///p' $outfile `
X sed '/^status /d' $outfile # print relaynews's stdout
X ;;
Xesac
Xcase "$status" in
X0)
X rm -f $rmlist # far out, it worked: clean up
X if test ! -f $NEWSCTL/sys; then
X echo "$0: $NEWSCTL/sys missing; your news can't leave this machine" >&2
X fi
X trap 0 # normal exit: cleanup done
X ;;
Xesac
Xexit $status # trap 0 may cleanup, make dead.article
X) &
X$waitcmd # wait if -W given
Xtrap 0 # let the background run on unmolested
Xexit
!
echo done