/*
* $Revision: 1.1 $ *
*
* History file routines.
*/
#include <stdlib.h>
#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
void
hisincore(flag)
int flag;
{
HISincore = flag;
}
void
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;
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;
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);
}
}
return 0;
}
/*
* * 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 = '/';
}
void
IOError(error)
char *error;
{
fprintf(stderr, "%s\n", error);
}
/* BOOL */
int
myHISwrite(key, remain)
datum *key;
char *remain;
{
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;
{
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;
}