E#include <ascii.h>
#include "fido.h"
#include "fidomem.h"
#include "proto.h"

/*

Handle LASTUSER.BBS files, that communicate with external programs.

read_lastuser()	
	If enabled, load LASTUSER.BBS (if no taskID) or LASTUSRn.BBS
	and after sanity checking, update last-read pointers from
	the external program.

write_lastuser()
	Generates a LASTUSER.BBS or LASTUSRn.BBS file and a
	SCHED.BBS, presumably at logoff.
*/

/* Version 11 callers record (modified). */

#define OLDMAXLREAD 10

struct  _usr {
	char name[36];		/* users ASCII name, */
	char city[36];		/* city and state, */

/* This structure takes exactly 40 bytes, and replaces the old 
first date. (Which was luckily int[20] by mistake!) */

	struct {
		int area;	/* message area */
		int msg;	/* last msg read */
	} lastmsg[OLDMAXLREAD];

	char pwd[16];		/* what else, */
	int times;		/* # times called, */
	int help;		/* last help setting, */
	int tabs;		/* 1 == expand tabs, */
	int nulls;		/* number of nulls after CR, */
	int msg;		/* last selected message area, */
	int more;		/* last MORE setting, */
	int priv;		/* user privelege level, */
	char ldate[20];		/* last time called, */
	int time;		/* total time on system in 1 day, */
	unsigned bits;		/* various bit flags, */
	unsigned upld;		/* total K byte uploaded, */
	unsigned dnld;		/* total K bytes downloaded, */
	unsigned dnldl;		/* download, for limiting, */
	int files;		/* last selected file area, */
	char width;		/* screen width, */
	char len;		/* screen length, */
	int credit;		/* credit, in cents, */
	int debit;		/* debit, in cents, */

/* These are NOT part of the Fido 11 caller record; they were added simply to
provide the extra info needed for various utilities. */

/* 180 bytes */

	int next_event;		/* time til next event (0 - 1440 mins) or -1 */
	unsigned baud;		/* callers last baud rate */
	unsigned tleft;		/* time remaining this call (0 for sysop) */
	char lang;		/* language selection */
	char extra_byte;	/* RESERVED */
	unsigned extra[14];	/* oh, something for later */
};

/* Version 11 scheduler structure. */

#define OLDSCHEDS (5 * DAYS_WK)	/* size of time table */

struct _oldsched {
	struct {
		int year,month,day,daywk;
		int hour,mins,sec;
	} time;
	int len;
	int enable;		/* 1 == enabled -1 == disabled 0 == deleted */
	int trigger;
	int result;		/* returned value */
	char tag;		/* schedule tag */
	int a,b,c,d,e;
};

/* Read LASTUSER and update the in-memory caller record. */

void read_lastuser() {
struct _usr user;
int f,i;
char fname[SS],buff[SS];

#define OLDAREA (user.lastmsg[i].area)
#define OLDMSG (user.lastmsg[i].msg)
#define CLRAREA (caller.lastmsg[j].area)
#define CLRMSG (caller.lastmsg[j].msg)

	if (! fbit(FBIT_V11)) return;		/* not enabled */

	if (taskid) maketid(buff,"LASTUSR.BBS");
	else strcpy(buff,"LASTUSER.BBS");
	makesname(fname,buff);			/* in system path */
	f= open(fname,0);
	if (f == -1) return;			/* doesnt exist */

	read(f,&user,sizeof(struct _usr));	/* read it in */
	close(f);
	if (! same(user.name,caller.name)) return;
	if (user.times != caller.times) return;	/* must be same call */

/* Old: areas run 1 - N, 0 means unallocated. New: areas run 0 - 254,
255 means unallocated. */

	cprintf(SM+104,fname);			/* "Updating caller record from fn" */
	for (i= 0; i < OLDMAXLREAD; i++) {
		if (OLDAREA == 0) continue;	/* ignore blank spots */
		setlmsg(OLDMSG,OLDAREA - 1);	/* store number */
	}
}

/* Locate the specified stored area, or a free slot, and save new highest
read message and area. */

static setlmsg(n,a)
int n;		/* msg number */
int a;		/* area number */
{
int i,m;

	m= -1;					/* remember first free slot */

/* Look for the desired area # in the table; also remember the first unused slot
in case we dont find the one we want. */

	for (i= 0; i < MAXLREAD; i++) {	
		if ((caller.lastmsg[i].area == 255) && (m == -1)) m= i;
		if (caller.lastmsg[i].area == a) break;
	}

	if (i >= MAXLREAD) {			/* if area was not found */
		if (m == -1) m= 0;		/* (bug prevention) */
		i= m;				/* slot to use this time */
		if (++m >= MAXLREAD) m= 0;	/* turn next slot into */
		caller.lastmsg[m].area= 255;	/* free slot */
		caller.lastmsg[m].msg= 0;
	} 
	caller.lastmsg[i].area= a;		/* set area number */
	caller.lastmsg[i].msg= n;		/* set new number */
}

/* Generate a LASTUSER.BBS file. */

