/* 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
 */
/* 'expire n' 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
 *
 * where 'filename' is the name of the .txt file under '~/spool/mail'
 * containing the messages (without the .txt extension)
 * filename can be extended into subdirectories, and can have either
 * '/', '\' or '.' to indicate subdirectories.
 *
 * 'age' is an integer giving the maximum age of a message in days,
 * after which expiry is performed.
 * If no age is given, the default age is 21 days.
 *
 */
#include "global.h"
#include "commands.h"
#include "files.h"
#ifdef MSDOS
#include <dir.h>
#include <dos.h>
#else
#include "ctype.h"
#include <time.h>
#include <sys/stat.h>
#endif
#include "timer.h"
#include "proc.h"
#include "bm.h"
#include "delegate.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: expire.c,v 1.28 2000/05/09 16:48:25 brian Exp $";
#endif

int ExpireActive = 0;
char *ExpireArea = NULLCHAR;
time_t ExpireLast = 0;
extern char *nntp_name_expansion (char *name);
extern void updateCtl (const char *who, struct let * info);
extern int expired;

#if 0
static time_t j_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...
 */
/* Simple emulation of the mktime() call (sort-of works :-) )
 * doesn't do any error checking,
 * no timezone adjustments or value adjustments
 * neglects seconds,
 * and might be off by a day for leap year corrections
 * Simply 'sort-a' calculates the number of seconds since 1970 - WG7J
 */
time_t
j_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 in case year was passed in as 19xx, instead of xx */
	if (years >= 1900)
		years -= 1900;
	/* adjust for leap-years */
	leapyears = (years + 2) / 4;
	if (!((years + 70) & 3) && (t->tm_mon < 2))
		--leapyears;

#ifdef linux
	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);
#else
	days = years * 365L + leapyears + total[t->tm_mon] + t->tm_mday;
	return (days * 86400L + t->tm_hour * 3600L + t->tm_min * 60L - 19 * 3600L);
#endif
}
#endif


#if (defined(EXPIRY) || defined(DELEGATE))

/* Default expiry values: */
#define DEFAULT_AGE 21		/* 21 days from arrival date */
#define MSPHOUR (1000L*60L*60L)
static struct timer Expiretimer;
extern int PruneAge;

static void Expireprocess (int a, void *v1, void *v2);
void Expiretick (void *);
void expire (char *, int);


int
doexpire (int argc, char **argv, void *p OPTIONAL)
{
	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 (*)(void *)) Expiretick;	/* what to call on timeout */
	Expiretimer.arg = NULL;	/* dummy value */
	set_timer (&Expiretimer, atol (argv[1]) * MSPHOUR);	/* set timer duration */
	start_detached_timer (&Expiretimer);
	return 0;
}



void
Expiretick (void *p OPTIONAL)
{
	start_detached_timer (&Expiretimer);
	/* Spawn off the process */
	if (!ExpireActive)
		if (newproc ("Expiry", 2048, Expireprocess, 0, NULL, NULL, 0) == NULLPROC)
			log (-1, "Couldn't start Expiration process");
}



static void
Expireprocess (int a OPTIONAL, void *v1 OPTIONAL, void *v2 OPTIONAL)
{
char line[80];
int age = DEFAULT_AGE;
char *cp;
FILE *ctl;

	ExpireActive = 1;
	log (-1, "EXPIRE process started");
#ifdef DELEGATE
	purge_delegate ();
#endif
	setMaintenance ();
	if ((ctl = fopen (Expirefile, "r")) != NULLFILE) {
		(void) time (&ExpireLast);
		/* read lines from the control file */
		while (fgets (line, sizeof (line), ctl) != NULLCHAR) {
			kwait (NULL);	/* be nice */
			if ((*line == '#') || (*line == '\n'))	/* comment or blank line */
				continue;
			rip (line);
			age = DEFAULT_AGE;
			/* terminate area name */
			if ((cp = strpbrk (line, " \t")) != NULLCHAR) {
				/* there is age info */
				*cp++ = '\0';
				age = atoi (cp);
				if (age <= 0)
					age = DEFAULT_AGE;
			}
			kwait (NULL);	/* be nice */
			ExpireArea = line;	/*lint !e789 */
			(void) nntp_name_expansion (line);
			expire (line, age);
			ExpireArea = NULLCHAR;
		}
		(void) fclose (ctl);
	}
	clearMaintenance ();
	log (-1, "EXPIRE process completed");
	ExpireActive = 0;
}



