/*
Copyright 2001-2022 John Wiseman G8BPQ

This file is part of LinBPQ/BPQ32.

LinBPQ/BPQ32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

LinBPQ/BPQ32 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with LinBPQ/BPQ32.  If not, see http://www.gnu.org/licenses
*/	

//
//	Interface to allow G8BPQ switch to use a KISS over TCP TNC for HF style use (ATTACH and single channel operation)



#define _CRT_SECURE_NO_DEPRECATE

#include <stdio.h>
#include <time.h>

#include "cheaders.h"


extern int (WINAPI FAR *GetModuleFileNameExPtr)();
extern int (WINAPI FAR *EnumProcessesPtr)();

#include "bpq32.h"

#include "tncinfo.h"

static int Socket_Data(int sock, int error, int eventcode);

VOID MoveWindows(struct TNCINFO * TNC);
static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen);
int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error);
BOOL SerialWriteCommBlock(struct TNCINFO * TNC);
void SerialCheckRX(struct TNCINFO * TNC);
int SerialSendData(struct TNCINFO * TNC, UCHAR * data, int txlen);
int SerialSendCommand(struct TNCINFO * TNC, UCHAR * data);
int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len);
VOID SendInitScript(struct TNCINFO * TNC);
int KISSHFGetLine(char * buf);
int ProcessEscape(UCHAR * TXMsg);
VOID KISSHFProcessReceivedPacket(struct TNCINFO * TNC, int Channel);
static int KissEncode(struct TNCINFO * TNC, UCHAR * inbuff, UCHAR * outbuff, int len, int Channel);
int ConnecttoKISS(int port);
TRANSPORTENTRY * SetupNewSession(TRANSPORTENTRY * Session, char * Bufferptr);
BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls);
BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK);
VOID RESET2(struct _LINKTABLE * LINK);
VOID L2SENDXID(struct _LINKTABLE * LINK);
VOID SENDSABM(struct _LINKTABLE * LINK);

static char ClassName[]="KISSSTATUS";
static char WindowTitle[] = "KISSHF";
static int RigControlRow = 165;

#ifndef LINBPQ
#include <commctrl.h>
#endif

extern int SemHeldByAPI;

static RECT Rect;

static int ProcessLine(char * buf, int Port);

VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len);

#define	FEND	0xC0	// KISS CONTROL CODES 
#define	FESC	0xDB
#define	TFEND	0xDC
#define	TFESC	0xDD

static int ProcessLine(char * buf, int Port)
{
	UCHAR * ptr,* p_cmd;
	char * p_ipad = 0;
	char * p_port = 0;
	unsigned short WINMORport = 0;
	int BPQport;
	int len=510;
	struct TNCINFO * TNC;
	char errbuf[256];

	strcpy(errbuf, buf);

	ptr = strtok(buf, " \t\n\r");

	if(ptr == NULL) return (TRUE);

	if(*ptr =='#') return (TRUE);			// comment

	if(*ptr ==';') return (TRUE);			// comment

	if (_stricmp(buf, "ADDR"))
		return FALSE;						// Must start with ADDR

	ptr = strtok(NULL, " \t\n\r");

	BPQport = Port;
	p_ipad = ptr;

	TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO));
	memset(TNC, 0, sizeof(struct TNCINFO));

	TNC->DefaultMode = TNC->WL2KMode = 0;	// Packet 1200

	TNC->InitScript = malloc(1000);
	TNC->InitScript[0] = 0;
	
	if (p_ipad == NULL)
		p_ipad = strtok(NULL, " \t\n\r");

	if (p_ipad == NULL) return (FALSE);
	
	p_port = strtok(NULL, " \t\n\r");
			
	if (p_port == NULL) return (FALSE);

	WINMORport = atoi(p_port);

	TNC->destaddr.sin_family = AF_INET;
	TNC->destaddr.sin_port = htons(WINMORport);
	TNC->Datadestaddr.sin_family = AF_INET;
	TNC->Datadestaddr.sin_port = htons(WINMORport+1);

	TNC->HostName = malloc(strlen(p_ipad)+1);

	if (TNC->HostName == NULL) return TRUE;

	strcpy(TNC->HostName,p_ipad);

	ptr = strtok(NULL, " \t\n\r");

	if (ptr)
	{
		if (_stricmp(ptr, "PTT") == 0)
		{
			ptr = strtok(NULL, " \t\n\r");

			if (ptr)
			{
				DecodePTTString(TNC, ptr);
				ptr = strtok(NULL, " \t\n\r");
			}
		}
	}
		
	if (ptr)
	{
		if (_memicmp(ptr, "PATH", 4) == 0)
		{
			p_cmd = strtok(NULL, "\n\r");
			if (p_cmd) TNC->ProgramPath = _strdup(p_cmd);
		}
	}

	// Read Initialisation lines

	while (TRUE)
	{
		if (GetLine(buf) == 0)
			return TRUE;

		strcpy(errbuf, buf);

		if (memcmp(buf, "****", 4) == 0)
			return TRUE;

		ptr = strchr(buf, ';');
		if (ptr)
		{
			*ptr++ = 13;
			*ptr = 0;
		}

		if (_memicmp(buf, "UPDATEMAP", 9) == 0)
			TNC->PktUpdateMap = TRUE;
		else if (standardParams(TNC, buf) == FALSE)
			strcat(TNC->InitScript, buf);	
	}
	return (TRUE);	
}

