home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Unix / CNews / Source / relay / fileart.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-11  |  14.1 KB  |  488 lines

  1. /*
  2.  * fileart - file an article, given its temporary file name and its headers
  3.  *
  4.  * It may be desirable to, some day, prevent cross-postings across
  5.  * "universes", where a universe might be "alt" or "comp,news".
  6.  *
  7.  * There are three classes of newsgroup for the purposes of filing:
  8.  * "wanted" (in the active file and missing the "x" flag);
  9.  * "not wanted" ("x"ed in active, or not in active and not matched by sys
  10.  *    file's subscription list for this machine), so ignore it; or
  11.  * "don't know it" (not in active and matched by subscription list,
  12.  *    so file the article in junk once, iff there are no good groups).
  13.  * junk *must* be in the active file or it's an error (ST_DROPPED),
  14.  * but junk may have an "x" flag to prevent filing.
  15.  *
  16.  * Use the active file 'x' flag to snuff groups quietly, even when your
  17.  * subscription list permits them, without filing in junk.
  18.  */
  19.  
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <ctype.h>
  23. #include <errno.h>
  24. #include "fixerrno.h"
  25. #include <sys/types.h>
  26.  
  27. #include "libc.h"
  28. #include "news.h"
  29. #include "config.h"
  30. #include "control.h"
  31. #include "active.h"
  32. #include "fileart.h"
  33. #include "mkdirs.h"
  34. #include "headers.h"
  35. #include "article.h"
  36. #include "history.h"
  37. #include "ngmatch.h"
  38. #include "system.h"
  39.  
  40. #define XREFDELIM ':'
  41.  
  42. static long artnum;            /* asgnartnum sets artnum */
  43. static int goodngs;            /* asgnartnum reads goodngs */
  44.  
  45. static boolean debug = NO;
  46.  
  47. /* imports from news */
  48. extern void prefuse();
  49.  
  50. /* forwards */
  51. FORWARD void asgnartnum(), gotgoodng(), mkjunklink(), mklinks();
  52. FORWARD boolean openorlink(), mkonelink(), tryartnum();
  53.  
  54. void
  55. filedebug(state)        /* set debugging state */
  56. boolean state;
  57. {
  58.     debug = state;
  59. }
  60.  
  61. /*
  62.  * File in the spool directory the article in art & fill in art->a_files.
  63.  * Generate Xref: header if needed (successfully cross-posted).
  64.  * (Thus must be called before emitting any article body!)
  65.  *
  66.  * If a_unlink is true, there is a temp file, so openfirst should
  67.  * be false, and vice versa.
  68.  *
  69.  * If openfirst (!art->a_unlink) is true, fill in a_tmpf with the name of
  70.  * the first link, fopen it (into art->a_artf), and make any remaining links.
  71.  * If openfirst is false, just make links to a_tmpf, which is already
  72.  * open as art->a_artf.  openfirst means "Newsgroups:" was seen in time.
  73.  *
  74.  * Could make empty files for cross-posting "links" here, but make copies into
  75.  * them later.  This sort of thing is needed on old SysVs with NEWSARTS split
  76.  * across partitions (and apparently on Plan 9).
  77.  */
  78. void
  79. fileart(art)
  80. register struct article *art;
  81. {
  82.     register boolean openfirst = !art->a_unlink;
  83.     int junkgroups = 0;        /* count "junked" groups */
  84.     char artnumstr[MAXCOMP];    /* article number in ascii */
  85.     extern boolean genxref;        /* TODO: move into a header */
  86.  
  87.     if (art->a_filed)
  88.         return;            /* don't file twice */
  89.     artnum = 0;
  90.     goodngs = 0;
  91.     mklinks(art, openfirst, artnumstr, &junkgroups);
  92.     mkjunklink(art, openfirst, artnumstr, &junkgroups);
  93.     /* -g or article crossposted, and article is open? */
  94.     if ((genxref && goodngs > 0 || goodngs > 1) && art->a_artf != NULL)
  95.         emitxref(art);
  96. }
  97.  
  98. /*
  99.  * extract list of newsgroups (and possibly article numbers) from article
  100.  * headers and history file, as options indicate.  If article numbers are
  101.  * being dictated by incoming Xref: or old history entry, the article numbers
  102.  * will be attached to the end of the group names by a colon as in Xref:
  103.  * (e.g. comp.lang.c:25780 general:12).
  104.  */
  105. char *
  106. extngs(art)
  107. register struct article *art;
  108. {
  109.     register char *ngs = NULL, *site, *groups;
  110.     extern boolean blvxref, dupsokay;    /* TODO: put in header */
  111.     extern char *blvsite, *dupsite;        /* TODO: put in header */
  112.  
  113.     if (blvxref) {
  114.         if (art->h.h_xref == NULL)
  115.             (void) fprintf(stderr,
  116.                 "%s: no Xref: in believe-Xref mode\n",
  117.                 progname);
  118.         else {
  119.             for (site = groups = skipsp(art->h.h_xref);
  120.                  *groups != '\0' &&
  121.                  isascii(*groups) && !isspace(*groups); groups++)
  122.                 ;        /* skip over site name */
  123.             if (*groups != '\0')
  124.                 *groups++ = '\0'; /* terminate site name */
  125.             groups = skipsp(groups);
  126.             if (!STREQ(site, blvsite))
  127.                 (void) fprintf(stderr,
  128.     "%s: article received from site `%s', not `%s' in believe-Xref mode\n",
  129.                     progname, site, blvsite);
  130.             else
  131.                 ngs = groups;    /* site is cool; rest is ngs */
  132.         }
  133.     } else if (dupsokay) {
  134.         groups = gethistory(art->h.h_msgid);    /* TODO: free this */
  135.         if (groups == NULL)
  136.             (void) fprintf(stderr,
  137.                 "%s: no history entry in duplicate-feed mode\n",
  138.                 progname);
  139.         else if ((groups = findfiles(groups)) == NULL)
  140.             (void) fprintf(stderr,
  141.             "%s: expired history entry in duplicate-feed mode\n",
  142.                 progname);
  143.         else {
  144.             site = strsvto(art->h.h_path, '!');
  145.             if (!STREQ(site, dupsite))
  146.                 (void) fprintf(stderr,
  147. "%s: article received from site `%s', not `%s' in duplicate-feed mode\n",
  148.                     progname, site, dupsite);
  149.             else
  150.                 /* convert group/art list to group:art list */
  151.                 for (ngs = groups; *groups != '\0'; groups++)
  152.                     if (*groups == FNDELIM)
  153.                         *groups = XREFDELIM;
  154.             free(site);
  155.         }
  156.     } else {
  157.         groups = art->h.h_ctlcmd != NULL? CONTROL: art->h.h_ngs; /* NCMP */
  158.         if (strchr(groups, XREFDELIM) != NULL)
  159.             (void) fprintf(stderr,
  160.                 "%s: colon not permitted in Newsgroups: list\n",
  161.                 progname);
  162.         else
  163.             ngs = groups;
  164.     }
  165.     if (ngs == NULL)
  166.         art->a_status |= ST_DROPPED;        /* bummer */
  167.     else
  168.         /* convert any spaces to commas for fileart */
  169.         for (groups = ngs; *groups != '\0'; groups++)
  170.             if (*groups == ' ')
  171.                 *groups = NGSEP;
  172.     return ngs;
  173. }
  174.  
  175. /*
  176.  * Store in spooldir.  Link temp file to spooldir/ng/article-number
  177.  * for each ng.  Control messages go in CONTROL, never in all.all.ctl.
  178.  */
  179. STATIC void
  180. mklinks(art, openfirst, artnumstr, junkgroupsp)
  181. register struct article *art;
  182. boolean openfirst;
  183. char *artnumstr;
  184. int *junkgroupsp;
  185. {
  186.     register char *ngs = extngs(art), *ng;
  187.     register char *comma, *numb;
  188.  
  189.     if (art->a_status&ST_REFUSED)
  190.         (void) fprintf(stderr,
  191.         "%s: mklinks called with ST_REFUSED set (can't happen)\n",
  192.             progname);
  193.     for (; ngs != NULL; ngs = comma) {
  194.         STRCHR(ngs, NGSEP, comma);
  195.         if (comma != NULL)
  196.             *comma = '\0';        /* will be restored below */
  197.  
  198.         numb = strchr(ngs, XREFDELIM);
  199.         if (numb != NULL)        /* number supplied? */
  200.             *numb++ = '\0';        /* cleave number from group */
  201.  
  202.         /*
  203.          * Map group names in Newsgroups: header to local group
  204.          * names for filing.
  205.          */
  206.         ng = realngname(ngs);
  207.         if (ng == NULL)
  208.             ng = strsave(ngs);
  209.  
  210.         /* attempt to file */
  211.         asgnartnum(art, openfirst, ng, numb, artnumstr);
  212.  
  213.         /*
  214.          * If no such group in active or link failed, and the group
  215.          * wasn't 'x'ed in active, but our subscription list permits
  216.          * this group, then set flag to file it under "junk" later.
  217.          */
  218.         if ((artnum < 1 || art->a_status != ST_OKAY) &&
  219.             art->a_status != ST_REFUSED &&
  220.             ngpatmat(oursys()->sy_trngs, ng))
  221.                 ++*junkgroupsp;
  222.         /*
  223.          * If article # was assigned & link succeeded,
  224.          * update art->a_files list for history.
  225.          */
  226.         if (artnum >= 1 && art->a_status == ST_OKAY)
  227.             gotgoodng(art, ng, artnumstr);
  228.         free(ng);
  229.  
  230.         if (numb != NULL)        /* number supplied? */
  231.             *--numb = XREFDELIM;    /* restore lost byte */
  232.         if (comma != NULL)
  233.             *comma++ = NGSEP;    /* step past comma */
  234.  
  235.         /* asgnartnum refused just this ng */
  236.         art->a_status &= ~ST_REFUSED;
  237.     }
  238. }
  239.  
  240. /*
  241.  * File once in "junk" iff no ngs were filed due to absence from
  242.  * active, but some were permitted by sys.  This will make one junk
  243.  * link, no matter how many bad groups, and only if all are bad
  244.  * (e.g. rec.drugs,talk.chew-the-fat).
  245.  */
  246. STATIC void
  247. mkjunklink(art, openfirst, artnumstr, junkgroupsp)
  248. register struct article *art;
  249. boolean openfirst;
  250. char *artnumstr;
  251. int *junkgroupsp;
  252. {
  253.     if (goodngs != 0)
  254.         return;        /* shouldn't be here, with valid groups */
  255.  
  256.     if (*junkgroupsp > 0) {
  257.         /*
  258.          * All groups were "junked".  Try to file this article in
  259.          * junk.
  260.          */
  261.         asgnartnum(art, openfirst, JUNK, (char *)NULL, artnumstr);
  262.         if (artnum >= 1 && art->a_status == ST_OKAY) {
  263.             gotgoodng(art, JUNK, artnumstr);
  264.             art->a_status |= ST_JUNKED;
  265.         } else
  266.         /* couldn't file article in junk.  why? */
  267.         if (art->a_status&ST_REFUSED) {    /* junk is 'x'ed */
  268.             prefuse(art);
  269.             (void) printf(
  270.         "no known groups in `%s' and %s group is excluded in active\n",
  271.                 art->h.h_ngs, JUNK);
  272.         } else {            /* junk is missing? */
  273.             static boolean warned = NO;
  274.  
  275.             art->a_status |= ST_REFUSED|ST_DROPPED;
  276.             if (!warned) {
  277.                 warned = YES;
  278.                 (void) fprintf(stderr,
  279.         "%s: can't file in %s group; is it absent from active?\n",
  280.                     progname, JUNK);
  281.             }
  282.             prefuse(art);
  283.             (void) printf(
  284.             "no known groups in `%s' and no %s group\n",
  285.                 art->h.h_ngs, JUNK);
  286.         }
  287.     } else {
  288.         extern boolean histreject;
  289.  
  290.         /*
  291.          * Groups were permitted by subscription list, but all
  292.          * were 'x'ed in active, or otherwise refused.
  293.          */
  294.         if (histreject)
  295.             history(art, NOLOG);
  296.         prefuse(art);
  297.         (void) printf("all groups `%s' excluded in active\n",
  298.             art->h.h_ngs);
  299.         art->a_status |= ST_REFUSED;
  300.     }
  301. }
  302.  
  303. /*
  304.  * Append ng/artnumstr to art's list of files, and bump goodngs.
  305.  */
  306. STATIC void
  307. gotgoodng(art, ng, artnumstr)
  308. struct article *art;
  309. char *ng, *artnumstr;
  310. {
  311.     ++goodngs;
  312.     histupdfiles(art, ng, artnumstr);
  313. }
  314.  
  315. /*
  316.  * Assign a permanent name and article number to the temporary name
  317.  * art->a_tmpf in newsgroup "ng" & store the ascii form of the article
  318.  * number into "artnumstr", returning the article number in "artnum".
  319.  * If numb is non-null, it's the ascii article number to file under.
  320.  *
  321.  * If openfirst is true and goodngs is zero, set inname to artname,
  322.  * fopen artname and store the result in art->a_artf.
  323.  */
  324. STATIC void
  325. asgnartnum(art, openfirst, ng, numb, artnumstr)
  326. struct article *art;
  327. boolean openfirst;                /* open first link? */
  328. register char *ng;                /* read-only */
  329. char *numb, *artnumstr;
  330. {
  331.     register char *slashng;            /* a group, slashed */
  332.  
  333.     /* field active 'x' flag: don't file this group, quietly */
  334.     if (unwanted(ng)) {
  335.         artnum = -1;
  336.         art->a_status |= ST_REFUSED;
  337.         return;
  338.     }
  339.  
  340.     slashng = strsave(ng);
  341.     mkfilenm(slashng);            /* relative to spooldir */
  342.     if (numb != NULL) {            /* number supplied? */
  343.         artnum = atol(numb);        /* believe number */
  344.         if (!tryartnum(art, openfirst, slashng, artnumstr)) {
  345.             (void) fprintf(stderr,
  346.         "%s: article # %ld in directory `%s' supplied but occupied!\n",
  347.                 progname, artnum, slashng);
  348.             art->a_status |= ST_DROPPED;
  349.         }
  350.     } else
  351.         while ((artnum = nxtartnum(ng)) >= 1)
  352.             if (tryartnum(art, openfirst, slashng, artnumstr))
  353.                 break;
  354.     free(slashng);
  355. }
  356.  
  357. /*
  358.  * Construct a link name (slashng/artnum) for this article,
  359.  * and try to link art to it.
  360.  *
  361.  * We changed directory to spooldir in main(), so the generated name
  362.  * is relative to spooldir, therefore artname can be used as is.
  363.  *
  364.  * Return value is identical to mkonelink's.
  365.  */
  366. STATIC boolean
  367. tryartnum(art, openfirst, slashng, artnumstr)
  368. register struct article *art;
  369. boolean openfirst;
  370. register char *slashng;
  371. char *artnumstr;            /* side-effect returned here */
  372. {
  373.     register char *artname;        /* article file name */
  374.     register boolean ret;
  375.  
  376.     (void) sprintf(artnumstr, "%ld", artnum);
  377.     artname = nemalloc((unsigned) (strlen(slashng) +
  378.         STRLEN(SFNDELIM) + strlen(artnumstr) + SIZENUL));
  379.     (void) strcpy(artname, slashng);
  380.     (void) strcat(artname, SFNDELIM);
  381.     (void) strcat(artname, artnumstr);
  382. #ifdef notdef
  383.     char *tartname = strsave(artfile(artname));
  384.     free(artname);
  385.     artname = tartname;
  386. #endif
  387.     ret = mkonelink(art, artname, openfirst);
  388.     free(artname);
  389.     return ret;
  390. }
  391.  
  392. /*
  393.  * Try to link art to artname.
  394.  * If the attempt fails, maybe some intermediate directories are missing,
  395.  * so create any missing directories and try again.  If the second attempt
  396.  * also fails, look at errno; if it is EEXIST, artname already exists
  397.  * (presumably because the active file is out of date, or the existing
  398.  * file is a directory such as net/micro/432), so indicate that higher
  399.  * levels should keep trying, otherwise we are unable to create the
  400.  * necessary directories, so complain and set bad status in art.
  401.  *
  402.  * Returns YES iff there is no point in trying to file this article again,
  403.  * usually because it has been successfully filed, but sometimes because
  404.  * the necessary directories cannot be made.
  405.  */
  406. STATIC boolean
  407. mkonelink(art, artname, openfirst)
  408. register struct article *art;
  409. register char *artname;
  410. boolean openfirst;
  411. {
  412.     if (openorlink(artname, art, openfirst, goodngs))
  413.         return YES;
  414.     else {
  415.         (void) mkdirs(artname, getuid(), getgid());
  416.         if (openorlink(artname, art, openfirst, goodngs))
  417.             return YES;
  418.         else if (errno != EEXIST) {
  419.             warning("can't link to `%s'", artname);
  420.             art->a_status |= ST_DROPPED;
  421.             return YES;        /* hopeless - give up */
  422.         } else
  423.             return NO;
  424.     }
  425. }
  426.  
  427. /*
  428.  * Try to make a link of art (actually art->a_tmpf) to artname.
  429.  * If no links have been made yet, record and open artname iff no
  430.  * link yet exists to that name (by any file, to avoid overwriting
  431.  * existing articles, e.g. due to an out-of-date active file).
  432.  * If links already exist, try to make artname a link to the first link
  433.  * (art->a_tmpf).
  434.  *
  435.  * Liberally sprinkled with debugging output, as this is the heart
  436.  * of article filing.
  437.  */
  438. STATIC boolean
  439. openorlink(artname, art, openfirst, goodngcnt)
  440. register char *artname;
  441. register struct article *art;
  442. boolean openfirst;            /* open art's first link? */
  443. int goodngcnt;                /* count of good news groups */
  444. {
  445.     register boolean worked;        /* open or link worked? */
  446.  
  447.     errno = 0;            /* paranoia */
  448.     if (openfirst && goodngcnt == 0) {
  449.         if (debug)
  450.             (void) fprintf(stderr, "opening `%s'... ", artname);
  451.         nnfree(&art->a_tmpf);
  452.         art->a_tmpf = strsave(artname);
  453.         art->a_artf = fopenexcl(art->a_tmpf);
  454.         worked = art->a_artf != NULL;
  455.     } else {
  456.         if (debug)
  457.             (void) fprintf(stderr, "linking `%s' to `%s'... ",
  458.                 art->a_tmpf, artname);
  459.         worked = link(art->a_tmpf, artname) == 0;
  460.         if (!worked)
  461.             /*
  462.              * If art->a_tmpf really *is* a temporary name (because
  463.              * the whole header didn't fit in core), then this will
  464.              * produce a symbolic link to a non-existent name when
  465.              * art->a_tmpf is unlinked, which will be soon.
  466.              * Moral: don't run Eunice on your PDP-11.
  467.              */
  468.             worked = symlink(fullartfile(art->a_tmpf), artname)==0;
  469.     }
  470.     if (debug)
  471.         if (worked)
  472.             (void) fprintf(stderr, "success.\n");
  473.         else
  474.             warning("failed.", "");
  475.     return worked;
  476. }
  477.  
  478. #if 0
  479. /* called after copyart */
  480. mkcopies(art)
  481. register struct article *art;
  482. {
  483.     if (any links failed & empty files could be made)
  484.         for (each failed link)
  485.             copy open 1st link to this one;
  486. }
  487. #endif
  488.