/* telnet.c */

#define	TELNETMASTER

	/* Linemode Option */
	/*
	   This option is under	development ...
	   The current implementation causes certain
	   systems to spew a large number of extra negotiations
	   (e.g. The 'Next') when it can't take 'SLC_NOSUPPORT'
	   for a response.  After almost every input command
	   it tries to renegotiate the complete linemode
	   configuration. (*%$#&!)
	   Most other systems don't support it anyway.
	 */

/*
	#define	T_LINEMODE
*/

/*
 *  Includes
 */
#include <string.h>
#include <stdio.h>

#include "os.h"
#include "vrsion.h"
#include "vtel.h"
#include "dfault.h"
#include "evtdef.h"
#include "hstdef.h"
#include "prodef.h"
#include "kbkeys.h"
#include "tsxutl.h"
#include "rtfile.h"
#include "kbdutl.h"
#include "cliutl.h"
#include "usrblk.h"
#include "inicli.h"
#include "suspnd.h"

/*
 * Internal routines
 */

/*
 * Due to DECUS C aborts from lack of space
 * all functions/subroutines of type 'int'
 * are no longer explicitly defined.
 *
 * DECUS C defaults to 'int', VOID == int
 */

#if 0
extern	int	dosess();	/* process loop */
extern	int	initsession();	/* (sknum) inititialize session socket */
extern	int	inprocess();	/* (sknum) process incoming data */
extern	VOID	parsewrite();	/* (tw,str,len) write from parse */
extern	VOID	parse();	/* (tw,st,cnt) telnet negotiations */
extern	int	newkey();	/* (tw) filter for command sequences */
extern	int	dokey();	/* (tw,c) process keyboard keys */
extern	VOID	lsterr();	/* list error routine for TSXUTL.C */
extern	int	question();	/* (cstr,clen,qstr) question routine */
extern	int	rtresp();	/* (st,ln) get response */
extern	int	keygets();	/* (str,lim,echo) get line from keyboard */
extern	int	getword();	/* (string,word) extract a word */
extern	VOID	strlwr();	/* (st) convert string to lower case */
extern	int	chkusr();	/* check username / password */
extern	int	Scompass();	/* (ps,en) compare passwords */
extern	VOID	printtxt();	/* (txt) printout text */
#endif

extern	char	*stptok();	/* (p,to,len,dlm) stop on token */
extern	char	*stpblk();	/* (ch) stop on blank */

/*
 * debug -
 *
 *	bit 0	(0x01)	enable printing of internal events
 *	bit 1	(0x02)	enable printing of telnet negotiations
 */
#define DBUGTRUE	-1	/* all debuggers */

static char *usetxt[] = {
       "",
#ifdef	DEBUGOPTION
       "  TELNET destination [?] [-hp] [-d level]",
       "	?		List the Help Text and Exit TELNET",
       "	d  level	Debug Level",
       "		1 -	enable event printing",
       "		2 -	enable telnet negotiation printing",
#else
       "  TELNET destination [?] [-hp]",
       "	?		List the Help Text and Exit TELNET",
#endif
       "	h		List the Help Text and Open Connection",
       "	p   filename	Specify the Password Filespec",
       "",
	0
	};

static char *hlptxt[] = {
"",
"Keyboard usage for Telnet-11",
"-------- ----- --- ---------",
"",
"The metacharacter 'M->' is ^A",
"M->C    open capture file             M->Q     are you there?",
"M->D    close capture file            M->R     toggle <CR> follow character",
"M->F    FTP [internet address]        M->S     skip to end of buffer",
"M->H    this help screen              M->T     toggle backspace/delete",
"M->I    type my internet address      M->U     erase line",
"M->K    erase character               M->X     close connection",
"M->O    abort output                  M->Y     interrupt process",
"M->@    send a true null character",
"",
" ^?     abort Telnet session",
"",
0
};


char	xs[2];			/* dumby array for TSXUTL.C */

static char			/* password file */
	*pass = "PAS:paswrd.fil";

int	pswdreqd = 1;		/* password required */
int	viewmode = 1;
struct twin *current = NULL;	/* current session */
char	parsedat[80];		/* parsing data */


/* Definitions for telnet protocol */

#define	ABORT			(-3)

#define STNORM			0

#define SE			240	/* \360 */
#define NOP			241	/* \361 */
#define DM			242	/* \362 */
#define BREAK			243	/* \363 */
#define IP			244	/* \364 */
#define AO			245	/* \365 */
#define AYT			246	/* \366 */
#define EC			247	/* \367 */
#define EL			248	/* \370 */
#define GOAHEAD 		249	/* \371 */
#define SB			250	/* \372 */
#define WILLTEL 		251	/* \373 */
#define WONTTEL 		252	/* \374 */
#define DOTEL		 	253	/* \375 */
#define DONTTEL 		254	/* \376 */
#define IAC		 	255	/* \377 */


/* Assigned Telnet Options */
#define BINARY			0
#define ECHO			1
#define RECONNECT		2
#define SGA 			3
#define AMSN			4
#define STATUS			5
#define TIMING			6
#define RCTAN			7
#define OLW			8
#define OPS			9
#define OCRD			10
#define OHTS			11
#define OHTD			12
#define OFFD			13
#define OVTS			14
#define OVTD			15
#define OLFD			16
#define XASCII			17
#define LOGOUT			18
#define BYTEM			19
#define DET			20
#define SUPDUP			21
#define SUPDUPOUT		22
#define SENDLOC			23
#define TERMTYPE 		24
#define EOR			25
#define TACACSUID		26
#define OUTPUTMARK		27
#define TERMLOCNUM		28
#define REGIME3270		29
#define X3PAD			30
#define NAWS			31
#define TERMSPEED		32
#define TFLOWCNTRL		33
#define LINEMODE 		34
#define	    MODE		1
#define		EDIT    	1
#define		TRAPSIG 	2
#define 	MODE_ACK	4

#define	    FORWARDMASK		2

#define	    SLC			3
#define		NO_SUPPORT	0
#define		CANTCHANGE	1
#define		SLC_VALUE	2
#define		SLC_DEFAULT	3
#define		SLC_LEVELBITS	3
#define		SLC_AWK		128
#define		SLC_SYNCH	1
#define		SLC_BRK		2
#define		SLC_IP		3
#define		SLC_AO		4
#define		SLC_AYT		5
#define		SLC_EOR		6
#define		SLC_ABORT	7
#define		SLC_EOF		8
#define		SLC_SUSP	9
#define		SLC_EC		10
#define		SLC_EL   	11
#define		SLC_EW   	12
#define		SLC_RP		13
#define		SLC_LNEXT	14
#define		SLC_XON		15
#define		SLC_XOFF	16
#define		SLC_FORW1	17
#define		SLC_FORW2	18