char * Config;
static char * ptr1, * ptr2;

int KISSHFGetLine(char * buf)
{
loop:

	if (ptr2 == NULL)
		return 0;

	memcpy(buf, ptr1, ptr2 - ptr1 + 2);
	buf[ptr2 - ptr1 + 2] = 0;
	ptr1 = ptr2 + 2;
	ptr2 = strchr(ptr1, 13);

	if (buf[0] < 0x20) goto loop;
	if (buf[0] == '#') goto loop;
	if (buf[0] == ';') goto loop;

	if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0;
	if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0;
	buf[strlen(buf)] = 13;

	return 1;
}

VOID SuspendOtherPorts(struct TNCINFO * ThisTNC);
VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC);
VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len);

static time_t ltime;

static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen)
{
	if (TNC->hDevice)
	{
		// Serial mode. Queue to Hostmode driver
		
		PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();

		if (buffptr == 0) return;			// No buffers, so ignore

		buffptr->Len = EncLen;
		memcpy(&buffptr->Data[0], Encoded, EncLen);
		
		C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr);
		TNC->Streams[Stream].FramesQueued++;

		return;
	}
}


static SOCKADDR_IN sinx; 
static SOCKADDR_IN rxaddr;

static int addrlen=sizeof(sinx);


static size_t ExtProc(int fn, int port, PDATAMESSAGE buff)
{
	int datalen;
	PMSGWITHLEN buffptr;
	char txbuff[500];
	unsigned int bytes,txlen = 0;
	UCHAR * TXMsg;

	size_t Param;
	int Stream = 0;
	HKEY hKey=0;
	struct TNCINFO * TNC = TNCInfo[port];
	struct STREAMINFO * STREAM = &TNC->Streams[0];
	struct ScanEntry * Scan;
	int Channel = ((TNC->PortRecord->PORTCONTROL.CHANNELNUM - 1) & 15) << 4;

	if (TNC == NULL)
		return 0;							// Port not defined

	if (TNC->CONNECTED == 0 && TNC->CONNECTING == 0)
	{
		// Try to reopen every 30 secs

		if (fn > 3  && fn < 7)
			goto ok;

		TNC->ReopenTimer++;

		if (TNC->ReopenTimer < 150)
			return 0;

		TNC->ReopenTimer = 0;

		ConnecttoKISS(TNC->Port);
		
		return 0;

		SendInitScript(TNC);

	}
ok:

	switch (fn)
	{
		case 7:			

		// 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances

		SerialCheckRX(TNC);
		return 0;

		case 1:				// poll

			STREAM = &TNC->Streams[0];

			if (STREAM->NeedDisc)
			{
				STREAM->NeedDisc--;

				if (STREAM->NeedDisc == 0)
				{
					// Send the DISCONNECT

					SerialSendCommand(TNC, "DISCONNECT\r");
				}
			}

			if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0)
			{
				// New Attach

				int calllen;
				char Msg[80];

				STREAM->Attached = TRUE;

				calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall);
				TNC->Streams[Stream].MyCall[calllen] = 0;


				// Stop other ports in same group

				SuspendOtherPorts(TNC);

				sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall);
				MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

				// Stop Scanning

				sprintf(Msg, "%d SCANSTOP", TNC->Port);

				Rig_Command( (TRANSPORTENTRY *) -1, Msg);
			}

			if (STREAM->Attached)
				CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete);

			// See if any frames for this port


			STREAM = &TNC->Streams[0];

			if (STREAM->BPQtoPACTOR_Q)
			{
				PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q);
				UCHAR * data = &buffptr->Data[0];
				STREAM->FramesQueued--;
				txlen = (int)buffptr->Len;
				STREAM->bytesTXed += txlen;

				bytes=SerialSendData(TNC, data, txlen);
				WritetoTrace(TNC, data, txlen);
			}

			if (STREAM->PACTORtoBPQ_Q != 0)
			{
				buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q);

				datalen = (int)buffptr->Len;

				buff->PORT = Stream;						// Compatibility with Kam Driver
				buff->PID = 0xf0;
				memcpy(&buff->L2DATA, &buffptr->Data[0], datalen);		// Data goes to + 7, but we have an extra byte
				datalen += sizeof(void *) + 4;

				PutLengthinBuffer(buff, datalen);

				ReleaseBuffer(buffptr);

				return (1);
			}

			if (STREAM->ReportDISC)		// May need a delay so treat as a counter
			{
				STREAM->ReportDISC--;

				if (STREAM->ReportDISC == 0)
				{
					buff->PORT = Stream;
					return -1;
				}
			}

			return (0);

	case 2:				// send

		Stream = 0;

		if (!TNC->CONNECTED)
			return 0;		// Don't try if not connected

		STREAM = &TNC->Streams[0];
		
		if (TNC->SwallowSignon)
		{
			TNC->SwallowSignon = FALSE;		// Discard *** connected
			return 0;
		}

		// We may get KISS packets (UI or session related) or text commands such as RADIO, CONNECT

		txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN);

		TXMsg = &buff->L2DATA[0];

		if (buff->PID != 240)			// ax.25 address
		{
			txlen = KissEncode(TNC, &buff->PID, txbuff, txlen, Channel);

			// We need to che check for ackmode

			txlen = send(TNC->TCPSock, txbuff, txlen, 0);
			return 1;
		}

		TXMsg[txlen - 1] = 0;
		strcpy(txbuff, TXMsg);

		if (STREAM->Attached == 0)
			return 0;

		if (STREAM->Connected)
		{
			PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();

			if (buffptr == 0) return 1;			// No buffers, so ignore

			buffptr->Len  = txlen;
			memcpy((UCHAR *)&buffptr->Data[0], &buff->L2DATA[0], txlen);
			C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);

			// connected data

			return 1;
		}

		// Process as Text Command

		if (_memicmp(txbuff, "D\r", 2) == 0 || _memicmp(txbuff, "BYE\r", 4) == 0)
		{
			STREAM->ReportDISC = TRUE;		// Tell Node
			return 0;
		}
	
		if (_memicmp(txbuff, "RADIO ", 6) == 0)
		{
			char cmd[56];

			strcpy(cmd, &buff->L2DATA[6]);
			sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, cmd);

	
			if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0]))
			{
			}
			else
			{
				PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();

				if (buffptr == 0) return 1;			// No buffers, so ignore

				buffptr->Len  = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]);
				C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
			}
			return 1;
		}


		if (_memicmp(txbuff, "OVERRIDEBUSY", 12) == 0)
		{
			PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();

			TNC->OverrideBusy = TRUE;

			if (buffptr)
			{
				buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "KISSHF} OK\r");
				C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
			}

			return 0;

		}

		if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0)
		{
			if (buff->L2DATA[16] != 13)
			{					
				PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();

				TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60;

				if (buffptr)
				{
					buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "KISSHF} OK\r");
					C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
				}
				return 0;
			}
		}

		if ((toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2)	// Connect
			|| (toupper(buff->L2DATA[0]) == 'N' && buff->L2DATA[1] == ' ' && txlen > 2))	// Connect
		{
			// Connect Command. Pass to L2 code to start session

			char * ptr = strchr(&buff->L2DATA[2], 13);
			TRANSPORTENTRY * NewSess = L4TABLE;
			struct _LINKTABLE * LINK;
			TRANSPORTENTRY * Session = TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK;
			struct PORTCONTROL * PORT = &TNC->PortRecord->PORTCONTROL;

			UCHAR axcalls[64];
			UCHAR ourcall[7];					// Call we are using (may have SSID bits inverted
			int Stay = 0, Spy = 0, CQFLAG = 0, n;

			if (ptr)
				*ptr = 0;

			_strupr(&buff->L2DATA[2]);

			if (DecodeCallString(&buff->L2DATA[2], &Stay, &Spy, &axcalls[0]) == 0)
			{
				PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
				if (buffptr)
				{
					buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "KISSHF} Invalid Call\r");
					C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
				}
				return 0;
			}

			// Code copied from cmdc00

			// Get Session Entry for Downlink

			NewSess = SetupNewSession(Session, NULL);
	
			if (NewSess == NULL)
				return 0;

			NewSess->L4CIRCUITTYPE = L2LINK + DOWNLINK;

			//	FORMAT LINK TABLE ENTRY FOR THIS CONNECTION

			memcpy(Session->L4USER, NewSess->L4USER, 7);
			memcpy(ourcall, NewSess->L4USER, 7);

			// SSID SWAP TEST - LEAVE ALONE FOR HOST or Pactor like (unless UZ7HO)

			if ((Session->L4CIRCUITTYPE & BPQHOST))// host
				goto noFlip3;

			if ((Session->L4CIRCUITTYPE & PACTOR))
			{
				// incoming is Pactorlike - see if UZ7HO

				if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0)
					goto noFlip3;

				if (Session->L4TARGET.EXTPORT->MAXHOSTMODESESSIONS < 2)	// Not multisession	
					goto noFlip3;

				ourcall[6] ^= 0x1e;		// UZ7HO Uplink - flip
			}
			else

				// Must be L2 uplink - flip

				ourcall[6] ^= 0x1e;									// Flip SSID

