home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / cnews.tar / relay / procart.c < prev    next >
C/C++ Source or Header  |  1993-03-13  |  15KB  |  484 lines

  1. /*
  2.  * process a single incoming article
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <sys/types.h>
  9. #include <sys/timeb.h>        /* solely for getindate call */
  10.  
  11. #include "libc.h"
  12. #include "news.h"
  13. #include "active.h"
  14. #include "headers.h"
  15. #include "relay.h"
  16. #include "history.h"
  17. #include "msgs.h"
  18. #include "ngmatch.h"
  19. #include "system.h"
  20.  
  21. #define DAY (24L*60L*60L)
  22.  
  23. /*
  24.  * seconds of slop permitted: article dates may be this many seconds in the
  25.  * future.  It should be an hour, but for sites (e.g. in Australia) that
  26.  * emit local time incorrectly labelled as GMT.  They really should fix
  27.  * their software, but in the mean time, a day's slop will prevent their
  28.  * articles from being dropped.
  29.  */
  30. #define CLOCKSLOP DAY
  31.  
  32. /*
  33.  * COPYSIZE is the length of a bulk-copying buffer: the bigger the better,
  34.  * though fewer than 3% of articles exceed 8192 bytes (may 1988).
  35.  * It holds header lines first, and later holds bytes of the body.
  36.  * This buffer is allocated once at the start and never deallocated.
  37.  */
  38. #ifndef COPYSIZE
  39. #ifdef SMALLMEM
  40. #define COPYSIZE BUFSIZ        /* conserve memory at the expense of speed */
  41. #else
  42. #define COPYSIZE 8192        /* big enough even for worst-case 4.2bsd blocks */
  43. #endif                /* SMALLMEM */
  44. #endif                /* COPYSIZE */
  45.  
  46. /* imports */
  47. extern void decline();
  48.  
  49. /*
  50.  * Unlink all files in filelist, and optionally return article numbers.
  51.  * When removing a link, note any failure, but don't issue an error message.
  52.  * For one thing, cancel controls fail routinely because the article has been
  53.  * removed manually or never existed (a previous cancel arrived before its
  54.  * subject and generated a fake history entry).
  55.  */
  56. STATIC statust
  57. snuffmayreturn(filelist, artret)
  58. char *filelist;
  59. boolean artret;        /* return article numbers & note unlink errors? */
  60. {
  61.     register statust status = ST_OKAY;
  62.     register char *arts, *spacep, *slashp, *artnm;
  63.  
  64.     /* this is a deadly tedious job and I really should automate it */
  65.     for (arts = filelist; arts != NULL && arts[0] != '\0';
  66.          arts = (spacep == NULL? NULL: spacep+1)) {
  67.         spacep = strchr(arts, ' ');
  68.         if (spacep != NULL)
  69.             spacep[0] = '\0';    /* will be restored below */
  70.         artnm = strsave(arts);
  71.         if (spacep != NULL)
  72.             spacep[0] = ' ';    /* restore space */
  73.  
  74.         slashp = strchr(artnm, FNDELIM);
  75.         if (slashp != NULL)
  76.             slashp[0] = '\0';    /* will be restored below */
  77.         if (artret)
  78.             /* prevartnum will complain on i/o error to active */
  79.             (void) prevartnum(artnm); /* return assigned # */
  80.         if (slashp != NULL)
  81.             slashp[0] = FNDELIM;    /* restore slash */
  82.  
  83.         mkfilenm(artnm);
  84.         if (unlink(artnm) < 0) {
  85.             persistent(NOART, '\0', "can't unlink", "");
  86.             status |= ST_ACCESS;
  87.         }
  88.         free(artnm);
  89.     }
  90.     return status;
  91. }
  92.  
  93. statust
  94. snufffiles(filelist)        /* just unlink all files in filelist */
  95. char *filelist;
  96. {
  97.     /* don't return article numbers (NO) & return unlink errors */
  98.     return snuffmayreturn(filelist, NO);
  99. }
  100.  
  101. /*
  102.  * "Uninstall" an article: remove art->a_files (permanent names) and
  103.  * a_tmpf (temporary name if a_unlink set), and return assigned article #'s.
  104.  * If a_unlink isn't set, a_tmpf is a copy of the first link in art->a_files.
  105.  * Must be called before history() is called (or after it has failed),
  106.  * else there will be a history entry for the article, but no spool files.
  107.  * insart() need not be called first.
  108.  */
  109. void
  110. uninsart(art)
  111. register struct article *art;
  112. {
  113.     if (art->a_unlink && art->a_tmpf != NULL) {
  114.         (void) unlink(art->a_tmpf);    /* I don't wanna know... */
  115.         art->a_unlink = NO;
  116.     }
  117.     /* return article numbers (YES) & ignore unlink errors */
  118.     (void) snuffmayreturn(art->a_files, YES);
  119. }
  120.  
  121. /*
  122.  * If nothing has gone wrong yet,
  123.  * install the article on art->a_tmpf or art->a_files:
  124.  * The article should have been accepted and filed in copyart().
  125.  * Add history entries for the article.  Log arrival.
  126.  * Transmit the article to our neighbours.
  127.  * Process control mess(age)es.  ctlmsg can call transmit(fakeart,x)
  128.  * and generate log lines for cancels and ihave/sendme.
  129.  */
  130. STATIC void
  131. insart(art)
  132. register struct article *art;
  133. {
  134.     if (!(art->a_status&(ST_DROPPED|ST_REFUSED|ST_NEEDATTN))) {
  135.         if (!art->a_filed)            /* paranoia */
  136.             canthappen(art, 'i', "%s not filed by copyart!",
  137.                 art->h.h_msgid);
  138.         if (opts.dupsokay) {
  139.             time_t now;
  140.  
  141.             timestamp(stdout, &now);
  142.             if (printf(" %s + %s", /* TODO: special code for dup? */
  143.                 sendersite(nullify(art->h.h_path)),
  144.                 nullify(art->h.h_msgid)) == EOF)
  145.                 fulldisk(art, "stdout");
  146.         } else
  147.             history(art, STARTLOG);    /* history may be unwritable */
  148.         if (art->a_status&(ST_DROPPED|ST_REFUSED|ST_NEEDATTN)) {
  149.             uninsart(art);        /* t'was; can't keep article */
  150.             (void) putchar('\n');    /* ends the log line */
  151.         } else {
  152.             /* transmit() writes system names on stdout */
  153.             transmit(art, opts.exclude);
  154.             (void) putchar('\n');    /* ends the log line */
  155.             ctlmsg(art);        /* NCMP */
  156.         }
  157.         /* don't bother flushing stdout; it's only a log file */
  158.     }
  159.     art->a_status &= ~ST_REFUSED;    /* refusal is quite casual & common */
  160. }
  161.  
  162. /*
  163.  * print the leader of a refusal message about the article in "art".
  164.  */
  165. void
  166. prefuse(art)
  167. register struct article *art;
  168. {
  169.     timestamp(stdout, (time_t *)NULL);
  170.     (void) printf(" %s - %s ", sendersite(nullify(art->h.h_path)),
  171.         nullify(art->h.h_msgid));
  172. }
  173.  
  174. /*
  175.  * Reject articles.  This can be arbitrarily picky.
  176.  * Only the headers are used to decide, so this can be called before
  177.  * the article is filed but after all the headers are read.
  178.  * Try to put the fastest tests first, especially if they often result
  179.  * in rejections.
  180.  */
  181. void
  182. reject(art)
  183. register struct article *art;
  184. {
  185.     register struct headers *hdrs = &art->h;
  186.     register char *ngs = hdrs->h_ngs;
  187.     register char *errstr;
  188.     register time_t date;
  189.     static time_t now, datestale;
  190.     extern time_t getindate();
  191.  
  192.     if (art->a_status&ST_REFUSED)
  193.         return;            /* already rejected */
  194.     if (now == 0) {
  195.         now = time(&now);
  196.         datestale = now - opts.staledays*DAY;
  197.     }
  198.     errstr = hdrreq(hdrs);
  199.     if (errstr != NULL) {
  200.         prefuse(art);
  201.         (void) fputs(errstr, stdout);
  202. #ifdef notdef
  203.     } else if (art->a_badhdr) {
  204.         prefuse(art);
  205.         (void) fputs("article \"header\" contains non-header line\n",
  206.             stdout);
  207. #endif
  208.     } else if (!msgidok(art))
  209.         (void) putchar('\n');    /* msgidok complained; end log line */
  210.     else if (hdrs->h_approved == NULL && moderated(ngs)) {
  211.         prefuse(art);
  212.         /* lots of logaudit()s here ... */
  213.         (void) printf("unapproved article in moderated group(s) `%s'\n",
  214.             ngs);
  215.     } else if ((date =
  216.         getindate(hdrs->h_date, (struct timeb *)NULL)) == -1) {
  217.         prefuse(art);
  218.         (void) printf("unparsable Date: `%s'\n", hdrs->h_date);
  219.     } else if (date > now + CLOCKSLOP) {
  220.         prefuse(art);
  221.         (void) printf("Date: too far in the future: `%s'\n",
  222.             hdrs->h_date);
  223.     } else if (opts.staledays > 0 && date < datestale) {
  224.         prefuse(art);
  225.         (void) printf("ancient date `%s'\n", hdrs->h_date);
  226.     } else if (spacein(ngs)) {
  227.         prefuse(art);
  228.         (void) printf("space in groups `%s'\n", ngs);
  229.     } else if (alreadyseen(hdrs->h_msgid)) {
  230.         if (opts.dupsokay)
  231.             return;
  232.         prefuse(art);
  233.         (void) fputs("duplicate\n", stdout);
  234.     } else if (hopcount(hdrs->h_path) > 0 &&
  235.         !ngpatmat(oursys()->sy_trngs, ngs)) {
  236.         /*
  237.          * non-local article, with all bad groups.
  238.          * (local articles with bad groups will be bounced
  239.          * by fileart when the groups aren't in active.)
  240.          */
  241.         if (opts.histreject)
  242.             history(art, NOLOG);
  243.         prefuse(art);
  244.         (void) printf("no subscribed groups in `%s'\n", ngs);
  245.     } else
  246.         return;            /* art was accepted */
  247.     decline(art);
  248. }
  249.  
  250. /*
  251.  * The loop copies header lines from input to output or a
  252.  * header output cache.  On exit, hdr will contain the first
  253.  * non-header line, if any, left over from the end of header copying.
  254.  *
  255.  * If the byte count is positive, read a line; if it doesn't return
  256.  * EOF and is a header, then adjust byte count, stash and munge headers.
  257.  * strlen(line) must be computed before hdrstash is called,
  258.  * as hdrstash (and thus hdrdigest) removes newlines.
  259.  *
  260.  * RFC 822 defines the message header as ending at a blank line, *not* at
  261.  * the first line that cannot syntactically be a header nor a header
  262.  * continuation.  As a result of this stunning bit of brilliance, we can end
  263.  * up with non-header lines in the message header, though they are illegal.
  264.  *
  265.  * Don't complain if this article has already been refused.
  266.  *
  267.  * TODO: Cope with NULs in header input, which bugger fgets and friends
  268.  * and throw off our byte count, thus buggering unbatching.
  269.  */
  270. char *                    /* first body line, from gethdr */
  271. hdrcopy(art, in)
  272. register struct article *art;
  273. FILE *in;
  274. {
  275.     register char *hdr = NULL;
  276.     long limit = art->a_unread + SIZENUL;
  277.     int is_hdr = NO;
  278.  
  279.     while (limit > SIZENUL && (hdr = gethdr(in, &limit, &is_hdr)) != NULL &&
  280.         is_hdr) {
  281.             hdrdigest(art, hdr, strlen(hdr));
  282.         hdr = NULL;            /* freed inside gethdr */
  283.     }
  284.     /* If we read a body line, gethdr has adjusted limit appropriately. */
  285.     art->a_unread = limit - SIZENUL;
  286.     if (!is_hdr && hdr != NULL && *hdr != '\n' &&
  287.         !(art->a_status&ST_REFUSED)) {
  288.         register char *hdrnonl = strsave(hdr);
  289.  
  290. #ifdef notdef
  291.         art->a_badhdr = YES;    
  292. #endif
  293.         trim(hdrnonl);
  294.         decline(art);
  295.         prefuse(art);
  296.         /* transient(art, '-', ...); */
  297.         (void) printf(
  298.         "article \"header\" contains non-RFC-1036-header line `%s'\n",
  299.             hdrnonl);
  300.         free(hdrnonl);
  301.     }
  302.     /* if is_hdr, there is no body: header fills limit */
  303.     return (is_hdr? NULL: hdr);
  304. }
  305.  
  306. /*
  307.  * If not yet uninstalled, and the disk filled (or the news system was found
  308.  * to be otherwise unwell), uninstall this article
  309.  * to remove any (zero-length) links and decrement the active article number.
  310.  * The ST_NEEDATTN status will prevent a history entry being generated later.
  311.  */
  312. void
  313. surveydamage(art, installedp)
  314. register struct article *art;
  315. register boolean *installedp;
  316. {
  317.     if (art->a_unread > 0 && art->a_blvmax) {
  318.         char bytes[30];
  319.  
  320.         (void) sprintf(bytes, "%ld", (long)art->a_unread);
  321.         logaudit(art, 'b', "short by %s bytes", bytes);
  322.         art->a_status |= ST_SHORT;
  323.             /* truncated input; NB.: don't uninstall this art. */
  324.     }
  325.     if (*installedp && art->a_status&ST_NEEDATTN) {
  326.         uninsart(art);
  327.         *installedp = NO;
  328.     }
  329. #ifdef WATCHCORE
  330.     {
  331.         char stbot;
  332.         extern char *sbrk();
  333.  
  334.         (void) printf("debugging memory use: top of data=%u",
  335.             (unsigned)sbrk(0));
  336.         (void) printf(", bottom of stack=%u\n", (unsigned)&stbot);
  337.     }
  338. #endif
  339. }
  340.  
  341. /*
  342.  * Copy article body.
  343.  * body will contain the first non-header line, if any,
  344.  * left over from the end of header copying.  Write it.
  345.  * Copy at most COPYSIZE bytes of body at a time and exactly art->a_unread
  346.  * bytes in total, barring EOF or a full disk. Then "block" is no longer needed.
  347.  * Force the article to disk, mostly for the benefit of control message
  348.  * processing.
  349.  *
  350.  * The copying buffer, block, is static because it is used repeatedly
  351.  * and persists through most of execution, so dynamic allocation
  352.  * and deallocation seems wasteful, but also for the benefit
  353.  * of compilers for odd machines (e.g. PE, 370s) which make
  354.  * implementing "large" automatic arrays difficult.
  355.  *
  356.  * Some people think the loop is ugly; I'm not sure why.
  357.  */
  358. STATIC void
  359. cpybody(art, in, body)
  360. register struct article *art;
  361. FILE *in;
  362. register char *body;
  363. {
  364.     register int readcnt;
  365.     register FILE *out = art->a_artf;
  366.     static char block[COPYSIZE];
  367.  
  368.     if (body != NULL) {            /* read too far? */
  369.         register int bodylen = strlen(body);
  370.  
  371.         if (out != NULL && fwrite(body, 1, bodylen, out) != bodylen)
  372.             fulldisk(art, spoolnm(art));
  373.         art->a_charswritten += bodylen;
  374.     }
  375.     for (; art->a_unread > 0 && !(art->a_status&ST_NEEDATTN) && !feof(in) &&
  376.         (readcnt = fread(block, 1, (int)min(art->a_unread, COPYSIZE), in)) >
  377.         0; art->a_unread -= readcnt, art->a_charswritten += readcnt)
  378.         if (out != NULL && fwrite(block, 1, readcnt, out) != readcnt)
  379.             fulldisk(art, spoolnm(art));
  380.     if (out != NULL && fflush(out) == EOF)
  381.         fulldisk(art, spoolnm(art));
  382. }
  383.  
  384. /*
  385.  * Copy the next charcnt bytes of "in" (may be not a disk file)
  386.  * to a permanent file under a (possibly) temporary name.
  387.  * After the headers are seen, accept or reject the article.
  388.  * Either uninstall the article described by art, or accept it and file it.
  389.  * If rejecting it, remove any links and give back assigned #'s
  390.  * (art->a_artf may still be open; arguably uninsart should close it).
  391.  * If rejected and the headers fit in core, no files will be opened.
  392.  * Must munge certain headers on the way & remember certain values.
  393.  * hdrmunge() or hdrdump() sets art->a_tmpf & art->a_artf.
  394.  * Unlink art->a_tmpf, if a temporary link.
  395.  */
  396. /* ARGSUSED inname */
  397. STATIC void
  398. copyart(art, in, inname)
  399. register struct article *art;
  400. register FILE *in;
  401. char *inname;
  402. {
  403.     boolean installed = YES;
  404.     char *body = hdrcopy(art, in);
  405.     char bytehdr[64];
  406.  
  407.     hdrdeflt(&art->h);
  408.     reject(art);                /* duplicate, etc.? */
  409.     if (art->a_status&(ST_DROPPED|ST_REFUSED)) {
  410.         uninsart(art);
  411.         installed = NO;
  412.     } else {
  413.         fileart(art);
  414.         hdrdump(art);
  415.     }
  416.     cpybody(art, in, body);    /* consume article body from input batch */
  417.     if (!(art->a_status&(ST_DROPPED|ST_REFUSED)))
  418.         mkcopies(art);
  419.     if (art->a_unlink) {
  420.         /* a_tmpf has had links made to it, so it can be removed. */
  421.         if (unlink(art->a_tmpf) < 0) {
  422.             persistent(art, 'f', "copyart can't unlink `%s'",
  423.                 art->a_tmpf);
  424.             art->a_status |= ST_ACCESS;
  425.         }
  426.         art->a_unlink = NO;        /* caution */
  427.     }
  428.     /* assertion: header values (art->h) can be forgotten here */
  429.     if (!(art->a_status&(ST_DROPPED|ST_REFUSED|ST_NEEDATTN))) {
  430.         /* fake Bytes: header for readers */
  431.         (void) sprintf(bytehdr, "Bytes: %ld\n",
  432.                    (long)art->a_charswritten);
  433.         wrhdrstrm(art, bytehdr, strlen(bytehdr));
  434.     }
  435.     flshdrstrm(art);
  436.     surveydamage(art, &installed);
  437. }
  438.  
  439. /*
  440.  * Copy the article on "in" to a temporary name in the news spool directory,
  441.  * unlink temp name; *or* copy into the final names, if known early enough.
  442.  * (Sets a_tmpf in or near hdrmunge() or hdrdump().)
  443.  * If the spool file opened, install the article it contains.
  444.  *
  445.  * copyart() may reject() the article, and may fill the disk.
  446.  * it calls fileart and logs rejected articles.  it may call uninsart.
  447.  */
  448. statust
  449. cpinsart(in, inname, maxima, blvmax)
  450. FILE *in;
  451. register char *inname;
  452. long maxima;
  453. boolean blvmax;                /* believe maxima? */
  454. {
  455.     struct article art;
  456.     register struct article *artp = &art;
  457.     register statust status;
  458.  
  459.     artinit(artp);
  460.     artp->a_blvmax = blvmax;
  461.     artp->a_unread = maxima;
  462.     copyart(artp, in, inname);
  463.     if (artp->a_status&ST_REFUSED) {
  464.         /* no good ngs (in fileart) or reject()ed; not serious */
  465.         artp->a_status &= ~ST_REFUSED;
  466.         /* paranoia; shouldn't happen */
  467.         nnfclose(artp, &artp->a_artf, inname);
  468.     } else if (artp->a_artf == NULL) {
  469.         persistent(artp, 'f', "can't open spool file `%s'",
  470.             artp->a_tmpf);
  471.     } else {
  472.         nnfclose(artp, &artp->a_artf, inname);
  473.         insart(artp);    /* logs accepted art.s during transmission */
  474.         if (artp->a_status&ST_JUNKED) {    /* yer welcome, henry */
  475.             artp->a_status &= ~ST_JUNKED;
  476.             logaudit(artp, 'j', "junked due to groups `%s'",
  477.                  artp->h.h_ngs);
  478.         }
  479.     }
  480.     status = artp->a_status;
  481.     artfree(artp);
  482.     return status;
  483. }
  484.