/*--------------------------------------------------------------------*/ /* h i s t o r y . c */ /* */ /* News history file maintenance for UUPC/extended. */ /* */ /* Written by Mike Lipsie */ /* */ /* The history file */ /* */ /* This file describes and implements the history file. */ /* */ /* The history file is [newsdir]/history and is "added" to by */ /* rnews. It is "pruned" by expire and is used by rnews (to */ /* check that the article has not arrived before), expire (to */ /* find all copies of the article), and rn (to mark as read all */ /* copies of an article.) */ /* */ /* The history file is entirely ASCII. */ /* */ /* The first line is a code that identifies the version level */ /* (so that future versions can automatically upgrade.) For */ /* version one that code is "ZIP1". */ /* */ /* Every line (except the version line) is the record of a */ /* single incoming article. That line is exactly as described */ /* without added spaces or other punctuation. The first field */ /* is the Message-ID including (or added if they don't exist), */ /* the "<" and ">", a space, the date received (dd/mm/yyyy with */ /* days and months being zero filled if necessary), a space, */ /* and the "destination" information. */ /* */ /* The destination information is the Newsgroups: with a colon */ /* and the article number for each group added (article number */ /* zero is used for newsgroups not accepted.) */ /* */ /* For example, if article <123@pyramid> was received on 3 Jan */ /* 1992 and was posted to ba.food, ba.transportation, and */ /* soc.singles and ended up in article 4334, 1234, and 56789 */ /* (respectively), the record would be */ /* */ /* <123@pyramid> 03/01/1992 */ /* ba.food:4334,ba.transportation:1234,soc.singles:56789 */ /* */ /* To speed access into the history file there is a parallel */ /* file history.ndx which contains information where each day's */ /* worth of history records begins. The first line is a */ /* version number and must be the same as that of the history */ /* file. All the other records are the 10 character date code, */ /* a space, and a ten digit location code (right justified, */ /* zero or blank filled.) The location code is the byte */ /* displacement into the history file of the first record with */ /* that date code. */ /*--------------------------------------------------------------------*/ /*--------------------------------------------------------------------*/ /* System include files */ /*--------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include /*--------------------------------------------------------------------*/ /* UUPC/extended include files */ /*--------------------------------------------------------------------*/ #include "lib.h" #include "hlib.h" #include "timestmp.h" #include "active.h" #include "history.h" /* These variables are used to improve the performance of checking * for previous existance of an article. * * The basic theory of how this feature works is that a file * (history.dbm) is built with enough information (it is hoped) * to eliminate 99% (well, most) of the non matching message IDs. * A check is made when a match is made confiming that the match * is real. Since most articles are not "re-sends" (all if you * have only one feed) this won't impose a large overhead. * * The information that is used to eliminate unnecessary checks into * the history file for duplicate messageIDs is the first ten * characters of the message ID (not including the leading "<"), * the number of characters in the full ID (including the "<" and ">"), * a checksum of the full ID (also including the "<" and ">"), and * the location in the history file of the full ID. * * Additionally, an array of MAXbuf (currently 8) buffers of history.dbm * are maintained in memory to avoid disk I/O. * */ int cur_buff = 0; int cur_off = 0; #define MAXbuf 8 char *hb[MAXbuf]; int hb_blk[MAXbuf]; extern FILE *hfile; FILE *hdbm_file = NULL; struct perf_elem { char IDpart[10]; /* The first 10 characters (after the "<") of the message-ID */ char cksum; /* Checksum of the entire ID (including <>) */ char IDlen; /* Number of characters in the ID (including <>) */ long where; /* ftell() in file of actual record */ }; /*--------------------------------------------------------------------*/ /* h i s t o r y _ e x i s t s */ /* */ /* Determine if the history file exists and is a file (instead */ /* of a directory). */ /*--------------------------------------------------------------------*/ boolean history_exists( void ) { char hfile_name[FILENAME_MAX]; struct stat buff; mkfilename(hfile_name, E_newsdir, "history"); if ((stat(hfile_name, &buff) == 0) && buff.st_mode & S_IFREG) return TRUE; else return FALSE; } /* history_exists */ /*--------------------------------------------------------------------*/ /* o p e n _ h i s t o r y */ /* */ /* Open the history file and verify that it is of the correct */ /* version. If any error occurs, NULL is returned. */ /*--------------------------------------------------------------------*/ #ifdef __TURBOC__ #pragma argsused #endif FILE *open_history(char *history_date) { #ifdef NEXT_RELEASE_MAYBE char hfile_name[FILENAME_MAX]; FILE *hfile; FILE *index_file; char buff[BUFSIZ]; mkfilename(hfile_name, newsdir, "history"); hfile = fopen(hfile_name, "a+b"); if (hfile == NULL) { printmsg(0,"Unable to open history file"); return hfile; } fseek(hfile, 0L, SEEK_SET); fgets(buff, sizeof(buff), hfile); buff[strlen(buff)-1] = '\0'; if (buff[strlen(buff)-1] == '\r') buff[strlen(buff)-1] = '\0'; if ((strlen(buff)) != strlen(H_VERSION)) { printmsg(0, "History version incorrect"); return NULL; } if (!equal(buff, H_VERSION)) { printmsg(0, "History version incorrect"); return NULL; } fseek(hfile, 0L, SEEK_END); /* I know it is unnecessary */ /* Now the index file */ strcat(hfile_name, ".ndx"); index_file = fopen(hfile_name, "a+b"); if (index_file == NULL) { printmsg(0,"Unable to open history file index"); } fseek(index_file, 0L, SEEK_SET); fgets(buff, sizeof(buff), index_file); buff[strlen(buff)-1] = '\0'; if (buff[strlen(buff)-1] == '\r') buff[strlen(buff)-1] = '\0'; if ((strlen(buff)) != strlen(H_VERSION)) { printmsg(0, "History index version incorrect"); } if (!equal(buff, H_VERSION)) { printmsg(0, "History index version incorrect"); } fseek(index_file, -21L, SEEK_END); fgets(buff, 25, index_file); if (strncmp(history_date, buff, strlen(history_date)) != 0) { fseek(index_file, 0L, SEEK_END); sprintf(buff, "%s %9.9ld\n", history_date, ftell(hfile)); fwrite(buff, sizeof(char), strlen(buff), index_file); } fclose(index_file); #else hfile = NULL; #endif return NULL; } /* Create a history file. */ FILE *create_history(char *history_date) { char hfile_name[FILENAME_MAX]; FILE *hfile; FILE *index_file; char buff[BUFSIZ]; mkfilename(hfile_name, E_newsdir, "history"); hfile = fopen(hfile_name, "wb"); if (hfile == NULL) { printmsg(0, "Unable to create history file"); return hfile; } strcpy(buff, H_VERSION); strcat(buff, "\n"); fwrite(buff, sizeof(char), strlen(buff), hfile); strcat(hfile_name, ".ndx"); index_file = fopen(hfile_name, "wb"); fwrite(buff, sizeof(char), strlen(buff), index_file); sprintf(buff, "%s %9.9ld\n", history_date, ftell(hfile)); fwrite(buff, sizeof(char), strlen(buff), index_file); fclose(index_file); return hfile; } void init_history_dbm(void) { #ifdef NEXT_RELEASE_MAYBE int not_eof; char *t; long l; int i; int j; int cur_block; struct perf_elem *p; char dbm_name[FILENAME_MAX]; char hbuf[BUFSIZ]; /* Buffer for each history file record */ mkfilename(dbm_name, tempdir, "history.dbm"); hdbm_file = fopen(dbm_name, "w+b"); if (hdbm_file == NULL) { printmsg(0,"Unable to create history dbm file"); exit(5); } for (i = 0; i < MAXbuf; i++) { hb[i] = malloc(BUFSIZ); checkref(hb[i]); hb_blk[i] = -1; } cur_block = 0; cur_buff = 0; cur_off = 0; /* Now build the file */ fseek(hfile, (long)(strlen(H_VERSION)+1), SEEK_SET); /* Back to the beginning */ not_eof = TRUE; while (not_eof) { l = ftell(hfile); t = fgets(hbuf, BUFSIZ, hfile); if (t == NULL) { not_eof = FALSE; } else { if (cur_off >= (BUFSIZ/sizeof(struct perf_elem))) { fwrite(hb[cur_buff], sizeof(char), BUFSIZ, hdbm_file); hb_blk[cur_buff] = cur_block++; cur_off = 0; if (++cur_buff >= MAXbuf) { cur_buff = 0; } } p = (struct perf_elem *)hb[cur_buff]; p += cur_off++; t = strchr(hbuf, ' '); if (t == NULL) t = hbuf; *t = '\0'; p->where = l; p->IDlen = strlen(t); j = 0; for (i = p->IDlen-1; i >= 0; i--) { j = j + t[i]; } p->cksum = (char)j; strncpy(p->IDpart, &(t[1]), sizeof(p->IDpart)); } } /* while not eof */ /* Dump out last (probably partial) block */ if (cur_off < (BUFSIZ/sizeof(struct perf_elem))) { p = (struct perf_elem *)hb[cur_buff]; p += cur_off; strnset((char *)p, '\0', (&hb[cur_buff][BUFSIZ] - &(char *)p)); } fwrite(hb[cur_buff], sizeof(char), BUFSIZ, hdbm_file); hb_blk[cur_buff] = cur_block; #endif return; } /*--------------------------------------------------------------------*/ /* i s _ i n _ h i s t o r y */ /* */ /* Check whether messageID is already in the history file. */ /*--------------------------------------------------------------------*/ boolean is_in_history(FILE *hfile, char *messageID) { #ifdef NEXT_RELEASE_MAYBE int this_blk; struct perf_elem *p; if (hdbm_file == NULL) { /* Initialize */ init_history_dbm(); } /* First check the blocks in memory */ this_blk = 0; cur_off = 0; while (this_blk < MAXbuf) { if (cur_off >= (BUFSIZ/sizeof(struct perf_elem))) { cur_off = 0; if (++this_blk >= MAXbuf) { break; } if (hb_blk[this_blk] == -1) break; } p = (struct perf_elem *)hb[this_blk]; p += cur_off++; if (check_out_this_one(messageID, p)) { return TRUE; } } /* Now check out the rest of the file */ #else /*--------------------------------------------------------------------*/ /* Slow but true version extracted from rnews.c by ahd */ /*--------------------------------------------------------------------*/ boolean not_eof = TRUE; fseek(hfile, (long)(strlen(H_VERSION)+1), SEEK_SET); /* Back to the beginning */ /*--------------------------------------------------------------------*/ /* Scan the entire disk file from the beginning */ /*--------------------------------------------------------------------*/ while (not_eof) { char hist_record[BUFSIZ]; char *gc_ptr = fgets(hist_record, sizeof(hist_record), hfile); if (gc_ptr == NULL) not_eof = FALSE; else { gc_ptr = strchr(hist_record, ' '); if (gc_ptr == NULL) gc_ptr = hist_record; *gc_ptr = '\0'; printmsg(9, "rnews:Comparing to history:%s", hist_record); if (equal(hist_record, messageID)) return TRUE; } /* else */ } /* while (not_eof) */ #endif return FALSE; } /* is_in_history */ #ifdef NEXT_RELEASE_MAYBE int check_out_this_one(char *m_id, struct perf_elem *p) { printf("check_out_this_one called \n"); return TRUE; } #endif