#define XDISPLOC		35
#define XOPTIONS		255
	
#define ESCFOUND		5
#define IACFOUND		6
#define NEGOTIATE		1


#ifdef	DEBUGOPTION
char *telstates[4]={
	"Will",
	"Won't",
	"Do",
	"Don't"
};

char *teloptions[256]={
	"Binary",				/* 0 */
	"Echo",
	"Reconnection",
	"Supress Go Ahead",
	"Message Size Negotiation",
	"Status",				/* 5 */
	"Timing Mark",
	"Remote Controlled Trans and Echo",
	"Output Line Width",
	"Output Page Size",
	"Output Carriage-Return Disposition",	/* 10 */
	"Output Horizontal Tab Stops",
	"Output Horizontal Tab Disposition",
	"Output Formfeed Disposition",
	"Output Vertical Tabstops",
	"Output Vertical Tab Disposition",	/* 15 */
	"Output Linefeed Disposition",
	"Extended ASCII",
	"Logout",
	"Byte Macro",
	"Data Entry Terminal",			/* 20 */
	"SUPDUP",
	"SUPDUP Output",
	"Send Location",
	"Terminal Type",
	"End of Record",			/* 25 */
	"TACACS User Identification",
	"Output Marking",
	"Terminal Location Number",
	"3270 Regime",
	"X.3 PAD",				/* 30 */
	"Negotiate About Window Size",
	"Terminal Speed",
	"Toggle Flow Control",
	"Linemode",
	"X Display Location",			/* 35 */
	"36","37","38","39",
	"40","41","42","43","44","45","46","47","48","49",
	"50","51","52","53","54","55","56","57","58","59",
	"60","61","62","63","64","65","66","67","68","69",
	"70","71","72","73","74","75","76","77","78","79",
	"80","81","82","83","84","85","86","87","88","89",
	"90","91","92","93","94","95","96","97","98","99",
	"100","101","102","103","104","105","106","107","108","109",
	"110","111","112","113","114","115","116","117","118","119",
	"120","121","122","123","124","125","126","127","128","129",
	"130","131","132","133","134","135","136","137","138","139",
	"140","141","142","143","144","145","146","147","148","149",
	"150","151","152","153","154","155","156","157","158","159",
	"160","161","162","163","164","165","166","167","168","169",
	"170","171","172","173","174","175","176","177","178","179",
	"180","181","182","183","184","185","186","187","188","189",
	"190","191","192","193","194","195","196","197","198","199",
	"200","201","202","203","204","205","206","207","208","209",
	"210","211","212","213","214","215","216","217","218","219",
	"220","221","222","223","224","225","226","227","228","229",
	"230","231","232","233","234","235","236","237","238","239",
	"240","241","242","243","244","245","246","247","248","249",
	"250","251","252","253","254",
	"Extended Options List"		/* 255 */
};

char *LMoptions[]={
	"None",
	"SYNCH",
	"BREAK",
	"IP",
	"ABORT OUTPUT",
	"AYT",
	"EOR",
	"ABORT",
	"EOF",
	"SUSP",
	"EC",
	"EL",
	"EW",
	"RP",
	"LNEXT",
	"XON",
	"XOFF",
	"FORW1",
	"FORW2"
};

char *LMflags[]={
	"NOSUPPORT",
	"CANTCHANGE",
	"VALUE",
	"DEFAULT"
};
#endif

main(argc,argv)
int argc;
char *argv[];
{
	int c;
	register int i,j,k;
	int ev,sknum;
	struct socket *skt;

	/*
	 * Initialize for TSX+ clients
	 */
	inicli();

	/*
	 * Initialize kb handler
	 * Set the break character to ^C
	 */
	kb_init(0x03);

	/*
	 * identify self
	 */
	printtxt(vrstxt);

	/*
	 * parse arguments
	 */
	parsedat[0] = '\0';
	for(i=1; i<argc; i++) {
		if(argv[i][0]=='?') {
			printtxt(usetxt);
			printtxt(hlptxt);
			exit(0);
		} else
		if(argv[i][0] == '-') {
		    j = i;
		    k = 1;
		    while((c = argv[j][k]) != '\0') {
			switch(tolower(c)) {
			/*
			 * This is a 'hidden' option for bypassing
			 * the password processing.
			 */
			case 'b':	/* bypass password processing */
				pswdreqd = 0;
				break;
			case 'd':	/* debug, optional level */
#ifdef	DEBUGOPTION
				if(sscanf(argv[++i],"%d",&debug) <= 0)
					debug = DBUGTRUE;
#else
				++i;
				kb_puts("Dubug not available.\r\n");
#endif
				break;

			case 'h':	/* display help */
				printtxt(hlptxt);
				break;

			case 'p':	/* password file */
				pass = argv[++i];
				break;
#if ts$sys
			/*
			 * This is a 'hidden' option for setting the
			 * the default subdirectory base 'ld' unit
			 * and setting the maximum subdirectory nesting
			 *	note:	subdbase + subdnest <= 8
			 */
			case 's':
				sscanf(argv[++i],"%d",&subdbase);
				sscanf(argv[++i],"%d",&subdnest);
				break;
#endif
			default:	/* unknown option */
				printf("Unrecognized option %s ignored\r\n",
					&argv[i][0]);
				break;
			}
			k++;
		    }
		} else {
			if(parsedat[0]!='\0')
				strncat(parsedat," ",79);
			strncat(parsedat,argv[i],79);
		}
	}
	if (*parsedat == '\0') {
		kb_puts("Enter machine name/address -->> ");
		if (keygets(parsedat,sizeof(parsedat),1) == ABORT) {
			exit(0);
		}
	}

#ifdef	DEBUGOPTION
	if(debug)
		kbprintf("Debug = %d\r\n", debug);
#endif
	/*
	 * Test Username / Password
	 */
	if(pswdreqd) {
		if(chkusr()) {
			kb_puts("\r\nEscaping from Telnet\r\n");
			exit(0);
		}
	}
	/*
	 * Initialize kb handler
	 * Set the break character to ^?
	 */
	kb_init(0x1F);
	/*
	 * Initialize various things
	 */
	Snetinit();
	/*
	 *  Open a connection to the machine named.
	 *  Start up all of the configuration on the connection.
	 */
	sknum = addsess(0,parsedat,HTELNET);
	if(sknum<0) {
		kbprintf("\r\nEscaping from Telnet\r\n");
		return(0);
	}

	skt = (struct socket *) mapskt(sknum);
	current = &(skt->session);

	do {
		ev = dosess();
		if(ev==0)
			suspnd(0);
	} while (ev != -1);

	suspnd(0);
	errhandle();

	kb_puts("\r\nEscaping from Telnet\r\n");
	exit(0);
}