noFlip3:

			//	SET UP NEW SESSION (OR RESET EXISTING ONE)

			FindLink(axcalls, ourcall, TNC->Port, &LINK);

			if (LINK == NULL)
			{			
				PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
				if (buffptr)
				{
					buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "KISSHF} Sorry - System Tables Full\r");
					C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
				}
				return 0;
			}

			memcpy(LINK->LINKCALL, axcalls, 7);
			memcpy(LINK->OURCALL, ourcall, 7);

			LINK->LINKPORT = PORT;

			LINK->L2TIME = PORT->PORTT1;

			// Copy Digis

			n = 7;
			ptr = &LINK->DIGIS[0];

			while (axcalls[n])
			{
				memcpy(ptr, &axcalls[n], 7);
				n += 7;
				ptr += 7;

				LINK->L2TIME += 2 * PORT->PORTT1;	// ADJUST TIMER VALUE FOR 1 DIGI
			}

			LINK->LINKTYPE = 2;						// DOWNLINK
			LINK->LINKWINDOW = PORT->PORTWINDOW;

			RESET2(LINK);						// RESET ALL FLAGS

			if (toupper(buff->L2DATA[0]) == 'N' && SUPPORT2point2)
				LINK->L2STATE = 1;					// New (2.2) send XID
			else
				LINK->L2STATE = 2;					// Send SABM

			LINK->CIRCUITPOINTER = NewSess;

			NewSess->L4TARGET.LINK = LINK;

			if (PORT->PORTPACLEN)
				NewSess->SESSPACLEN = Session->SESSPACLEN = PORT->PORTPACLEN;

			STREAM->Connecting = TRUE;

			if (CQFLAG == 0)			// if a CQ CALL  DONT SEND SABM
			{
				if (LINK->L2STATE == 1)
					L2SENDXID(LINK);
				else	
					SENDSABM(LINK);
			}
			return 0;
		}	

		return 0;

	case 3:	
		
		// CHECK IF OK TO SEND (And check TNC Status)

		return ((TNC->CONNECTED) << 8 | TNC->Streams[0].Disconnecting << 15);		// OK
		
	case 4:				// reinit7

		return 0;

	case 5:				// Close

		return 0;

	case 6:				// Scan Stop Interface

		Param = (size_t)buff;
	
		if (Param == 2)		// Check  Permission (Shouldn't happen)
		{
			Debugprintf("Scan Check Permission called on KISSHF");
			return 1;		// OK to change
		}

		if (Param == 1)		// Request Permission
		{
			if (!TNC->CONNECTED)
				return 0;					// No connection so no interlock

			if (TNC->ConnectPending == 0 && TNC->PTTState == 0)
			{
				SerialSendCommand(TNC, "CONOK OFF");
				TNC->GavePermission = TRUE;
				return 0;	// OK to Change
			}

			if (TNC->ConnectPending)
				TNC->ConnectPending--;		// Time out if set too long

			return TRUE;
		}

		if (Param == 3)		// Release  Permission
		{
			if (TNC->GavePermission)
			{
				TNC->GavePermission = FALSE;
				if (TNC->ARDOPCurrentMode[0] != 'S')	// Skip
					SerialSendCommand(TNC, "CONOK ON");
			}
			return 0;
		}

		// Param is Address of a struct ScanEntry

		Scan = (struct ScanEntry *)buff;
		return 0;
	}
	return 0;
}

