rsalz@uunet.uu.net (Rich Salz) (06/30/89)
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 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.