/*
 *  dosess
 *
 *  dosess is an infinite loop serving two sources
 *  of external input in order of precedence:
 *   - keyboard strokes
 *   - message events
 */
int dosess()
{
	register int c;
	int cl,ev,dat;

	/*
	 * do all key translations
	 */
	switch(viewmode) {
	  default:
	    /*
	     * This gives precedence to the
	     * keyboard over the network.
	     */
	    while(0<=(c=newkey(current))) {
	      if(c==A_BRK)
		return(-1);
	    }
	    break;

	  case 1:
	    if(fndbrk)
		return(-1);

	    c = kb_char();
	    if(c == A_X) {
		return(-1);
	    } else
	    if(c != -1) {
		kb_puts("\r\nOther commands require an open session.");
		kb_puts("\r\n^A X - Escape from Telnet.\r\n");
	    }
	    break;
	}
	/*
	 * Check for any relevant messages
	 * that need to be handled by me
	 */
	ev = Sgetevent(SCKTCLASS | CONCLASS | USERCLASS |
			ERRCLASS | MSGCLASS | ABORTCLASS, &cl, &dat);
	if(ev!=0) {
	  switch(cl) {
	    case CONCLASS:
	      switch(ev) {
		case CONOPEN:	/* a connection has just opened */
		  if(initsession(dat))
		    break;
		  /* Start negotiation on network */
		  /* IAC, DOTEL, ECHO, IAC, DOTEL, SGA, IAC, WILLTEL, NAWS */
		  skwrite(dat,"\377\375\001\377\375\003\377\373\037",9);
		  skenque(dat,1);
#ifdef	DEBUGOPTION
if(debug&0x02) {
		  /* Print to the console what we just did */
		  printf("SEND: IAC %s %s\r\n",
		    telstates[DOTEL-WILLTEL],teloptions[ECHO]);
		  printf("SEND: IAC %s %s\r\n",
		    telstates[DOTEL-WILLTEL],teloptions[SGA]);
		  printf("SEND: IAC %s %s\r\n",
		    telstates[WILLTEL-WILLTEL],teloptions[NAWS]);
}
#endif
		  viewmode = 0;
		  break;

		case CONCLOSE:	/* connection is closing */
		  if(0<skqlen(dat))
		    skptuev(CONCLASS,CONCLOSE,dat);  /* call me again */
		  viewmode = 1;
		  /* drop through, process any data */

		case CONDATA:
		  if(inprocess(dat)==A_BRK)
		    return(-1);
		  break;

		case CONFAIL:	/* can't open connection */
		  kb_puts("\r\nCan't open connection\r\n");
		  skclose(dat);	    /* close out attempt */
		  skrelease(dat,1);
		  return(-1);
		  break;

		default:
		  break;
	      }
	      break;

	    case MSGCLASS:	/* messages */
	    case ERRCLASS:	/* error message */
	      kb_puts(skerrstring(ev));
	      kb_nline();
	      break;

	    case ABORTCLASS:	/* abort */
	      switch(ev) {
		case CLIENT:
		  kbprintf("\r\nTELNET session aborted by TCPIP\r\n");
		  Stmrset(ABORTCLASS,EXITABORT,dat,5);

		case USERABORT:
		  if(skrelease(dat,0) < 0) {
		    skptuev(CONCLASS,CONDATA,dat);
		  } else {
		    skclose(dat);
		    Stmrset(ABORTCLASS,USERABORT,dat,1);
		  }
		  break;

		case EXITABORT:
		  return(-1);
		  break;

		default:
		  break;
	      }
	      break;

	    default:
	      break;
	  }
	}
	return(ev);
}

/*
 *  initsession
 *
 *  initializes the session within the socket
 */
int initsession(sknum)
int sknum;
{
	register struct socket *skt;
	register struct machinfo *mp;
	register struct twin *tw;
	int i;
	char st[100];

	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(-1);

	/*
	 * Log the Session
	 */
	sprintf(st,"Telnet: %s", parsedat);
	logsession(sknum,st);

	/*
	 * clear the machine string
	 */
	tw = &(skt->session);
	mp = skt->mpp;

	for(i=1; i<19; i++)
		tw->slc[i] = -1;

	/*
	 * All parameters were zeroed when the socket
	 * was allocated in TCPIP.
	 */

	tw->pnum = sknum;

	tw->termstate = VTTYPE;
	strncpy(tw->termtype,"VT100",sizeof(tw->termtype));
	tw->termtype[sizeof(tw->termtype)-1] = 0;

	if(mp->delete==127) {
		tw->bksp = 8;
		tw->del = 127;
	} else {
		tw->bksp = 127;
		tw->del = 8;
	}
	tw->crfollow = mp->crmap;
	tw->halfdup = mp->halfdup;
	tw->width = 80;
	tw->rows = 24;
	tw->slc[SLC_IP] = 3;
	tw->slc[SLC_EC] = tw->del;
	tw->slc[SLC_EL] = 21;
	tw->slc[SLC_EOF] = 4;
	tw->slc[SLC_ABORT] = 3;

	return(0);
}

/* 
 *  inprocess
 *
 *  take incoming data and process it.  Close the connection if it
 *  is the end of the connection.
 */
int inprocess(sknum)
int sknum;
{
	register struct socket *skt;
	register struct twin *tw;
	register int cnt;
	char s[80];

	skt = (struct socket *) skvalid(sknum);
	if(skt==-1)
		return(A_BRK);
	tw = &(skt->session);

	cnt = skread(sknum,s,80);	/* get some from incoming queue */
	if(cnt<0) {			/* close this session, if over */
	  skclose(sknum);
	  skrelease(sknum,1);

	  if(tw->capon) {
	    rtclose(tw->capfp);		/* close the capture file */
	    tw->capon = 0;
	  }

	  kb_puts("\r\nConnection closed.\r\n");
	  return(A_BRK);
	}

	if(cnt) {
	  skdeque(sknum);		/* dequeue the data */
	  parse(tw,s,cnt);		/* display on screen, etc.*/
	}

	return(0);
}

/*
 *  parsewrite
 *
 *  write out some chars from parse
 *  Has a choice of where to send the stuff
 */
