home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Pier Shareware 6
/
The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso
/
024
/
psi110g.zip
/
EXPIRE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-26
|
23KB
|
847 lines
/* Expire old messages.
* Inspired by 'expire.c' by Bernie Roehl.
* Substantially rewritten for integration into KA9Q NOS,
* WG7J v1.01 and later
* by Johan. K. Reinalda, WG7J/PA3DIS, March/April 92
*
* Old bid expiry by WG7J, March 92
* Index file support for jnos 1.10x3 and later added. WG7J Summer 1993
* Error checking/logging added by N5KNX 14Jun94
* NNTPS expiry written by VK5XXX, added 28Jun94 and extended for NNTP
* for jnos 1.10f -- n5knx
*/
/* 'expire #' sets the expire interval for n hours.
* Each time the timer goes off, a new process is started,
* that expires the old messages...
*
* The control file '~/spool/expire.dat' contains lists of
* filename age
* -or-
* !newsgroup age
*
* where 'filename' is the name of the .txt file under '~/spool/mail'
* containing the messages (but specified WITHOUT the ending '.txt').
* filename can be extended into subdirectories, and can have either
* '/', '\' or '.' to indicate subdirectories.
*
* and where 'newsgroup' is the NNTP newsgroup name whose articles are
* to be expired. The active and history files are also updated when
* necessary.
*
* 'age' is an integer giving the maximum age of a message or newsgroup
* article, in days, after which expiry is performed.
* If no age is given, the default age is 21 days.
*/
#ifdef MSDOS
#include <dir.h>
#include <dos.h>
#endif
#include <alloc.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/timeb.h>
#include <ctype.h>
#include <time.h>
#include "global.h"
#include "timer.h"
#include "proc.h"
#include "bm.h"
#include "files.h"
#include "smtp.h"
#include "socket.h"
#include "index.h"
#ifdef NNTPS
#include "dirutil.h"
#include <limits.h>
#endif
#if defined(NNTPS) || defined(NNTP)
#define NN_EITHER
#endif
#ifdef LINUX
extern int unlink __ARGS((char *));
#endif
#ifndef __BORLANDC__
#ifndef UNIX
time_t mktime(struct tm *);
/* If you're using BC++ 2.0 or higher, you don't need this,
* but TCC 2.0 doesn't have it... (But, TC++ v1.01 does)
*/
/* Simple emulation of the mktime() call (sort-of works :-) )
* doesn't do any error checking,
* no timezone adjustments or value adjustments
* Simply 'sort-a' calculates the number of seconds since 1970 - WG7J
*/
time_t
mktime(t)
struct tm *t;
{
static int total[12]={0,31,59,90,120,151,181,212,243,273,304,334};
int years;
int leapyears;
int days;
/* time count start at jan 1, 1970, 00:00 hrs */
years = t->tm_year - 70;
/* adjust for leap-years */
leapyears = (years + 2) / 4;
if (!((years+70) & 3) && (t->tm_mon < 2))
--leapyears; /* no extra day until 3/1 or after */
days = years*365L + leapyears + total[t->tm_mon] + (t->tm_mday-1);
return( days*86400L + t->tm_hour*3600L + t->tm_min*60L + t->tm_sec);
}
#endif /* UNIX */
#endif /* __BORLANDC__ */
/* Include the rest in #ifdef's, so we don't pull in the whole module
* when we only have AT defined and NOT EXPIRE !
* (since these are the two modules that needs the surrogate mktime()
* if compiling with Turbo C 2.0 )
*/
#ifdef EXPIRY
/* Default expiry values: */
#define DEFAULT_AGE 21 /* 21 days from origination date */
#define MSPHOUR (1000L*60L*60L)
#define DAYStoSECS (24L*60L*60L)
static struct timer Expiretimer;
static int Eproc;
static char OpenErr[] = "expire: err %d opening %s";
static char RenErr[] = "expire: err %d renaming %s to %s";
static char WriteErr[] = "expire: err %d writing %s";
static char IRMsg[] = "expire: err %d reading index for msg %d of %s";
static char IWMsg[] = "expire: err %d writing index for msg %d of %s";
static char TRMsg[] = "expire: err %d reading msg %d of %s";
static char TWMsg[] = "expire: err %d writing msg %d to %s";
static void Expireprocess __ARGS((int a,void *v1,void *v2));
static void Expiretick __ARGS((void *));
static void expire __ARGS((char *,int));
static void Oldbidtick __ARGS((void *p));
/* NNTP Expire stuff */
#ifdef NN_EITHER
static void update_nntp_history __ARGS((int));
static int convert_num __ARGS((char **));
#endif
#ifdef NNTPS
static void expire_nntpsrv __ARGS((char *, int));
static void update_nntp_active __ARGS((void));
static void update_newsrc __ARGS((int, int, char *));
static char *newsgroup_to_path __ARGS((char *));
#endif
int
doexpire(argc,argv,p)
int argc;
char *argv[];
void *p;
{
if(argc < 2) {
tprintf("timer: %lu/%lu hrs\n",
read_timer(&Expiretimer)/MSPHOUR,
dur_timer(&Expiretimer)/MSPHOUR);
return 0;
}
if(*argv[1] == 'n') {
Expiretick(NULL);
return 0;
}
/* set the timer */
stop_timer(&Expiretimer); /* Just in case */
Expiretimer.func = (void (*)())Expiretick;/* what to call on timeout */
Expiretimer.arg = NULL; /* dummy value */
set_timer(&Expiretimer,atol(argv[1])*MSPHOUR); /* set timer duration */
start_timer(&Expiretimer);
return 0;
}
void
Expiretick(p)
void *p;
{
start_timer(&Expiretimer);
/* Spawn off the process */
if(!Eproc)
newproc("Expiry", 1536, Expireprocess, 0, NULL, NULL, 0);
}
static void
Expireprocess(a,v1,v2)
int a;
void *v1, *v2;
{
char line[80];
int age;
#ifdef NN_EITHER
int expire_nntp_history = 0;
#endif
char *cp;
FILE *ctl;
Eproc = 1;
if ((ctl = fopen(Expirefile, "r")) == NULLFILE) {
Eproc = 0;
return;
}
/* read lines from the control file */
while(fgets(line, sizeof(line), ctl) != NULLCHAR) {
pwait(NULL); /* be nice */
if((*line == '#') || (*line == '\n')
#ifndef NN_EITHER
|| (*line == '!')
#endif
) /* comment or blank line */
continue;
rip(line);
/* terminate area name */
age = DEFAULT_AGE;
if((cp=strpbrk(line, " \t")) != NULLCHAR) {
/* there is age info */
*cp++ = '\0';
age = atoi(cp);
if (age <= 0) age = DEFAULT_AGE;
}
#ifdef NN_EITHER
if (*line == '!') { /* Expire NNTP entry if line begins with ! */
#ifdef NNTPS
expire_nntpsrv(&line[1], age);
#endif
#ifdef NNTP
expire(&line[1], age);
#endif
if (expire_nntp_history < age)
expire_nntp_history = age;
}
else
#endif
expire(line,age);
}
fclose(ctl);
#ifdef NN_EITHER
if (expire_nntp_history) {
update_nntp_history(expire_nntp_history);
#ifdef NNTPS
update_nntp_active(); /* and news.rc */
#endif
}
#endif
Eproc = 0;
}
#ifdef NN_EITHER
/*
expire NNTP articles
coded by brett england (future amateur) and rob vk5xxx.
*/
static
int
convert_num( p )
char **p;
{
int i;
char *pp = *p;
i = (*pp - '0') * 10 + *(pp+1) - '0';
(*p)+=2;
return (i);
}
static
void
update_nntp_history(age)
int age;
{
FILE *old, *new;
char newfile[FILE_PATH_SIZE];
char line[LINELEN];
char *p;
struct tm tm_time;
time_t expire_time, file_time, now;
int i,history_records_expired = 0;
for (i=10; i>0; i--) {
if(!mlock(History,NULLCHAR)) break;
pause(2000L);
}
if (!i) return; /* can't lock, we'll have to wait until next time */
expire_time = (long)age * DAYStoSECS; /* days to seconds */
time(&now);
sprintf(newfile,"%s.new",History);
unlink(newfile);
if((old = fopen(History,READ_TEXT)) == NULLFILE) {
log(-1,OpenErr, errno, History);
rmlock(History,NULLCHAR);
return;
}
if((new = fopen(newfile,WRITE_TEXT)) == NULLFILE) {
fclose(old);
log(-1,OpenErr, errno, newfile);
rmlock(History,NULLCHAR);
return;
}
/* Scan the history file, deleting old article references */
/* format: <message-id> yymmdd hhmmss ngname/Art# */
for(;;) {
pwait(NULL);
if(fgets(line, LINELEN, old) == NULL)
break;
p = line;
while (*p != '\0' && *p != '>')
p++;
if (*p != '>') /* Some sort of corrupt line */
continue;
p+=2;
tm_time.tm_year = convert_num(&p);
tm_time.tm_mon = convert_num(&p)-1;
tm_time.tm_mday = convert_num(&p);
p++; /* Point to time component */
tm_time.tm_hour = convert_num(&p);
tm_time.tm_min = convert_num(&p);
tm_time.tm_sec = 0;
file_time = mktime(&tm_time);
if (file_time > ( now - expire_time ))
fputs(line, new);
else
history_records_expired++;
}
i=ferror(new);
fclose(new);
fclose(old);
if(!i) {
unlink(History);
if(rename(newfile, History) == -1)
log(-1, RenErr, errno, newfile, History);
}
else log(-1, WriteErr, errno, newfile);
rmlock(History,NULLCHAR);
if(history_records_expired > 0)
log(-1,"Expired: %d in %s", history_records_expired, History);
}
#endif
#ifdef NNTPS
/* Check the news.rc file if the number contained is less than min
replace the news.rc file contents with min.
*/
static
void
update_newsrc( min, max, path )
int min, max;
char *path;
{
FILE *newsrcfd;
char news_file[FILE_PATH_SIZE], line[LINELEN];
int no;
sprintf(news_file,"%s/news.rc",path);
if((newsrcfd = fopen(news_file,"r+")) == NULL) {
log(-1,OpenErr, errno, news_file);
return;
}
fgets(line, LINELEN, newsrcfd);
rewind(newsrcfd);
no = atoi(line);
if((no < min) || (no > max))
fprintf(newsrcfd,"%d",min);
fclose(newsrcfd);
}
/* Update the active file and return the lowest news article
*/
static
void
update_nntp_active() {
FILE *old, *new;
char newfile[FILE_PATH_SIZE];
char line[LINELEN];
char *p, *path, *rpath;
int min, max;
int command, file_num;
struct ffblk *file;
sprintf(newfile,"%s.new",Active);
unlink(newfile);
if((old = fopen(Active,READ_TEXT)) == NULLFILE) {
log(-1,OpenErr,errno,Active);
return;
}
if((new = fopen(newfile,WRITE_TEXT)) == NULLFILE) {
fclose(old);
log(-1,OpenErr,newfile);
return;
}
file = (struct ffblk *)mallocw(sizeof(struct ffblk));
/* rewrite the active file, with current max and min article numbers */
/* format: ngname MaxArt# MinArt# Postflag */
for(;;) {
pwait(NULL);
if(fgets(line, LINELEN, old) == NULL)
break;
p = line;
while (*p != '\0' && ! isspace(*p))
p++;
if (*p == '\0')
continue;
*p = '\0';
p++;
while(*p != '\0' && (isspace(*p) || isdigit(*p)))
p++;
rpath = newsgroup_to_path( line );
path = wildcardize(rpath);
command = 0;
min = INT_MAX;
max = INT_MIN;
for(;;) {
pwait(NULL);
if (!nextname(command, path, file))
break;
command = 1; /* Find next */
if (file->ff_name[0] == '.')
continue; /* ignore . and .. */
file_num = atoi(file->ff_name);
if (file_num > 0) { /* should always be greater than 0 */
if (file_num < min)
min = file_num;
if (file_num > max)
max = file_num;
}
}
if (min == INT_MAX && max == INT_MIN) { /* No files */
min = 1;
max = 0;
}
update_newsrc(min, max, rpath);
fprintf(new,"%s %05d %05d %s", line, max, min, p);
}
command=ferror(new);
fclose(new);
fclose(old);
free(file);
if(!command) {
unlink(Active);
if(rename(newfile,Active) == -1)
log(-1,RenErr,newfile,Active);
}
else log(-1, WriteErr, errno, newfile);
}
static
char
*newsgroup_to_path( group )
char *group;
{
FILE *f;
static char line[LINELEN];
char *cp;
if((f = fopen(Pointer,"r")) == NULL)
return((char *)NULL);
for (;;) {
pwait(NULL);
if (fgets(line,LINELEN,f) == NULL)
break;
if (strcspn(line," ") != strlen(group))
continue;
if (strnicmp(group,line,strlen(group)) == 0) {
cp = (strchr(line,' ')) + 1;
rip(cp);
fclose(f);
return (cp);
}
}
fclose(f);
return (NULL);
}
void
expire_nntpsrv(nntp_name, age)
char *nntp_name;
int age;
{
char *path;
int command = 0;
struct ffblk file;
struct tm tm_time;
time_t file_time, expire_time, now;
char nntp_file[FILE_PATH_SIZE];
char save_path[FILE_PATH_SIZE];
int file_num, expired = 0;
/* Resolve nntp name into directory path */
if ((path = newsgroup_to_path( nntp_name )) == NULL)
return;
expire_time = (long)age * DAYStoSECS;
time(&now);
strcpy(save_path, path);
path = wildcardize(path);
for(;;) {
pwait(NULL);
if (!nextname(command, path, &file))
break;
command = 1; /* Find next */
if (file.ff_name[0] == '.')
continue; /* ignore . and .. */
file_num = atoi(file.ff_name);
if (file_num > 0) {
/* only expire a file that is numeric :-) */
tm_time.tm_sec = 0; /* DOS doesn't store this */
tm_time.tm_min = (file.ff_ftime >> 5) & 0x3f;
tm_time.tm_hour = (file.ff_ftime >> 11) & 0x1f;
tm_time.tm_mday = file.ff_fdate & 0x1f;
tm_time.tm_mon = ((file.ff_fdate >> 5) & 0xf)-1;
tm_time.tm_year = (file.ff_fdate >> 9) + 80;
file_time = mktime(&tm_time);
if (( now - expire_time ) > file_time) {
sprintf(nntp_file, "%s/%s", save_path, file.ff_name);
unlink(nntp_file);
expired++;
}
}
}
if(expired)
log(-1,"Expired: %d in %s",expired, nntp_name);
}
#endif NNTPS
void
expire(filename,age)
char *filename;
int age;
{
int idx,idxnew,expired,i,err;
FILE *txt,*new;
long start,pos,msgsize;
time_t now;
struct indexhdr hdr;
struct mailindex index;
char file[FILE_PATH_SIZE];
char newfile[FILE_PATH_SIZE];
char buf[LINELEN];
#define IRERR -1 /* Index read error */
#define IWERR -2 /* Index write error */
#define TRERR -3 /* area text file read error */
#define TWERR -4 /* area text file write error */
#define TPERR -5 /* area text file position error */
dirformat(filename);
if(mlock(Mailspool,filename))
/* can't get a lock */
return;
sprintf(file,"%s/%s.txt",Mailspool,filename);
if((txt=fopen(file,READ_BINARY)) == NULLFILE) {
rmlock(Mailspool, filename);
return;
}
sprintf(file,"%s/%s.ind",Mailspool,filename);
if((idx=open(file,READBINARY)) == -1) { /* Open the index file */
fclose(txt);
log(-1, OpenErr, errno, file);
rmlock(Mailspool,filename);
return;
}
/* Create new text file */
sprintf(file,"%s/%s.new",Mailspool,filename);
if((new=fopen(file,WRITE_TEXT)) == NULLFILE) {
fclose(txt);
close(idx);
log(-1, OpenErr, errno, file);
rmlock(Mailspool, filename);
return;
}
/* Create the new index file */
sprintf(file,"%s/%s.idn",Mailspool,filename);
if((idxnew=open(file,CREATETRUNCATEBINARY,CREATEMODE)) == -1) {
fclose(txt);
close(idx);
fclose(new);
rmlock(Mailspool,filename);
log(-1, OpenErr, errno, file);
return;
}
memset(&index,0,sizeof(index));
time(&now);
start = pos = 0L;
expired = err = 0;
/* Write a default header to the new index file */
default_header(&hdr);
if (write_header(idxnew,&hdr) == -1) {
log(-1, IWMsg, errno, -1, filename);
err=IWERR;
}
/* Read the header from the index file */
if (read_header(idx,&hdr) == -1) {
log(-1, IRMsg, errno, -1, filename);
err=IRERR;
}
/* Now read all messages, and expire old ones */
for(i = 1; i <= hdr.msgs && !err; i++) {
default_index(filename,&index);
if (read_index(idx,&index) == -1) {
log(-1,IRMsg,errno,i,filename);
err=IRERR;
break;
}
msgsize = index.size;
if(now - index.date < ((long)age*DAYStoSECS)) {
/* This one we should keep ! Copy it from txt to new */
fseek(txt,start,SEEK_SET);
pos = ftell(new);
/* Read the first line, should be 'From ' line */
if (bgets(buf,sizeof(buf),txt) == NULL) {
log(-1, TRMsg, errno,i,filename);
err=TRERR;
break;
}
if (strncmp(buf,"From ",5)) {
log(-1, "expire: format error in msg %d of %s.txt",i,filename);
err=TPERR;
break;
}
/* Now copy to output until we find another 'From ' line or
* reach end of file.
*/
do {
if (fprintf(new,"%s\n",buf) == -1) {
log(-1, TWMsg,errno,i,filename);
err=TWERR;
break; /* copy of msg will have shrunk ... */
}
if (bgets(buf,sizeof(buf),txt) == NULL) break;
pwait(NULL);
} while (strncmp(buf,"From ",5));
/* write the index for the new copy of the message */
index.size = ftell(new) - pos;
if (index.size < 0 || pos < 0) {
log(-1, "expire: ftell err %d for %s", errno, filename);
err=TPERR;
break;
}
if (WriteIndex(idxnew,&index) == -1) {
log(-1,IWMsg,errno,i,filename);
err=IWERR;
break;
}
} else
expired++;
start += msgsize; /* starting offset of the next message */
}
default_index("",&index);
close(idx);
close(idxnew);
pos=ftell(new); /* how long is new area */
fclose(new);
fclose(txt);
if (!err) {
sprintf(file,"%s/%s.txt",Mailspool,filename);
sprintf(newfile,"%s/%s.new",Mailspool,filename);
unlink(file);
if(pos)
rename(newfile,file);
else
/* remove a zero length file */
unlink(newfile);
sprintf(file,"%s/%s.ind",Mailspool,filename);
sprintf(newfile,"%s/%s.idn",Mailspool,filename);
unlink(file);
if(pos)
rename(newfile,file);
else {
/* remove a zero length file */
unlink(newfile);
#ifdef USERLOG
sprintf(file,"%s/%s.inf", Mailspool, filename);
unlink(file);
#endif
}
}
rmlock(Mailspool,filename);
if(expired)
log(-1,"Expired: %d in %s",expired,filename);
}
/******************************************************************/
/* This program will deleted old BID's from the history file,
* after making a backup copy.
*
* Eg. 'oldbids 24 30' will try to delete all bids older then 30 days
* every 24 hours.
* 'oldbids now' will do it now, with set value of age.
*
* Copyright 1992, Johan. K. Reinalda, WG7J/PA3DIS
* email : johan@ece.orst.edu
* packet: wg7j@wg7j.or.usa.na
*
* Any part of this source may be freely distributed for none-commercial,
* amateur radio use only, as long as credit is given to the author.
*
* v1.0 920325
*/
static struct timer Oldbidtimer;
static int Oldbid_age = 30;
static
void
Oldbidtick(p)
void *p;
{
int i,expired = 0;
char *cp;
FILE *old, *new;
time_t now;
time_t age;
time_t bidtime;
#define LEN 80
char newfile[LEN];
char buf[LEN];
stop_timer(&Oldbidtimer);
/* We will rewrite the msgid history file, renaming it when we are
done. Since this is not protected by a lock, we won't call pwait()
so as to minimize chances we'll lose an update -- N5KNX */
sprintf(newfile,"%s.new",Historyfile);
unlink(newfile);
if((old = fopen(Historyfile,READ_TEXT)) == NULLFILE) {
log(-1, OpenErr, errno, Historyfile);
return;
}
if((new = fopen(newfile,WRITE_TEXT)) == NULLFILE) {
fclose(old);
log(-1, OpenErr, errno, newfile);
return;
}
now = time(&now);
age = (time_t)(Oldbid_age*DAYStoSECS);
while(fgets(buf,LEN,old) != NULL) {
rip(buf);
if((cp=strchr(buf,' ')) != NULLCHAR) {
/*found one with timestamp*/
*cp = '\0';
cp++; /* now points to timestamp */
if((bidtime = atol(cp)) == 0L)
/*something wrong, re-stamp */
fprintf(new,"%s %ld\n",buf,now);
else {
/* Has this one expired yet ? */
if(now - bidtime < age)
fprintf(new,"%s %ld\n",buf,bidtime);
else
expired++;
}
} else {
/* This is an old one without time stamp,
* add to the new file with current time as timestamp
*/
fprintf(new,"%s %ld\n",buf,now);
}
}
i=ferror(new);
fclose(old);
fclose(new);
if(!i) {
unlink(Historyfile);
if(rename(newfile,Historyfile) == -1)
log(-1, RenErr, errno, newfile, Historyfile);
}
else log(-1, WriteErr, errno, newfile);
if(expired)
log(-1,"Oldbids: %d expired",expired);
start_timer(&Oldbidtimer);
return;
}
int
dooldbids(argc,argv,p)
int argc;
char *argv[];
void *p;
{
if(argc < 2){
tprintf("timer: %lu/%lu hrs; age: %d days\n",
read_timer(&Oldbidtimer)/MSPHOUR,
dur_timer(&Oldbidtimer)/MSPHOUR,
Oldbid_age);
return 0;
}
if(*argv[1] == 'n') {
Oldbidtick(NULL);
return 0;
}
/* set the timer */
stop_timer(&Oldbidtimer); /* Just in case */
Oldbidtimer.func = (void (*)())Oldbidtick;/* what to call on timeout */
Oldbidtimer.arg = NULL; /* dummy value */
set_timer(&Oldbidtimer,atol(argv[1])*MSPHOUR); /* set timer duration */
if(argc > 2)
Oldbid_age = atoi(argv[2]);
Oldbidtick(NULL); /* Do one now and start it all!*/
return 0;
}
#endif /* EXPIRE */