/* cnct.c */

#define	CNCTMASTER

/*
 *  Includes
 */
#include <string.h>
#include <stdio.h>

#include "os.h"
#include "vrsion.h"
#include "vcnct.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	inprocess();	/* (sknum) process incoming data */
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
 */
#define DBUGTRUE	-1	/* all debuggers */

static char *usetxt[] = {
       "",
#ifdef	DEBUGOPTION
       "  CNCT destination [?] [-hp] [-d level]",
       "	?		List the Help Text and Exit CNCT",
       "	d   level	Debug Level",
       "		1 -	enable event printing",
#else
       "  CNCT destination [?] [-hp]",
       "	?		List the Help Text and Exit CNCT",
#endif
       "	h		List the Help Text and Open Connection",
       "	p   filename	Specify the Password Filespec",
       "",
	0
	};

static char *hlptxt[] = {
"",
"Keyboard usage for CNCT-11",
"-------- ----- --- -------",
"",
"The metacharacter 'M->' is ^A",
"M->C    open capture file             M->O    abort output",
"M->D    close capture file            M->Q    are you there?",
"M->F    FTP [internet address]        M->S    skip to end of buffer",
"M->H    this help screen              M->X    close connection",
"M->I    type my internet address      M->Y    interrupt process",
"",
" ^?     abort CNCT session",
"",
0
};


char	xs[2];				/* dumby array for TSXUTL.C */

char	*pass = "PAS:paswrd.fil";	/* password file */

int	pswdreqd = 1;			/* password required */
int	viewmode = 1;

int	cnctopen = 0;			/* connection open flag */
int	cnctskt = -1;			/* socket number */
struct socket *skt = -1;		/* socket */
struct twin *tw = NULL;			/* session */

char	parsedat[80];			/* parsing data */


#define	ABORT			(-3)


main(argc,argv)
int argc;
char *argv[];
{
	int c;
	register int i,j,k;
	int ev;

	/*
	 * 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;
#ifdef	DEBUGOPTION
			case 'd':	/* debug, optional level */
				if(sscanf(argv[++i],"%d",&debug) <= 0)
					debug = DBUGTRUE;
				break;
#endif
			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)
		printf("Debug = %d\r\n", debug);
#endif
	/*
	 * Test Username / Password
	 */
	if(pswdreqd) {
		if(chkusr()) {
			kb_puts("\r\nEscaping from CNCT\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.
	 */
	cnctskt = addsess(0,parsedat,HCNCT);
	if(cnctskt<0) {
		printf("\r\nEscaping from CNCT\r\n");
		return(0);
	}

	skt = (struct socket *) mapskt(cnctskt);
	tw = &(skt->session);

	do {
		ev = dosess();
		if(ev==0)
			suspnd(0);
	} while (ev != -1);

	suspnd(0);
	errhandle();

	kb_puts("\r\nEscaping from CNCT\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;
	char st[100];

	/*
	 * do all key translations
	 */
	switch(viewmode) {
	  default:
	    /*
	     * This gives precedence to the
	     * keyboard over the network.
	     */
	    while(0<=(c=newkey())) {
	      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 CNCT\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 && dat==cnctskt) {
	  switch(cl) {
	    case CONCLASS:
	      switch(ev) {
		case CONOPEN:	/* a connection has just opened */
		  /*
		   * Log the Session
		   */
		  sprintf(st,"CNCT: %s", parsedat);
		  logsession(cnctskt,st);
		  cnctopen = 1;
		  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:
		  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:
		  printf("\r\nCNCT 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;
	  }
	}
	if(cnctopen) {
	  c = inprocess();
	  if(c) {
	    if(c == A_BRK) {
	      return(-1);
	    }
	    return(c);
	  }
	}
	return(ev);
}

/* 
 *  inprocess
 *
 *  take incoming data and process it.  Close the connection if it
 *  is the end of the connection.
 */
int inprocess()
{
	register int cnt;
	char s[82];

	cnt = skread(cnctskt,s,80);	/* get some from incoming queue */
	if(cnt<0) {			/* close this session, if over */
	  cnctopen = 0;
	  skclose(cnctskt);
	  skrelease(cnctskt,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(cnctskt);		/* dequeue the data */
	  /*
	   *  send the string where it belongs
	   *  1. Check for a capture file.
	   *  2. send to screen
	   */
	  if(tw->capon)
	    fwrite(s,cnt,1,tw->capfp);

	  s[cnt] = '\0';
	  kb_puts(s);
	  return(1);
	}

	return(0);
}

/*  newkey
 *
 *  filter for command key sequences
 */
int newkey()
{
	register int c;

	if(fndbrk) {
		fndbrk = 0;
		c = A_BRK;
	} else {
		c = kb_char();
	}

	if(c>0)
		c = dokey(c);
	if(pushsk)
		skenque(cnctskt,1);
	return(c);
}

/*
 *  dokey
 *
 *  Translates,
 *  filters for command keys,
 *  and sends keys.
 */
int dokey(c)
register int c;
{
	register char *xxip;
	register int i;
	char s[80];

	switch (c) {
	  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 */
	    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(cnctskt,s,strlen(s));
	    c=0;
	    break;

	  case A_H:		/* help display */
	    printtxt(hlptxt);
	    c = 0;
	    break;

	  case A_I:		/* my internet address */
	  case A_J:		/* their internet address */
	    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(cnctskt,s,strlen(s));
	    c = 0;
	    break;

	  case A_O:		/* abort output */
	    skwrite(cnctskt,"\377\365",2);
	    skempty(cnctskt);
	    c = 0;
	    break;
	      
	  case A_Q:		/* are you there? */
	    skwrite(cnctskt,"\377\366",2);
	    c = 0;
	    break;

	  case A_S:		/* skip to end */
	    skempty(cnctskt);
	    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,cnctskt);
	    }
	    kb_nline();
	    c = 0;
	    break;

	  case A_Y:		/* interrupt */
	    skwrite(cnctskt,"\377\364",2);
	    skempty(cnctskt);
	    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(cnctskt);
	      skrelease(cnctskt,1);
	      return(A_BRK);
	    }
	    c = 0;
	    break;

	  default:
	    break;
	}

	if(c>0)
	  skwchar(cnctskt,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 CNCT privileges
			 */
			if(!(user.userflag[1] & LCNCT_PRIV)) {
				kb_puts("No Privilege for CNCT\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();
		}
	}
}
                                                                                                                                                                                                                                                         