VOID KISSHFReleaseTNC(struct TNCINFO * TNC)
{
	// Set mycall back to Node or Port Call, and Start Scanner

	UCHAR TXMsg[64];

	//	Start Scanner
				
	sprintf(TXMsg, "%d SCANSTART 15", TNC->Port);
	Rig_Command( (TRANSPORTENTRY *) -1, TXMsg);

	strcpy(TNC->WEB_TNCSTATE, "Free");
	MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

	ReleaseOtherPorts(TNC);
}

VOID KISSHFSuspendPort(struct TNCINFO * TNC, struct TNCINFO * THISTNC)
{
	TNC->PortRecord->PORTCONTROL.PortSuspended = 1;
	strcpy(TNC->WEB_TNCSTATE, "Interlocked");
	MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
}

VOID KISSHFReleasePort(struct TNCINFO * TNC)
{
	TNC->PortRecord->PORTCONTROL.PortSuspended = 0;
	strcpy(TNC->WEB_TNCSTATE, "Free");
	MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
}

static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL)
{
	int Len = sprintf(Buff, "<html><meta http-equiv=expires content=0><meta http-equiv=refresh content=15>"
		"<script type=\"text/javascript\">\r\n"
		"function ScrollOutput()\r\n"
		"{var textarea = document.getElementById('textarea');"
		"textarea.scrollTop = textarea.scrollHeight;}</script>"
		"</head><title>KISSHF Status</title></head><body id=Text onload=\"ScrollOutput()\">"
		"<h2><form method=post target=\"POPUPW\" onsubmit=\"POPUPW = window.open('about:blank','POPUPW',"
		"'width=440,height=50');\" action=ARDOPAbort?%d>KISSHF Status"
		"<input name=Save value=\"Abort Session\" type=submit style=\"position: absolute; right: 20;\"></form></h2>",
		TNC->Port);


	Len += sprintf(&Buff[Len], "<table style=\"text-align: left; width: 500px; font-family: monospace; align=center \" border=1 cellpadding=2 cellspacing=2>");

	Len += sprintf(&Buff[Len], "<tr><td width=110px>Comms State</td><td>%s</td></tr>", TNC->WEB_COMMSSTATE);
	Len += sprintf(&Buff[Len], "<tr><td>TNC State</td><td>%s</td></tr>", TNC->WEB_TNCSTATE);
//	Len += sprintf(&Buff[Len], "<tr><td>Mode</td><td>%s</td></tr>", TNC->WEB_MODE);
//	Len += sprintf(&Buff[Len], "<tr><td>Channel State</td><td>%s</td></tr>", TNC->WEB_CHANSTATE);
//	Len += sprintf(&Buff[Len], "<tr><td>Proto State</td><td>%s</td></tr>", TNC->WEB_PROTOSTATE);
//	Len += sprintf(&Buff[Len], "<tr><td>Traffic</td><td>%s</td></tr>", TNC->WEB_TRAFFIC);
//	Len += sprintf(&Buff[Len], "<tr><td>TNC Restarts</td><td></td></tr>", TNC->WEB_RESTARTS);
	Len += sprintf(&Buff[Len], "</table>");

	Len += sprintf(&Buff[Len], "<textarea rows=2 style=\"width:500px; height:50px;\" id=textarea >%s</textarea>", TNC->WebBuffer);
	Len = DoScanLine(TNC, Buff, Len);

	return Len;
}