VOID parsewrite(tw,str,len)
struct twin *tw;
register char *str;
int len;
{
	register int i;
	register char *t;
	char s[86];

	/*
	 *  send the string where it belongs
	 *  1. Check for a capture file.  If so, echo a copy of the string
	 *  2. Check for dumb terminal type, convert special chars
	 *  3. send to screen
	 */
	if(tw->capon)		  /* capture to file? */
	  fwrite(str,len,1,tw->capfp);
	/*
	 * raw mode for debugging, passes through escape sequences
	 * and other special characters as <27> symbols
	 *
	 * this attempts to output strings of characters
	 * to reduce the overhead of single character i/o
	 */
	if(tw->termstate==DUMBTYPE) {
	  for (t=s,i=0; i<len; i++,str++) {
	    if((*t = *str) == 0) {
	      if(t!=s)
		kb_puts(s);
	      t = s;
	    } else
	    if(*str<32||*str>126) {
	      kbprintf("<%d>",*str&0xFF);
	      kb_puts(s);
	      t = s;
	    } else {
	      t++;
	    }
	  }
	} else {
	  for(t=s,i=0; i<len; i++,str++) {
	    if((*t = *str) == 0) {
	      if(t!=s)
		kb_puts(s);
	      t = s;
	    } else {
	      t++;
	    }
	  }
	}
	if(t!=s) {
	  *t = 0;
	  kb_puts(s);
	}
}

/*
 *  parse
 *
 *  Do the telnet negotiation parsing.
 *
 *  look at the string which has just come in from outside and
 *  check for special sequences that we are interested in.
 *
 *  Tries to pass through routine strings immediately, waiting for special
 *  characters ESC and IAC to change modes.
 */