void write_lastuser() {

char *cp,fname[SS],buff[SS];
int i;
struct _usr user;

	if (! fbit(FBIT_V11)) return;		/* not enabled */

	eventsys();				/* generate a SCHED.BBS file */
	for (cp= (char *) &user, i= sizeof(struct _usr); i--;) *cp++= 0;

	strproc(user.name,caller.name,99);	/* fixed-format name */
	strcpy(user.city,caller.city);
	strcpy(user.pwd,caller.pwd);
	strcpy(user.ldate,caller.date);

/* Old: areas run 1 - N, 0 means unallocated. New: areas run 0 - 254,
255 means unallocated. */

	for (i= 0; i < OLDMAXLREAD; i++) {	/* last read msg stuff */
		if (caller.lastmsg[i].area != 0) {
			user.lastmsg[i].area= caller.lastmsg[i].area + 1;
			user.lastmsg[i].msg= caller.lastmsg[i].msg;
		}
	}
	switch (uval(CLR_PRV,CLR_PRVS)) {	/* translate priv */
		case 0: user.priv= -1; break;	/* TWIT */
		case 2: user.priv= 2; break;	/* NORMAL */
		case 3: user.priv= 4; break;	/* PRIVEL */
		case 4: user.priv= 6; break;	/* EXTRA */
		case 7: user.priv= 10; break;	/* SYSOP */
		default:
		case 1: user.priv= 0; break;	/* DISGRACE */
	}
	switch (uval(CLR_HLP,CLR_HLPS)) {	/* translate help */
		case 0: user.help= 2; break;
		case 1: user.help= 4; break;
		case 2: 
		default: user.help= 6; break;
	}
	user.width= uval(CLR_WID,CLR_WIDS);
	user.len= uval(CLR_LEN,CLR_LENS);
	user.nulls= uval(CLR_NUL,CLR_NULS);
	user.more= uval(CLR_LEN,CLR_LENS) ? 1 : 0;
	user.tabs= 1;

	user.msg= caller.msg;
	user.files= caller.files;
	user.time= caller.time;
	user.times= caller.times;
	user.dnld= caller.dnld;
	user.dnldl= caller.dnldl;
	user.upld= caller.upld;
	user.credit= caller.credit;
	user.debit= caller.debit;

/* Now fill in the extra, non-fido-11 stuff. */

	user.baud= caller.baud;			/* last-used baud rate */
	user.tleft= caller.tleft;		/* time remaining this call */
	user.lang= uval(CLR_LAN,CLR_LANS);	/* current language */

	i= til_sched('/',caller.tleft,0);	/* if an event within limit, */
	if (i >= 0) user.next_event= til_event(i); /* report it */
	else user.next_event= -1;

	if (taskid) maketid(buff,"LASTUSR.BBS"); /* filename for output */
	else strcpy(buff,"LASTUSER.BBS");
	makesname(fname,buff);			/* in system path */
	i= creat(fname,1);
	if (i == -1) {
		mprintf(CM+151,fname);		/* "cant create filename" */
		lprintf(LM+39,fname);
		return;
	}
	write(i,&user,sizeof(struct _usr));
	close(i);
}

/* Generate a version 11 SCHED.BBS file, putting in it only those events
that are currently enabled. */

static eventsys() {
struct _oldsched o;
int f,i,t;
char *cp,fname[SS];

	makesname(fname,"SCHED.BBS");		/* make pathname */
	f= creat(fname,2);
	if (i == -1) {
		mprintf(CM+151,fname);		/* "cant create filename" */
		lprintf(LM+39,fname);
		return;
	}
	for (cp= (char *) &o, i= sizeof(struct _oldsched); i--;)
		*cp++= 0;			/* clear it out, */

	for (i= t= 0; i < SCHEDS; i++) {
		if (! fido.sched[i].tag) break;	/* end of table */
		if (! event_on(i)) continue;	/* only enabled ones */

		o.enable= 1;
		o.time.daywk= fido.sched[i].daywk;
		o.time.hour= fido.sched[i].hr;
		o.time.mins= fido.sched[i].min;

		o.tag= fido.sched[i].tag;
		o.len= fido.sched[i].len;
		o.result= fido.sched[i].result;
		o.trigger= fido.sched[i].swtch;		/* N.A. in v11 ... */

		write(f,&o,sizeof(struct _oldsched));
		++t;
	}
	o.tag= o.enable= 0;
	while (t < OLDSCHEDS) {
		write(f,&o,sizeof(struct _oldsched));
		++t;
	}
	close(f);
}

/* Return true if this event is enabled and ours.*/

static event_on(i)
int i;
{
struct _sched *sc;

	sc= &fido.sched[i];			/* keep it simple! */
	if ((sc-> tag < 'A') || (sc-> tag > 'X')) 
		return(0);			/* must be a runnable event */

	if (sc-> taskid && (sc-> taskid != taskid))
		return(0);			/* only our task ID */

	if (sc-> swtch) {			/* if a switch specified, */
		if (! istoggle(sc-> swtch - 1)) 
			return(0);		/* it must be on */
	}
	return(1);
}
