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