void
expire (char *filename, int age)
{
char file[128], bckfile[128], bckctl[128], *cp;
char buf[128];
int keep, copy, kept = 0;
FILE *old;
FILE *new;
long pos;
time_t now;
time_t then;
struct tm t;
struct let lt;
int entrynum = 1;
int ctlfound = 0;

	if (age == -1)
		age = PruneAge;
	/* first replace all '.' and '\' with '/' */
	for (cp = filename; *cp != '\0'; cp++)
		if ((*cp == '.') || (*cp == '\\'))
			*cp = '/';

	if (mlock (Mailspool, filename)) {
		/* can't get a lock */
		return;
	}
	/* rename the old *.ctl file */
	sprintf (file, "%s/control/%s.ctl", Mailspool, filename);
	sprintf (bckctl, "%s/control/%s.exp", Mailspool, filename);
	unlink (bckctl);
	if (rename (file, bckctl) != -1)
		ctlfound = 1;

	/* now append the 'home dir' in front of name */
	sprintf (file, "%s/%s", Mailspool, filename);

	/* get the name for the backup file */
	sprintf (bckfile, "%s.exp", file);
	strcat (file, ".txt");

	/* rename the file */
	unlink (bckfile);
	if (rename (file, bckfile) == -1) {
		rmlock (Mailspool, filename);
		return;
	}
	kwait (NULL);
	/* open the backup file and the new txt file */
	if ((old = fopen (bckfile, READ_TEXT)) == NULLFILE) {
		rmlock (Mailspool, filename);
		return;
	}
	if (!ctlfound && filelength(fileno(old)) == 0)	{
		(void) fclose (old);
		unlink (bckfile);
		rmlock (Mailspool, filename);
		log (-1, "Expire: removing empty file for area %s", filename);
		return;
	}
	if ((new = fopen (file, WRITE_TEXT)) == NULLFILE) {
		rmlock (Mailspool, filename);
		return;
	}
	kwait (NULL);
	(void) time (&now);
	copy = expired = 0;
	pos = ftell (old);
	while (fgets (buf, sizeof (buf), old) != NULLCHAR) {
		kwait (NULL);
		if (!strncmp (buf, "From ", 5)) {
			/* start of next message; at this point
			 * pos has the offset of the start of this line
			 */
			keep = copy = 0;
			while (fgets (buf, sizeof (buf), old) != NULLCHAR) {
				kwait (NULL);
				if (*buf == '\n')
					break;	/* end of headers */
				if (htype (buf) == DATE) {
					/* find age from ARPA style date */
					/* check to see if there is a "Day, " field */
					if ((cp = strchr (buf, ',')) != NULLCHAR) {
						/* probably standard ARPA style header */
						cp = &buf[11];	/* get past header and DAY field */
					} else {
						/* probably a NNTP style message, that has no
						 * "Day, " part in the date line
						 */
						cp = &buf[6];
					}
					/* now we should be at the start of the
					 * "14 Apr 92 08:14:32" string
					 */
					if (strlen (cp) < 17)	/* Too short */
						break;
					t.tm_mday = atoi (cp);
					if (*(++cp) != ' ')
						++cp;
					++cp;
					for (t.tm_mon = 0; t.tm_mon < 12; t.tm_mon++)
						if (strnicmp (Months[t.tm_mon], cp, 3) == 0)
							break;
					if (t.tm_mon == 12)
						break;	/* invalid */
#if 1
					/* modified to accept either
					 * "14 Apr 92 08:14:32" string or
					 * "14 Apr 1992 08:14:32" string
					 * WA3DSP 11/94 - modified by KO4KS
					 */

					while (*cp != ' ')
						++cp;
					++cp;
					t.tm_year = atoi (cp);
					if (t.tm_year > 1900)  /* 4 digit year */
						t.tm_year -= 1900;
					else {
						/* Message has two digit year */
						if (t.tm_year < 70)
							t.tm_year += 100;
					}
                                 
					while (*cp != ' ')
						++cp;
					++cp;
					t.tm_hour = atoi (cp);
					t.tm_min = atoi (cp + 3);
					t.tm_sec = atoi (cp + 6);
					t.tm_isdst = 0;

#else
					t.tm_year = atoi (cp + 4);
					t.tm_hour = atoi (cp + 7);
					t.tm_min = atoi (cp + 10);
					t.tm_sec = atoi (cp + 13);
#endif
					if ((then = mktime (&t)) == (time_t) -1) {
						/* For now, print a warning if there is a big problem ! */
						/* Single line following is here for testing only */
						tcmdprintf ("Invalid article date in expire\n");
						break;	/* invalid, delete */
					}
					/* Now check against age */
					if (now - then < (time_t) (age * 86400L))
						keep = 1;
					break;
				}
			}
			statusCtl (filename, "exp", &lt, entrynum, 1);
			if (!keep && (lt.status & BM_PERMANENT))
				keep = 1;	/* don't expire permanent messages */
			if (keep) {
				/* rewind to start of this message,
				 * write from-line and copy the rest
				 */
				kwait (NULL);
				if (lt.status & BM_DELETE)
					expired++;
				else {
					lt.start = ftell (new);
					updateCtl (filename, &lt);
					fseek (old, pos, SEEK_SET);
					(void) fgets (buf, sizeof (buf), old);
					fputs (buf, new);
					copy = 1;
					kept++;
				}
			} else
				expired++;
			entrynum++;
		} else {	/* Any none 'from' line */
			if (copy)
				/* we're in the middle of copying a message */
				fputs (buf, new);
		}
		kwait (NULL);
		pos = ftell (old);
	}
	(void) fclose (old);
	(void) fclose (new);
	kwait (NULL);
	unlink (bckfile);
	unlink (bckctl);
	rmlock (Mailspool, filename);
	if (expired)
		log (-1, "Expired: %d in %s", expired, filename);


	if (!kept)	{
		log (-1, "Expire: removing empty file for area %s", filename);
		sprintf (file, "%s/%s.txt", Mailspool, filename);
		unlink (file);
	}
}


#endif /* EXPIRY */
