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

  1. /* :ts=4
  2.  * history file bashing
  3.  *
  4.  * B 2.10.3+ rnews puts out a leading space before received
  5.  * time if the article contains an Expires: header; tough.
  6.  * C news does this right instead of compatibly.
  7.  *
  8.  * The second history field is really two: time-received and Expires: value,
  9.  * separated by a tilde.  This is an attempt at partial compatibility with
  10.  * B news, in that C expire can cope with B news history files.
  11.  *
  12.  * There is no point to storing seek offsets in network byte order in the
  13.  * dbm file, since dbm files are machine-dependent and so can't be shared
  14.  * by dissimilar machines anyway.
  15.  *
  16.  *    $Log$
  17.  */
  18. #include <string.h>        /* for memcpy */
  19. #include <errno.h>
  20. #include "fixerrno.h"
  21.  
  22. #include <stdio.h>
  23. #ifdef unix
  24. # include <sys/types.h>
  25. #endif /* unix */
  26. #include "libc.h"
  27. #include "news.h"
  28. #include "config.h"
  29. #include "dbz.h"
  30. #include "fgetmfs.h"
  31. #include "headers.h"
  32. #include "article.h"
  33. #include "history.h"
  34. #include "msgs.h"
  35. #include "dbz.h"
  36.  
  37. #if defined(AZTEC_C) && __VERSION < 500
  38. # define memcpy(d,s,n)    movmem((s),(d),(n))
  39. #endif
  40.  
  41. #define HISTNAME "history"    /* name of the history file in $NEWSCTL */
  42. #define FIELDSEP '\t'
  43. #define SUBFIELDSEP '~'
  44.  
  45. /* give 0 & 2 pretty, SVIDish names */
  46. #ifndef SEEK_SET
  47. #define SEEK_SET 0
  48. #define SEEK_END 2
  49. #endif
  50.  
  51. /* private data */
  52. static FILE *fp = NULL;
  53. static char *filename;        /* absolute name of the ascii history file */
  54. static boolean writable;
  55.  
  56. /* libdbm imports */
  57. extern int dbminit(), store();
  58. extern datum fetch();
  59.  
  60. /* other imports */
  61. extern void prefuse();
  62. extern boolean okrefusal;    /* flag from command line */
  63.  
  64. /* forward decls */
  65. FORWARD datum getposhist();
  66. FORWARD void mkhistent(), sanitise(), subsanitise();
  67. void decline();
  68.  
  69. STATIC void
  70. histname()
  71. {
  72.     if (filename == NULL)
  73.         filename = strsave(ctlfile(HISTNAME));
  74. }
  75.  
  76. /*
  77.  * open the history files: ascii first, then dbm.
  78.  * Try a+ mode first, then r mode, as dbm(3) does nowadays,
  79.  * so that this routine can be used by any user to read history files.
  80.  */
  81. STATIC boolean
  82. openhist()
  83. {
  84.     histname();
  85.     if (fp == NULL) {
  86.         if ((fp = fopenclex(filename, "a+")) != NULL)
  87.             writable = YES;
  88.         else if ((fp = fopenwclex(filename, "r")) != NULL)
  89.             writable = NO;
  90.         /* else fp==NULL and fopenwclex just complained */
  91.  
  92.         errno = 0;
  93.         if (fp != NULL && dbminit(filename) < 0) {
  94.             /*
  95.              * no luck.  dbm's dbminit will have just honked (on
  96.              * stdout, alas) but dbz's won't have, so bitch.
  97.              */
  98.             warning(
  99.         "database files for `%s' incomprehensible or unavailable",
  100.                 filename);
  101.             (void) nfclose(fp);    /* close ascii file */
  102.             fp = NULL;        /* and mark it closed */
  103.         }
  104.     }
  105.     return fp != NULL;
  106. }
  107.  
  108. STATIC datum
  109. getposhist(msgid)        /* return seek offset of history entry */
  110. char *msgid;
  111. {
  112.     register char *clnmsgid;
  113.     datum msgidkey, keypos;
  114.  
  115.     msgidkey.dptr = NULL;
  116.     msgidkey.dsize = 0;
  117.     if (!openhist())
  118.         return msgidkey;
  119.     clnmsgid = strsave(msgid);
  120.     sanitise(clnmsgid);
  121.     msgidkey.dptr = clnmsgid;
  122.     msgidkey.dsize = strlen(clnmsgid) + SIZENUL;
  123.     keypos = dbzfetch(msgidkey);        /* offset into ascii file */
  124.     free(clnmsgid);
  125.     return keypos;
  126. }
  127.  
  128. boolean
  129. alreadyseen(msgid)        /* return true if found in the data base */
  130. char *msgid;
  131. {
  132.     datum posdatum;
  133.  
  134.     posdatum = getposhist(msgid);
  135.     return posdatum.dptr != NULL;
  136. }
  137.  
  138. char *                /* NULL if no history entry */
  139. gethistory(msgid)        /* return existing history entry, if any */
  140. char *msgid;
  141. {
  142.     long pos = 0;
  143.     datum posdatum;
  144.  
  145.     posdatum = getposhist(msgid);
  146.     if (posdatum.dptr != NULL && posdatum.dsize == sizeof pos) {
  147.         static char *histent = NULL;
  148.  
  149.         (void) memcpy((char *)&pos, posdatum.dptr, sizeof pos); /* align */
  150.         nnfree(&histent);
  151.         if (fseek(fp, pos, SEEK_SET) != -1 &&
  152.             (histent = fgetms(fp)) != NULL)
  153.             return histent;        /* could note move from EOF */
  154.     }
  155.     return NULL;
  156. }
  157.  
  158. /*
  159.  * Return a pointer to the "files" field of a history entry.
  160.  * Side-effect: trims \n from the history entry.
  161.  */
  162. char *
  163. findfiles(histent)
  164. char *histent;
  165. {
  166.     register char *tabp;
  167.  
  168.     trim(histent);
  169.     /* find start of 2nd field (arrival~expiry) */
  170.     tabp = strchr(histent, FIELDSEP);
  171.     if (tabp == NULL)
  172.         return NULL;                /* mangled entry */
  173.     /* find start of 3rd field (files list) */
  174.     else if ((tabp = strchr(tabp + 1, FIELDSEP)) == NULL)
  175.         return NULL;            /* cancelled or expired art. */
  176.     else
  177.         return tabp + 1;
  178. }
  179.  
  180. /*
  181.  * Generate a history entry from art.
  182.  * The history entry will have tabs and newlines deleted from the
  183.  * interior of fields, to keep the file format sane.
  184.  * Optionally print the start of an "accepted" log file line (no \n)
  185.  * (transmit() prints site names).
  186.  */
  187. void
  188. history(art, startlog)
  189. register struct article *art;
  190. boolean startlog;
  191. {
  192.     register char *msgid, *expiry;
  193.     time_t now;
  194.  
  195.     if (!msgidok(art))        /* complains in log if unhappy */
  196.         return;            /* refuse to corrupt history */
  197.     msgid = strsave(nullify(art->h.h_msgid));
  198.     sanitise(msgid);    /* RFC 1036 forbids whitespace in msg-ids */
  199.     expiry = strsave(nullify(art->h.h_expiry));
  200.     sanitise(expiry);
  201.     subsanitise(expiry);
  202.  
  203.     if (startlog) {
  204.         timestamp(stdout, &now);
  205.         if (printf(" %s + %s", sendersite(nullify(art->h.h_path)),
  206.             msgid) == EOF)
  207.             fulldisk(art, "stdout");
  208.     } else
  209.         now = time(&now);
  210.     if (!openhist())
  211.         art->a_status |= ST_DROPPED|ST_NEEDATTN;    /* serious */
  212.     else if (!writable) {
  213.         (void) fprintf(stderr, "%s: no write permission on `%s'\n",
  214.             progname, filename);
  215.         art->a_status |= ST_DROPPED|ST_NEEDATTN;    /* serious */
  216.     } else if (fseek(fp, 0L, SEEK_END) == -1) {
  217.         /* could avoid fseek if still at EOF */
  218.         warning("can't seek to end of `%s'", filename);
  219.         art->a_status |= ST_DROPPED;
  220.     } else
  221.         mkhistent(art, msgid, now, expiry);
  222.     free(msgid);
  223.     free(expiry);
  224. }
  225.  
  226. void
  227. decline(art)                    /* mark art as undesirable */
  228. struct article *art;
  229. {
  230.     art->a_status |= ST_REFUSED|(okrefusal? 0: ST_DROPPED);
  231. }
  232.  
  233. int
  234. msgidok(art)                    /* if bad, complain in log */
  235. register struct article *art;
  236. {
  237.     register char *msgid = art->h.h_msgid;
  238.  
  239.     if (msgid == NULL || msgid[0] == '\0') {
  240.         prefuse(art);
  241.         (void) printf("missing Message-ID\n");
  242.     } else if (strchr(msgid, ' ') != NULL || strchr(msgid, '\t') != NULL) {
  243.         prefuse(art);
  244.         (void) printf("whitespace in Message-ID\n");
  245.     } else if (msgid[0] != '<' || msgid[strlen(msgid)-1] != '>') {
  246.         prefuse(art);
  247.         (void) printf("Message-ID not bracketed by <>\n");
  248.     } else
  249.         return YES;
  250.     decline(art);
  251.     return NO;
  252. }
  253.  
  254. /*
  255.  * Internal interface to generate a history file entry,
  256.  * assuming all sanity checking has been done already.
  257.  * Record the (msgid, position) pair in the data base.
  258.  *
  259.  * The fflush is crash-proofing.
  260.  */
  261. STATIC void
  262. mkhistent(art, msgid, now, expiry)
  263. register struct article *art;
  264. char *msgid, *expiry;
  265. time_t now;
  266. {
  267.     long pos;
  268.     datum msgidkey, posdatum;
  269.  
  270.     pos = ftell(fp);  /* get seek ptr for dbm; could keep track instead */
  271.     if (fprintf(fp, "%s%c%ld%c%s", msgid, FIELDSEP, now, SUBFIELDSEP, expiry)
  272.         == EOF)
  273.         fulldisk(art, filename);
  274.     /* don't write 3rd field for cancelled but unseen articles */
  275.     if (art->a_files != NULL && art->a_files[0] != '\0')
  276.         if (fprintf(fp, "%c%s", FIELDSEP, art->a_files) == EOF)
  277.             fulldisk(art, filename);
  278.     (void) putc('\n', fp);
  279.     if (fflush(fp) == EOF)
  280.         fulldisk(art, filename);
  281.  
  282.     msgidkey.dptr = msgid;
  283.     msgidkey.dsize = strlen(msgid) + SIZENUL;
  284.     posdatum.dptr = (char *)&pos;
  285.     posdatum.dsize = sizeof pos;
  286.     if (dbzstore(msgidkey, posdatum) < 0)
  287.         fulldisk(art, filename);
  288. }
  289.  
  290. /*
  291.  * Turn \n & FIELDSEP into ' ' in s.
  292.  */
  293. STATIC void
  294. sanitise(s)
  295. register char *s;
  296. {
  297.     for (; *s != '\0'; ++s)
  298.         if (*s == FIELDSEP || *s == '\n')
  299.             *s = ' ';
  300. }
  301.  
  302. /*
  303.  * Turn SUBFIELDSEP into ' ' in s.
  304.  */
  305. STATIC void
  306. subsanitise(s)
  307. register char *s;
  308. {
  309.     for (; *s != '\0'; ++s)
  310.         if (*s == SUBFIELDSEP)
  311.             *s = ' ';
  312. }
  313.  
  314. /*
  315.  * Generate a fake history file entry, given a message-id, an Expires:
  316.  * value, and a "file" list ("net.foo/123").
  317.  */
  318. statust
  319. fakehist(fkmsgid, fkexpiry, fkfiles)
  320. char *fkmsgid, *fkexpiry, *fkfiles;
  321. {
  322.     struct article art;
  323.  
  324.     artinit(&art);
  325.     art.h.h_msgid = fkmsgid;
  326.     art.h.h_expiry = fkexpiry;
  327.     art.a_files = fkfiles;
  328.     history(&art, STARTLOG);
  329.     return art.a_status;
  330. }
  331.  
  332. /*
  333.  * Append "group/artnumstr" to the file list in *art.
  334.  */
  335. void
  336. histupdfiles(art, group, artnumstr)
  337. register struct article *art;
  338. register char *group;
  339. register char *artnumstr;
  340. {
  341.     unsigned addlen = strlen(group) + STRLEN(SFNDELIM) +
  342.         strlen(artnumstr) + SIZENUL;
  343.  
  344.     art->a_filed = YES;            /* make a note */
  345.     if (art->a_files == NULL) {
  346.         art->a_files = nemalloc(addlen);
  347.         art->a_files[0] = '\0';
  348.     } else {
  349.         art->a_files = realloc(art->a_files, (unsigned)
  350.             strlen(art->a_files) + strlen(" ") + addlen);
  351.         if (art->a_files == NULL)
  352.             errunlock("can't grow a_files", "");
  353.         (void) strcat(art->a_files, " ");
  354.     }
  355.     (void) strcat(art->a_files, group);    /* normal case */
  356.     (void) strcat(art->a_files, SFNDELIM);
  357.     (void) strcat(art->a_files, artnumstr);
  358. }
  359.  
  360. statust
  361. closehist()
  362. {
  363.     register statust status = ST_OKAY;
  364.  
  365.     if (fp != NULL) {
  366.         /* dbmclose is only needed by dbz, to flush statistics to disk */
  367.         if (dbmclose() < 0) {
  368.             warning("error closing dbm history file", "");
  369.             status |= ST_DROPPED;
  370.         }
  371.         if (nfclose(fp) == EOF) {
  372.             warning("error closing history file", "");
  373.             status |= ST_DROPPED;
  374.         }
  375.         fp = NULL;        /* mark file closed */
  376.     }
  377.     return status;
  378. }
  379.