VOID * KISSHFExtInit(EXTPORTDATA * PortEntry)
{
	int port;
	char Msg[255];
	char * ptr;
	struct TNCINFO * TNC;
	char * TempScript;
	struct PORTCONTROL * PORT = &PortEntry->PORTCONTROL;
	
	port = PORT->PORTNUMBER;

	if (TNCInfo[port])					// If restarting, free old config
		free(TNCInfo[port]);

	TNC = TNCInfo[port] = malloc(sizeof(struct TNCINFO));
	memset(TNC, 0, sizeof(struct TNCINFO));

	TNC->InitScript = malloc(1000);
	TNC->InitScript[0] = 0;

	if (PortConfig[port])			// May not have config
		ReadConfigFile(port, ProcessLine);

	TNC = TNCInfo[port];

	if (TNC == NULL)
	{
		// Not defined in Config file

		sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n");
		WritetoConsole(Msg);

		return ExtProc;
	}
	
	TNC->Port = port;
	TNC->PortRecord = PortEntry;
	TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_KISSHF;
	TNC->ARDOPBuffer = malloc(8192);


	if (PortEntry->PORTCONTROL.PORTCALL[0] == 0)
		memcpy(TNC->NodeCall, MYNODECALL, 10);
	else
		ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall);

	if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0)
		TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK;

	PortEntry->PORTCONTROL.PROTOCOL = 10;
	PortEntry->PORTCONTROL.PORTQUALITY = 0;
	PortEntry->PORTCONTROL.USERS = 1;			// Max 1 Session

	TNC->PacketChannels = 0;

	PortEntry->MAXHOSTMODESESSIONS = 1;

	PortEntry->SCANCAPABILITIES = SIMPLE;			// Scan Control - pending connect only
	PortEntry->PERMITGATEWAY = TRUE;				// Can change ax.25 call on each stream

	PortEntry->PORTCONTROL.UICAPABLE = TRUE;

	if (PortEntry->PORTCONTROL.PORTPACLEN == 0)
		PortEntry->PORTCONTROL.PORTPACLEN = 236;

	TNC->SuspendPortProc = KISSHFSuspendPort;
	TNC->ReleasePortProc = KISSHFReleasePort;

//	PortEntry->PORTCONTROL.PORTSTARTCODE = KISSStartPort;
//	PortEntry->PORTCONTROL.PORTSTOPCODE = KISSStopPort;

	ptr=strchr(TNC->NodeCall, ' ');
	if (ptr) *(ptr) = 0;					// Null Terminate

	// Set Essential Params and MYCALL

	// Put overridable ones on front, essential ones on end

	TempScript = zalloc(1000);

	// cant think of any yet

	if (TNC->InitScript)
	{
		strcat(TempScript, TNC->InitScript);
		free(TNC->InitScript);
	}

	TNC->InitScript = TempScript;

	if (TNC->WL2K == NULL)
		if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0])			// Alrerady decoded
			TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo;

	PortEntry->PORTCONTROL.TNC = TNC;

	TNC->WebWindowProc = WebProc;
	TNC->WebWinX = 520;
	TNC->WebWinY = 500;
	TNC->WebBuffer = zalloc(5000);

	TNC->WEB_COMMSSTATE = zalloc(100);
	TNC->WEB_TNCSTATE = zalloc(100);
	TNC->WEB_CHANSTATE = zalloc(100);
	TNC->WEB_BUFFERS = zalloc(100);
	TNC->WEB_PROTOSTATE = zalloc(100);
	TNC->WEB_RESTARTTIME = zalloc(100);
	TNC->WEB_RESTARTS = zalloc(100);

	TNC->WEB_MODE = zalloc(20);
	TNC->WEB_TRAFFIC = zalloc(100);


#ifndef LINBPQ

	CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 700, 450, ForcedClose);

	CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL);
	
	CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL);

	CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL);
 
	CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL);
 
 	CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL);
 
	CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL);

	CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,40,20 , TNC->hDlg, NULL, hInstance, NULL);
	CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL);
	TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL);

	TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD |  WS_VISIBLE  | LBS_NOINTEGRALHEIGHT | 
            LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL,
			0,170,250,300, TNC->hDlg, NULL, hInstance, NULL);

	TNC->ClientHeight = 450;
	TNC->ClientWidth = 500;

	TNC->hMenu = CreatePopupMenu();

	AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill TNC");
	AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart TNC");
	AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection");	
	CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED);
	AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session");

	MoveWindows(TNC);
#endif
	Consoleprintf("KISSHF Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port));

	ConnecttoKISS(port);

	time(&TNC->lasttime);			// Get initial time value

	return ExtProc;
}


VOID TidyClose(struct TNCINFO * TNC, int Stream)
{
	// If all acked, send disc
	
	if (TNC->Streams[Stream].BytesOutstanding == 0)
		SerialSendCommand(TNC, "DISCONNECT\r");
}

VOID ForcedClose(struct TNCINFO * TNC, int Stream)
{
	SerialSendCommand(TNC, "DISCONNECT\r");
}

VOID CloseComplete(struct TNCINFO * TNC, int Stream)
{
	if (Stream == 0)
	{
		KISSHFReleaseTNC(TNC);
	}
}


VOID KISSThread(void * portptr);

int ConnecttoKISS(int port)
{
	_beginthread(KISSThread, 0, (void *)(size_t)port);

	return 0;
}