VOID parse(tw,st,cnt)
register struct twin *tw;
char *st;
int cnt;
{
	register int cst;
	register char *ss;
	int i;
	char s[80];
	char *mark,*orig;

	orig = st;		/* remember beginning point */
	mark = st+cnt;		/* set to end of input string */
	/*
	 * traverse string, looking for any special characters
	 * which indicate that we need to change modes.
	 */
	while (st<mark) {

	  cst = *st & 0xFF;
	  switch(tw->telstate) {
	    case ESCFOUND:
	      parsewrite(tw,"\033",1);	/* send the missing ESC */
	      tw->telstate = STNORM;
	      break;

	    case IACFOUND:		/* telnet option negotiation */
	      if(cst==IAC) {		/* real data=255 */
		tw->telstate = STNORM;
		st++;
		break;
	      }

	      if(248<cst) {
		tw->telstate = cst;	/* by what the option is */
		st++;
		break;
	      }

	      kbprintf("\r\n strange telnet option %d\r\n", cst & 0xFF);
	      orig = st;
	      tw->telstate = STNORM;
	      break;

	    case GOAHEAD:
#ifdef	DEBUGOPTION
if(debug&0x02) {
	      kb_puts("RECV: IAC GOAHEAD\r\n");
}
#endif
	      orig = st;
	      tw->telstate = STNORM;
	      break;

	    case DOTEL:
#ifdef	DEBUGOPTION
if(debug&0x02) {
	      printf("RECV: IAC %s %s\r\n",
		telstates[tw->telstate-WILLTEL],teloptions[cst]);
}
#endif
	      switch(cst) {
		case SGA:
		  if(!tw->igoahead) {		/* suppress go-ahead */
		    /* IAC, WILLTEL, cst */
		    skwrite(tw->pnum,"\377\373",2);
		    skwchar(tw->pnum,cst);
		    tw->igoahead = 1;
		    st++;
#ifdef	DEBUGOPTION
if(debug&0x02) {
		    printf("SEND: IAC %s %s\r\n",
		      telstates[WILLTEL-WILLTEL],teloptions[cst]);
}
#endif
		  } else {
#ifdef	DEBUGOPTION
if(debug&0x02) {
		    printf("NO REPLY NEEDED: %s %s\r\n",
		      telstates[WILLTEL-WILLTEL],teloptions[SGA]);
}
#endif
		  }
		  orig = st;
		  tw->telstate = STNORM;
		  break;

		case TERMTYPE:
		  if (!tw->termsent) {
		    /* IAC, WILLTEL, cst */
		    skwrite(tw->pnum,"\377\373",2);
		    skwchar(tw->pnum,cst);
		    tw->termsent = 1;
		    st++;
#ifdef	DEBUGOPTION
if(debug&0x02) {
		    printf("SEND: IAC %s %s\r\n",
		      telstates[WILLTEL-WILLTEL],teloptions[cst]);
}
#endif
		  } else {
#ifdef	DEBUGOPTION
if(debug&0x02) {
		    printf("NO REPLY NEEDED: %s %s\r\n",
		      telstates[WILLTEL-WILLTEL],teloptions[TERMTYPE]);
}
#endif
		  }
		  orig = st;
		  tw->telstate = STNORM;
		  break;

#ifdef	T_LINEMODE
		case LINEMODE:
		  /* IAC, WILLTEL, LINEMODE, IAC, SB, LINEMODE, SLC */
		  skwrite(tw->pnum,"\377\373\042\377\372\042\003",7);
#ifdef	DEBUGOPTION
if(debug&0x02) {
	kb_puts("SEND: IAC WILLTEL LINEMODE IAC SB LINEMODE SLC\r\n");
}
#endif
		  for (i=1; i<19; i++) {
		    ss = s;
		    *ss++ = i;
		    if (tw->slc[i]==-1) {
		      *ss++ = NO_SUPPORT; *ss++ = 0;
		    } else {
		      *ss++ = SLC_VALUE; *ss++ = tw->slc[i];
		    }
		    skwrite(tw->pnum,s,3);
#ifdef	DEBUGOPTION
if(debug&0x02) {
		    if(tw->slc[i]==-1) {
		      printf("      %s NO_SUPPORT 0\r\n",
			LMoptions[i]);
		    } else {
		      printf("      %s SLC_VALUE %d\r\n",
			LMoptions[i], tw->slc[i]);
		    }
}
#endif
		  }

		  /* IAC, SE */
		  skwrite(tw->pnum,"\377\360",2);
		  st++;
		  orig = st;
		  tw->telstate = STNORM;
		  break;
#endif	/* T_LINEMODE */

		case NAWS:

		  /* IAC, SB, NAWS */
		  skwrite(tw->pnum,"\377\372\037",3);
		  ss = s;
		  *ss++ = 0; *ss++ = tw->width;
		  *ss++ = 0; *ss++ = tw->rows;
		  skwrite(tw->pnum,s,4);
		  /* IAC, SE */
		  skwrite(tw->pnum,"\377\360",2);
#ifdef	DEBUGOPTION
if(debug&0x02) {
		  printf("SEND: IAC SB NAWS 0 %d 0 %d IAC SE\r\n",
		    tw->width & 0xFF, tw->rows & 0xFF);
}
#endif
		  st++;
		  orig = st;
		  tw->telstate = STNORM;
		  break;

		default:		/* refuse it */
		  /* IAC, WONTTEL, cst */
		  skwrite(tw->pnum,"\377\374",2);
		  skwchar(tw->pnum,cst);
		  st++;
#ifdef	DEBUGOPTION
if(debug&0x02) {
		  printf("SEND: IAC %s %s\r\n",
		    telstates[WONTTEL-WILLTEL],teloptions[cst]);
}
#endif
		  orig = st;
		  tw->telstate = STNORM;
		  break;

	      }
	      break;

	    case DONTTEL:
#ifdef	DEBUGOPTION
if(debug&0x02) {
	      printf("RECV: IAC %s %s\r\n",
		telstates[tw->telstate-WILLTEL],teloptions[cst]);
}
#endif
	      st++;
	      orig = st;
	      tw->telstate = STNORM;
	      break;

	    case WILLTEL:
#ifdef	DEBUGOPTION
if(debug&0x02) {
	      printf("RECV: IAC %s %s\r\n",
		telstates[tw->telstate-WILLTEL],teloptions[cst]);
}
#endif
	      st++;
	      tw->telstate = STNORM;
	      switch(cst) {
		case SGA:		/* suppress go-ahead */
		  if(tw->ugoahead)
		    break;

		  /* IAC, DOTEL, SGA */
		  skwrite(tw->pnum,"\377\375\003",3);
		  tw->ugoahead = 1;
#ifdef	DEBUGOPTION
if(debug&0x02) {
		  printf("SEND: IAC %s %s\r\n",
		    telstates[DOTEL-WILLTEL],teloptions[cst]);
}
#endif
		  break;

		case ECHO:		/* echo */
		  if(tw->echo)
		    break;

		  tw->echo = 1;
		  /* IAC, DOTEL, ECHO */
		  skwrite(tw->pnum,"\377\375\001",3);
		  skwrite(tw->pnum,tw->linemode,strlen(tw->linemode));
		  tw->linemode[0] = 0;
#ifdef	DEBUGOPTION
if(debug&0x02) {
		  printf("SEND: IAC %s %s\r\n",
		    telstates[DOTEL-WILLTEL],teloptions[cst]);
}
#endif
		  break;

		case TIMING:
		  break;

		default:		/* refuse it */
		  /* IAC, DONTTEL, cst */
		  skwrite(tw->pnum,"\377\376",2);
		  skwchar(tw->pnum,cst);
#ifdef	DEBUGOPTION
if(debug&0x02) {
		  printf("SEND: IAC %s %s\r\n",
		    telstates[DONTTEL-WILLTEL],teloptions[cst]);
}
#endif
		  break;

	      }
	      orig = st;
	      break;
		    
	    case WONTTEL:
#ifdef	DEBUGOPTION
if(debug&0x02) {
	      printf("RECV: IAC %s %s\r\n",
		telstates[tw->telstate-WILLTEL],teloptions[cst]);
}
#endif
	      switch(cst) {		/* which option? */
		case ECHO:		/* echo */
		  if(!tw->echo)
		    break;

		  /* IAC, DONTTEL, ECHO */
		  skwrite(tw->pnum,"\377\376\001",3);
		  tw->echo = 0;
		  break;

		case TIMING:
		  break;

		default:
		  break;

	      }
	      st++;
	      orig = st;
	      tw->telstate = STNORM;
	      break;

	    case SB:
#ifdef	DEBUGOPTION
if(debug&0x02) {
	kb_puts("RECV: IAC SB\r\n");
}
#endif
	      orig = st;
	      i = tw->substate = 0;	      /* Defined for each */
	      tw->telstate = NEGOTIATE;
	      break;

	    case NEGOTIATE:
	      if(tw->substate <200) {
		switch(cst) {
		  case IAC:
		    parsedat[i] = 0;
		    tw->substate = cst;
		    break;

		  default:
		    if(i<(sizeof(parsedat)-1)) {
		      parsedat[i++] = cst;
		    }
		    break;
		}
		st++;
	      } else
	      if((tw->substate == IAC) && (cst == IAC)) {
		if(i<(sizeof(parsedat)-1)) {
		  parsedat[i++] = 255;
		}
		tw->substate = NEGOTIATE;
		st++;
	      } else {
		switch( tw->substate) {
		  case IAC:
		    tw->substate = cst;
		    st++;
		    if (tw->substate==SE) {
		      switch( parsedat[0]) {
			case TERMTYPE:
			  if (parsedat[1]==1) {
			    /* IAC, SB, TERMTYPE, 0 */
			    skwrite(tw->pnum,"\377\372\030\000",4);
			    skwrite(tw->pnum,
			      tw->termtype, strlen(tw->termtype));
			    /* IAC, SE */
			    skwrite(tw->pnum,"\377\360",2);
#ifdef	DEBUGOPTION
if(debug&0x02) {
			    kb_puts("      SB TERMINAL-TYPE SEND\r\n");
			    kb_puts("SEND: IAC SB TERMINAL-TYPE IS ");
			    kb_puts(tw->termtype);
			    kb_nline();
}
#endif
			  }
			  break;

#ifdef	T_LINEMODE
			case LINEMODE:
			  switch(parsedat[1]) {
			    case MODE:
			      if (parsedat[2] & EDIT) {
			      	tw->lmflag = 1;
			      } else {
			      	tw->lmflag = 0;
			      }
			      if (parsedat[2] & ~(EDIT|TRAPSIG|MODE_ACK)) {
				/* IAC, SB, LINEMODE, MODE */
				skwrite(tw->pnum,"\377\372\042\001",4);
				skwchar(tw->pnum,parsedat[2]&(EDIT|TRAPSIG));
				/* IAC, SE */
				skwrite(tw->pnum,"\377\360",2);
#ifdef	DEBUGOPTION
if(debug&0x02) {
	printf("      LINEMODE MODE %d IAC SE\r\n",parsedat[2] & 0xFF);
	printf("SEND: IAC SB LINEMODE MODE %d IAC SE\r\n",
			parsedat[2]&(EDIT|TRAPSIG));
}
#endif
			      } else
			      if (!(parsedat[2]&MODE_ACK)) {
				/* IAC, SB, LINEMODE, MODE */
				skwrite(tw->pnum,"\377\372\042\001",4);
				skwchar(tw->pnum,parsedat[2]|MODE_ACK);
				/* IAC, SE */
				skwrite(tw->pnum,"\377\360",2);
#ifdef	DEBUGOPTION
if(debug&0x02) {
	printf("      LINEMODE MODE %d IAC SE\r\n",parsedat[2] & 0xFF);
	printf("SEND: IAC SB LINEMODE MODE %d IAC SE\r\n",
			parsedat[2]|MODE_ACK);
}
#endif
			      } else {
#ifdef	DEBUGOPTION
if(debug&0x02) {
	printf("      LINEMODE MODE %d IAC SE\r\n",parsedat[2] & 0xFF);
}
#endif
			      }
			      break;

			    case DOTEL:
			      /* IAC, SB, LINEMODE */
			      /* WONTTEL, TRAPSIG */
			      /* IAC, SE */
			      skwrite(tw->pnum,
				"\377\372\042\374\002\377\360",7);
			      break;

			    case WILLTEL:
			      /* IAC, SB, LINEMODE */
			      /* DONTTEL, TRAPSIG */
			      /* IAC, SE */
			      skwrite(tw->pnum,
				"\377\372\042\376\002\377\360",7);
			      break;

			    case SLC:
#ifdef	DEBUGOPTION
if(debug&0x02) {
	kb_puts("      LINEMODE SLC\r\n");
	for(i=2;(parsedat[i]!=0) &&
		(parsedat[i]!=IAC); i+=3) {
		if(parsedat[i] <= SLC_FORW2) {
			if(parsedat[i+1] & SLC_AWK) {
				printf("      %s %s|AWK %d\r\n",
				    LMoptions[parsedat[i]],
				    LMflags[parsedat[i+1] & SLC_LEVELBITS],
				    parsedat[i+2]);
			} else {
				printf("      %s %s %d\r\n",
				    LMoptions[parsedat[i]],
				    LMflags[parsedat[i+1] & SLC_LEVELBITS],
				    parsedat[i+2]);
			}
		} else {
			if(parsedat[i+1] & SLC_AWK) {
				printf("      ?%d %s|AWK %d\r\n",
				    parsedat[i],
				    LMflags[parsedat[i+1] & SLC_LEVELBITS],
				    parsedat[i+2]);
			} else {
				printf("      ?%d %s %d\r\n",
				    parsedat[i],
				    LMflags[parsedat[i+1] & SLC_LEVELBITS],
				    parsedat[i+2]);
			}
		}
	}
	kb_puts("      IAC SE\r\n");
}
#endif
			      /* First check to see if we need to reply */
			      for(i=2;(parsedat[i]!=0) &&
				(parsedat[i]!=IAC); i+=3) {
			      	if(!(parsedat[i+1] & SLC_AWK))
				  break;
			      }	
			      /* if we do then send a reply */
			      if ((parsedat[i]!=0) &&
				  (parsedat[i]!=IAC) &&
				 !(parsedat[i+1] & SLC_AWK)) {

				/* IAC, SB, LINEMODE, SLC */
			      	skwrite(tw->pnum,"\377\372\042\003",4);
#ifdef	DEBUGOPTION
if(debug&0x02) {
	kb_puts("SEND: IAC SB LINEMODE SLC\r\n");
}
#endif
			      	for (i=2; (parsedat[i]!=0) &&
				  (parsedat[i]!=IAC); i+=3) {
				  if (!(parsedat[i+1] & SLC_AWK)) {
				    ss = s;
				    *ss++ = i;
				    if ((parsedat[i] > SLC_FORW2) ||
					(tw->slc[parsedat[i]]==-1)) {
				      *ss++ = NO_SUPPORT|SLC_AWK;
				      *ss++ = 0;
				    } else {
				      *ss++ = CANTCHANGE|SLC_AWK;
				      *ss++ = tw->slc[parsedat[i]];
				    }
				    skwrite(tw->pnum,s,3);
#ifdef	DEBUGOPTION
if(debug&0x02) {
	if (parsedat[i] > SLC_FORW2) {
		printf("      ?%d NO_SUPPORT|AWK 0\r\n",
			parsedat[i]);
	} else
	if (tw->slc[parsedat[i]]==-1) {
		printf("      %s NO_SUPPORT|AWK 0\r\n",
			LMoptions[parsedat[i]]);
	} else {
		printf("      %s CANTCHANGE|AWK %d\r\n",
			LMoptions[parsedat[i]],
			tw->slc[parsedat[i]]);
	}
}
#endif
				  }
			      	}
				/* IAC, SE */
				skwrite(tw->pnum,"\377\360",2);
#ifdef	DEBUGOPTION
if(debug&0x02) {
	kb_puts("      IAC SE\r\n");
}
#endif
			      }

			      /* otherwise just exit */
			      break;

			    default:
			      break;
			  }
			  break;
#endif	/* T_LINEMODE */
			
			default:
			  break;
		      }
		    }
		    orig = st;
		    tw->telstate = STNORM;
		    break;

		  default:
		    orig = st;
		    tw->telstate = STNORM;
		    break;
		}
	      }
	      break;

	    default:
	      tw->telstate = STNORM;
	      break;
	  }

	/*
	 * quick scan of the remaining string,
	 * skip chars while they are uninteresting
	 */
	  if(tw->telstate==STNORM) {
	/*
	 * skip along as fast as possible until
	 * an interesting character is found
	 */
	    while(st<mark && *st!=27 && (*st&0xFF)<IAC) {
	      if(!tw->binary) 
		*st &= 0x7F;		/* mask off high bit */
	      st++;
	    }
	    parsewrite(tw,orig,st-orig);
	    orig = st;	      /* forget what we have sent already */
	    if(st<mark) {
	      cst = *st++ & 0xFF;
	      switch (cst) {
		case IAC:		/* telnet IAC */
		  tw->telstate = IACFOUND;
		  break;

		case 27:		/* ESCape code */
		  if(st==mark || (*st & 0xFF)==12 || (*st & 0xFF)=='^')
		    tw->telstate = ESCFOUND;
		  break;

		default:
		  kbprintf("strange char -- value = 0x%4.4X\r\n", cst);
		  break;
	      }
	    }
	  }
	}
	/*
	 * process this
	 */
	if(pushsk)
	  skenque(tw->pnum,1);
}

