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

extern char banner[];

/* caller record functions for Fido:

signon() 
	Initial log on and new caller entry. 

	NOTE: If the autolog record number is not zero, blindly
	log that caller in; if not found, then log on as a new caller.

	A kludge in the line-input code does a frc_abort() with 'crashcode'
	set when 'crashmail' is on; this is to allow incoming mail. We
	turn crashmail off once we pass the password field. 

	Return values are 1 == good signon, 0 == special login, 
	-1 == bad signon. 


1.	Asks for your name. Must be one or two words, and less than 36
	characters long. The name is cleaned up: no leading spaces, all
	lower case, capitalized 1st letter, one seperating space, no
	trailing spaces. Verifies that the name is correct.

2.	Looks in the caller file for the name. (Error if file not found.)
	If found, increments the number of times called. Seeks the file
	back to the start of that record. If not found, blabs about using
	the help command, creates a new record.

3.	If 'FBIT_NEWCLR' is false, aborts if the name doesnt exist. 

*/

int signon() {
char *cp,*dp,name[SS],lname[SS],pwd[SS];
int i,n,eof,f;
unsigned recd;
char namesum,c;
FLAG found;
long o;

/* Set defaults for initial signon. */
	echo= 1;				/* line input char echo */

	localin= 				/* no local keyboard */
	    lmtstate= 				/* reset time limit warning state machine */
	    kblock= 				/* no ^C lockout */
	    abort= 				/* no ^C abort */
	    column= 				/* callers screen */
	    line= 0;

	cp= (char *) &caller;			/* clear the caller recd */
	for (i= 0; i < sizeof(struct _clr); i++)
		*cp++= NUL;
	setuval(NOVICE,CLR_HLP,CLR_HLPS);	/* set defaults, */
	setuval(80,CLR_WID,CLR_WIDS);		/* screen width, */
	setuval(24,CLR_LEN,CLR_LENS);		/* screen length "MORE" OFF */
	setuval(fido.def_priv,CLR_PRV,CLR_PRVS); /* normal priv */
	caller.keys= KNONE;
	caller.baud= datarate;			/* last used baud rate */
	fido.caller= -1;			/* no caller on yet */

	limit= fido.slimit;			/* signon limit */
	user_limit= limit;
	klimit= fido.klimit;
	dlimit= fido.dlimit;			/* set real limits */

	if (fido.autolog != -1) {		/* if an auto login, */
		n= fido.autolog;		/* caller recd to login */
		fido.autolog= -1;		/* restore to normal, */
		i= openclr();			/* open CALLER.SYS */
		if (i == -1) {
			lprintf(LM+4,fido.caller);	/* "cant autolog" */
			cprintf(SM+87,fido.caller);
			logoff(0,1);			/* stop now */
		}
		o= 0L + n; o *= sizeof(struct _clr);	/* bad compiler long arith */
		lseek(i,o,0);
		read(i,&caller,sizeof(struct _clr));
		close(i);

		fido.caller= n;				/* this caller is on */
		setbaud(caller.baud);			/* set correct data rate */
		logclr(caller.name);			/* start log entry */
		if (! loadlang(uval(CLR_LAN,CLR_LANS))) {/* load callers language */
			setuval(0,CLR_LAN,CLR_LANS);	/* if error get system */
			loadlang(0);			/* default language */
		}
		if (caller.times++) limit= fido.nlimit;	/* assume normal limits */
		else limit= fido.first_limit;		/* (currently signon limit) */
		if (uval(CLR_PRV,CLR_PRVS) == SYSOP) limit= 0;
		adj_limit();				/* adj for priv level */

		if (limit && (caller.tleft < limit)) {	/* for non-sysops */
			limit= caller.tleft;		/* set time left (even 0) */
			if (limit < 2) limit= 2;	/* reasonable minimum */
		}
		chk_limit();				/* enforce them, */
		read_lastuser();			/* possibly read lastuser junk */
		return(1);
	}

/* Its a physical login, display the usual stuff. */

	mprintf(0,"\r\r%sFido/FidoNet v%d%c\r\n",banner,FIDOVER,96+FILEVER);
	if (id.number | id.net | id.zone)
		mprintf(CM+280,str_node(&id));
	dispbbs("welcome1.bbs");	/* display the welcome files, */


/* Input the callers name. Prompt for first then last name. If at the FIRST
name prompt, we get more than one word, we assume that they entered their
complete name (smart people that they are). */

	*name= NUL;			/* the raw, global name as inputted */
	while (1) {
		getfield(name,string(CM+29),1,99,sizeof(caller.name),0); /* "your FIRST name" */
		if (num_args(name) < 2) {
			getfield(lname,string(CM+30),0,99,sizeof(caller.name) - strlen(name) - 1,0);
			if (strlen(lname) > 0) {	/* "LAST name" */
				strcat(name," "); 
				strcat(name,lname);
			}
		} 

/* Here we look for "Y" as the last name, and truncate it if it exists. 
Fix people who refuse to repair their scripts. */

		if (fbit(FBIT_YNAME)) {
			for (dp= cp= name; *cp; cp= next_arg(cp))
				dp= cp;			/* remember last word */
			cpyarg(lname,dp);		/* clean copy */
			stoupper(lname);		/* make upper case */
			if (same(lname,"Y")) {		/* if last name is "Y" */
				*dp= NUL;		/* remove it */
			}
		}

/* This assumes that "name?" is a valid syntactical format for a question. It
is (though brief) in english. Probably not elsewhere. Oh well, yet another
thing to fix "later". */

		strproc(lname,name,99);		/* copy/process it */
		if (fbit(FBIT_FNAME)) strcpy(pwd,name); /* ask "name?" -- */
		else strcpy(pwd,lname);		/* fixed or free format */
		strcat(pwd,"?");		/*   add the question mark */
		if (yverf(0,pwd)) break;	/*   ask */
	}
	namesum= strsum(lname);			/* set the namesum, */

/* Look for the guy in the caller list. Update the list if we find him,
else create a new entry. */

	mprintf(CM+8);				/* "wait..." */
	if (fbit(FBIT_FNAME)) logclr(name);	/* log this login attempt */
	else logclr(lname);			/* fixed or free format */
	f= openclr();				/* open CALLER.SYS */
	if (f == -1) {				/* cant create the caller file */
		mprintf(CM+23,"CALLER.SYS");	/* "cant find caller file!" */
		lprintf(LM+31,"CALLER.SYS");
		doscode= 1;
		frc_abort();
	}

/*
	name=	raw caller name
	lname=	fixed caller name
	namesum= fixed caller namesum

Eiter name or lname will be stored in the caller record, depending on
the fbit. */

	found= recd= 0;				/* caller record number, */
	while (read(f,&caller,sizeof(struct _clr))) {
		if (namesum == caller.sum) {	/* if the checksum matches */
			strproc(pwd,caller.name,99); /* then (slow) cvt to fixed fmt */
			if (same(lname,pwd)) {	/* compare; if the same, */
				found= 1;	/* found 'em */
				break;
			}
		}
		++recd;				/* next recd ... */
	}
	close(f);				/* done with the file */

/* This name not found in the list. */

	if (! found) {
		for (cp= (char *) &caller, i= sizeof(struct _clr); i--;)
			*cp++= NUL;		/* zap the structure */
		for (i= 0; i < MAXLREAD; i++) 	/* mark all as unused */
			caller.lastmsg[i].area= 255;
		caller.version= CLRVER;		/* record version */

		if (fbit(FBIT_FNAME)) strcpy(caller.name,name);/* copy name */
		else strcpy(caller.name,lname);	/* fixed-or free-format */

		caller.sum= namesum;		/* and name check sum */
		gtod(caller.date);		/* set some date, */
		setuval(NOVICE,CLR_HLP,CLR_HLPS); /* set defaults, */
		setuval(80,CLR_WID,CLR_WIDS);	/* screen width, */
		setuval(24,CLR_LEN,CLR_LENS);	/* screen length */
		setuval(fido.def_priv,CLR_PRV,CLR_PRVS);
		caller.msg= fido.defmnbr;	/* set default areas */
		caller.files= fido.deffnbr;
		caller.keys= fido.def_keys;
		caller.tleft= 0;		/* time limit left */
		caller.baud= datarate;		/* last used baud rate */

		if (fbit(FBIT_NEWCLR)) {	/* if public system, */
			dispbbs("newclr1.bbs");	/* ask for stuff, */
			mconflush();

			if (fbit(FBIT_LINGUAL))	/* if multi-lingual, */
				picklang();	/* pick language first */

			do {
				getfield(caller.city,string(CM+31),1,99,sizeof(caller.city),0);
			} while (!yverf(0,caller.city)); /* "where are you calling from" */

			do {
				getfield(caller.pwd,string(CM+33),1,1,sizeof(caller.pwd),1);
			} while (!yverf(0,caller.pwd)); /* "choose a password" */

			line= 0;
			abort= 0;
			if (question("newclr.qes","newclr.ans") == 0) {
				dispbbs("newclr2.bbs");
			}
			found= 1;		/* mark as "found caller" */
		}
	} 

/* Either the name was found in CALLER.SYS, or a new caller just logged in. */

	if (found) {
		found= 0;			/* REUSE OF 'FOUND' */
		if (fbit(FBIT_FNAME)) strcpy(caller.name,name);/* copy name */
		else strcpy(caller.name,lname);	/* fixed-or free-format */
		for (i= 0; (i < 3) && !found; i++) {
			echo= 0;		/* stop echo, */
			getfield(pwd,string(CM+34),1,1,sizeof(caller.pwd),1); /* "Password" */
			echo= 1;		/* enable again, */

			if (strcmp(pwd,caller.pwd) == 0) found= 1;
			else {
				mprintf(CM+238);	/* "wrong!" */
				lprintf(LM+6,pwd);
			}
		}
	}
	crashmail= 0;					/* disable fidonet detection */

/* Sucessful login: if it's one of the special logins, exit now, otherwise
adjust the time limits and return logged in sucessfully. */

	if (found) {
		if (caller.times++) limit= fido.nlimit;
		else limit= fido.first_limit;
		user_limit= limit;

		fido.caller= recd;			/* this caller is on */

		if (! loadlang(uval(CLR_LAN,CLR_LANS))) {/* load callers language */
			setuval(0,CLR_LAN,CLR_LANS);	/* if error get system */
			loadlang(0);			/* default language */
		}
		if (extlogin(caller.city)) {		/* look for special login */
			cp= skip_delim(caller.city);	/* first look for special */
			cp= next_arg(cp);		/* first words in the address */
			c= tolower(*next_arg(cp));	/* c == <X> */
			c -= 'a';			/* convert to 0 - N */
			clprintf(0," * ExternalLogin '%c': ",c + 'A');
			if (c >= sizeof(fido.ext_login)) clprintf(SM+217,'A' + sizeof(fido.ext_login) - 1);
			else {
				if (doscode= fido.ext_login[c]) {
					clprintf(0,"ERRORLEVEL %d\r\n",doscode);
					mprintf(CM+8);
					return(0);	/* "special login" */

				} else clprintf(SM+218);
			}
		}
		adj_limit();			/* adjust limits, etc */
		chk_limit();			/* enforce limits, */
		dispbbs("welcome2.bbs");	/* display welcome messages, */
		dispbbs("welcome3.bbs");
		dispbbs("welcome4.bbs");
		dispbbs("welcome5.bbs");
		return(1);			/* logged in OK */
	}

/* Either a private system or the idiot forgot his password. If there
is a questionaire, fill it in, else let the guy leave a message to the sysop. */

	if (! question("nopwd.qes","nopwd.ans")) {
		dispbbs("nopwd.bbs");		/* "no password" spiel, */
		goodbye("y");			/* logoff, ask for a message. */
	}
	goodbye("n");				/* logoff questionaire */
}