VOID KISSThread(void * portptr)
{
	// Opens socket and looks for data on control and data sockets.
	
	int port = (int)(size_t)portptr;
	char Msg[255];
	int err, i, ret;
	u_long param=1;
	BOOL bcopt=TRUE;
	struct hostent * HostEnt;
	struct TNCINFO * TNC = TNCInfo[port];
	fd_set readfs;
	fd_set errorfs;
	struct timeval timeout;
	char * ptr1;
	char * ptr2;
	UINT * buffptr;
	int Channel = ((TNC->PortRecord->PORTCONTROL.CHANNELNUM - 1) & 15) << 4;

	if (TNC->HostName == NULL)
		return;

	TNC->BusyFlags = 0;

	TNC->CONNECTING = TRUE;

	Sleep(3000);		// Allow init to complete 

#ifdef WIN32
	if (strcmp(TNC->HostName, "127.0.0.1") == 0)
	{
		// can only check if running on local host
		
		TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port);
		if (TNC->PID == 0)
		{
			TNC->CONNECTING = FALSE;
			return;						// Not listening so no point trying to connect
		}

		// Get the File Name in case we want to restart it.

		if (TNC->ProgramPath == NULL)
		{
			if (GetModuleFileNameExPtr)
			{
				HANDLE hProc;
				char ExeName[256] = "";

				hProc =  OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID);
	
				if (hProc)
				{
					GetModuleFileNameExPtr(hProc, 0,  ExeName, 255);
					CloseHandle(hProc);

					TNC->ProgramPath = _strdup(ExeName);
				}
			}
		}
	}
#endif

//	// If we started the TNC make sure it is still running.

//	if (!IsProcess(TNC->PID))
//	{
//		RestartTNC(TNC);
//		Sleep(3000);
//	}


	TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName);
	TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName);

	if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE)
	{
		//	Resolve name to address

		HostEnt = gethostbyname (TNC->HostName);
		 
		 if (!HostEnt)
		 {
		 	TNC->CONNECTING = FALSE;
			return;			// Resolve failed
		 }
		 memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4);
		 memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4);
	}

//	closesocket(TNC->TCPSock);
//	closesocket(TNC->TCPDataSock);

	TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0);

	if (TNC->TCPSock == INVALID_SOCKET)
	{
		i=sprintf(Msg, "Socket Failed for KISSHF socket - error code = %d\r\n", WSAGetLastError());
		WritetoConsole(Msg);

	 	TNC->CONNECTING = FALSE;
  	 	return; 
	}

	setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4);

	sinx.sin_family = AF_INET;
	sinx.sin_addr.s_addr = INADDR_ANY;
	sinx.sin_port = 0;

	if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0)
	{
		//
		//	Connected successful
		//
	}
	else
	{
		if (TNC->Alerted == FALSE)
		{
			err=WSAGetLastError();
   			i=sprintf(Msg, "Connect Failed for KISSHF socket - error code = %d\r\n", err);
			WritetoConsole(Msg);
			sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed");
			MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);

			TNC->Alerted = TRUE;
		}
		
		closesocket(TNC->TCPSock);
		TNC->TCPSock = 0;
	 	TNC->CONNECTING = FALSE;
		return;
	}

	Sleep(1000);

	TNC->LastFreq = 0;			//	so V4 display will be updated

 	TNC->CONNECTING = FALSE;
	TNC->CONNECTED = TRUE;
	TNC->BusyFlags = 0;
	TNC->InputLen = 0;

	// Send INIT script

	// VARA needs each command in a separate send

	ptr1 = &TNC->InitScript[0];

	// We should wait for first RDY. Cheat by queueing a null command

	GetSemaphore(&Semaphore, 52);

	while(TNC->BPQtoWINMOR_Q)
	{
		buffptr = Q_REM(&TNC->BPQtoWINMOR_Q);

		if (buffptr)
			ReleaseBuffer(buffptr);
	}


	while (ptr1 && ptr1[0])
	{
		unsigned char c;
		
		ptr2 = strchr(ptr1, 13);
		
		if (ptr2)
		{
			c = *(ptr2 + 1);		// Save next char
			*(ptr2 + 1) = 0;		// Terminate string
		}
//		VARASendCommand(TNC, ptr1, TRUE);

		if (ptr2)
			*(1 + ptr2++) = c;		// Put char back 

		ptr1 = ptr2;
	}
	
	TNC->Alerted = TRUE;

	sprintf(TNC->WEB_COMMSSTATE, "Connected to KISS TNC");		
	MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);

	FreeSemaphore(&Semaphore);

	sprintf(Msg, "Connected to KISS TNC Port %d\r\n", TNC->Port);
	WritetoConsole(Msg);


	#ifndef LINBPQ
//	FreeSemaphore(&Semaphore);
	Sleep(1000);		// Give VARA time to update Window title