/*  newkey
 *
 *  filter for command key sequences
 */
int newkey(tw)
register struct twin *tw;
{
	register int c;
	int ch;

	if(fndbrk) {
	  fndbrk = 0;
	  c = A_BRK;
	} else
	if(tw->lmflag) {	/* Telnet line mode is on */
	  c = kb_gets(tw->linemode,sizeof(tw->linemode),!tw->echo);
	  if(c>0) {		/* write string, pass c to command interp */
	    if(tw->linemode[0]) {
	      skwrite(tw->pnum,tw->linemode,strlen(tw->linemode));
	      tw->linemode[0] = 0;
	    }
	  }
	} else
	if(tw->echo) {
	  c = kb_char();	/* a char available ? */
	} else
	if(tw->halfdup) {	/* half duplex */
	  c = kb_char();
	  if(c==13) {
	    parse(tw,"\r\n",2);	/* echo crlf */
	  } else 
	  if(c>0 && c<128) {
	    ch = c;
	    parse(tw,(char *)&ch,1);	  /* echo char */
	  }
	} else {		/* kludge linemode */
	  c = kb_gets(tw->linemode,sizeof(tw->linemode),1);
	  if(c>0) {		/* write string, pass c to command interp */
	    if(tw->linemode[0]) {
	      skwrite(tw->pnum,tw->linemode,strlen(tw->linemode));
	      tw->linemode[0] = 0;
	    }
	  }
	}

	if(c>0)
	  c = dokey(tw,c);
	if(pushsk)
	  skenque(tw->pnum,1);
	return(c);
}