/* Start a log file entry for this caller. */

static logclr(name)
char *name;
{
	lprintf(0,"----\377");
	lprintf(LM+8,name,date);			/* " %s on at %s" */
	if (test) lprintf(LM+9);			/* "test mode" */
	else lprintf(LM+10,datarate);			/* "%d baud" */
}

/* Adjust caller limits according to privilege level. */

static adj_limit() {

	switch (uval(CLR_PRV,CLR_PRVS)) {
		case TWIT:
			limit /= 2;	/* 1/2 time limit, */
			klimit /= 2;	/* 1/2 download limit, */
			dlimit /= 2;	/* 1/2 daily limit, */
			break;

		case DISGRACE:
		case NORMAL:
			break;

		case PRIVEL:
			limit *= 2;
			klimit *= 2;
			dlimit *= 2;
			break;

		case EXTRA:
			limit *= 4;
			klimit *= 4;
			dlimit *= 4;
			break;

		case SYSOP:
			limit= 0;	/* no limit */
			dlimit= 0;
			klimit= 0;
			break;

		default:
			setuval(NORMAL,CLR_PRV,CLR_PRVS);
			break;
	}
}

/* Check time limits:
 (1) Look for an event that wants to run within the caller time limit. If
so, shorten the callers limit to enable the event to run, then ask if 
this is OK.

 (2) Check that this caller hasnt exceeded the daily cumulative
time limit. 

If either limit is hit, this function never returns; it goes immediately
to logoff(). */