//	EnumWindows(EnumVARAWindowsProc, (LPARAM)TNC);
//	GetSemaphore(&Semaphore, 52);
#endif


	while (TNC->CONNECTED)
	{
		FD_ZERO(&readfs);	
		FD_ZERO(&errorfs);

		FD_SET(TNC->TCPSock,&readfs);
		FD_SET(TNC->TCPSock,&errorfs);

		timeout.tv_sec = 600;
		timeout.tv_usec = 0;				// We should get messages more frequently that this

		ret = select((int)TNC->TCPSock + 1, &readfs, NULL, &errorfs, &timeout);
		
		if (ret == SOCKET_ERROR)
		{
			Debugprintf("KISSHF Select failed %d ", WSAGetLastError());
			goto Lost;
		}
		if (ret > 0)
		{
			//	See what happened

			if (FD_ISSET(TNC->TCPSock, &readfs))
			{
				GetSemaphore(&Semaphore, 52);
				KISSHFProcessReceivedPacket(TNC, Channel);
				FreeSemaphore(&Semaphore);
			}
								
			if (FD_ISSET(TNC->TCPSock, &errorfs))
			{
Lost:	
				sprintf(Msg, "KISSHF Connection lost for Port %d\r\n", TNC->Port);
				WritetoConsole(Msg);

				sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost");
				MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);

				TNC->CONNECTED = FALSE;
				TNC->Alerted = FALSE;

				if (TNC->PTTMode)
					Rig_PTT(TNC, FALSE);			// Make sure PTT is down

				if (TNC->Streams[0].Attached)
					TNC->Streams[0].ReportDISC = TRUE;

				TNC->TCPSock = 0;
				return;
			}
		}
	}
	sprintf(Msg, "KISSHF Thread Terminated Port %d\r\n", TNC->Port);
	WritetoConsole(Msg);
}

static int	KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len)
{
	int i,txptr=0;
	UCHAR c;

	for (i=0;i<len;i++)
	{
		c=inbuff[i];

		if (c == FESC)
		{
			c=inbuff[++i];
			{
				if (c == TFESC)
					c=FESC;
				else
				if (c == TFEND)
					c=FEND;
			}
		}

		outbuff[txptr++]=c;
	}

	return txptr;

}

#define ACKMODE	4				// CAN USE ACK REQURED FRAMES
#define MSGHDDRLEN (USHORT)(sizeof(VOID *) + sizeof(UCHAR) + sizeof(USHORT))
#define	ONEMINUTE 60*3

static int	KissEncode(struct TNCINFO * TNC, UCHAR * inbuff, UCHAR * outbuff, int len, int Channel)
{
	int i,txptr=0;
	UCHAR c;

	outbuff[0] = FEND;
	outbuff[1] = Channel;
	txptr=2;

	// See if we need ackmode

	if (TNC->PortRecord->PORTCONTROL.KISSFLAGS & ACKMODE)
	{
		UCHAR * ptr =  inbuff - MSGHDDRLEN;
		PMESSAGE Buffer = (PMESSAGE)ptr;

		if (Buffer->Linkptr)					// Frame Needs ACK
		{
			UINT ACKWORD = (UINT)(Buffer->Linkptr - LINKS);
			outbuff[1] |= 0x0c;			// ACK OPCODE 
			outbuff[2] = ACKWORD & 0xff;
			outbuff[3] = (ACKWORD >> 8) &0xff;

			Buffer->Linkptr->L2TIMER = ONEMINUTE;		// Extend timeout

			txptr = 4;

			// have to reset flag so trace doesnt clear it

			Buffer->Linkptr = 0;
		}
	}

	for (i=0;i<len;i++)
	{
		c=inbuff[i];
		
		switch (c)
		{
		case FEND:
			outbuff[txptr++]=FESC;
			outbuff[txptr++]=TFEND;
			break;

		case FESC:

			outbuff[txptr++]=FESC;
			outbuff[txptr++]=TFESC;
			break;

		default:

			outbuff[txptr++]=c;
		}
	}

	outbuff[txptr++]=FEND;

	return txptr;

}


VOID KISSHFProcessReceivedPacket(struct TNCINFO * TNC, int Channel)
{
	int InputLen, MsgLen;
	unsigned char * ptr;
	char Buffer[4096];

	if (TNC->InputLen > 8000)	// Shouldnt have packets longer than this
		TNC->InputLen=0;

	InputLen = recv(TNC->TCPSock, &TNC->ARDOPBuffer[TNC->InputLen], 8192 - TNC->InputLen, 0);

	if (InputLen == 0 || InputLen == SOCKET_ERROR)
	{
		// Does this mean closed?

		int err = GetLastError();

		closesocket(TNC->TCPSock);

		TNC->TCPSock = 0;

		TNC->CONNECTED = FALSE;
		TNC->Streams[0].ReportDISC = TRUE;

		sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost");
		MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);

		return;					
	}

	TNC->InputLen += InputLen;

	// Extract and decode KISS frames

	ptr = memchr(TNC->ARDOPBuffer + 1, FEND, TNC->InputLen - 1);		// Ignore leading FEND

	while (ptr)	//  FEND in buffer
	{
		ptr++;

		MsgLen = ptr - TNC->ARDOPBuffer;

		if (MsgLen > 360)
		{
			TNC->InputLen = 0;
			return;
		}

		TNC->InputLen -= MsgLen;

		if (MsgLen > 1)
		{
			PMESSAGE Buff;

			if ((TNC->ARDOPBuffer[1] & 0xf0) != Channel)
				goto ignoreit;

			Buff = GetBuff();

			MsgLen = KissDecode(TNC->ARDOPBuffer, Buffer, MsgLen);

			// See if ACKMODE ACK

			if ((Buffer[1] & 0xf) == 12)
			{
				//Ackmode

				struct _LINKTABLE * LINK;
				int ACKWORD = Buffer[2] | Buffer[3] << 8;

				if (ACKWORD < MAXLINKS)
				{
					LINK = LINKS + ACKWORD;

					if (LINK->L2TIMER)
						LINK->L2TIMER = LINK->L2TIME;
				}

				ReleaseBuffer(Buff);
				goto ignoreit;
			}

			if ((Buffer[1] & 0xf) != 0)
			{
				ReleaseBuffer(Buff);
				goto ignoreit;				// Not data
			}

			// we dont need the FENDS or control byte

			MsgLen -= 3;
				
			if (Buff)
			{
				memcpy(&Buff->DEST, &Buffer[2], MsgLen);
				MsgLen += (3 + sizeof(void *));

				PutLengthinBuffer((PDATAMESSAGE)Buff, MsgLen);		// Needed for arm5 portability

				C_Q_ADD(&TNC->PortRecord->PORTCONTROL.PORTRX_Q, (UINT *)Buff);
			}
		}

ignoreit:

		if (TNC->InputLen == 0)
			return;

		memmove(TNC->ARDOPBuffer, ptr, TNC->InputLen);
		ptr = memchr(TNC->ARDOPBuffer + 1, FEND, TNC->InputLen - 1);		// Ignore leading FEND
	}
		
}



