home *** CD-ROM | disk | FTP | other *** search
/ Gold Fish 1 / GoldFishApril1994_CD2.img / d4xx / d473 / cnewssrc / cnews_src.lzh / relay / procart.c < prev    next >
C/C++ Source or Header  |  1990-12-09  |  14KB  |  461 lines

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