static chk_limit() {
int i,n;

	hlimit= 0;				/* do not force disconnect */
	n= limit;				/* test initial limit, */
	if (n == 0) n= fido.nlimit;		/* normal limit for sysops */

	i= til_sched('/',n,0);			/* if an event within limit, */
	if (i >= 0) n= til_event(i);		/* shorten callers limit */

	user_limit= limit;			/* for later use */

	if ((n < limit) ||			/* if caller limit was shortened */
	    ((limit == 0) && (n < fido.nlimit)) ) { /* or sysop now has one */

	mcrlf(); mcrlf(); mcrlf();
		if ((n < 1) && (uval(CLR_PRV,CLR_PRVS) == SYSOP)) {
			mprintf(CM+253);	/* "event pending right now" */

		} else {
			clr_clk();
			lmtstate= 2;		/* KLUDGE */
			limit= 1;		/* must finish this quick */
			mconflush();
			if (n < 1) {		/* not enough time */
				mprintf(CM+239);/* "has become unavailable" */
				goodbye("n");
			}
			limit= n;
			hlimit= 1;		/* yes, a hard limit */
			mprintf(CM+240,limit);	/* "unavailable in N minutes" */
			if (!yverf(CM+269)) goodbye("n");
			lmtstate= 0;		/* KLUDGE */
		}
	}

	if (uval(CLR_PRV,CLR_PRVS) == SYSOP) return;	/* no daily limit for sysops */

/* Check the global limits, see if we can let him go. */

	if (days(caller.date,date) >= 1) {	/* clear limits if more */
		caller.time= 0;			/* than one day, */
		caller.dnldl= 0;
	} 

	if ((dlimit > 0) && (caller.time > dlimit)) {
		mprintf(CM+241,caller.time);
		mprintf(CM+242,fido.dlimit);
		mprintf(CM+243);
		mprintf(CM+244,fido.dlimit);

		lprintf(LM+11);			/* "24 hr limit" */
		fido.caller= -1;		/* dont update caller record */
		goodbye("n");
	}
}
