/* * $Revision: 1.1 $ * * * History file routines. */ #include "innbbsconf.h" #include "bbslib.h" #include "his.h" #define STATIC static /* STATIC char HIShistpath[] = _PATH_HISTORY; */ STATIC FILE *HISwritefp; STATIC int HISreadfd; STATIC int HISdirty; STATIC int HISincore = XINDEX_DBZINCORE; STATIC char *LogName = "xindexchan"; #ifndef EXPIREDAYS #define EXPIREDAYS 4 #endif #ifndef DEFAULT_HIST_SIZE #define DEFAULT_HIST_SIZE 100000 #endif hisincore(flag) int flag; { HISincore = flag; } makedbz(histpath, entry) char *histpath; long entry; { long size; size = dbzsize(entry); dbzfresh(histpath, size, '\t', 0, 1); dbmclose(); } void HISsetup(); void HISclose(); void mkhistory(srchist) char *srchist; { FILE *hismaint; time_t lasthist, now; char maintbuff[256]; char *ptr; hismaint = fopen(srchist, "r"); if (hismaint == NULL) { return; } { char newhistpath[1024]; char newhistdirpath[1024]; char newhistpagpath[1024]; sprintf(newhistpath, "%s.n", srchist); sprintf(newhistdirpath, "%s.n.dir", srchist); sprintf(newhistpagpath, "%s.n.pag", srchist); if (!isfile(newhistdirpath) || !isfile(newhistpagpath)) { makedbz(newhistpath, DEFAULT_HIST_SIZE); } myHISsetup(newhistpath); while (fgets(maintbuff, sizeof(maintbuff), hismaint) != NULL) { datum key; ptr = (char *)strchr(maintbuff, '\t'); if (ptr != NULL) { *ptr = '\0'; ptr++; } key.dptr = maintbuff; key.dsize = strlen(maintbuff); myHISwrite(&key, ptr); } (void)HISclose(); /* * rename(newhistpath, srchist); rename(newhistdirpath, * fileglue("%s.dir", srchist)); rename(newhistpagpath, * fileglue("%s.pag", srchist)); */ } fclose(hismaint); } time_t gethisinfo() { FILE *hismaint; time_t lasthist, now; char maintbuff[4096]; char *ptr; hismaint = fopen(HISTORY, "r"); if (hismaint == NULL) { return 0; } fgets(maintbuff, sizeof(maintbuff), hismaint); fclose(hismaint); ptr = (char *)strchr(maintbuff, '\t'); if (ptr != NULL) { ptr++; lasthist = atol(ptr); return lasthist; } return 0; } void HISmaint() { FILE *hismaint; time_t lasthist, now; char maintbuff[4096]; char *ptr; if (!isfile(HISTORY)) { makedbz(HISTORY, DEFAULT_HIST_SIZE); } hismaint = fopen(HISTORY, "r"); if (hismaint == NULL) { return; } fgets(maintbuff, sizeof(maintbuff), hismaint); ptr = (char *)strchr(maintbuff, '\t'); if (ptr != NULL) { ptr++; lasthist = atol(ptr); time(&now); if (lasthist + 86400 * Expiredays * 2 < now) { char newhistpath[1024]; char newhistdirpath[1024]; char newhistpagpath[1024]; (void)HISclose(); sprintf(newhistpath, "%s.n", HISTORY); sprintf(newhistdirpath, "%s.n.dir", HISTORY); sprintf(newhistpagpath, "%s.n.pag", HISTORY); if (!isfile(newhistdirpath)) { makedbz(newhistpath, DEFAULT_HIST_SIZE); } myHISsetup(newhistpath); while (fgets(maintbuff, sizeof(maintbuff), hismaint) != NULL) { datum key; ptr = (char *)strchr(maintbuff, '\t'); if (ptr != NULL) { *ptr = '\0'; ptr++; lasthist = atol(ptr); } else { continue; } if (lasthist + 99600 * Expiredays < now) continue; key.dptr = maintbuff; key.dsize = strlen(maintbuff); myHISwrite(&key, ptr); } (void)HISclose(); rename(HISTORY, (char *)fileglue("%s.o", HISTORY)); rename(newhistpath, HISTORY); rename(newhistdirpath, (char *)fileglue("%s.dir", HISTORY)); rename(newhistpagpath, (char *)fileglue("%s.pag", HISTORY)); (void)HISsetup(); } } fclose(hismaint); } /* * * Set up the history files. */ void HISsetup() { myHISsetup(HISTORY); } int myHISsetup(histpath) char *histpath; { if (HISwritefp == NULL) { /* Open the history file for appending formatted I/O. */ if ((HISwritefp = fopen(histpath, "a")) == NULL) { syslog(LOG_CRIT, "%s cant fopen %s %m", LogName, histpath); exit(1); } CloseOnExec((int)fileno(HISwritefp), TRUE); /* Open the history file for reading. */ if ((HISreadfd = open(histpath, O_RDONLY)) < 0) { syslog(LOG_CRIT, "%s cant open %s %m", LogName, histpath); exit(1); } CloseOnExec(HISreadfd, TRUE); /* Open the DBZ file. */ /* (void)dbzincore(HISincore); */ (void)dbzincore(HISincore); (void)dbzwritethrough(1); if (dbminit(histpath) < 0) { syslog(LOG_CRIT, "%s cant dbminit %s %m", histpath, LogName); exit(1); } } } /* * * Synchronize the in-core history file (flush it). */ void HISsync() { if (HISdirty) { if (dbzsync()) { syslog(LOG_CRIT, "%s cant dbzsync %m", LogName); exit(1); } HISdirty = 0; } } /* * * Close the history files. */ void HISclose() { if (HISwritefp != NULL) { /* * Since dbmclose calls dbzsync we could replace this line with * "HISdirty = 0;". Oh well, it keeps the abstraction clean. */ HISsync(); if (dbmclose() < 0) syslog(LOG_ERR, "%s cant dbmclose %m", LogName); if (fclose(HISwritefp) == EOF) syslog(LOG_ERR, "%s cant fclose history %m", LogName); HISwritefp = NULL; if (close(HISreadfd) < 0) syslog(LOG_ERR, "%s cant close history %m", LogName); HISreadfd = -1; } } #ifdef HISset /* * * File in the DBZ datum for a Message-ID, making sure not to copy any * * illegal characters. */ STATIC void HISsetkey(p, keyp) register char *p; datum *keyp; { static BUFFER MessageID; register char *dest; register int i; /* Get space to hold the ID. */ i = strlen(p); if (MessageID.Data == NULL) { MessageID.Data = NEW(char, i + 1); MessageID.Size = i; } else if (MessageID.Size < i) { RENEW(MessageID.Data, char, i + 1); MessageID.Size = i; } for (keyp->dptr = dest = MessageID.Data; *p; p++) if (*p == HIS_FIELDSEP || *p == '\n') *dest++ = HIS_BADCHAR; else *dest++ = *p; *dest = '\0'; keyp->dsize = dest - MessageID.Data + 1; } #endif /* * * Get the list of files under which a Message-ID is stored. */ char * HISfilesfor(key, output) datum *key; datum *output; { char *dest; datum val; long offset; register char *p; register int i; int Used; /* Get the seek value into the history file. */ val = dbzfetch(*key); if (val.dptr == NULL || val.dsize != sizeof offset) { /* printf("fail here val.dptr %d\n",val.dptr); */ return NULL; } /* Get space. */ if (output->dptr == NULL) { printf("fail here output->dptr null\n"); return NULL; } /* Copy the value to an aligned spot. */ for (p = val.dptr, dest = (char *)&offset, i = sizeof offset; --i >= 0;) *dest++ = *p++; if (lseek(HISreadfd, offset, SEEK_SET) == -1) { printf("fail here lseek %d\n", offset); return NULL; } /* Read the text until \n or EOF. */ for (output->dsize = 0, Used = 0;;) { i = read(HISreadfd, &output->dptr[output->dsize], LEN - 1); if (i <= 0) { printf("fail here i %d\n", i); return NULL; } Used += i; output->dptr[Used] = '\0'; if ((p = (char *)strchr(output->dptr, '\n')) != NULL) { *p = '\0'; break; } } /* Move past the first two fields -- Message-ID and date info. */ if ((p = (char *)strchr(output->dptr, HIS_FIELDSEP)) == NULL) { printf("fail here no HIS_FILE\n"); return NULL; } return p + 1; /* * if ((p = (char*)strchr(p + 1, HIS_FIELDSEP)) == NULL) return NULL; */ /* Translate newsgroup separators to slashes, return the fieldstart. */ } /* * * Have we already seen an article? */ #ifdef HISh BOOL HIShavearticle(MessageID) char *MessageID; { datum key; datum val; HISsetkey(MessageID, &key); val = dbzfetch(key); return val.dptr != NULL; } #endif /* * * Turn a history filename entry from slashes to dots. It's a pity * we * have to do this. */ STATIC void HISslashify(p) register char *p; { register char *last; for (last = NULL; *p; p++) { if (*p == '/') { *p = '.'; last = p; } else if (*p == ' ' && last != NULL) *last = '/'; } if (last) *last = '/'; } IOError(error) char *error; { fprintf(stderr, "%s\n", error); } /* BOOL */ myHISwrite(key, remain) datum *key; char *remain; { static char NOPATHS[] = ""; long offset; datum val; int i; val = dbzfetch(*key); if (val.dptr != NULL) { return FALSE; } flock(fileno(HISwritefp), LOCK_EX); offset = ftell(HISwritefp); i = fprintf(HISwritefp, "%s%c%s", key->dptr, HIS_FIELDSEP, remain); if (i == EOF || fflush(HISwritefp) == EOF) { /* The history line is now an orphan... */ IOError("history"); syslog(LOG_ERR, "%s cant write history %m", LogName); flock(fileno(HISwritefp), LOCK_UN); return FALSE; } /* Set up the database values and write them. */ val.dptr = (char *)&offset; val.dsize = sizeof offset; if (dbzstore(*key, val) < 0) { IOError("my history database"); syslog(LOG_ERR, "%s cant dbzstore %m", LogName); flock(fileno(HISwritefp), LOCK_UN); return FALSE; } if (++HISdirty >= ICD_SYNC_COUNT) HISsync(); flock(fileno(HISwritefp), LOCK_UN); return TRUE; } /* * * Write a history entry. */ BOOL HISwrite(key, date, paths) datum *key; char *paths; long date; { static char NOPATHS[] = ""; long offset; datum val; int i; flock(fileno(HISwritefp), LOCK_EX); offset = ftell(HISwritefp); i = fprintf(HISwritefp, "%s%c%ld%c%s\n", key->dptr, HIS_FIELDSEP, (long)date, HIS_FIELDSEP, paths); if (i == EOF || fflush(HISwritefp) == EOF) { /* The history line is now an orphan... */ IOError("history"); syslog(LOG_ERR, "%s cant write history %m", LogName); flock(fileno(HISwritefp), LOCK_UN); return FALSE; } /* Set up the database values and write them. */ val.dptr = (char *)&offset; val.dsize = sizeof offset; if (dbzstore(*key, val) < 0) { IOError("history database"); syslog(LOG_ERR, "%s cant dbzstore %m", LogName); flock(fileno(HISwritefp), LOCK_UN); return FALSE; } if (++HISdirty >= ICD_SYNC_COUNT) HISsync(); flock(fileno(HISwritefp), LOCK_UN); return TRUE; }