/*

// we dont need the control byte

len --;

if (Buffer)
{
memcpy(&Buffer->DEST, &Port->RXMSG[1], len);
len += (3 + sizeof(void *));

PutLengthinBuffer((PDATAMESSAGE)Buffer, len);		// Needed for arm5 portability

C_Q_ADD(TNC->PortRecord->PORTCONTROL.PORTRX_Q, (UINT *)Buffer);
}

*/

//	TNC->InputLen -= MsgLen;
//	goto loop;




void AttachKISSHF(struct PORTCONTROL * PORT, MESSAGE * Buffer)
{
	// SABM on HFKISS port. L2 code will accepr call and connect to appl if necessary, but
	// need to attach the port

	char Call[16] = "";
	char OrigCall[16] = "";
	struct TNCINFO * TNC = PORT->TNC;
	struct WL2KInfo * WL2K = TNC->WL2K;

	if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0)
	{
		TRANSPORTENTRY * SESS;
		struct TNCINFO * TNC = PORT->TNC;

		// Incoming Connect

		Call[ConvFromAX25(Buffer->DEST, Call)] = 0;
		OrigCall[ConvFromAX25(Buffer->ORIGIN, OrigCall)] = 0;

		// Stop other ports in same group

		SuspendOtherPorts(TNC);

		TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit;		// Reset Limit

		ProcessIncommingConnectEx(TNC, Call, 0, FALSE, FALSE);

		SESS = TNC->PortRecord->ATTACHEDSESSIONS[0];

		SESS->Mode = TNC->WL2KMode;

		TNC->ConnectPending = FALSE;

		if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT"))
		{
			sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", OrigCall, Call, TNC->RIG->Valchar);
			SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500;		// Convert to Centre Freq
			if (SESS->Frequency == 1500)
			{
				// try to get from WL2K record

				if (WL2K)
				{
					SESS->Frequency = WL2K->Freq;
				}
			}
			SESS->Mode = TNC->WL2KMode;
		}
		else
		{
			sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", OrigCall, Call);
			if (WL2K)
			{
				SESS->Frequency = WL2K->Freq;
				SESS->Mode = WL2K->mode;
			}
		}

		if (WL2K)
			strcpy(SESS->RMSCall, WL2K->RMSCall);

		MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
	}
}

void DetachKISSHF(struct PORTCONTROL * PORT)
{
	// L2 Link Closed. Detach.

	struct TNCINFO * TNC = PORT->TNC;
	struct STREAMINFO * STREAM = &TNC->Streams[0];

	if (STREAM->Attached)
		STREAM->ReportDISC = 10;		// Tell Node but give time for error message to display

	STREAM->Connecting = FALSE;
	STREAM->Connected = FALSE;

}

void KISSHFConnected(struct PORTCONTROL * PORT, struct _LINKTABLE * LINK)
{
	// UA received when connecting

	struct TNCINFO * TNC = PORT->TNC;
	struct STREAMINFO * STREAM = &TNC->Streams[0];
	struct WL2KInfo * WL2K = TNC->WL2K;

	TRANSPORTENTRY * SESS;
	char Call[16] = "";
	char OrigCall[16] = "";


	if (STREAM->Connecting)
	{
		STREAM->Connecting = FALSE;
		STREAM->Connected = TRUE;

		Call[ConvFromAX25(LINK->LINKCALL, Call)] = 0;
		OrigCall[ConvFromAX25(LINK->OURCALL, OrigCall)] = 0;

		TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit;		// Reset Limit

		SESS = TNC->PortRecord->ATTACHEDSESSIONS[0];
		SESS->Mode = TNC->WL2KMode;

		if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT"))
		{
			sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", OrigCall, Call, TNC->RIG->Valchar);
			SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500;		// Convert to Centre Freq
			
			if (SESS->Frequency == 1500)
			{
				// try to get from WL2K record

				if (WL2K)
				{
					SESS->Frequency = WL2K->Freq;
				}
			}
			SESS->Mode = TNC->WL2KMode;
		}
		else
		{
			sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", OrigCall, Call);

			if (WL2K)
			{
				SESS->Frequency = WL2K->Freq;
				SESS->Mode = WL2K->mode;
			}
		}

		if (WL2K)
			strcpy(SESS->RMSCall, WL2K->RMSCall);

		MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
	}
}