/*
 *  dokey
 *
 *  Translates,
 *  filters for command keys,
 *  and sends keys.
 */
int dokey(tw,c)
register struct twin *tw;
register int c;
{
	register struct socket *skt;
	char *xxip;
	char s[80];
	int i;

	switch (c) {
	  case 8:
	    c = tw->bksp;	/* different bs mapping */
	    break;

	  case 13:		/* different CR mapping */
	    skwchar(tw->pnum,c);
	    c = tw->crfollow;
	    if(c == BSDCRNUL) {
	      skwchar(tw->pnum,0);
	      c = 0;
	    }
	    break;

	  case 127:
	    c = tw->del;	/* different del mapping */
	    break;

	  case THENUL:		/* user wants the true NUL char */
	    skwchar(tw->pnum,0);
	    c = 0;
	    break;

	  case A_C:		/* open capture file */
	    if(!tw->capon) {
	      kb_puts("\r\nEnter capture file name, ESC to abort: ");
	      s[0] = 0;
	      while(0>=(i = kb_gets(s,sizeof(s),1)))
		suspnd(0);
	      if(i!=27 && s[0]!=0) {
		kb_nline();
		tw->capfp = rtopen(s,"wn",1,1);
		if(tw->capfp == -1)
		  tw->capfp = NULL;
		if(tw->capfp != NULL) {
		  tw->capon = 1;
		  stptok(tw->capfp->io_name, tw->capfil, 15, "[");
		  strlwr(tw->capfil);
		}
	      }
	    }
	    if(tw->capon) {
	      kbprintf("\r\nCapture file %s is open.", tw->capfil);
	    }
	    kb_nline();
	    c = 0;
	    break;

	  case A_D:		/* close capture file */
	    if(tw->capon) {
	      rtclose(tw->capfp);
	      tw->capon = 0;
	      kbprintf("\r\nCapture file %s is closed.\r\n", tw->capfil);
	    } else {
	      kb_puts("\r\nCapture file not open.\r\n");
	    }
	    c = 0;
	    break;

	  case A_F:		/* an ftp command */
	    skt = (struct socket *) mapskt(tw->pnum);
	    xxip = skt->tcpout.i.ipsource;
	    sprintf(s,"ftp %d.%d.%d.%d\r\n",
	      *(xxip+0)&0xFF, *(xxip+1)&0xFF,
	      *(xxip+2)&0xFF, *(xxip+3)&0xFF);
	    skwrite(tw->pnum,s,strlen(s));
	    if(!tw->echo)
	      parse(tw,s,strlen(s));
	    c=0;
	    break;

	  case A_H:		/* help display */
	    printtxt(hlptxt);
	    switch(tw->crfollow) {
	      default:
		tw->crfollow = 0;

	      case 0:
		strcpy(s,"NONE");
		break;

	      case 10:
		strcpy(s,"LINEFEED");
		break;

	      case BSDCRNUL:
		strcpy(s,"4.3BSDCRNUL");
		break;
	    }
	    kbprintf("bksp = %d / del = %d / crfollow = %s\r\n\r\n",
	      tw->bksp, tw->del, s);
	    c = 0;
	    break;

	  case A_I:		/* my internet address */
	  case A_J:		/* their internet address */
	    skt = (struct socket *) mapskt(tw->pnum);
	    if(c==A_I) {
	      xxip = skt->tcpout.i.ipsource;
	    } else {
	      xxip = skt->tcpout.i.ipdest;
	    }
	    sprintf(s,"%d.%d.%d.%d",
		*(xxip+0)&0xFF, *(xxip+1)&0xFF,
		*(xxip+2)&0xFF, *(xxip+3)&0xFF);
	    skwrite(tw->pnum,s,strlen(s));
	    if(!tw->echo)	/* echo the string */
	      parse(tw,s,strlen(s));
	    c = 0;
	    break;

	  case A_K:		/* erase char */
	    skwrite(tw->pnum,"\377\367",2);
	    c = 0;
	    break;
	      
	  case A_O:		/* abort output */
	    skwrite(tw->pnum,"\377\365",2);
	    skempty(tw->pnum);
	    c = 0;
	    break;
	      
	  case A_Q:		/* are you there? */
	    skwrite(tw->pnum,"\377\366",2);
	    c = 0;
	    break;

	  case A_R:		/* toggle crfollow character */
	    switch(tw->crfollow) {
	      case 0:
		tw->crfollow = 10;
		kb_puts("crfollow = LINEFEED\r\n");
		break;

	      case 10:
		tw->crfollow = BSDCRNUL;
		kb_puts("crfollow = 4.3BSDCRNUL\r\n");
		break;

	      default:
	      case BSDCRNUL:
		tw->crfollow = 0;
		kb_puts("crfollow = NONE\r\n");
		break;
	    }
	    c = 0;
	    break;

	  case A_S:		/* skip to end */
	    skempty(tw->pnum);
	    c = 0;
	    break;

	  case A_T:		/* toggle backspace / delete */
	    c = tw->bksp;
	    tw->bksp = tw->del;
	    tw->del = c;
	    kbprintf("bksp = %d / del = %d\r\n", tw->bksp,tw->del);
	    c = 0;
	    break;

	  case A_U:		/* erase line */
	    skwrite(tw->pnum,"\377\370",2);
	    c = 0;
	    break;
	      
	  case A_X:		/* close the connection */
	    kb_puts("\r\nClose the connection? (Y/N)  ");
	    kb_out(c = kb_gchar());
	    if(tolower(c)=='y') {
	      kb_puts("\r\n Attempting to close . . .");
	      skptuev(ABORTCLASS,USERABORT,tw->pnum);
	    }
	    kb_nline();
	    c = 0;
	    break;

	  case A_Y:		/* interrupt */
	    skwrite(tw->pnum,"\377\364",2);
	    skempty(tw->pnum);
	    c = 0;
	    break;
	      
	  case A_BRK:		/* abort Telnet */
	    kb_puts("\r\nAbort the connection? (Y/N)  ");
	    kb_out(c = kb_gchar());
	    kb_nline();
	    if(tolower(c)=='y') {
	      if(tw->capon) {
		rtclose(tw->capfp);	/* close the capture file */
		tw->capon = 0;
	      }
	      skclose(tw->pnum);
	      skrelease(tw->pnum,1);
	      return(A_BRK);
	    }
	    c = 0;
	    break;

	  default:
	    break;
	}

	if(c>0)
	  skwchar(tw->pnum,c);

	return(c);
}

