home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume19 / cnews2 / part15 < prev    next >
Text File  |  1989-06-29  |  51KB  |  1,770 lines

  1. Subject:  v19i092:  Cnews production release, Part15/19
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: utzoo!henry
  7. Posting-number: Volume 19, Issue 92
  8. Archive-name: cnews2/part15
  9.  
  10. : ---CUT HERE---
  11. echo 'relay/fileart.c':
  12. sed 's/^X//' >'relay/fileart.c' <<'!'
  13. X/*
  14. X * fileart - file an article, given its temporary file name and its headers
  15. X *
  16. X * It may be desirable to, some day, prevent cross-postings across
  17. X * "universes", where a universe might be "alt" or "comp,news".
  18. X *
  19. X * There are three classes of newsgroup for the purposes of filing:
  20. X * "wanted" (in the active file and missing the "x" flag);
  21. X * "not wanted" ("x"ed in active, or not in active and not matched by sys
  22. X *    file's subscription list for this machine), so ignore it; or
  23. X * "don't know it" (not in active and matched by subscription list,
  24. X *    so file the article in junk once, iff there are no good groups).
  25. X * junk *must* be in the active file or it's an error (ST_DROPPED),
  26. X * but junk may have an "x" flag to prevent filing.
  27. X *
  28. X * Use the active file 'x' flag to snuff groups quietly, even when your
  29. X * subscription list permits them, without filing in junk.
  30. X */
  31. X
  32. X#include <stdio.h>
  33. X#include <errno.h>
  34. X#include <sys/types.h>
  35. X
  36. X#include "libc.h"
  37. X#include "news.h"
  38. X#include "config.h"
  39. X#include "active.h"
  40. X#include "mkdirs.h"
  41. X#include "headers.h"
  42. X#include "article.h"
  43. X#include "history.h"
  44. X#include "system.h"
  45. X
  46. X#define JUNK "junk"            /* lost+found pseudo-ng. */
  47. X#define CONTROL "control"        /* control message pseudo-ng. */
  48. X
  49. Xstatic long artnum;            /* asgnartnum sets artnum */
  50. Xstatic int goodngs;            /* asgnartnum reads goodngs */
  51. X
  52. Xstatic boolean debug = NO;
  53. X
  54. X/* imports from news */
  55. Xextern void prefuse();
  56. X
  57. X/* forwards */
  58. XFORWARD void asgnartnum(), gotgoodng(), mkjunklink(), mklinks();
  59. XFORWARD boolean openorlink(), mkonelink(), tryartnum();
  60. X
  61. Xvoid
  62. Xfiledebug(state)        /* set debugging state */
  63. Xboolean state;
  64. X{
  65. X    debug = state;
  66. X}
  67. X
  68. X/*
  69. X * File in the spool directory the article in art & fill in art->a_files.
  70. X * Generate Xref: header if needed (successfully cross-posted).
  71. X *
  72. X * If a_unlink is true, there is a temp file, so openfirst should
  73. X * be false, and vice versa.
  74. X *
  75. X * If openfirst (!art->a_unlink) is true, fill in a_tmpf with the name of
  76. X * the first link, fopen it (into art->a_artf), and make any remaining links.
  77. X * If openfirst is false, just make links to a_tmpf, which is already
  78. X * open as art->a_artf.  openfirst means "Newsgroups:" was seen in time.
  79. X */
  80. Xvoid
  81. Xfileart(art)
  82. Xregister struct article *art;
  83. X{
  84. X    register boolean openfirst = !art->a_unlink;
  85. X    int junkgroups = 0;        /* count "junked" groups */
  86. X    char artnumstr[MAXCOMP];    /* article number in ascii */
  87. X
  88. X    if (art->a_filed)
  89. X        return;            /* don't file twice */
  90. X    artnum = 0;
  91. X    goodngs = 0;
  92. X    mklinks(art, openfirst, artnumstr, &junkgroups);
  93. X    mkjunklink(art, openfirst, artnumstr, &junkgroups);
  94. X    if (goodngs > 1 && art->a_artf != NULL)    /* cross-posted? */
  95. X        emitxref(art);
  96. X}
  97. X
  98. X/*
  99. X * Store in spooldir.  Link temp file to spooldir/ng/article-number
  100. X * for each ng.  Control messages go in CONTROL, never in all.all.ctl.
  101. X */
  102. XSTATIC void
  103. Xmklinks(art, openfirst, artnumstr, junkgroupsp)
  104. Xregister struct article *art;
  105. Xboolean openfirst;
  106. Xchar *artnumstr;
  107. Xint *junkgroupsp;
  108. X{
  109. X    register char *ngs, *ng;
  110. X    register char *comma;
  111. X
  112. X    ngs = (art->h.h_ctlcmd != NULL? CONTROL: art->h.h_ngs);
  113. X    if (art->a_status&ST_REFUSED)
  114. X        (void) fprintf(stderr,
  115. X        "%s: mklinks called with ST_REFUSED set (can't happen)\n",
  116. X            progname);
  117. X    for (; ngs != NULL; ngs = comma) {
  118. X        comma = index(ngs, NGSEP);
  119. X        if (comma != NULL)
  120. X            *comma = '\0';        /* will be restored below */
  121. X        ng = realngname(ngs);
  122. X        if (ng == NULL)
  123. X            ng = strsave(ngs);
  124. X        asgnartnum(art, openfirst, ng, artnumstr);
  125. X        /*
  126. X         * If no such group in active or link failed, and the group
  127. X         * wasn't 'x'ed in active, but our subscription list permits
  128. X         * this group, then set flag to file it under "junk" later.
  129. X         */
  130. X        if ((artnum < 1 || art->a_status != ST_OKAY) &&
  131. X            art->a_status != ST_REFUSED &&
  132. X            ngmatch(oursys()->sy_ngs, ng))
  133. X                ++*junkgroupsp;
  134. X        /*
  135. X         * If article # was assigned & link succeeded,
  136. X         * update art->a_files list for history.
  137. X         */
  138. X        if (artnum >= 1 && art->a_status == ST_OKAY)
  139. X            gotgoodng(art, ng, artnumstr);
  140. X        free(ng);
  141. X
  142. X        if (comma != NULL)
  143. X            *comma++ = NGSEP;    /* step past comma */
  144. X
  145. X        /* asgnartnum refused just this ng */
  146. X        art->a_status &= ~ST_REFUSED;
  147. X    }
  148. X}
  149. X
  150. X/*
  151. X * File once in "junk" iff no ngs were filed due to absence from
  152. X * active, but some were permitted by sys.  This will make one junk
  153. X * link, no matter how many bad groups, and only if all are bad
  154. X * (e.g. rec.drugs,talk.chew-the-fat).
  155. X */
  156. XSTATIC void
  157. Xmkjunklink(art, openfirst, artnumstr, junkgroupsp)
  158. Xregister struct article *art;
  159. Xboolean openfirst;
  160. Xchar *artnumstr;
  161. Xint *junkgroupsp;
  162. X{
  163. X    if (*junkgroupsp > 0 && goodngs == 0) {    /* all groups were "junked"? */
  164. X        asgnartnum(art, openfirst, JUNK, artnumstr);
  165. X        if (artnum >= 1 && art->a_status == ST_OKAY) {
  166. X            gotgoodng(art, JUNK, artnumstr);
  167. X            art->a_status |= ST_JUNKED;
  168. X        } else {
  169. X            /*
  170. X             * couldn't file article in junk.
  171. X             * was JUNK not 'x'ed (i.e. doesn't exist)?
  172. X             */
  173. X            if (art->a_status != ST_REFUSED) {
  174. X                static boolean warned = NO;
  175. X
  176. X                art->a_status |= ST_REFUSED|ST_DROPPED;
  177. X                if (!warned) {
  178. X                    warned = YES;
  179. X                    (void) fprintf(stderr, "%s: no %s group\n",
  180. X                        progname, JUNK);
  181. X                }
  182. X            }
  183. X            prefuse(art);
  184. X            (void) printf("no known groups in `%s' and no %s group\n",
  185. X                art->h.h_ngs, JUNK);
  186. X        }
  187. X    } else if (goodngs == 0) {
  188. X        extern boolean histreject;
  189. X
  190. X        /*
  191. X         * Groups were permitted by subscription list, but all
  192. X         * were 'x'ed in active, or otherwise refused.
  193. X         */
  194. X        if (histreject)
  195. X            history(art, NOLOG);
  196. X        prefuse(art);
  197. X        (void) printf("all groups `%s' excluded in active\n", art->h.h_ngs);
  198. X        art->a_status |= ST_REFUSED;
  199. X    }
  200. X}
  201. X
  202. X/*
  203. X * Append ng/artnumstr to art's list of files, and bump goodngs.
  204. X */
  205. XSTATIC void
  206. Xgotgoodng(art, ng, artnumstr)
  207. Xstruct article *art;
  208. Xchar *ng, *artnumstr;
  209. X{
  210. X    ++goodngs;
  211. X    histupdfiles(art, ng, artnumstr);
  212. X}
  213. X
  214. X/*
  215. X * Assign a permanent name and article number to the temporary name
  216. X * art->a_tmpf in newsgroup "ng" & store the ascii form of the article
  217. X * number into "artnumstr", returning the article number in "artnum".
  218. X *
  219. X * If openfirst is true and goodngs is zero, set inname to artname,
  220. X * fopen artname and store the result in art->a_artf.
  221. X */
  222. XSTATIC void
  223. Xasgnartnum(art, openfirst, ng, artnumstr)
  224. Xstruct article *art;
  225. Xboolean openfirst;                /* open first link? */
  226. Xregister char *ng;                /* read-only */
  227. Xchar *artnumstr;
  228. X{
  229. X    register char *slashng;            /* a group, slashed */
  230. X
  231. X    /* field active 'x' flag: don't file this group, quietly */
  232. X    if (unwanted(ng)) {
  233. X        artnum = -1;
  234. X        art->a_status |= ST_REFUSED;
  235. X        return;
  236. X    }
  237. X
  238. X    slashng = strsave(ng);
  239. X    mkfilenm(slashng);            /* relative to spooldir */
  240. X    while ((artnum = nxtartnum(ng)) >= 1)
  241. X        if (tryartnum(art, openfirst, slashng, artnumstr))
  242. X            break;
  243. X    free(slashng);
  244. X}
  245. X
  246. X/*
  247. X * Construct a link name (slashng/artnum) for this article,
  248. X * and try to link art to it.
  249. X *
  250. X * We changed directory to spooldir in main(), so the generated name
  251. X * is relative to spooldir, therefore artname can be used as is.
  252. X *
  253. X * Return value is identical to mkonelink's.
  254. X */
  255. XSTATIC boolean
  256. Xtryartnum(art, openfirst, slashng, artnumstr)
  257. Xregister struct article *art;
  258. Xboolean openfirst;
  259. Xregister char *slashng;
  260. Xchar *artnumstr;            /* side-effect returned here */
  261. X{
  262. X    register char *artname;        /* article file name */
  263. X    register boolean ret;
  264. X
  265. X    (void) sprintf(artnumstr, "%ld", artnum);
  266. X    artname = nemalloc((unsigned) (strlen(slashng) +
  267. X        STRLEN(SFNDELIM) + strlen(artnumstr) + 1));
  268. X    (void) strcpy(artname, slashng);
  269. X    (void) strcat(artname, SFNDELIM);
  270. X    (void) strcat(artname, artnumstr);
  271. X#ifdef notdef
  272. X    char *tartname = strsave(artfile(artname));
  273. X    free(artname);
  274. X    artname = tartname;
  275. X#endif
  276. X    ret = mkonelink(art, artname, openfirst);
  277. X    free(artname);
  278. X    return ret;
  279. X}
  280. X
  281. X/*
  282. X * Try to link art to artname.
  283. X * If the attempt fails, maybe some intermediate directories are missing,
  284. X * so create any missing directories and try again.  If the second attempt
  285. X * also fails, look at errno; if it is EEXIST, artname already exists
  286. X * (presumably because the active file is out of date, or the existing
  287. X * file is a directory such as net/micro/432), so indicate that higher
  288. X * levels should keep trying, otherwise we are unable to create the
  289. X * necessary directories, so complain and set bad status in art.
  290. X *
  291. X * Returns YES iff there is no point in trying to file this article again,
  292. X * usually because it has been successfully filed, but sometimes because
  293. X * the necessary directories cannot be made.
  294. X */
  295. XSTATIC boolean
  296. Xmkonelink(art, artname, openfirst)
  297. Xregister struct article *art;
  298. Xregister char *artname;
  299. Xboolean openfirst;
  300. X{
  301. X    if (openorlink(artname, art, openfirst, goodngs))
  302. X        return YES;
  303. X    else {
  304. X        (void) mkdirs(artname, getuid(), getgid());
  305. X        if (openorlink(artname, art, openfirst, goodngs))
  306. X            return YES;
  307. X        else if (errno != EEXIST) {
  308. X            warning("can't link to `%s'", artname);
  309. X            art->a_status |= ST_DROPPED;
  310. X            return YES;        /* hopeless - give up */
  311. X        } else
  312. X            return NO;
  313. X    }
  314. X}
  315. X
  316. X/*
  317. X * Try to make a link of art (actually art->a_tmpf) to artname.
  318. X * If no links have been made yet, record and open artname iff no
  319. X * link yet exists to that name (by any file, to avoid overwriting
  320. X * existing articles, e.g. due to an out-of-date active file).
  321. X * If links already exist, try to make artname a link to the first link
  322. X * (art->a_tmpf).
  323. X *
  324. X * Liberally sprinkled with debugging output, as this is the heart
  325. X * of article filing.
  326. X */
  327. XSTATIC boolean
  328. Xopenorlink(artname, art, openfirst, goodngcnt)
  329. Xregister char *artname;
  330. Xregister struct article *art;
  331. Xboolean openfirst;            /* open art's first link? */
  332. Xint goodngcnt;                /* count of good news groups */
  333. X{
  334. X    register boolean worked;        /* open or link worked? */
  335. X
  336. X    if (openfirst && goodngcnt == 0) {
  337. X        if (debug)
  338. X            (void) fprintf(stderr, "opening `%s'... ", artname);
  339. X        nnfree(&art->a_tmpf);
  340. X        art->a_tmpf = strsave(artname);
  341. X        art->a_artf = fopenexcl(art->a_tmpf);
  342. X        worked = art->a_artf != NULL;
  343. X    } else {
  344. X        if (debug)
  345. X            (void) fprintf(stderr, "linking `%s' to `%s'... ",
  346. X                art->a_tmpf, artname);
  347. X        worked = link(art->a_tmpf, artname) == 0;
  348. X        if (!worked)
  349. X            /*
  350. X             * If art->a_tmpf really *is* a temporary name (because
  351. X             * the whole header didn't fit in core), then this will
  352. X             * produce a symbolic link to a non-existent name when
  353. X             * art->a_tmpf is unlinked, which will be soon.
  354. X             * Moral: don't run Eunice on your PDP-11.
  355. X             */
  356. X            worked = symlink(art->a_tmpf, artname) == 0;
  357. X    }
  358. X    if (debug)
  359. X        if (worked)
  360. X            (void) fprintf(stderr, "success.\n");
  361. X        else
  362. X            warning("failed.", "");
  363. X    return worked;
  364. X}
  365. !
  366. echo 'relay/fileart.h':
  367. sed 's/^X//' >'relay/fileart.h' <<'!'
  368. X/* imports from fileart.c */
  369. Xextern void filedebug();
  370. Xextern void fileart();
  371. !
  372. echo 'relay/altctl/rmgroup.auto':
  373. sed 's/^X//' >'relay/altctl/rmgroup.auto' <<'!'
  374. X#! /bin/sh
  375. X# rmgroup group - snuff group. active file is locked at entry
  376. X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
  377. X. ${NEWSCONFIG-/usr/lib/news/bin/config}
  378. Xexport NEWSCTL NEWSBIN NEWSARTS
  379. XPATH=$NEWSCTL/bin:$NEWSBIN:$NEWSPATH ; export PATH
  380. Xumask $NEWSUMASK
  381. X
  382. XF=/tmp/nc$$
  383. X
  384. Xcat >$F
  385. Xgrep -s '^Approved:' $F || { rm -f $F; exit 1; }    # unapproved ctl msg? then quit
  386. XSENDER="`grep '^Sender:' $F | sed 's/^[^:]*: *//'`"
  387. Xcase "$SENDER" in
  388. X"")
  389. X    SENDER="`grep '^From:' $F | sed 's/^[^:]*: *//'`"
  390. X    ;;
  391. Xesac
  392. X
  393. X# remove active entry
  394. Xsed "/^`echo $1 | sed 's/\./\\\\./g'` /d" $NEWSCTL/active >$F.act
  395. Xcp $NEWSCTL/active $NEWSCTL/active.old
  396. Xcp $F.act $NEWSCTL/active
  397. X
  398. X# rm -rf $NEWSARTS/`echo $1 | tr . / `    # remove the directory
  399. Xdir=$NEWSARTS/`echo $1 | tr . / `    # name the directory
  400. Xexport dir                # for sub-shell below
  401. X(
  402. X    if test -x $dir; then
  403. X        cd $dir                # go there
  404. X        rm -f *
  405. X        cd ..
  406. X        rmdir `basename "$dir" '' `    # remove the empty directory
  407. X    fi
  408. X)
  409. X
  410. X# tell the local usenet administrator the bad news
  411. Xecho "rmgrouped $1 cuz $SENDER said to" | mail $NEWSMASTER
  412. X
  413. Xrm -f $F*
  414. !
  415. echo 'relay/altctl/checkgroups.new':
  416. sed 's/^X//' >'relay/altctl/checkgroups.new' <<'!'
  417. X#! /bin/sh
  418. X# checkgroups - check active file for missing or extra newsgroups.
  419. X#    stdin must a checkgroups news article, sends mail to $NEWSMASTER
  420. X#    after updating $nl/newsgroups from $nl/localgroups
  421. X# v1.4 of 9/4/84, adapted to C news
  422. X# added handling of changes in moderated for groups.  Dan
  423. X
  424. X# =()<. ${NEWSCONFIG-@<NEWSCONFIG>@}>()=
  425. X. ${NEWSCONFIG-/usr/lib/news/bin/config}
  426. Xexport NEWSCTL NEWSBIN NEWSARTS
  427. XPATH=$NEWSCTL/bin:$NEWSBIN:$NEWSPATH ; export PATH
  428. Xumask $NEWSUMASK
  429. X
  430. X# generate newsgroups from localgroups & stdin
  431. Xcp $NEWSCTL/localgroups $NEWSCTL/newsgroups
  432. Xsed '1,/^$/d' >>$NEWSCTL/newsgroups    # behead the article (snuff headers)
  433. X
  434. X# generate list of approved newsgroups from $nl/newsgroups
  435. Xecho junk >/tmp/$$a
  436. Xecho control >>/tmp/$$a
  437. X# [^.]*\. in next two egreps was net.|mod.|fa., which is inadequate - geoff
  438. Xsed 's/[ \    ].*//' $NEWSCTL/newsgroups |
  439. X    egrep "^([^.]*\.|general)" >>/tmp/$$a
  440. Xsort -u /tmp/$$a -o /tmp/$$a
  441. X
  442. X# generate list of locally-present newsgroups from $nl/active
  443. Xegrep "^([^.]*\.|general|junk|control)" $NEWSCTL/active |
  444. X    sed 's/ .*//' | sort  -u >/tmp/$$b
  445. X
  446. Xcomm -13 /tmp/$$a /tmp/$$b >/tmp/$$missing
  447. Xcomm -23 /tmp/$$a /tmp/$$b >/tmp/$$remove
  448. X
  449. Xegrep "^([^.]*\.|general|junk|control)" $NEWSCTL/active | sed -n "/m\$/s/ .*//p" |
  450. X      sort -u > /tmp/$$amod.all
  451. Xegrep "^([^.]*\.|general)"  $NEWSCTL/newsgroups |
  452. Xsed -n "/Moderated/s/[     ][     ]*.*//p" | sort -u > /tmp/$$ng.mod
  453. X
  454. Xcomm -12 /tmp/$$missing /tmp/$$ng.mod >/tmp/$$add.mod
  455. Xcomm -23 /tmp/$$missing /tmp/$$ng.mod >/tmp/$$add.unmod
  456. Xcat /tmp/$$add.mod /tmp/$$add.unmod >>/tmp/$$add
  457. X
  458. Xcomm -23 /tmp/$$amod.all /tmp/$$remove >/tmp/$$amod
  459. Xcomm -13 /tmp/$$ng.mod /tmp/$$amod >/tmp/$$ismod
  460. Xcomm -23 /tmp/$$ng.mod /tmp/$$amod >/tmp/$$nm.all
  461. Xcomm -23 /tmp/$$nm.all /tmp/$$add >/tmp/$$notmod
  462. X
  463. X
  464. Xif test -s /tmp/$$remove; then
  465. X    echo "The following newsgroups are not valid and should be removed."
  466. X    sed "s/^/    /" /tmp/$$remove
  467. X    echo ""
  468. X    echo "You can do this by executing the command:"
  469. X    echo "    $NEWSCTL/ctl/rmgroup.auto \\"
  470. X    sed 's;.*;        & \\;' /tmp/$$remove
  471. X    echo ""
  472. Xfi 2>&1 >/tmp/$$out
  473. X
  474. Xif test -s /tmp/$$add; then
  475. X    echo "The following newsgroups were missing." # "and were added."
  476. X    sed "s/^/    /" /tmp/$$add
  477. X    echo ""
  478. X    echo "You can add them by executing the command(s):"
  479. X    for i in `cat /tmp/$$add.unmod`
  480. X    do
  481. X        echo "$NEWSBIN/ctl/newgroup $i </dev/null"
  482. X    done
  483. X    for i in `cat /tmp/$$add.mod`
  484. X    do
  485. X        echo "$NEWSBIN/ctl/newgroup $i moderated </dev/null"
  486. X    done
  487. X    echo ""
  488. X
  489. X#    for i in `cat /tmp/$$add`
  490. X#    do
  491. X# *** "Subject: cmsg " is a hideous botch of a kludge-hack; avoid it!
  492. X#        inews -h <<!
  493. X#Control: newgroup $i
  494. X#Newsgroups: control
  495. X#Subject: newgroup $i
  496. X#Distribution: general
  497. X#
  498. X#Create $i locally.
  499. X#!
  500. X#    done
  501. X
  502. Xfi 2>&1 >>/tmp/$$out
  503. X
  504. Xif test -s /tmp/$$ismod;then
  505. X    echo "The following newsgroups are not moderated and are marked moderated."
  506. X    sed "s/^/    /" /tmp/$$ismod
  507. X    echo ""
  508. X    echo "You can correct this by executing the command(s):"
  509. X    for i in `cat /tmp/$$ismod`
  510. X    do
  511. X        echo  "$NEWSBIN/ctl/newgroup $i </dev/null"
  512. X    done
  513. X    echo ""
  514. Xfi 2>&1 >>/tmp/$$out
  515. X
  516. X
  517. Xif test -s /tmp/$$notmod; then
  518. X    echo "The following newsgroups are moderated and not marked so."
  519. X    sed "s/^/    /" /tmp/$$notmod
  520. X    echo ""
  521. X    echo "You can correct this by executing the command(s):"
  522. X    for i in `cat /tmp/$$notmod`
  523. X    do
  524. X        echo "$NEWSBIN/ctl/newgroup $i moderated </dev/null"
  525. X    done
  526. X    echo ""
  527. Xfi 2>&1 >>/tmp/$$out
  528. X
  529. X
  530. Xif test -s /tmp/$$out; then
  531. X    (echo "Subject: Problems with your active file"; echo "";
  532. X     cat /tmp/$$out) | mail $NEWSMASTER
  533. Xfi
  534. X
  535. Xrm -f /tmp/$$*        # clean up temporaries
  536. !
  537. echo 'relay/altctl/README':
  538. sed 's/^X//' >'relay/altctl/README' <<'!'
  539. XThis is a rmgroup that will do automatic deletion, and a checkgroups
  540. Xthat ties into same.  WE DO NOT RECOMMEND INSTALLATION OF THESE.
  541. !
  542. echo 'relay/hdrcommon.c':
  543. sed 's/^X//' >'relay/hdrcommon.c' <<'!'
  544. X/*
  545. X * Usenet header common code.
  546. X */
  547. X
  548. X#include <stdio.h>
  549. X#include <sys/types.h>
  550. X#include "libc.h"
  551. X#include "news.h"
  552. X#include "headers.h"
  553. X#include "hdrint.h"
  554. X
  555. Xvoid
  556. Xhdrdebug(state)
  557. Xint state;
  558. X{
  559. X    headdebug = state;
  560. X}
  561. X
  562. Xvoid
  563. Xhdrinit(hdrs)            /* zero all elements of hdrs */
  564. Xregister struct headers *hdrs;
  565. X{
  566. X    hdrs->h_subj = NULL;
  567. X    hdrs->h_ngs = NULL;
  568. X    hdrs->h_distr = NULL;
  569. X    hdrs->h_ctlcmd = NULL;
  570. X    hdrs->h_approved = NULL;
  571. X    hdrs->h_msgid = NULL;
  572. X    hdrs->h_artid = NULL;
  573. X    hdrs->h_expiry = NULL;
  574. X    hdrs->h_path = NULL;
  575. X    hdrs->h_sender = NULL;
  576. X}
  577. X
  578. Xboolean
  579. Xoldctl(hdrs)            /* true iff ngs are OLDCNTRL (cache in hdrs) */
  580. Xregister struct headers *hdrs;
  581. X{
  582. X#ifdef SLOWCTLMATCH
  583. X    return ngmatch(OLDCNTRL, hdrs->h_ngs);
  584. X#else
  585. X    register int ngslen = strlen(hdrs->h_ngs);
  586. X
  587. X    if (ngslen < STRLEN(SFXOLDCNTRL))    /* ngs too short */
  588. X        return NO;
  589. X    else                    /* check for .ctl suffix */
  590. X        /*
  591. X         * This is more general than RFC 850 specifies, but this
  592. X         * generality seems harmless.  This doesn't work for e.g.
  593. X         * x.y.ctl,z.q, which is a darn shame, but that's a violation
  594. X         * of common sense.
  595. X         */
  596. X        return STREQ(&hdrs->h_ngs[ngslen-STRLEN(SFXOLDCNTRL)],
  597. X            SFXOLDCNTRL);
  598. X#endif                        /* SLOWCTLMATCH */
  599. X}
  600. X
  601. Xvoid
  602. Xfreeheaders(hdrs)        /* free (assumed) malloced storage */
  603. Xregister struct headers *hdrs;
  604. X{
  605. X    nnfree(&hdrs->h_subj);
  606. X    nnfree(&hdrs->h_ngs);
  607. X    nnfree(&hdrs->h_distr);
  608. X    nnfree(&hdrs->h_ctlcmd);
  609. X    nnfree(&hdrs->h_approved);
  610. X    nnfree(&hdrs->h_msgid);
  611. X    nnfree(&hdrs->h_artid);
  612. X    nnfree(&hdrs->h_expiry);
  613. X    nnfree(&hdrs->h_path);
  614. X    nnfree(&hdrs->h_sender);
  615. X}
  616. !
  617. echo 'relay/hdrdefs.c':
  618. sed 's/^X//' >'relay/hdrdefs.c' <<'!'
  619. X/*
  620. X * Usenet header definitions (see ARPA Internet RFCs 1036 nee 850 & 822;
  621. X *    for a second opinion, see The Hideous Name by Pike & Weinberger).
  622. X *
  623. X * Headers are parsed and modified and copied in one pass.
  624. X * Nevertheless, the code is in pieces: hdrdefs.c, hdrcommon.c,
  625. X * hdrparse.c, hdrmunge.c.
  626. X */
  627. X
  628. X#include <stdio.h>
  629. X#include <sys/types.h>
  630. X#ifdef REALSTDC
  631. X#include <stdlib.h>
  632. X#endif                /* REALSTDC */
  633. X#include "news.h"
  634. X#include "headers.h"
  635. X#include "hdrint.h"        /* may define "const" */
  636. X
  637. X#ifndef offsetof
  638. X#define offsetof(type, mem) ((char *)&((type *)NULL)->mem - (char *)NULL)
  639. X#endif
  640. X
  641. X/* "mandatory" headers (also From:, Date:) */
  642. Xstatic const char msgnm[] =    "Message-ID:";    /* for rejection */
  643. Xstatic const char ngsnm[] =    "Newsgroups:";    /* filing, clone for Xref */
  644. Xstatic const char pathnm[] =    "Path:";    /* rejection, extend (damn) */
  645. Xstatic const char subjnm[] =    "Subject:";    /* for ctl. msgs. */
  646. X
  647. X/* optional headers */
  648. Xstatic const char appnm[] =    "Approved:";    /* for mod. groups */
  649. Xstatic const char ctlnm[] =    "Control:";    /* ctl. msg. */
  650. Xstatic const char expnm[] =    "Expires:";    /* for history */
  651. Xstatic const char distrnm[] =    "Distribution:";    /* for transmission */
  652. Xstatic const char sendnm[] =    "Sender:";    /* for mod. groups */
  653. Xstatic const char xrefnm[] =    "Xref:";    /* to *replace* (damn!)*/
  654. X
  655. X/* obsolete "useful" headers */
  656. Xstatic const char artnm[] =    "Article-I.D.:";    /* obs. Message-ID: */
  657. X
  658. X/* obsolete useless headers: delete them all on contact */
  659. Xstatic const char datercvnm[] = "Date-Received:";
  660. Xstatic const char rcvnm[] =    "Received:";    /* obsolete Date-Received: */
  661. Xstatic const char postnm[] =    "Posted:";    /* obsolete Date: */
  662. Xstatic const char postversnm[] = "Posting-Version:";
  663. Xstatic const char rlyversnm[] = "Relay-Version:";
  664. Xstatic const char illobjnm[] = "Illegal-Object:";    /* zmailer bitching */
  665. X
  666. Xstatic const struct hdrdef msghdr = {
  667. X    msgnm, STRLEN(msgnm), offsetof(struct headers, h_msgid) };
  668. Xstatic const struct hdrdef ngshdr = {
  669. X    ngsnm, STRLEN(ngsnm), offsetof(struct headers, h_ngs) };
  670. Xconst struct hdrdef pathhdr = {
  671. X    pathnm, STRLEN(pathnm), offsetof(struct headers, h_path) };
  672. Xstatic const struct hdrdef subjhdr = {
  673. X    subjnm, STRLEN(subjnm), offsetof(struct headers, h_subj) };
  674. X
  675. Xstatic const struct hdrdef apphdr = {
  676. X    appnm, STRLEN(appnm), offsetof(struct headers, h_approved) };
  677. Xstatic const struct hdrdef ctlhdr = {
  678. X    ctlnm, STRLEN(ctlnm), offsetof(struct headers, h_ctlcmd) };
  679. Xstatic const struct hdrdef exphdr = {
  680. X    expnm, STRLEN(expnm), offsetof(struct headers, h_expiry) };
  681. Xstatic const struct hdrdef distrhdr = {
  682. X    distrnm, STRLEN(distrnm), offsetof(struct headers, h_distr) };
  683. Xstatic const struct hdrdef sendhdr = {
  684. X    sendnm, STRLEN(sendnm), offsetof(struct headers, h_sender) };
  685. Xconst struct hdrdef xrefhdr = { xrefnm, STRLEN(xrefnm), -1 };
  686. X
  687. Xstatic const struct hdrdef arthdr = {
  688. X    artnm, STRLEN(artnm), offsetof(struct headers, h_artid) };
  689. X
  690. Xstatic const struct hdrdef datrcvhdr = { datercvnm, STRLEN(datercvnm), -1 };
  691. Xstatic const struct hdrdef rcvhdr = { rcvnm, STRLEN(rcvnm), -1 };
  692. Xstatic const struct hdrdef psthdr = { postnm, STRLEN(postnm), -1 };
  693. Xstatic const struct hdrdef pstvrshdr = { postversnm, STRLEN(postversnm), -1 };
  694. Xstatic const struct hdrdef rlyvrshdr = { rlyversnm, STRLEN(rlyversnm), -1 };
  695. Xstatic const struct hdrdef illobjhdr = { illobjnm, STRLEN(illobjnm), -1 };
  696. X
  697. Xconst hdrlist parsehdrs = {    /* these are parsed into a struct headers */
  698. X    &msghdr,
  699. X    &arthdr,        /* obsolete */
  700. X    &ngshdr,
  701. X    &pathhdr,        /* modified by hdrmunge.c (emithdr()) */
  702. X    &subjhdr,
  703. X    /* start optional headers */
  704. X    &apphdr,
  705. X    &ctlhdr,
  706. X    &distrhdr,
  707. X    &exphdr,
  708. X    &sendhdr,
  709. X    NULL
  710. X};
  711. X/*
  712. X * the following noxious headers are deleted on contact because neighbours
  713. X * still send them and they are big.  in an ideal world, they wouldn't be
  714. X * sent and thus we wouldn't need to delete them.
  715. X * It is tempting to delete Article-I.D.: too, but it may be too soon for that.
  716. X */
  717. Xconst hdrlist hdrvilest = {
  718. X    &xrefhdr,        /* regenerated by fileart() if needed */
  719. X    &datrcvhdr,
  720. X    &rcvhdr,
  721. X    &psthdr,
  722. X    &pstvrshdr,
  723. X    &rlyvrshdr,
  724. X    &illobjhdr,
  725. X    NULL,
  726. X};
  727. X
  728. Xboolean headdebug = NO;
  729. !
  730. echo 'relay/hdrint.h':
  731. sed 's/^X//' >'relay/hdrint.h' <<'!'
  732. X/*
  733. X * definitions internal to the header modules (hdr*.c)
  734. X */
  735. X#ifdef REALSTDC
  736. X#undef REALSTDC
  737. X#endif
  738. X#ifdef __STDC__
  739. X#if __STDC__ >= 1        /* microsoft, etc. brain-damage */
  740. X#define REALSTDC        /* truth in advertising: really ANSI */
  741. X#endif                /* __STDC__ < 1 */
  742. X#endif                /* __STDC__ */
  743. X
  744. X#ifndef REALSTDC
  745. X#define const
  746. X#endif                /* REALSTDC */
  747. X
  748. X#ifndef DEFDIST
  749. X#define DEFDIST "world"        /* default Distribution: */
  750. X#endif
  751. X#ifndef DEFMSGID
  752. X#define DEFMSGID "<message-id@absent>"
  753. X#endif
  754. X
  755. X#define JUNK "junk"
  756. X#define ALL "all"
  757. X
  758. X#ifdef SLOWCTLMATCH
  759. X#define OLDCNTRL "all.all.ctl"
  760. X#endif
  761. X#define SFXOLDCNTRL ".ctl"
  762. X
  763. Xstruct hdrdef {
  764. X    const char *hdrnm;    /* ascii name */
  765. X    unsigned hdrlen;    /* STRLEN(hdrnm) */
  766. X    int hdroff;        /* offset into struct header */
  767. X};
  768. Xtypedef const struct hdrdef *hdrlist[];
  769. X
  770. Xextern const struct hdrdef pathhdr, xrefhdr;
  771. Xextern const hdrlist parsehdrs, hdrvilest;
  772. X
  773. Xextern boolean headdebug;
  774. !
  775. echo 'relay/hdrmunge.c':
  776. sed 's/^X//' >'relay/hdrmunge.c' <<'!'
  777. X/*
  778. X * Usenet header modification & generation
  779. X *
  780. X * Ideally, headers should never be modified; message text, including
  781. X * headers, should be passed untouched.  Path: and Xref: demand that this
  782. X * rule be violated, and we delete huge obsolete headers to save space.
  783. X *
  784. X * Delete obsolete & large headers and Xref (can't be right),
  785. X * as headers are read.
  786. X * Recognise Newsgroups: and if more than one group, generate Xref: &
  787. X * leave holes for the article numbers - fileart will fill them in.
  788. X * (Make rn look in history instead?)
  789. X *
  790. X * Pile up headers into a static buffer until end of buffer (i.e. next
  791. X * line might not fit) [checked in hdrsave()], end of headers, end of
  792. X * file, or byte count is exhausted [checked in cparttofp].  Then write
  793. X * them to disk.  Prepend hostname! to Path: value, during output.
  794. X */
  795. X
  796. X#include <stdio.h>
  797. X#include <sys/types.h>
  798. X#include "libc.h"
  799. X#include "news.h"
  800. X#include "fileart.h"
  801. X#include "headers.h"
  802. X#include "article.h"
  803. X#include "hdrint.h"
  804. X#include "msgs.h"
  805. X
  806. X/*
  807. X * HDRMEMSIZ is the length of a header-stashing buffer, which is used
  808. X * only during article-header copying.
  809. X * HDRMEMSIZ can be too small if memory is tight & will only hurt performance.
  810. X * Derivation: 630 bytes is a large header (after discarding *-Version:, etc.).
  811. X */
  812. X#ifndef HDRMEMSIZ
  813. X#ifdef SMALLMEM
  814. X#define HDRMEMSIZ 630
  815. X#else
  816. X#define HDRMEMSIZ 8192            /* # bytes for saving headers in core */
  817. X#endif    /* SMALLMEM */
  818. X#endif    /* HDRMEMSIZ */
  819. X
  820. X/* private */
  821. Xstatic char **hptrs = NULL;    /* saved-headers-ptrs array; allocated once */
  822. X
  823. X/* forwards */
  824. XFORWARD void emithdr(), hdrsave();
  825. X
  826. X/*
  827. X * Generate an Xref: header from art->a_files.
  828. X * Turn slashes in art->a_files into colons in Xref:.
  829. X */
  830. Xvoid
  831. Xemitxref(art)
  832. Xregister struct article *art;
  833. X{
  834. X    register char *slashp, *xrefs;
  835. X
  836. X    if (!art->a_xref) {
  837. X        art->a_xref = YES;
  838. X        xrefs = strsave(art->a_files);
  839. X        for (slashp = xrefs; (slashp = index(slashp, FNDELIM)) != NULL; )
  840. X            *slashp++ = ':';
  841. X        if (fprintf(art->a_artf, "%s %s %s\n",
  842. X            xrefhdr.hdrnm, hostname(), xrefs) == EOF)
  843. X            fulldisk(art, spoolnm(art));
  844. X        free(xrefs);
  845. X    }
  846. X}
  847. X
  848. X/*
  849. X * --- header copying starts here ---
  850. X */
  851. X
  852. X/*
  853. X * Copy headers and delete or modify a few.  Assumes hdrparse has been called.
  854. X * Delete obsolete & large headers and Xref.
  855. X * Pile up other headers for later output (Path: is changed during output).
  856. X *
  857. X * art->a_artf may be NULL, and may get set by hdrsave.
  858. X */
  859. XSTATIC void
  860. Xhdrmunge(art, buffer, hdrlen, hdrlst)
  861. Xregister struct article *art;
  862. Xregister char *buffer;
  863. Xint hdrlen;            /* optimisation only */
  864. Xhdrlist hdrlst;            /* headers of negative utility: snuff 'em */
  865. X{
  866. X    register struct hdrdef **vhp;
  867. X
  868. X    if (headdebug)
  869. X        (void) fputs(buffer, stderr);
  870. X    for (vhp = hdrlst; *vhp != NULL; vhp++)
  871. X        if (STREQN(buffer, (*vhp)->hdrnm, (int)(*vhp)->hdrlen))
  872. X            return;            /* don't save this header */
  873. X    hdrsave(art, buffer, hdrlen);
  874. X}
  875. X
  876. X/*
  877. X * If headers already dumped, just write to art->a_artf.
  878. X * Else if there is room, stash "hdr" away until end of headers is seen
  879. X * (could just wait until Newsgroups: and Control: are seen, if seen)
  880. X * or there is no room left in the header buffer, then open the first
  881. X * article link (on art->a_artf) and dump the saved headers and the current
  882. X * header to it.
  883. X *
  884. X * Copy into art->a_haccum (in future, could read in directly,
  885. X * iff copying is high on the profile).
  886. X *
  887. X * hdrstore is static because it is used repeatedly, it only makes sense
  888. X * to have one active at a time, and there is no memory saving in allocating
  889. X * and deallocating it, particularly since copyart's (header) buffer must
  890. X * coexist with hdrstore.
  891. X */
  892. XSTATIC void
  893. Xhdrsave(art, hdr, hdrlen)
  894. Xregister struct article *art;
  895. Xchar *hdr;
  896. Xregister int hdrlen;            /* optimisation only */
  897. X{
  898. X    if (art->a_artf != NULL) {
  899. X        emithdr(art, hdr, hdrlen);
  900. X        return;
  901. X    }
  902. X    if (art->a_haccum == NULL) {
  903. X        static char hdrstore[HDRMEMSIZ];
  904. X
  905. X        art->a_haccum = hdrstore;
  906. X        art->a_haccum[0] = '\0';
  907. X        art->a_hnext = art->a_haccum;
  908. X        art->a_hbytesleft = HDRMEMSIZ;
  909. X    }
  910. X    if (art->a_hbytesleft > hdrlen) {
  911. X        /* add new ptr.-to-this-header to tail of saved-hdr-ptr.-list */
  912. X        if (art->a_hptrs == NULL) {
  913. X            art->a_hpused = 0;
  914. X            art->a_hpalloced = MINSHPTRS;
  915. X            if (hptrs == NULL)    /* once only */
  916. X                hptrs = (char **) nemalloc((unsigned)
  917. X                    (art->a_hpalloced * sizeof(char *)));
  918. X            art->a_hptrs = hptrs;
  919. X        }
  920. X        while (art->a_hpused >= art->a_hpalloced) {
  921. X            art->a_hpalloced += MINSHPTRS;
  922. X            art->a_hptrs = hptrs = (char **)
  923. X                realloc((char *)art->a_hptrs, (unsigned)
  924. X                (art->a_hpalloced * sizeof(char *)));
  925. X            if (art->a_hptrs == NULL)
  926. X                errunlock("out of memory (for art->a_hptrs)", "");
  927. X        }
  928. X        art->a_hptrs[art->a_hpused++] = art->a_hnext;
  929. X
  930. X        /* (void) strcat(art->a_haccum, hdr); */
  931. X        (void) strcpy(art->a_hnext, hdr);
  932. X        art->a_hnext += hdrlen;        /* points at NUL byte */
  933. X        art->a_hbytesleft -= hdrlen;
  934. X    } else {
  935. X        hdrdump(art, NOTALLHDRS);    /* don't file */
  936. X        if (art->a_artf != NULL)
  937. X            emithdr(art, hdr, hdrlen);
  938. X    }
  939. X}
  940. X
  941. X/*
  942. X * Change Path: while writing it out, just dump other headers (hdr) verbatim.
  943. X */
  944. XSTATIC void
  945. Xemithdr(art, hdr, hdrlen)
  946. Xregister struct article *art;
  947. Xchar *hdr;
  948. Xregister int hdrlen;
  949. X{
  950. X    if (STREQN(hdr, pathhdr.hdrnm, (int)pathhdr.hdrlen)) {
  951. X        register char *oldpath, *hostnm = hostname();
  952. X
  953. X        oldpath = skipsp(&hdr[pathhdr.hdrlen]);
  954. X        /*
  955. X         * V7 f?printf return 0 or EOF, not a byte count, so it is
  956. X         * not portable to use fprintf's return value as a byte count.
  957. X         */
  958. X        if (fprintf(art->a_artf, "%s %s!", pathhdr.hdrnm, hostnm) ==
  959. X            EOF || fputs(oldpath, art->a_artf) == EOF)
  960. X            fulldisk(art, spoolnm(art));
  961. X        else {
  962. X            static unsigned hostlen = 0;
  963. X
  964. X            if (hostlen == 0)
  965. X                hostlen = strlen(hostnm);
  966. X            art->a_charswritten += pathhdr.hdrlen + STRLEN(" ") +
  967. X                hostlen + STRLEN("!") + strlen(oldpath);
  968. X        }
  969. X    } else {
  970. X        if (fwrite(hdr, hdrlen, 1, art->a_artf) != 1)
  971. X            fulldisk(art, spoolnm(art));
  972. X        else
  973. X            art->a_charswritten += hdrlen;
  974. X    }
  975. X}
  976. X
  977. X/*
  978. X * Write out saved headers after opening on art->a_artf either a temporary
  979. X * file (using mktemp(3)) or the first article link, based on art->h.h_ngs &
  980. X * nxtartnum(); set a_tmpf to which ever name is opened.
  981. X * Modify Path: value on the way.
  982. X *
  983. X * If all headers were seen, then open the first link, link to the rest,
  984. X * and generate Xref:, else open a temporary name and write the article
  985. X * there (it will get filed later by hdrdump(...,ALLHDRS) or in insart()).
  986. X */
  987. Xvoid
  988. Xhdrdump(art, allhdrsseen)
  989. Xregister struct article *art;
  990. Xboolean allhdrsseen;        /* all headers seen & hdrdeflt() called? */
  991. X{
  992. X    if (art->a_filed)
  993. X        return;
  994. X    if (allhdrsseen)
  995. X        fileart(art);            /* set a_tmpf */
  996. X    else if (art->a_artf == NULL) {
  997. X        nnfree(&art->a_tmpf);
  998. X        art->a_tmpf = strsave(SPOOLTMP);
  999. X        (void) mktemp(art->a_tmpf);
  1000. X        art->a_unlink = YES;
  1001. X        art->a_artf = fopenwclex(art->a_tmpf, "w");
  1002. X        if (art->a_artf == NULL)
  1003. X            art->a_status |= ST_DROPPED;
  1004. X    }
  1005. X    if (art->a_artf != NULL &&
  1006. X        art->a_haccum != NULL && art->a_haccum[0] != '\0') {
  1007. X        register int i;
  1008. X        register char **pp;
  1009. X        register char *nxtln;
  1010. X        register int saved;
  1011. X
  1012. X        for (i = 0, pp = art->a_hptrs; i < art->a_hpused; ++i, ++pp) {
  1013. X            if (i >= art->a_hpused-1)    /* last in-core hdr? */
  1014. X                nxtln = art->a_hnext;
  1015. X            else
  1016. X                nxtln = pp[1];
  1017. X            saved = *nxtln;
  1018. X            *nxtln = '\0';
  1019. X            emithdr(art, *pp, nxtln - *pp);
  1020. X                 *nxtln = saved;            /* restore */
  1021. X        }
  1022. X        /* art->a_haccum could be freed and zeroed here, if malloced */
  1023. X        art->a_haccum[0] = '\0';    /* paranoia */
  1024. X        art->a_hnext = art->a_haccum;    /* for next article */
  1025. X        art->a_hpused = 0;        /* trash saved-header-ptr-list */
  1026. X        /* reduce saved-header-ptr-list to original size */
  1027. X        if (art->a_hpalloced > MINSHPTRS) {
  1028. X            art->a_hpalloced = MINSHPTRS;
  1029. X            art->a_hptrs = hptrs = (char **)
  1030. X                realloc((char *)art->a_hptrs, (unsigned)
  1031. X                (art->a_hpalloced * sizeof(char *)));
  1032. X            if (hptrs == NULL)
  1033. X                errunlock("can't free a_hptrs memory", "");
  1034. X        }
  1035. X    }
  1036. X}
  1037. X
  1038. X/*
  1039. X * hdrparse remembers this header if it's interesting.
  1040. X * hdrmunge needs art->h.h_ngs to be set, so it is called second.
  1041. X * hdrmunge saves or writes this header, unless it's deemed a waste of bytes.
  1042. X * hdrmunge may call hdrdump(art, NOTALLHDRS).
  1043. X * hdrdump counts art->a_charswritten.
  1044. X */
  1045. Xvoid
  1046. Xhdrdigest(art, line, hdrlen)
  1047. Xregister struct article *art;
  1048. Xregister char *line;
  1049. Xint hdrlen;
  1050. X{
  1051. X        hdrparse(&art->h, line, parsehdrs);
  1052. X    hdrmunge(art, line, hdrlen, hdrvilest);
  1053. X}
  1054. !
  1055. echo 'relay/hdrparse.c':
  1056. sed 's/^X//' >'relay/hdrparse.c' <<'!'
  1057. X/*
  1058. X * Usenet header parsing and remembering.
  1059. X */
  1060. X
  1061. X#include <stdio.h>
  1062. X#include <ctype.h>
  1063. X#include <sys/types.h>
  1064. X#include "libc.h"
  1065. X#include "news.h"
  1066. X#include "headers.h"
  1067. X#include "hdrint.h"
  1068. X
  1069. X/*
  1070. X * Reset internal state of header parser.
  1071. X * (Empty the stomach of partially-digested headers.)
  1072. X */
  1073. Xvoid
  1074. Xhdrwretch()
  1075. X{
  1076. X    /* historical stub */
  1077. X}
  1078. X
  1079. X/*
  1080. X * Parse RFC822/850/1036 header into "hdrs".  Retain significant values.
  1081. X * Assumes ishdr has been called first.
  1082. X *
  1083. X * If a keyword matches one in hdrlst, store the value in *malloc'ed memory*
  1084. X * (N.B.).  freeheader() will free this memory.
  1085. X */
  1086. Xvoid
  1087. Xhdrparse(hdrs, line, hdrlst)
  1088. Xregister struct headers *hdrs;
  1089. Xregister char *line;
  1090. Xhdrlist hdrlst;                /* headers of positive utility */
  1091. X{
  1092. X    register struct hdrdef **hpp;
  1093. X
  1094. X    for (hpp = hdrlst; *hpp != NULL; hpp++)
  1095. X        if (STREQN(line, (*hpp)->hdrnm, (int)(*hpp)->hdrlen) &&
  1096. X            (*hpp)->hdroff >= 0) {    /* paranoia */
  1097. X            register char **ptrp =
  1098. X                (char **)((char *)hdrs+(*hpp)->hdroff);
  1099. X
  1100. X            nnfree(ptrp);    /* free prev. val. in this art. */
  1101. X            *ptrp = strsave(skipsp(&line[(*hpp)->hdrlen]));
  1102. X            if (*ptrp != NULL)
  1103. X                trim(*ptrp);    /* cut trailing \n */
  1104. X            break;
  1105. X        }
  1106. X}
  1107. X
  1108. X/*
  1109. X * default missing header values
  1110. X *
  1111. X * If strsave ever returns NULL on failure, instead of exiting,
  1112. X * then the strsave calls need to check for failure.
  1113. X *
  1114. X * We support control message *backwards* compatibility: if no Control:
  1115. X * header exists and the newsgroup matches all.all.ctl, use the Subject:
  1116. X * as the control message.  Ugh.
  1117. X */
  1118. Xvoid
  1119. Xhdrdeflt(hdrs)
  1120. Xregister struct headers *hdrs;
  1121. X{
  1122. X    if (hdrs->h_ngs == NULL)
  1123. X        hdrs->h_ngs = strsave(JUNK);
  1124. X    if (hdrs->h_distr == NULL)
  1125. X        hdrs->h_distr = strsave(DEFDIST);
  1126. X    if (hdrs->h_msgid == NULL && hdrs->h_artid != NULL)    /* obs. art.id. */
  1127. X        hdrs->h_msgid = strsave(hdrs->h_artid);
  1128. X    if (hdrs->h_msgid == NULL)
  1129. X        hdrs->h_msgid = strsave(DEFMSGID);
  1130. X    if (hdrs->h_msgid[0] == '\0') {
  1131. X        free(hdrs->h_msgid);
  1132. X        hdrs->h_msgid = strsave(DEFMSGID);
  1133. X    }
  1134. X    if (hdrs->h_expiry == NULL)
  1135. X        hdrs->h_expiry = strsave(DEFEXP);
  1136. X    if (hdrs->h_expiry[0] == '\0') {
  1137. X        free(hdrs->h_expiry);
  1138. X        hdrs->h_expiry = strsave(DEFEXP);
  1139. X    }
  1140. X    if (hdrs->h_subj == NULL)
  1141. X        hdrs->h_subj = strsave("");
  1142. X
  1143. X    if (hdrs->h_ctlcmd == NULL && oldctl(hdrs))
  1144. X        hdrs->h_ctlcmd = strsave(hdrs->h_subj);
  1145. X}
  1146. !
  1147. echo 'relay/headers.h':
  1148. sed 's/^X//' >'relay/headers.h' <<'!'
  1149. X/*
  1150. X * All the article header values worth retaining.
  1151. X * (strictly from headers in input.)
  1152. X *
  1153. X * All members of struct headers must point at malloced memory so that
  1154. X * freeheaders() can free it without having to keep track of what's
  1155. X * malloced and what's static.
  1156. X *
  1157. X * Furthermore, each member of headers must point at its own private copy
  1158. X * of its value string, for the above reason, and no code outside hdr*.c
  1159. X * may copy any member nor a modified copy of any member, though it may
  1160. X * copy the string pointed to by a (possibly modified) member.
  1161. X *
  1162. X * Perhaps C++ will allow this to be enforced by a strings class.
  1163. X * See section 6.9 of The C++ Programming Language for a candidate.
  1164. X */
  1165. Xstruct headers {
  1166. X    char *h_subj;    /* subject: only needed for controls, -> h_ctlcmd */
  1167. X    char *h_ngs;    /* newsgroups: used in filing, sys matching & all.all.ctl matching */
  1168. X    char *h_distr;    /* distribution for transmit */
  1169. X    char *h_ctlcmd;    /* control command */
  1170. X    char *h_approved;    /* needed for acceptance in moderated groups */
  1171. X    char *h_msgid;    /* needed for history & rejection */
  1172. X    char *h_artid;    /* needed for history & rejection (obs.) */
  1173. X    char *h_expiry;    /* needed for history */
  1174. X    char *h_path;    /* needed for transmit - must munge */
  1175. X    char *h_sender;    /* needed for transmit in case of moderation */
  1176. X};
  1177. X
  1178. X/* common */
  1179. Xextern void hdrdebug(), hdrinit(), freeheaders();
  1180. Xextern boolean oldctl();
  1181. X
  1182. X/* munge */
  1183. Xextern void emitxref(), hdrdump(), hdrdigest();
  1184. X
  1185. X/* parse */
  1186. Xextern void hdrwretch(), hdrparse(), hdrdeflt();
  1187. Xextern boolean ishdr(), contin();
  1188. !
  1189. echo 'relay/history.c':
  1190. sed 's/^X//' >'relay/history.c' <<'!'
  1191. X/*
  1192. X * history file bashing
  1193. X *
  1194. X * B 2.10+ news pulls a dirty (and undocumented) trick and converts
  1195. X * message-id's to lower case before using them as keys in the dbm file.
  1196. X * We don't; you'll want to fix your news readers accordingly.
  1197. X *
  1198. X * B 2.10.3+ rnews puts out a leading space before received
  1199. X * time if the article contains an Expires: header; tough.
  1200. X * C news does this right instead of compatibly.
  1201. X *
  1202. X * The second history field is really two: time-received and Expires: value,
  1203. X * separated by a tilde.  This is an attempt at partial compatibility with
  1204. X * B news, in that C expire can cope with B news history files.
  1205. X *
  1206. X * There is no point to storing seek offsets in network byte order in the
  1207. X * dbm file, since dbm files are machine-dependent and so can't be shared
  1208. X * by dissimilar machines anyway.
  1209. X */
  1210. X
  1211. X#include <stdio.h>
  1212. X#include <sys/types.h>
  1213. X#include "libc.h"
  1214. X#include "news.h"
  1215. X#include "config.h"
  1216. X#include "fgetmfs.h"
  1217. X#include "headers.h"
  1218. X#include "article.h"
  1219. X#include "history.h"
  1220. X#include "msgs.h"
  1221. X
  1222. X#define HISTNAME "history"    /* name of the history file in $NEWSCTL */
  1223. X#define FIELDSEP '\t'
  1224. X#define SUBFIELDSEP '~'
  1225. X
  1226. X/* give 0 & 2 pretty, SVIDish names */
  1227. X#ifndef SEEK_SET
  1228. X#define SEEK_SET 0
  1229. X#define SEEK_END 2
  1230. X#endif
  1231. X
  1232. Xtypedef struct {
  1233. X    char *dptr;
  1234. X    int dsize;
  1235. X} datum;
  1236. X
  1237. X/* private data */
  1238. Xstatic FILE *fp = NULL;
  1239. Xstatic char *filename;        /* absolute name of the ascii history file */
  1240. Xstatic boolean writable;
  1241. X
  1242. X/* libdbm imports */
  1243. Xextern int dbminit(), store();
  1244. Xextern datum fetch();
  1245. X
  1246. X/* forward decls */
  1247. XFORWARD datum getposhist();
  1248. XFORWARD void mkhistent(), sanitise(), subsanitise();
  1249. X
  1250. XSTATIC void
  1251. Xhistname()
  1252. X{
  1253. X    if (filename == NULL)
  1254. X        filename = strsave(ctlfile(HISTNAME));
  1255. X}
  1256. X
  1257. X/*
  1258. X * open the history files: ascii first, then dbm.
  1259. X * Try a+ mode first, then r mode, as dbm(3) does nowadays,
  1260. X * so that this routine can be used by any user to read history files.
  1261. X */
  1262. XSTATIC boolean
  1263. Xopenhist()
  1264. X{
  1265. X    histname();
  1266. X    if (fp == NULL) {
  1267. X        if ((fp = fopenclex(filename, "a+")) != NULL)
  1268. X            writable = YES;
  1269. X        else if ((fp = fopenwclex(filename, "r")) != NULL)
  1270. X            writable = NO;
  1271. X        /* else fp==NULL and fopenwclex just complained */
  1272. X
  1273. X        if (fp != NULL && dbminit(filename) < 0) {
  1274. X            /* no luck. dbminit will have just honked */
  1275. X            (void) nfclose(fp);    /* close ascii file */
  1276. X            fp = NULL;        /* and mark it */
  1277. X        }
  1278. X    }
  1279. X    return fp != NULL;
  1280. X}
  1281. X
  1282. XSTATIC datum
  1283. Xgetposhist(msgid)        /* return seek offset of history entry */
  1284. Xchar *msgid;
  1285. X{
  1286. X    register char *clnmsgid;
  1287. X    datum msgidkey, keypos;
  1288. X
  1289. X    msgidkey.dptr = NULL;
  1290. X    msgidkey.dsize = 0;
  1291. X    if (!openhist())
  1292. X        return msgidkey;
  1293. X    clnmsgid = strsave(msgid);
  1294. X    sanitise(clnmsgid);
  1295. X    msgidkey.dptr = clnmsgid;
  1296. X    msgidkey.dsize = strlen(clnmsgid) + 1;    /* include NUL */
  1297. X    keypos = fetch(msgidkey);        /* offset into ascii file */
  1298. X    free(clnmsgid);
  1299. X    return keypos;
  1300. X}
  1301. X
  1302. Xboolean
  1303. Xalreadyseen(msgid)        /* return true if found in the data base */
  1304. Xchar *msgid;
  1305. X{
  1306. X    datum posdatum;
  1307. X
  1308. X    posdatum = getposhist(msgid);
  1309. X    return posdatum.dptr != NULL;
  1310. X}
  1311. X
  1312. Xchar *                /* NULL if no history entry */
  1313. Xgethistory(msgid)        /* return existing history entry, if any */
  1314. Xchar *msgid;
  1315. X{
  1316. X    long pos = 0;
  1317. X    datum posdatum;
  1318. X
  1319. X    posdatum = getposhist(msgid);
  1320. X    if (posdatum.dptr != NULL && posdatum.dsize == sizeof pos) {
  1321. X        static char *histent = NULL;
  1322. X
  1323. X        memcpy((char *)&pos, posdatum.dptr, sizeof pos); /* align */
  1324. X        nnfree(&histent);
  1325. X        if (fseek(fp, pos, SEEK_SET) != -1 &&
  1326. X            (histent = fgetms(fp)) != NULL)
  1327. X            return histent;
  1328. X    }
  1329. X    return NULL;
  1330. X}
  1331. X
  1332. X/*
  1333. X * Return a pointer to the "files" field of a history entry.
  1334. X * Side-effect: trims \n from the history entry.
  1335. X */
  1336. Xchar *
  1337. Xfindfiles(histent)
  1338. Xchar *histent;
  1339. X{
  1340. X    register char *tabp;
  1341. X
  1342. X    trim(histent);
  1343. X    /* find start of 2nd field (arrival~expiry) */
  1344. X    tabp = index(histent, FIELDSEP);
  1345. X    if (tabp == NULL)
  1346. X        return NULL;                /* mangled entry */
  1347. X    /* find start of 3rd field (files list) */
  1348. X    else if ((tabp = index(tabp + 1, FIELDSEP)) == NULL)
  1349. X        return NULL;            /* cancelled or expired art. */
  1350. X    else
  1351. X        return tabp + 1;
  1352. X}
  1353. X
  1354. X/*
  1355. X * Generate a history entry from art.
  1356. X * The history entry will have tabs and newlines deleted from the
  1357. X * interior of fields, to keep the file format sane.
  1358. X * Optionally print the start of an "accepted" log file line (no \n)
  1359. X * (transmit() prints site names).
  1360. X */
  1361. Xvoid
  1362. Xhistory(art, startlog)
  1363. Xregister struct article *art;
  1364. Xboolean startlog;
  1365. X{
  1366. X    register char *msgid, *expiry;
  1367. X    time_t now;
  1368. X
  1369. X    msgid = strsave(nullify(art->h.h_msgid));
  1370. X    sanitise(msgid);    /* RFC 1036 forbids whitespace in msg-ids */
  1371. X    expiry = strsave(nullify(art->h.h_expiry));
  1372. X    sanitise(expiry);
  1373. X    subsanitise(expiry);
  1374. X
  1375. X    if (startlog) {
  1376. X        timestamp(stdout, &now);
  1377. X        if (printf(" %s + %s", sendersite(nullify(art->h.h_path)),
  1378. X            msgid) == EOF)
  1379. X            fulldisk(art, "stdout");
  1380. X    } else
  1381. X        now = time(&now);
  1382. X    if (!openhist())
  1383. X        art->a_status |= ST_DROPPED;    /* fall through and return */
  1384. X    else if (!writable) {
  1385. X        (void) fprintf(stderr, "%s: no write permission on `%s'\n",
  1386. X            progname, filename);
  1387. X        art->a_status |= ST_DROPPED;
  1388. X    } else if (fseek(fp, 0L, SEEK_END) == -1) {
  1389. X        warning("can't seek to end of `%s'", filename);
  1390. X        art->a_status |= ST_DROPPED;
  1391. X    } else
  1392. X        mkhistent(art, msgid, now, expiry);
  1393. X    free(msgid);
  1394. X    free(expiry);
  1395. X}
  1396. X
  1397. X/*
  1398. X * Internal interface to generate a history file entry,
  1399. X * assuming all sanity checking has been done already.
  1400. X * Record the (msgid, position) pair in the data base.
  1401. X *
  1402. X * The fflush is crash-proofing.
  1403. X */
  1404. XSTATIC void
  1405. Xmkhistent(art, msgid, now, expiry)
  1406. Xregister struct article *art;
  1407. Xchar *msgid, *expiry;
  1408. Xtime_t now;
  1409. X{
  1410. X    long pos;
  1411. X    datum msgidkey, posdatum;
  1412. X                                        
  1413. X    pos = ftell(fp);            /* get seek ptr for dbm */
  1414. X    if (fprintf(fp, "%s%c%ld%c%s", msgid, FIELDSEP, now, SUBFIELDSEP, expiry)
  1415. X        == EOF)
  1416. X        fulldisk(art, filename);
  1417. X    /* don't write 3rd field for cancelled but unseen articles */
  1418. X    if (art->a_files != NULL && art->a_files[0] != '\0')
  1419. X        if (fprintf(fp, "%c%s", FIELDSEP, art->a_files) == EOF)
  1420. X            fulldisk(art, filename);
  1421. X    (void) putc('\n', fp);
  1422. X    if (fflush(fp) == EOF)
  1423. X        fulldisk(art, filename);
  1424. X
  1425. X    msgidkey.dptr = msgid;
  1426. X    msgidkey.dsize = strlen(msgid) + 1;    /* include NUL */
  1427. X    posdatum.dptr = (char *)&pos;
  1428. X    posdatum.dsize = sizeof pos;
  1429. X#ifdef NOSTOREVAL
  1430. X    /* original v7 dbm store() returned no value */
  1431. X    (void) store(msgidkey, posdatum);
  1432. X#else
  1433. X    if (store(msgidkey, posdatum) < 0)
  1434. X        fulldisk(art, filename);
  1435. X#endif
  1436. X}
  1437. X
  1438. X/*
  1439. X * Turn \n & FIELDSEP into ' ' in s.
  1440. X */
  1441. XSTATIC void
  1442. Xsanitise(s)
  1443. Xregister char *s;
  1444. X{
  1445. X    for (; *s != '\0'; ++s)
  1446. X        if (*s == FIELDSEP || *s == '\n')
  1447. X            *s = ' ';
  1448. X}
  1449. X
  1450. X/*
  1451. X * Turn SUBFIELDSEP into ' ' in s.
  1452. X */
  1453. XSTATIC void
  1454. Xsubsanitise(s)
  1455. Xregister char *s;
  1456. X{
  1457. X    for (; *s != '\0'; ++s)
  1458. X        if (*s == SUBFIELDSEP)
  1459. X            *s = ' ';
  1460. X}
  1461. X
  1462. X/*
  1463. X * Generate a fake history file entry, given a message-id, an Expires:
  1464. X * value, and a "file" list ("net.foo/123").
  1465. X */
  1466. Xstatust
  1467. Xfakehist(fkmsgid, fkexpiry, fkfiles)
  1468. Xchar *fkmsgid, *fkexpiry, *fkfiles;
  1469. X{
  1470. X    struct article art;
  1471. X
  1472. X    artinit(&art);
  1473. X    art.h.h_msgid = fkmsgid;
  1474. X    art.h.h_expiry = fkexpiry;
  1475. X    art.a_files = fkfiles;
  1476. X    history(&art, STARTLOG);
  1477. X    return art.a_status;
  1478. X}
  1479. X
  1480. X/*
  1481. X * Append "group/artnumstr" to the file list in *art.
  1482. X */
  1483. Xvoid
  1484. Xhistupdfiles(art, group, artnumstr)
  1485. Xregister struct article *art;
  1486. Xregister char *group;
  1487. Xregister char *artnumstr;
  1488. X{
  1489. X    unsigned addlen = strlen(group)+STRLEN(SFNDELIM)+strlen(artnumstr)+1;
  1490. X
  1491. X    art->a_filed = YES;            /* make a note */
  1492. X    if (art->a_files == NULL) {
  1493. X        art->a_files = nemalloc(addlen);
  1494. X        art->a_files[0] = '\0';
  1495. X    } else {
  1496. X        art->a_files = realloc(art->a_files, (unsigned)
  1497. X            strlen(art->a_files) + STRLEN(" ") + addlen);
  1498. X        if (art->a_files == NULL)
  1499. X            errunlock("can't grow a_files", "");
  1500. X        (void) strcat(art->a_files, " ");
  1501. X    }
  1502. X    (void) strcat(art->a_files, group);    /* normal case */
  1503. X    (void) strcat(art->a_files, SFNDELIM);
  1504. X    (void) strcat(art->a_files, artnumstr);
  1505. X}
  1506. !
  1507. echo 'relay/history.h':
  1508. sed 's/^X//' >'relay/history.h' <<'!'
  1509. X/* imports from history.c */
  1510. Xextern char *findfiles(), *gethistory();
  1511. Xextern boolean alreadyseen();
  1512. Xextern statust fakehist();
  1513. Xextern void history(), histupdfiles();
  1514. X
  1515. X#define STARTLOG YES
  1516. X#define NOLOG NO
  1517. !
  1518. echo 'relay/ihave.c':
  1519. sed 's/^X//' >'relay/ihave.c' <<'!'
  1520. X/*
  1521. X * Implement the Usenet ihave/sendme control messages,
  1522. X * as per RFC 1036 (nee 850).
  1523. X */
  1524. X
  1525. X#include <stdio.h>
  1526. X#include <ctype.h>
  1527. X#include <sys/types.h>
  1528. X
  1529. X#include "libc.h"
  1530. X#include "news.h"
  1531. X#include "config.h"
  1532. X#include "headers.h"
  1533. X#include "article.h"
  1534. X#include "history.h"
  1535. X#include "fgetmfs.h"
  1536. X#include "msgs.h"
  1537. X#include "transmit.h"
  1538. X
  1539. X#ifndef SENDMEDISTR
  1540. X#define SENDMEDISTR "sendme"    /* kludge: distinguished distribution for sendmes */
  1541. X#endif
  1542. X#ifndef IHAVEDISTR
  1543. X#define IHAVEDISTR "ihave"    /* kludge: distinguished distribution for ihaves */
  1544. X#endif
  1545. X#ifndef AVEARTSIZE
  1546. X#define AVEARTSIZE 3000
  1547. X#endif
  1548. X
  1549. X#define PROTO_IHAVE 0
  1550. X#define PROTO_SENDME 1
  1551. X
  1552. X/* static forwards */
  1553. XFORWARD void doproto(), procmsgids(), procbodymsgids();
  1554. XFORWARD statust faketrans();
  1555. X
  1556. X/*
  1557. X * Read message-IDs from args or control message body,
  1558. X * look them up in history, post a sendme to to.remotesys (via the batcher
  1559. X * to avoid deadlock) consisting of the message-IDs not in history.
  1560. X * The "posting" consists of transmitting to a system matching
  1561. X * "to.remotesys" in sys, which had better have the I flag on.
  1562. X * ihave message-ID-list remotesys    generate a sendme from message-ID-list
  1563. X */
  1564. Xvoid
  1565. Xihave(args, art)
  1566. Xchar *args;
  1567. Xstruct article *art;
  1568. X{
  1569. X    doproto(args, art, IHAVEDISTR, PROTO_IHAVE);
  1570. X}
  1571. X
  1572. X/*
  1573. X * Read message-IDs from args or control message body,
  1574. X * transmit the corresponding articles to a system matching
  1575. X * "to.remotesys/sendme" in sys, which will typically name a batch file.
  1576. X * sendme message-ID-list remotesys    send articles named to remotesys
  1577. X */
  1578. Xvoid
  1579. Xsendme(args, art)
  1580. Xchar *args;
  1581. Xstruct article *art;
  1582. X{
  1583. X    doproto(args, art, SENDMEDISTR, PROTO_SENDME);
  1584. X}
  1585. X
  1586. Xstatic void
  1587. Xdoproto(args, art, distr, proto)
  1588. Xchar *args;
  1589. Xregister struct article *art;
  1590. Xchar *distr;
  1591. Xint proto;
  1592. X{
  1593. X    register char *argscp = skipsp(args), *remotesys;
  1594. X
  1595. X    if (*argscp == '\n' || *argscp == '\0')    /* no args */
  1596. X        return;
  1597. X
  1598. X    argscp = strsave(argscp);
  1599. X
  1600. X    /* dig out the remote system name */
  1601. X    remotesys = rindex(argscp, ' ');    
  1602. X    if (remotesys == NULL)            /* no msg-ids in command */
  1603. X        remotesys = argscp;
  1604. X    else {
  1605. X        remotesys = argscp + strlen(argscp) - 1;    /* last byte */
  1606. X        while (isascii(*remotesys) && isspace(*remotesys))
  1607. X            *remotesys-- = '\0';    /* back up to non-whitespace */
  1608. X        remotesys = rindex(argscp, ' ');
  1609. X        if (remotesys == NULL)        /* no msg-ids in command */
  1610. X            remotesys = argscp;
  1611. X        else
  1612. X            *remotesys++ = '\0';    /* separate msg-ids & sys name */
  1613. X    }
  1614. X    if (strcmp(remotesys, hostname()) != 0)    /* remotesys may not be me */
  1615. X        if (remotesys != argscp)    /* msg-ids in command */
  1616. X            procmsgids(art, argscp, remotesys, distr, proto);
  1617. X        else
  1618. X            procbodymsgids(art, remotesys, distr, proto);
  1619. X    free(argscp);
  1620. X}
  1621. X
  1622. X/*
  1623. X * Process a list of message-ids in msgidln: look them up
  1624. X * and "transmit" the articles to to.remotesys.
  1625. X */
  1626. Xstatic void
  1627. Xprocmsgids(art, msgidln, remotesys, distr, proto)
  1628. Xstruct article *art;
  1629. Xchar *msgidln, *remotesys, *distr;
  1630. Xint proto;
  1631. X{
  1632. X    char *cpmsgid = strsave(skipsp(msgidln));
  1633. X    register char *msgid = cpmsgid, *endmsgid;
  1634. X    register int save, sendit;
  1635. X
  1636. X    for (; *msgid != '\n' && *msgid != '\0'; msgid = skipsp(endmsgid)) {
  1637. X        for (endmsgid = msgid; *endmsgid != '\0' &&
  1638. X            isascii(*endmsgid) && !isspace(*endmsgid); ++endmsgid)
  1639. X            ;            /* skip msgid */
  1640. X
  1641. X        save = *endmsgid;
  1642. X        *endmsgid = '\0';        /* terminate msgid at whitespace */
  1643. X        if (proto == PROTO_IHAVE)
  1644. X            sendit = !alreadyseen(msgid);    /* sendme from remotesys */
  1645. X        else
  1646. X            sendit = alreadyseen(msgid);    /* sendme to remotesys */
  1647. X        if (sendit)
  1648. X            art->a_status |= faketrans(msgid, remotesys, distr, proto);
  1649. X        *endmsgid = save;
  1650. X    }
  1651. X    free(cpmsgid);
  1652. X}
  1653. X
  1654. Xstatic void
  1655. Xprocbodymsgids(art, remotesys, distr, proto)
  1656. Xregister struct article *art;
  1657. Xchar *remotesys, *distr; 
  1658. Xint proto;
  1659. X{
  1660. X    register FILE *arttext;
  1661. X
  1662. X    arttext = fopenwclex(art->a_tmpf, "r");
  1663. X    if (arttext != NULL) {
  1664. X        char *line;
  1665. X
  1666. X        while ((line = fgetms(arttext)) != NULL && *line != '\n')
  1667. X            nnfree(&line);        /* skip header */
  1668. X        if (line != NULL) {        /* article body exists */
  1669. X            nnfree(&line);        /* toss blank separating line */
  1670. X            while ((line = fgetms(arttext)) != NULL) {
  1671. X                procmsgids(art, line, remotesys, distr, proto);
  1672. X                nnfree(&line);
  1673. X            }
  1674. X        }
  1675. X        (void) nfclose(arttext);
  1676. X    }
  1677. X}
  1678. X
  1679. X/*
  1680. X * Fake up a minimal article struct for msgid using group to.remotesys and
  1681. X * distribution distr, then invoke transmit().  If there is a history
  1682. X * entry for msgid, supply the list of file names too.
  1683. X * Generate a log entry for each message-id transmitted.
  1684. X */
  1685. Xstatic statust
  1686. Xfaketrans(msgid, remotesys, distr, proto)
  1687. Xregister char *msgid, *remotesys;
  1688. Xchar *distr;
  1689. Xint proto;
  1690. X{
  1691. X    struct article fakeart;
  1692. X    register char *ng;
  1693. X    register char *histent;
  1694. X    register struct article *fap = &fakeart;
  1695. X    time_t now;
  1696. X    statust status = ST_OKAY;
  1697. X
  1698. X    ng = nemalloc((unsigned)(STRLEN("to.") + strlen(remotesys) + 1));
  1699. X    (void) strcpy(ng, "to.");
  1700. X    (void) strcat(ng, remotesys);
  1701. X
  1702. X    artinit(fap);
  1703. X
  1704. X    fap->h.h_ngs = ng;
  1705. X    fap->h.h_distr = (distr != NULL? distr: ng);
  1706. X    fap->h.h_msgid = msgid;
  1707. X    fap->h.h_path = hostname();
  1708. X    fap->a_charswritten = AVEARTSIZE;
  1709. X    histent = gethistory(msgid);
  1710. X    if (histent == NULL || (fap->a_files = findfiles(histent)) == NULL)
  1711. X        fap->a_files = "no.such.article!";
  1712. X
  1713. X    timestamp(stdout, &now);    /* start log line */
  1714. X    if (printf(" %s %c %s", sendersite(nullify(fap->h.h_path)),
  1715. X        (proto == PROTO_IHAVE? 'i': 's'), fap->h.h_msgid) == EOF)
  1716. X        fulldisk(fap, "stdout");
  1717. X    transmit(fap, "");    /* write on batch file; write sys name on stdout */
  1718. X    (void) putchar('\n');        /* end log line */
  1719. X    status |= fap->a_status;    /* pass back failure writing batch file */
  1720. X
  1721. X    fap->h.h_ngs = NULL;
  1722. X    fap->h.h_distr = NULL;
  1723. X    fap->h.h_msgid = NULL;
  1724. X    fap->h.h_path = NULL;
  1725. X    fap->a_files = NULL;
  1726. X    artfree(fap);
  1727. X
  1728. X    free(ng);
  1729. X    return status;
  1730. X}
  1731. !
  1732. echo 'relay/io.c':
  1733. sed 's/^X//' >'relay/io.c' <<'!'
  1734. X/*
  1735. X * common i/o operations
  1736. X */
  1737. X
  1738. X#include <stdio.h>
  1739. X#include <sys/types.h>
  1740. X#include "news.h"
  1741. X#include "headers.h"
  1742. X#include "article.h"
  1743. X#include "msgs.h"
  1744. X
  1745. X/*
  1746. X * If *fpp is non-null, fclose it and check for errors.
  1747. X * On error, call fulldisk(art, name).
  1748. X */
  1749. Xvoid
  1750. Xnnfclose(art, fpp, name)
  1751. Xstruct article *art;
  1752. Xregister FILE **fpp;
  1753. Xchar *name;
  1754. X{
  1755. X    if (*fpp != NULL) {
  1756. X        if (nfclose(*fpp) == EOF)
  1757. X            fulldisk(art, name);
  1758. X        *fpp = NULL;        /* mark the stream closed */
  1759. X    }
  1760. X}
  1761. !
  1762. echo 'relay/io.h':
  1763. sed 's/^X//' >'relay/io.h' <<'!'
  1764. X/* imports from io.c */
  1765. Xextern void nnfclose();
  1766. !
  1767. echo done
  1768.  
  1769.  
  1770.