/*
 * This is the error print routine for TSXUTL.C
 * It will be called only if there is an internal error
 */
VOID lsterr()
{
	if(*errstr) {
		kb_puts(errstr);
		kb_nline();
		errstr[0] = '\0';
	}
}

/*
 * General looping question routine
 * loops until a response is given
 *	returns 1 on ABORT
 *	   else 0
 */
static int question(cstr,clen,qstr)
char *cstr,*qstr;
int clen;
{
	while(!(*(stpblk(cstr)))) {
		/*
		 * get arg from user
		 */
		kb_puts(qstr);
		if(rtresp(cstr,clen))
			return(1);
		}
	return(0);
}

/*
 * Routine to get a keyboard response
 *	returns 1 on ABORT
 *	   else 0
 */
int rtresp(st,ln)
char *st;
int ln;
{
	return((keygets(st,ln,1)==ABORT) ? 1 : 0);
}

/*
 * keygets
 *
 * read a line from the keyboard
 * returns ABORT if keyboard input aborted
 * or non-zero on success
 */

int keygets(str,lim,echo)
register char *str;	/* where to put the line */
int lim,echo;		/* max chars to read, echo? */
{
	register char *save;
	register int c;

	save = str;		/* beginning of line */
	*save = '\0';

	if(fndbrk) {
		fndbrk = 0;
		kb_puts("^C\r\n");
		return(ABORT);
	}
	while(1) {
		/*
		 * build string from keyboard
		 */
		c = kb_gets(str,lim,echo);
		if(1 <= c && c <= 31) {
			kb_nline();
			return(strlen(save));
		}
		/*
		 * check for abort
		 */
		if(fndbrk) {
			fndbrk = 0;
			*save = '\0';
			kb_puts("^C\r\n");
			return(ABORT);	
		}
		suspnd(0);
	}
}

/*
 * getword: remove a word from a string.  Things within quotes are
 * assumed to be one word.
 * return TRUE on success, FALSE on end of string
 */

static int getword(string,word)
char *string;
register char *word;
{
	register char *p;
	register int i;
	char *q;

	i = 0;

	/*
	 * skip leading blanks
	 */
	p = stpblk(string);
	if(!(*p)) {
		/*
		 * no words in string
		 */
		word[0] = '\0';
		return(FALSE);
	}
	if(*p == '\"') {
		/*
		 * word delimited by quotes
		 */
		while(p[++i] && p[i] != '\"')
			word[i-1] = p[i];
		word[i-1] = '\0';
		if(!p[i]) {
			/*
			 * Missing \". Assumed at end of string.
			 */
		} else {
			i++;
		}
		q = p+i;
	} else {
		/*
		 * get word, max len 79
		 */
		q = stptok(p, word, 79, " \t\r\n");
	}
     	/*
	 * remove trailing blanks
	 */
	p = stpblk(q);
	/*
	 * remove extracted stuff
	 */
	strcpy(string,p);
	return(TRUE);
}

static char *stptok( p, toword, ilen, delim)
register char *p;
char *toword;
int ilen;
char *delim;
{
	register char *adv;
	register int i;
	int j,end;
 
	adv = toword;
	end = 0;
	j = strlen(delim);

 	do { 
 		for(i=0; i<j; i++)
 			if(*p == delim[i] || (!*p))
				end++;
 		if(!end) {
 			if(adv >= (toword+ilen-1)) 
				end++;
 			*adv++=*p++;
 		}
 	} while(!end);
 	*adv='\0';
 	return(p);
}
 
static char *stpblk(ch)
register char *ch;
{
 	while(*ch == ' ' || *ch == '\t') ch++;
 	return(ch);
}

static VOID strlwr(st)
register char *st;
{
	while(*st) {
		*st = tolower(*st);
		*st++;
	}
}

/*
 *  chkusr
 *
 *  Check the password file for the user/password
 *  combination. An inaccessable password file
 *  results in an invalid return.
 *
 *  If the user/password are validated and
 *  the user has the rquired privelege,
 *  then a valid return is made.
 *
 *  Returns valid(0)/invalid(1)
 */
int chkusr()
{
	FILE *fp;
	struct userblock user;
	char username[USERPASSLEN+2];
	char password[USERPASSLEN+2];
	char command[100];
	char word[100];

	/*
	 * username
	 */
	if(question(command,sizeof(command),"Username: "))
		return(1);
	getword(command,word);
	username[0] = '\0';
	strncat(username,word,USERPASSLEN);
	strlwr(username);
	/*
	 * password
	 */
	if(!(*stpblk(command))) {
		kb_puts("Password: ");
		if(keygets(command,sizeof(command),0) == ABORT)
			return(1);
	}
	getword(command,word);
	password[0] = '\0';
	strncat(password,word,USERPASSLEN);
	strlwr(password);
	/*
	 * Check username / password file
	 */
	if(NULL==(fp = rtopen(pass,"rn",0,0))) {
		return(1);
	}
	while (NULL != fread(&user,sizeof(struct userblock),1,fp)) {
		/*
		 * does username / password check ?
		 */
		if(!strncmp(username,&user.username,USERPASSLEN) &&
				(user.userflag[0]&PASSNOTREQUIRED ||
				Scompass(password,&user.password))
			) {
			rtclose(fp);
			/*
			 * Check Telnet privileges
			 */
			if(!(user.userflag[1] & LTELNET_PRIV)) {
				kb_puts("No Privilege for TELNET\r\n");
				return(1);
			}
			/*
			 * User valid
			 */
			return(0);
		}
	}
	kb_puts("Invalid Username / Password.\r\n");
	rtclose(fp);
	return(1);
}

/*
 *  Scompass ( ps, en )
 *
 *  Compute and check the encrypted password
 */
int Scompass(ps,en)
register char *ps,*en;
{
	register int i,ck;
	char *p,c;

	ck = 0;
	p = ps;
	/*
	 * checksum the string
	 */
	while (*p)
		ck += *p++;
	c = ck;
	/*
	 * XOR with checksum
	 */
	i = USERPASSLEN;
	while (i--) {
		if((((*ps ^ c)|32)&127)!=*en)
			return(0);
		/*
		 * increment checksum to hide length
		 */
		if(*ps) {
			ps++;
		} else {
			c++;
		}
		en++;
	}
	return(1);
}

/*
 *  printout text
 */
VOID printtxt(txt)
char **txt;
{
	register char **dp;

	for (dp = txt; *dp; dp++) {
		if (**dp == '_') {
			kb_puts(*dp + 1);
		} else {
			kb_puts(*dp);
			kb_nline();
		}
	}
}
