/*
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.  S"paclenee 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
*/	


#define Kernel

#define _CRT_SECURE_NO_DEPRECATE 
#pragma data_seg("_BPQDATA")

//#include "windows.h"
//#include "winerror.h"


#include "time.h"
#include "stdio.h"
#include <fcntl.h>					 
//#include "vmm.h"
//#include "SHELLAPI.H"

#include "cheaders.h"
#include "bpqaprs.h"
#include "kiss.h"

#pragma pack()

#include "tncinfo.h"
#include "telnetserver.h"

//#include "GetVersion.h"

//#define DllImport	__declspec( dllimport )
//#define DllExport	__declspec( dllexport )

BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR *AXCalls);
VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port);
int APIENTRY ClearNodes();
VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value);
VOID SendHTTPRequest(SOCKET sock, char * Host, int Port, char * Request, char * Params, int Len, char * Return);
SOCKET OpenWL2KHTTPSock();
VOID FormatTime3(char * Time, time_t cTime);
VOID Format_Addr(unsigned char * Addr, char * Output, BOOL IPV6);
VOID Tel_Format_Addr(struct ConnectionInfo * sockptr, char * dst);
VOID FindLostBuffers();
BOOL CheckCMS(struct TNCINFO * TNC);
VOID L2SENDXID(struct _LINKTABLE * LINK);
int CountBits(unsigned long in);
VOID SaveMH();
BOOL RestartTNC(struct TNCINFO * TNC);
void GetPortCTEXT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID WriteMiniDump();
int CheckKissInterlock(struct PORTCONTROL * PORT, int Exclusive);
int seeifInterlockneeded(struct PORTCONTROL * PORT);
int CompareNode(const void *a, const void *b);
int CompareAlias(const void *a, const void *b);
int CompareRoutes(const void * a, const void * b);
void SendVARANetromNodes(struct TNCINFO * TNC, MESSAGE *Buffer);
VOID DoNetromConnect(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest, BOOL Spy, int Service);
VOID sendAlltoOneNeigbour(struct ROUTE * Route);
VOID CMDSTREAMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);

extern VOID KISSTX(struct KISSINFO * KISS, PMESSAGE Buffer);

char COMMANDBUFFER[81] = "";		// Command Hander input buffer
char OrigCmdBuffer[81] = "";		// Command Hander input buffer before toupper

struct DATAMESSAGE * REPLYBUFFER = NULL;
UINT APPLMASK = 0;
UCHAR SAVEDAPPLFLAGS = 0;

UCHAR ALIASINVOKED = 0;

extern int MONTOFILEFLAG;
extern int RIFInterval;

VOID * CMDPTR = 0;

short CMDPACLEN = 0;

char OKMSG[] = "Ok\r";
 
char CMDERRMSG[] = "Invalid command - Enter ? for command list\r";
#define CMDERRLEN sizeof(CMDERRMSG) - 1

char PASSWORDMSG[] = "Command requires SYSOP status - enter password\r";
#define LPASSMSG sizeof(PASSWORDMSG) - 1

char CMDLIST[] = "CONNECT BYE INFO NODES PORTS ROUTES USERS MHEARD";

#define CMDLISTLEN	sizeof(CMDLIST) - 1

char BADMSG[] = "Bad Parameter\r";
char BADPORT[] = "Invalid Port Number\r";
char NOTEXTPORT[] = "Only valid on EXT ports\r";
char NOVALCALLS[] = "No Valid Calls defined on this port\r";

char BADVALUEMSG[] = "Invalid parameter\r";

char BADCONFIGMSG[] = "Configuration File check falled - will continue with old config\r";
#ifdef LINBPQ
char REBOOTOK[] = "Rebooting\r";
#else
char REBOOTOK[] = "Rebooting in 20 secs\r";
#endif
char REBOOTFAILED[] = "Shutdown failed\r";

char RESTARTOK[] = "Restarting\r";
char RESTARTFAILED[] = "Restart failed\r";

UCHAR ARDOP[7] = {'A'+'A','R'+'R','D'+'D','O'+'O','P'+'P',' '+' '};		// ARDOP IN AX25
UCHAR VARA[7] = {'V'+'V','A'+'A','R'+'R','A'+'A',' '+' ',' '+' '};		// VARA IN AX25

int STATSTIME = 0;
int MAXBUFFS = 0;
int QCOUNT = 0;
int MINBUFFCOUNT = 65535;
int NOBUFFCOUNT = 0;
int BUFFERWAITS = 0;
int MAXDESTS = 0;
int NUMBEROFNODES = 0;
int L2CONNECTSOUT = 0;
int L2CONNECTSIN = 0;
int L4CONNECTSOUT = 0;
int L4CONNECTSIN = 0;
int L4FRAMESTX = 0;
int L4FRAMESRX = 0;
int L4FRAMESRETRIED = 0;
int OLDFRAMES = 0;
int L3FRAMES = 0;

VOID SENDSABM(struct _LINKTABLE * LINK);
VOID RESET2(struct _LINKTABLE * LINK);

int APPL1 = 0;
int PASSCMD = 0;

#pragma pack(1)

struct _EXTPORTDATA DP;			// Only way I can think of to get offets to port data into cmd table

char CMDALIAS[ALIASLEN][NumberofAppls] = {0};
char * ALIASPTR	= &CMDALIAS[0][0];

extern int RigReconfigFlag;

extern int DEBUGINP3;
extern int PREFERINP3ROUTES;

extern char FX25Modes[8][8];
extern char IL2PModes[8][10];


struct CMDX COMMANDS[];

int CMDXLEN	= sizeof (struct CMDX);

VOID SENDNODESMSG(int Portnum);
VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID LORACMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID STOPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID STARTROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID AXMHEARD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID SHOWTELNET(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID SHOWAGW(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID SHOWARP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID SHOWNAT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID PING(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID SHOWIPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID FLMSG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD);
void ListExcludedCalls(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID RECONFIGTELNET (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID HELPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID UZ7HOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD);
VOID QTSMCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD);
void hookL2SessionAttempt(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK);
VOID RHPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);

/* Paula's NetROMX includes a service number in a CREQX message which allows a node to host lots of applications without
   filling the Nodes table with SSID's

I could make these (or some of them) BPQ Commands but some will clash (eg INFO) or match an existing APPL (eg BBS)

I could use C APPL@CALL for an extended call to a node

Maybe I can detect APPL NODE so Paula's syntax will work

or both??

Standard Services are:
*/

struct NETROMX SERVICES[] = {
	{0, "CMD"},		// Normal connection to Node's command line
	{1, "INFO"},	// Standard Information server
	{2, "PMS"},		// Personal Message System
	{3, "BBS"},		// (reserved for Bulletin Board System)
	{4, "DX"},		// (reserved for DX cluster/dx-spot feed)
	{5, "TPP"},		// (reserved for "Tampa Ping-Pong" chat)
	{7, "ECHO"},	// Echoes data back to sender
	{8, "XRCHAT"},	// XRChat server 
	{9, "DISCARD"},	// Data sink
	{10, "RMS"},	// (reserved for winlink RMS}
	{11, "CHAT"},	// (reserved for BPQ chat server)
	{13, "DAYTIME"},	// Local date/time (similar to RFC867)
	{14, "APRS"},	// APRS Server
	{15, "CUSTINF"},// (reserved for custom information file server)
	{16, "WX"},		// Local weather information
	{17, "TELEM"},	// (reserved for Telemetry server)
	{18, "SMS"},	// Short Message System server
	{19, "CHARGEN"},// Generates a test pattern
	{20, "NDATA"},	// (reserved for NFTP extension)
	{21, "NFTP"},	// Netrom File Transfer Protocol
	{22, "NSSH"},	// (reserved for secure login - if legal?)
	{23, "TELNET"},	// Normal L4 login (same as 0)
	{25, "SMTP"},	// (reserved for Simple Mail Transfer Protocol)
	{26, "MHEARD"},	// MHEARD server (shows MH lists)
	{27, "DXLIST"},	// DX List server (shows DX lists)
	{79, "FINGER"},	// Finger server
	{80, "HTTP"},	// NetromWeb (HTTP over Netrom) server
	{87, "NTTY"},	// Netrom TTY - Keyboard to keyboard chat
	{1883, "MQTT"}	// MQTT server
};

int NUMBEROFSSERVICES = sizeof(SERVICES)/sizeof(struct NETROMX);


char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...)
{
	// Send Command response checking PACLEN

	char Mess[4096];
	va_list(arglist);
	int OldLen;
	int MsgLen;
	struct DATAMESSAGE * Buffer;
	char * Messptr = Mess;
	int Paclen = Session->SESSPACLEN;

	if (Paclen == 0)
		Paclen = 255;

	va_start(arglist, format);

	MsgLen = vsprintf(Mess, format, arglist);

	OldLen = (int)(Bufferptr - (char *)REPLYBUFFER->L2DATA);

	while ((OldLen + MsgLen) > Paclen)
	{
		// Have to send Paclen then get a new buffer

		int ThisBit = Paclen - OldLen;		// What we can send this time

		if (ThisBit < 0)
			ThisBit = 0;					// How can this happen??

		memcpy(Bufferptr, Messptr, ThisBit);
		Messptr += ThisBit;
		MsgLen -= ThisBit;

		// QUEUE IT AND GET ANOTHER BUFFER

		Buffer = (struct DATAMESSAGE *)GetBuff();

		if (Buffer == NULL)

			// No buffers, so just reuse the old one (better than crashing !!)

			Buffer = REPLYBUFFER;
		else
			SendCommandReply(Session, REPLYBUFFER, Paclen + (4 + sizeof(void *)));


		REPLYBUFFER = Buffer;
		Buffer->PID = 0xf0;

		Bufferptr =  &Buffer->L2DATA[0];
		OldLen = 0;
	}

	// Add last bit to buffer

	memcpy(Bufferptr, Messptr, MsgLen);

	return Bufferptr + MsgLen;
}

VOID POLLNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	int Portnum = atoi(CmdTail);
	struct PORTCONTROL * PORT = 0;
	MESSAGE * Buffer;
	UCHAR * ptr1;

	if (Portnum)
		PORT = GetPortTableEntryFromPortNum(Portnum);
		
	if (Portnum == 0 || PORT == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");			
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (PORT->PORTQUALITY == 0 || PORT->INP3ONLY)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Quality = 0 or INP3 Port\r");			
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}
	Buffer = GetBuff();

	if (Buffer == 0)
		return;

	Buffer->PORT = Portnum;

	memcpy(Buffer->ORIGIN, NETROMCALL, 7);
	memcpy(Buffer->DEST, NODECALL, 7);

	Buffer->ORIGIN[6] |= 0x61;		// SET CMD END AND RESERVED BITS

	Buffer->CTL = UI;
	Buffer->PID = 0xCF;				// Netrom

	ptr1 = &Buffer->L2DATA[0];
	
	*(ptr1++) = 0xfe;				// Nodes Poll Flag

	memcpy(ptr1, MYALIASTEXT, 6);

	ptr1+= 6;

	Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer);

	if (PORT->TNC && PORT->TNC->Hardware == H_VARA)
		SendVARANetromNodes(PORT->TNC, Buffer);
	else
		PUT_ON_PORT_Q(PORT, Buffer);

	strcpy(Bufferptr, OKMSG);
	Bufferptr += (int)strlen(OKMSG);
						
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

VOID SENDRIF(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	struct ROUTE * Route;
	int Portnum = atoi(CmdTail);
	unsigned char axCall[7];

	char * Call = strlop(CmdTail, ' ');

	if (Call && Portnum)
	{
		ConvToAX25(Call, axCall);

		if (FindNeighbour(axCall, Portnum, &Route))
		{
			sendAlltoOneNeigbour(Route);
			
			strcpy(Bufferptr, OKMSG);
			Bufferptr += (int)strlen(OKMSG);
						
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}
	}
	
	Bufferptr = Cmdprintf(Session, Bufferptr, "Route not found\r");			
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}

VOID SENDNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	int Portnum = atoi(CmdTail);
	struct PORTCONTROL * PORT;

	if (Portnum)
	{
		PORT = GetPortTableEntryFromPortNum(Portnum);
		if (PORT == 0)
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");			
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}
	}

	SENDNODESMSG(Portnum);

	strcpy(Bufferptr, OKMSG);
	Bufferptr += (int)strlen(OKMSG);
						
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

VOID SAVEMHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	SaveMH();
							
	strcpy(Bufferptr, OKMSG);
	Bufferptr += (int)strlen(OKMSG);
						
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

VOID SAVENODES(struct _TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail,  struct CMDX * CMD)
{
	SaveNodes();
							
	strcpy(Bufferptr, OKMSG);
	Bufferptr += (int)strlen(OKMSG);
						
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

VOID DUMPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	WriteMiniDump();

	strcpy(Bufferptr, OKMSG);
	Bufferptr += (int)strlen(OKMSG);
						
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

VOID RIGRECONFIG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	if (!ProcessConfig())
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Configuration File check falled - will continue with old config");
	}
	else
	{
		RigReconfigFlag = TRUE;	
		Bufferptr = Cmdprintf(Session, Bufferptr, "Rigcontrol Reconfig requested");
	}

	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

VOID REBOOT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	if (Reboot())
	{
		strcpy(Bufferptr, REBOOTOK);
		Bufferptr += (int)strlen(REBOOTOK);
	}
	else
	{
		strcpy(Bufferptr, REBOOTFAILED);
		Bufferptr += (int)strlen(REBOOTFAILED);
	}

	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}
	
VOID RESTART(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	if (Restart())
	{
		strcpy(Bufferptr, RESTARTOK);
		Bufferptr += (int)strlen(RESTARTOK);
	}
	else
	{
		strcpy(Bufferptr, RESTARTFAILED);
		Bufferptr += (int)strlen(RESTARTFAILED);
	}
							
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

VOID RESTARTTNC(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char * ptr, *Context;
	int portno;

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
	{
		portno = atoi (ptr);

		if (portno && portno < 33)
		{
			struct TNCINFO * TNC = TNCInfo[portno];
			
			if (TNC == NULL)
			{
				Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
			}
			else
			{
				if (TNC->ProgramPath)
				{
					if (RestartTNC(TNC))
						Bufferptr = Cmdprintf(Session, Bufferptr, "Restart %s Ok\r", TNC->ProgramPath);
					else
						Bufferptr = Cmdprintf(Session, Bufferptr, "Restart %s Failed\r", TNC->ProgramPath);
				}
				else
				{
					Bufferptr = Cmdprintf(Session, Bufferptr, "PATH not defined so can't restart TNC\r");
				}
			}
		}
		else
			Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");

	}
							
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

UCHAR VALNODESFLAG = 0, EXTONLY = 0;

VOID PORTVAL (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);

VOID VALNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	VALNODESFLAG = 1;
	PORTVAL(Session, Bufferptr, CmdTail, CMD);
}

VOID EXTPORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	EXTONLY = 1;
	PORTVAL(Session, Bufferptr, CmdTail, CMD);
}
VOID PORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	//	PROCESS PORT VALUE COMMANDS

	char * ptr, *Context, * ptr1;
	int portno;
	UCHAR oldvalue, newvalue;
	struct PORTCONTROL * PORT = PORTTABLE;
	int n = NUMBEROFPORTS;
	UCHAR * valueptr;

	// Get port number

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
	{
		portno = atoi (ptr);

		if (portno)
		{
			while (n--)
			{
				if (PORT->PORTNUMBER == portno)
				{
					if (VALNODESFLAG)
					{
						char * VNPtr = PORT->PERMITTEDCALLS;
						char Normcall[10];
						
						VALNODESFLAG = 0;

						if (VNPtr)
						{
							while (VNPtr[0])
							{
								Normcall[ConvFromAX25(VNPtr, Normcall)] = 0;
								Bufferptr = Cmdprintf(Session, Bufferptr, "%s ", Normcall);
								VNPtr += 7;
							}

							Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
						}
						else
						{								
							Bufferptr = Cmdprintf(Session, Bufferptr, "%s", NOVALCALLS);
						}
						
						SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
						
						return;

					}

					if (EXTONLY)
					{
						// Make sure an Extenal Port

						EXTONLY = 0;
						
						if (PORT->PORTTYPE != 0x10)
						{
							strcpy(Bufferptr, NOTEXTPORT);
							Bufferptr += (int)strlen(NOTEXTPORT);
							SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
							return;
						}
					}

					valueptr = (UCHAR *)PORT + CMD->CMDFLAG;
					oldvalue = *valueptr;

					// Display Param Namee

					ptr1 = &CMD->String[0];
					n = 12;

					while (*(ptr1) != ' ' && n--)
						*(Bufferptr++) = *(ptr1++);

					// See if another param - if not, just display current value

					ptr = strtok_s(NULL, " ", &Context);

					if (ptr && ptr[0])
					{
						// Get new value

							newvalue = atoi(ptr);
							*valueptr = newvalue;

							Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue);
					}

					else
						Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue);

					SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
					return;

				}
				PORT = PORT->PORTPOINTER;
			}
		}
	}
	
	// Bad port

	strcpy(Bufferptr, BADPORT);
	Bufferptr += (int)strlen(BADPORT);
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;

}

VOID SWITCHVAL (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	// Update switch 8 bit value
	
	char * ptr, *Context, * ptr1;
	UCHAR oldvalue, newvalue;
	int n;
	UCHAR * valueptr;

	valueptr = (UCHAR *)CMD->CMDFLAG;

	oldvalue = *valueptr;

	// Display Param Name

	ptr1 = &CMD->String[0];
	n = 12;

	while (*(ptr1) != ' ' && n--)
		*(Bufferptr++) = *(ptr1++);

	// See if a param - if not, just display current value

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr && ptr[0])
	{
		// Get new value

			newvalue = atoi(ptr);
			*valueptr = newvalue;

			Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue);

			if (memcmp(CMD->String, "NODESINT ", 8) == 0)
				L3TIMER = L3INTERVAL;
	}
	else
		Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue);

	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;

}

VOID SWITCHVALW (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	// Update switch 16 bit value
	
	char * ptr, *Context, * ptr1;
	USHORT oldvalue, newvalue;
	int n;
	USHORT * valueptr;

	valueptr = (USHORT *)CMD->CMDFLAG;

	oldvalue = (USHORT)*valueptr;

	// Display Param Name

	ptr1 = &CMD->String[0];
	n = 12;

	while (*(ptr1) != ' ' && n--)
		*(Bufferptr++) = *(ptr1++);

	// See if a param - if not, just display current value

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr && ptr[0])
	{
		// Get new value

			newvalue = atoi(ptr);
			*valueptr = newvalue;

			Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue);
	}
	else
		Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue);

	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;

}

TRANSPORTENTRY * SetupSessionFromSession(TRANSPORTENTRY * Session, PBPQVECSTRUC HOSTSESS, UINT APPLMASK)
{
	// Create a Transport (L4) session linked to an incoming Session

	TRANSPORTENTRY * NewSess = L4TABLE;
	int Index = 0;
	
	while (Index < MAXCIRCUITS)
	{
		if (NewSess->L4USER[0] == 0)
		{
			// Got One

			UCHAR * ourcall = &MYCALL[0];

			Session->L4CROSSLINK = NewSess;
			NewSess->L4CROSSLINK = Session;

			if (APPLMASK)
			{
				// Circuit for APPL - look for an APPLCALL

				APPLCALLS * APPL = APPLCALLTABLE;

				while ((APPLMASK & 1) == 0)
				{
					APPLMASK >>= 1;
					APPL++;
				}
				if (APPL->APPLCALL[0] > 0x40)		// We have an applcall
					ourcall = &APPL->APPLCALL[0];
			}

			memcpy(NewSess->L4USER, ourcall, 7);
			memcpy(NewSess->L4MYCALL, Session->L4MYCALL, 7);
	
			NewSess->CIRCUITINDEX = Index;				//OUR INDEX
			NewSess->CIRCUITID = NEXTID;

			NEXTID++;
			if (NEXTID == 0)
				NEXTID++;								// kEEP nON-ZERO

			NewSess->SESSIONT1 = Session->SESSIONT1;
			NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW;
			NewSess->SESSPACLEN = PACLEN;				// Default;

			NewSess->L4TARGET.HOST = HOSTSESS;
			NewSess->L4STATE = 5;
			return NewSess;
		}
		Index++;
		NewSess++;
	}
	return NULL;
}

extern int GETCONNECTIONINFO();


BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions)
{
	PBPQVECSTRUC HOSTSESS = BPQHOSTVECTOR;
	TRANSPORTENTRY * NewSess;
	int ApplNum;
	int n = BPQHOSTSTREAMS;
	int ConfigedPorts = 0;

	//	LOOK FOR A FREE HOST SESSION

	while (n--)
	{
		if (HOSTSESS->HOSTAPPLMASK & Mask)
		{
			// Right appl

			ConfigedPorts++;

			if (HOSTSESS->HOSTSESSION == NULL && (HOSTSESS->HOSTFLAGS & 3) == 0) // Not attached and no report outstanding
			{				
				//	WEVE GOT A FREE BPQ HOST PORT - USE IT

				NewSess = SetupSessionFromSession(Session, HOSTSESS, Mask);

				if (NewSess == NULL)
					return FALSE;					// Appl not available

				HOSTSESS->HOSTSESSION = NewSess;

				//	Convert APPLMASK to APPLNUM

				ApplNum = 1;

				while  (APPLMASK && (APPLMASK & 1) == 0)
				{
					ApplNum++;
					APPLMASK >>= 1;
				}

				HOSTSESS->HOSTAPPLNUM = ApplNum;
	
				HOSTSESS->HOSTFLAGS |= 2;			// Indicate State Change

				NewSess->L4CIRCUITTYPE = BPQHOST | DOWNLINK;

				PostStateChange(NewSess);

				NewSess->SESS_APPLFLAGS = HOSTSESS->HOSTAPPLFLAGS;

				NewSess->SESSPACLEN = Paclen;

				return TRUE;
			}
		}
		HOSTSESS++;
	}

	*AnySessions = ConfigedPorts;		// to distinguish between none and all in use
	return FALSE;
}

void ConnecttoService(TRANSPORTENTRY * Session, char * Bufferptr, int Service, char * Node, int Stay)
{
	struct DEST_LIST * Dest = DESTS;
	int n = MAXDESTS;
	int gotDest = 0;
	unsigned char axcall[7];
	char cmdName[80];
	int i;

	// Make Sure Node is Known

	strcat(Node, "     ");			// Node table has 6 byte Aliases
	
	while (n--)
	{
		if (memcmp(Dest->DEST_ALIAS, Node, 6) == 0)	
		{
			gotDest = 1;
			break;
		}
		Dest++;
	}

	if (gotDest == 0)
	{
		Dest = DESTS;
		n = MAXDESTS;

		ConvToAX25(Node, axcall);
			
		while (n--)
		{
			if (CompareCalls(Dest->DEST_CALL, axcall))	
			{
				gotDest = 1;
				break;
			}
			Dest++;
		}
	}

	strlop(Node, ' ');

	if (gotDest == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Node %s not found\r", Node);
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	Session->STAYFLAG = Stay;

	// Get command name if a named command

	sprintf(cmdName, "%d", Service);	// default to number

	for (i = 0; i < NUMBEROFSSERVICES; i++)
	{
		if (SERVICES[i].ServiceNo == Service)
		{
			strcpy(cmdName, SERVICES[i].ServiceName);
			break;
		}
	}

	Bufferptr = Cmdprintf(Session, Bufferptr, "Connecting to Service %s on Node %s \r", cmdName, Node);

	DoNetromConnect(Session, Bufferptr, Dest, 0, Service);

	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

int checkifService(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char APPName[13];
	int n = 12;
	BOOL Stay = FALSE;
	char * ptr, *Context;
	int i;
	char TailCopy[256];

	strcpy(TailCopy, CmdTail);


	ptr = strtok_s(TailCopy, " ", &Context);

	// see if any param. if longer than two chars treat as remote node

	if (ptr == 0 || strlen(ptr) < 3)
		return 0;

	memcpy(APPName, CMD->String, 13);

	strlop(APPName, ' ');
		
	 if (Context && Context[0] == 'S')
		Session->STAYFLAG = Stay;

	// See if APPL is one of Paula's service
			
	for (i = 0; i < NUMBEROFSSERVICES; i++)
	{
		if (strcmp(APPName, SERVICES[i].ServiceName) == 0)
		{
			ConnecttoService(Session, Bufferptr, SERVICES[i].ServiceNo, ptr, Stay);
			return 1;
		}
	}

	return 0;
}



VOID APPLCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{		
	BOOL CONFAILED = 0;
	UINT CONERROR ;
	char APPName[13];
	char * ptr1, *ptr2;
	int n = 12;
	BOOL Stay = FALSE;

	//	Copy Appl and Null Terminate

	ptr1 = &CMD->String[0];
	ptr2 = APPName;

	while (*(ptr1) != ' ' && n--)
		*(ptr2++) = *(ptr1++);

	*(ptr2) = 0;
	
	if (Session->LISTEN)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Can't use %s while listening\r", APPName);
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}


	if (CmdTail[0] == 'S')
		Stay = TRUE;

	// could be Node for NETROMX connect or S flag
		
	// We now use service@node, so can't get here

/*
	if (ptr)
	{
		int i;
		
		if (strlen(ptr) > 1)
		{
			 if (Context && Context[0] == 'S')
				Session->STAYFLAG = Stay;

			// See if APPL is one of Paula's service


			
			for (i = 0; i < NUMBEROFSSERVICES; i++)
			{
				if (strcmp(APPName, SERVICES[i].ServiceName) == 0)
				{
					ConnecttoService(Session, Bufferptr, i, ptr, Stay);
					return;
				}
			}

			// Not a service that can be accessed remotely

			Bufferptr = Cmdprintf(Session, Bufferptr, "Connection to %s on a remote node is not possible\r", APPName);
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}

		else if (ptr[0] == 'S')
			Session->STAYFLAG = Stay;
	}
*/		
	memcpy(Session->APPL, CMD->String, 12);

	//	SEE IF THERE IS AN ALIAS DEFINDED FOR THIS COMMAND

	if (ALIASPTR[0] > ' ')
	{
		//	COPY ALIAS TO COMMAND BUFFER, THEN REENTER COMMAND HANDLER

		int SaveSecure = Session->Secure_Session;

		memcpy(COMMANDBUFFER, ALIASPTR, ALIASLEN);
		_strupr(COMMANDBUFFER);
		memcpy(OrigCmdBuffer, ALIASPTR, ALIASLEN);	// In case original case version needed
		
		ALIASINVOKED = 1;							// To prevent Alias Loops 	

		// Set secure session for application alias in case telnet outward connect

		Session->Secure_Session = 1;
		DoTheCommand(Session);
		Session->Secure_Session = SaveSecure;

		return;
	}

	if (cATTACHTOBBS(Session, APPLMASK, CMDPACLEN, &CONERROR) == 0)
	{
		// No Streams

		if (CONERROR)
			Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, All %s Ports are in use - Please try later\r", APPName);
		else
			Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, Application %s is not running - Please try later\r", APPName);

		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	//	IF CMD_TO_APPL SET IN APPLFLAGS, SEND INPUT MSG TO APPL

	if (Session->L4CROSSLINK->SESS_APPLFLAGS & CMD_TO_APPL)
	{
		struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)GetBuff();
		TRANSPORTENTRY * XSession = Session->L4CROSSLINK;

		if (Msg)
		{
			COMMANDBUFFER[72] = 13;
			memcpy(Msg->L2DATA, COMMANDBUFFER, 73);
			Msg->LENGTH = 73 + 4 + sizeof(void *);
			Msg->PID = 0xf0;

			C_Q_ADD(&XSession->L4TX_Q, (UINT *)Msg);
			PostDataAvailable(XSession);
		}
	}

	if (Stay)
		Session->L4CROSSLINK->L4TARGET.HOST->HOSTFLAGS |= 0x20;

	//	IF MSG_TO_USER SET, SEND 'CONNECTED' MESSAGE TO USER

	Session->SESS_APPLFLAGS = Session->L4CROSSLINK->SESS_APPLFLAGS;
	
	if (Session->L4CROSSLINK->SESS_APPLFLAGS  & MSG_TO_USER)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Connected to %s\r", APPName);
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}


	//	DONT NEED BUFFER ANY MORE

	ReleaseBuffer((UINT *)REPLYBUFFER);
	return;
}


VOID CMDI00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	if (checkifService(Session, Bufferptr, CmdTail, CMD))		// See can be used remotely
		return;
		
	Bufferptr = Cmdprintf(Session, Bufferptr, "%s", INFOMSG);
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}
	
VOID CMDV00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	if (sizeof(void *) == 4)
		Bufferptr = Cmdprintf(Session, Bufferptr, "Version %s\r", VersionString);
	else
		Bufferptr = Cmdprintf(Session, Bufferptr, "Version %s (64 bit)\r", VersionString);
		
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

VOID BYECMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	CLOSECURRENTSESSION(Session);		// Kills any crosslink, plus local link
	ReleaseBuffer((UINT *)REPLYBUFFER);
	return;
}

VOID CMDPAC(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	//	SET PACLEN FOR THIS SESSION

	char * ptr, *Context;
	int newvalue;

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr && ptr[0])
	{
		// Get new value

		newvalue = atoi(ptr);
		if (newvalue > 29 && newvalue < 256)
			Session->SESSPACLEN = newvalue & 0xff;
	}

	Bufferptr = Cmdprintf(Session, Bufferptr, "PACLEN - %d\r", Session->SESSPACLEN);
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

VOID CMDIDLE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	//	SET IDLETIME FOR THIS SESSION

	char * ptr, *Context;
	int newvalue;

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr && ptr[0])
	{
		// Get new value

		newvalue = atoi(ptr);
		if (newvalue > 59 && newvalue < 901)
			Session->L4LIMIT = newvalue;
	}

	Bufferptr = Cmdprintf(Session, Bufferptr, "IDLETIME - %d\r", Session->L4LIMIT);
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));

}
VOID CMDT00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	//	SET L4 TIMEOUT FOR CONNECTS ON THIS SESSION

	char * ptr, *Context;
	int newvalue;

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr && ptr[0])
	{
		// Get new value

		newvalue = atoi(ptr);
		if (newvalue > 20)
			Session->SESSIONT1 = newvalue;
	}

	Bufferptr = Cmdprintf(Session, Bufferptr, "L4TIMEOUT - %d\r", Session->SESSIONT1);
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

UCHAR PWLen;
char PWTEXT[80];

VOID PWDCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char * ptr, *Context;
	USHORT pwsum = 0;
	int n = 5, p1, p2, p3, p4, p5;

	if (Session->Secure_Session)	// HOST - SET AUTHORISED REGARDLESS
	{
		Session->PASSWORD = 0xFFFF;		// SET AUTHORISED
		Session->Secure_Session = 1;
		strcpy(Bufferptr, OKMSG);
		Bufferptr += (int)strlen(OKMSG);				
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr && ptr[0])
	{
		// Check Password

		n = 5;

		while (n--)
			pwsum += *(ptr++);

		if (Session->PASSWORD == pwsum)
		{
			Session->PASSWORD = 0xFFFF;		// SET AUTHORISED
			Session->Secure_Session = 1;
			strcpy(Bufferptr, OKMSG);
			Bufferptr += (int)strlen(OKMSG);				
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}

		ReleaseBuffer((UINT *)REPLYBUFFER);
		return;
	}

	//	SEND PASSWORD PROMPT

	if (PWLen == 0)
		PWLen = 1;

	p1 = rand() % PWLen;
	pwsum += PWTEXT[p1++];

	p2 = rand() % PWLen;
	pwsum += PWTEXT[p2++];

	p3 = rand() % PWLen;
	pwsum += PWTEXT[p3++];

	p4 = rand() % PWLen;
	pwsum += PWTEXT[p4++];

	p5 = rand() % PWLen;
	pwsum += PWTEXT[p5++];

	Session->PASSWORD = pwsum;

	Bufferptr = Cmdprintf(Session, Bufferptr, "%d %d %d %d %d\r", p1, p2, p3, p4, p5);
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}

VOID CMDSTATS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char * ptr, *Context;
	int Port = 0, cols = NUMBEROFPORTS, i;
	struct PORTCONTROL * PORT = PORTTABLE;
	struct PORTCONTROL * STARTPORT;

	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	//	SEE IF ANY PARAM

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr && ptr[0])
		Port = atoi(ptr);

	//	IF ASKING FOR PORT STATS, DONT DO SYSTEM ONES

	if (Port == 0)
	{
		struct tm * TM;
		char UPTime[50];
		time_t szClock = STATSTIME * 60;

		TM = gmtime(&szClock);

		sprintf(UPTime, "Uptime (Days Hours Mins)     %.2d:%.2d:%.2d\r",
			TM->tm_yday, TM->tm_hour, TM->tm_min);

		Bufferptr = Cmdprintf(Session, Bufferptr, "%s", UPTime);

		Bufferptr = Cmdprintf(Session, Bufferptr, "Semaphore Get-Rel/Clashes   %9d%9d\r", 
					Semaphore.Gets - Semaphore.Rels, Semaphore.Clashes);

		Bufferptr = Cmdprintf(Session, Bufferptr, "Buffers:Max/Cur/Min/Out/Wait%9d%9d%9d%9d%9d\r", 
					MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS);

		Bufferptr = Cmdprintf(Session, Bufferptr, "Known Nodes/Max Nodes       %9d%9d\r", 
					NUMBEROFNODES, MAXDESTS);

		Bufferptr = Cmdprintf(Session, Bufferptr, "L4 Connects Sent/Rxed       %9d%9d\r", 
					L4CONNECTSOUT, L4CONNECTSIN);

		Bufferptr = Cmdprintf(Session, Bufferptr, "L4 Frames TX/RX/Resent/Reseq%9d%9d%9d%9d\r", 
					L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES);

		Bufferptr = Cmdprintf(Session, Bufferptr, "L3 Frames Relayed           %9d\r", L3FRAMES);

		if (ptr && ptr[0] == 'S')
		{
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}
	}

	//	POSITION TO REQUESTED PORT

	if (Port)
	{
		while (PORT && PORT->PORTNUMBER != Port)
		{
			PORT = PORT->PORTPOINTER;
			cols--;
		}
	}

	if (PORT == NULL)			//	REQUESTED PORT NOT FOUND
	{
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	STARTPORT = PORT;

	if (cols > 7)
		cols = 7;

	Bufferptr = Cmdprintf(Session, Bufferptr, "                  ");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Port %02d  ", PORT->PORTNUMBER);
		PORT = PORT->PORTPOINTER;	
	}

	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Digied");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2DIGIED);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Heard ");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMES);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Rxed  ");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMESFORUS);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Sent  ");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMESSENT);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Timeouts     ");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2TIMEOUTS);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "REJ Frames Rxed ");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2REJCOUNT);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "RX out of Seq   ");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2OUTOFSEQ);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Resequenced  ");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2RESEQ);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "Undrun/Poll T/o ");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2URUNC);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "RX Overruns     ");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2ORUNC);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "RX CRC Errors   ");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->RXERRORS);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "FRMRs Sent      ");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRMRTX);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "FRMRs Received  ");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRMRRX);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
	Bufferptr = Cmdprintf(Session, Bufferptr, "Frames abandoned");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L1DISCARD);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	PORT = STARTPORT;
//	Bufferptr = Cmdprintf(Session, Bufferptr, "Link Active %%   ");
	Bufferptr = Cmdprintf(Session, Bufferptr, "Active(TX/Busy) %%");

	for (i = 0; i < cols; i++)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "  %2d %3d ", PORT->AVSENDING, PORT->AVACTIVE);
		PORT = PORT->PORTPOINTER;	
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

#define	PFBIT 0x10		// POLL/FINAL BIT IN CONTROL BYTE

VOID InformPartner(struct _LINKTABLE * LINK, int Reason);
VOID L2SENDCOMMAND(struct _LINKTABLE * LINK, int CMD);
BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK);

VOID CMDL00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	//	PROCESS 'LINKS' MESSAGE

	struct _LINKTABLE * LINK = LINKS;
	int n = MAXLINKS;
	int len, Count;
	char Normcall[11] = "";
	UCHAR DEST[7];
	UCHAR ORIGIN[7];
	int Port = 0;
	char * ptr = 0, * Context;

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr && _stricmp(ptr, "reset") == 0)
	{
		ptr = strtok_s(NULL, " ", &Context);
		if (ptr)
			ConvToAX25(ptr, DEST);

		ptr = strtok_s(NULL, " ", &Context);
		if (ptr)
			ConvToAX25(ptr, ORIGIN);

		ptr = strtok_s(NULL, " ", &Context);
		if (ptr)
			Port = atoi(ptr);

		if (FindLink(DEST, ORIGIN, Port, &LINK))
		{
			InformPartner(LINK, NORMALCLOSE);	// TELL OTHER END ITS GONE

			LINK->L2RETRIES -= 1;		// Just send one DISC
			LINK->L2STATE = 4;			// CLOSING

			L2SENDCOMMAND(LINK, DISC | PFBIT);
			Bufferptr = Cmdprintf(Session, Bufferptr, "Link Reset\r");
		}
		else
			Bufferptr = Cmdprintf(Session, Bufferptr, "Link Not Found\r");

	}
	else
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Links\r");

		while (n--)
		{
			if (LINK->LINKCALL[0])
			{
				len = ConvFromAX25(LINK->LINKCALL, Normcall);
				Count = COUNT_AT_L2(LINK);

				Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Normcall);

				len = ConvFromAX25(LINK->OURCALL, Normcall);
				Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Normcall);

				if (LINK->Ver2point2)
					Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=2.2 Q=%d\r",
					LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE, Count);
				else
					Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=%d Q=%d\r",
					LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE, 2 - LINK->VER1FLAG, Count);
			
				if (Count > 16 || LINK->LINKWINDOW == 0)
				{
					// Dump Link State

					int secs = time(NULL) - LINK->LASTFRAMESENT;
						
					Bufferptr = Cmdprintf(Session, Bufferptr, "  Debug Info: LINK->LINKNS %d LINK->LINKOWS %d SDTSLOT %d LINKWINDOW %d L2FLAGS %d\r", LINK->LINKNS, LINK->LINKOWS, LINK->SDTSLOT, LINK->LINKWINDOW, LINK->L2FLAGS);
					Bufferptr = Cmdprintf(Session, Bufferptr, "  Debug Info: Slots %x %x %x %x %x %x %x %x\r", LINK->FRAMES[0], LINK->FRAMES[1], LINK->FRAMES[2], LINK->FRAMES[3],
					LINK->FRAMES[4], LINK->FRAMES[5], LINK->FRAMES[6], LINK->FRAMES[7]);
				}
			}
			LINK++;
		}
	}
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}


VOID CMDS00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	//	PROCESS 'USERS'

	int n = MAXCIRCUITS;
	TRANSPORTENTRY * L4 = L4TABLE;
	TRANSPORTENTRY * Partner;
	int MaxLinks = MAXLINKS;
	char State[12] = "", Type[12] = "Uplink";
	char LHS[50] = "", MID[10] = "", RHS[50] = "";
	char Line[100];
	
	Bufferptr = Cmdprintf(Session, Bufferptr, "%s%d)\r", SESSIONHDDR, QCOUNT);

	while (n--)
	{
		if (L4->L4USER[0])
		{
			RHS[0] = MID[0] = 0;

			if ((L4->L4CIRCUITTYPE & UPLINK) == 0)   //SHORT CMDS10A		; YES
			{
				// IF DOWNLINK, ONLY DISPLAY IF NO CROSSLINK

				if (L4->L4CROSSLINK == 0)	//jne 	CMDS60			; WILL PROCESS FROM OTHER END
				{
					// ITS A DOWNLINK WITH NO PARTNER - MUST BE A CLOSING SESSION
					// DISPLAY TO THE RIGHT FOR NOW

					strcpy(LHS, "(Closing) ");
					DISPLAYCIRCUIT(L4, RHS);
					goto CMDS50;
				}
				else
					goto CMDS60;		// WILL PROCESS FROM OTHER END
			}
			
			if (L4->L4CROSSLINK == 0)
			{
				// Single Entry

				DISPLAYCIRCUIT(L4, LHS);
			}
			else
			{
				DISPLAYCIRCUIT(L4, LHS);

				Partner = L4->L4CROSSLINK;

				if (Partner->L4STATE == 5)
					strcpy(MID, "<-->");
				else
					strcpy(MID, "<~~>");
					
				DISPLAYCIRCUIT(Partner, RHS);
			}
CMDS50:
			memset(Line, 32, 100);
			memcpy(Line, LHS, (int)strlen(LHS));
			memcpy(&Line[35], MID, (int)strlen(MID));
			strcpy(&Line[40], RHS);
			strcat(&Line[40], "\r");
			Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Line);
		}
CMDS60:			
		L4++;
	}
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

extern int MasterPort[MAXBPQPORTS+1];	// Pointer to first BPQ port for a specific MPSK or UZ7HO host

VOID CMDP00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	// Process PORTS Message

	// If extended show state of TNC (Open, Active, etc)

	struct PORTCONTROL * PORT = PORTTABLE;
	char Extended = CmdTail[0];
	struct PORTCONTROL * SAVEPORT;

	Bufferptr = Cmdprintf(Session, Bufferptr, "Ports\r");

	while (PORT)
	{
		char Status[32] = "???????";
		int Portno = PORT->PORTNUMBER;

		if (PORT->Hide) 
		{
			PORT = PORT->PORTPOINTER;
			continue;
		}

		if (Extended != 'E')
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %s\r", PORT->PORTNUMBER, PORT->PORTDESCRIPTION);

			PORT = PORT->PORTPOINTER;
			continue;
		}

		// Try to get port status - may not be possible with some

		if (PORT->PortStopped)
		{
			strcpy(Status, "Stopped");
			Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %-7s %s\r", PORT->PORTNUMBER, Status, PORT->PORTDESCRIPTION);

			PORT = PORT->PORTPOINTER;
			continue;
		}

		if (PORT->PORTTYPE == 0)
		{
			struct KISSINFO * KISS = (struct KISSINFO *)PORT;
			NPASYINFO Port;

			SAVEPORT = PORT;

			if (KISS->FIRSTPORT && KISS->FIRSTPORT != KISS)
			{
				// Not first port on device

				PORT = (struct PORTCONTROL *)KISS->FIRSTPORT;
				Port = KISSInfo[Portno];
			}

			Port = KISSInfo[PORT->PORTNUMBER];

			if (Port)
			{
				// KISS like - see if connected 

				if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE)
				{
					// KISS over UDP or TCP

					if (PORT->KISSTCP)
					{
						if (Port->Connected)
							strcpy(Status, "Open  ");
						else
							if (PORT->KISSSLAVE)
								strcpy(Status, "Listen");
							else
								strcpy(Status, "Closed");
					}
					else
						strcpy(Status, "UDP");
				}
				else
					if (Port->idComDev)			// Serial port Open
						strcpy(Status, "Open  ");
					else
						strcpy(Status, "Closed");

				PORT = SAVEPORT;
			}		
		}
		else if (PORT->PORTTYPE == 14)		// Loopback 
			strcpy(Status, "Open  ");

		else if (PORT->PORTTYPE == 16)		// External
		{
			if (PORT->PROTOCOL == 10)		// 'HF' Port
			{
				struct TNCINFO * TNC = TNCInfo[Portno];

				if (TNC == NULL)
				{
					PORT = PORT->PORTPOINTER;
					continue;
				}

				switch (TNC->Hardware)				// Hardware Type
				{
				case H_SCS:
				case H_KAM:
				case H_AEA:
				case H_HAL:
				case H_TRK:
				case H_SERIAL:

					// Serial

					if (TNC->hDevice)
						strcpy(Status, "Open  ");
					else
						strcpy(Status, "Closed");

					break;

				case H_UZ7HO:

					if (TNCInfo[MasterPort[Portno]]->CONNECTED)
						strcpy(Status, "Open  ");
					else
						strcpy(Status, "Closed");

					break;

				case H_WINMOR:
				case H_V4:

				case H_MPSK:
				case H_FLDIGI:
				case H_UIARQ:
				case H_ARDOP:
				case H_VARA:
				case H_KISSHF:
				case H_WINRPR:
				case H_FREEDATA:

					// TCP

					if (TNC->CONNECTED)
					{
						if (TNC->Streams[0].Attached)
							strcpy(Status, "In Use");
						else
							strcpy(Status, "Open  ");
					}
					else
						strcpy(Status, "Closed");

					break;

				case H_TELNET:

					strcpy(Status, "Open  ");
				}
			}
			else
			{
				// External but not HF - AXIP, BPQETHER VKISS, ??

				struct _EXTPORTDATA * EXTPORT = (struct _EXTPORTDATA *)PORT;

				strcpy(Status, "Open  ");
			}
		}

			Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %-7s %s\r", PORT->PORTNUMBER, Status, PORT->PORTDESCRIPTION);

		PORT = PORT->PORTPOINTER;
	}

	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

char *  DisplayRoute(TRANSPORTENTRY * Session, char * Bufferptr, struct ROUTE * Routes, char Verbose)
{
	char Normcall[10];
	char locked[4] = "   ";
	int NodeCount;
	int Percent = 0;
	char PercentString[20];
	int Iframes, Retries;
	char Active[10];
	int Queued;

	int Port = 0;

	int len = ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall);

	Normcall[9]=0;

	if (Routes->NEIGHBOUR_FLAG == LOCKEDBYCONFIG)
		strcpy(locked, "!");
	else if (Routes->NEIGHBOUR_FLAG == LOCKEDBYSYSOP)
		strcpy(locked, "!!");
	else if (Routes->NEIGHBOUR_FLAG == LOCKEDBYSYSOP + LOCKEDBYCONFIG)
		strcpy(locked, "!!!");
	else
		strcpy(locked, " ");


	NodeCount = COUNTNODES(Routes);
				
	if (Routes->NEIGHBOUR_LINK && Routes->NEIGHBOUR_LINK->L2STATE >= 5)
		strcpy(Active, ">");
	else
		strcpy(Active, " ");

	if (Verbose)
	{
		if (Routes->NEIGHBOUR_LINK)					
			Queued = COUNT_AT_L2(Routes->NEIGHBOUR_LINK);			// SEE HOW MANY QUEUED
		else
			Queued = 0;

		Iframes = Routes->NBOUR_IFRAMES;
		Retries = Routes->NBOUR_RETRIES;

		if (Iframes)
		{
			Percent = (Retries * 100) / Iframes;
			sprintf(PercentString, "%3d%%", Percent);
		}
		else
			strcpy(PercentString, "    ");

		
		Bufferptr = Cmdprintf(Session, Bufferptr, "%s%2d %s %3d %3d%s%4d %4d %s %d %d %02d:%02d  %d %d",
							Active, Routes->NEIGHBOUR_PORT, Normcall, 
							Routes->NEIGHBOUR_QUAL,	NodeCount, locked, Iframes, Retries, PercentString, Routes->NBOUR_MAXFRAME, Routes->NBOUR_FRACK,
							Routes->NEIGHBOUR_TIME >> 8, (Routes->NEIGHBOUR_TIME) & 0xff, Queued, Routes->OtherendsRouteQual);

		//	IF INP3 DISPLAY SRTT

		if (Routes->INP3Node)		// INP3 Enabled?
		{
			double srtt = Routes->SRTT/100.0;
			double nsrtt = Routes->NeighbourSRTT/100.0;

			Bufferptr = Cmdprintf(Session, Bufferptr, " %4.2fs %4.2fs %X", srtt, nsrtt, Routes->Status);
		}

		if (Routes->TCPPort)
			Bufferptr = Cmdprintf(Session, Bufferptr, " %d", Routes->localport);


		Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
	}
	else
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%s %d %s %d %d%s\r",
			Active, Routes->NEIGHBOUR_PORT, Normcall, Routes->NEIGHBOUR_QUAL, NodeCount, locked);
	}

	return Bufferptr;
}


VOID CMDR00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	struct ROUTE * Routes = NEIGHBOURS;
	int MaxRoutes = MAXNEIGHBOURS;
	char locked[] = " ! ";
	int Percent = 0;
	char * ptr, * Context;
	char Verbose = 0;
	int Port = 0;
	char AXCALL[7];
	BOOL Found;
	int count, i, n = 0;
	struct ROUTE * List[1000];

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr && (int)strlen(ptr) > 1)
	{
		// Route Update

		goto ROUTEUPDATE;
	}

	if (ptr)
	{
		Verbose = ptr[0];
		ptr = strtok_s(NULL, " ", &Context);
		if (ptr)
			Port = atoi(ptr);
	}

	Bufferptr = Cmdprintf(Session, Bufferptr, "Routes\r");

	// Build and sort list of routes
				
	for (count = 0; count < MaxRoutes; count++)
	{
		if (Routes->NEIGHBOUR_CALL[0] != 0)
		{
			List[n++] = Routes;

			if (n > 999)
				break;
		}

		Routes++;
	}

	if (n > 1)
		qsort(List, n, sizeof(void *), CompareRoutes);

	for (i = 0; i < n; i++)
	{
		Routes = List[i];

		if (Port == 0 || Port == Routes->NEIGHBOUR_PORT)
			Bufferptr = DisplayRoute(Session, Bufferptr, Routes, Verbose);
	}
	goto SendReply;

ROUTEUPDATE:
	
	if (Session->PASSWORD  != 0xFFFF)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG);
		goto SendReply;
	}

	// Line is 

	// ROUTES G8BPQ-2 2 100	  - Set quality to 100
    // ROUTES G8BPQ-2 2 !         - Toggle 'Locked Route' flag
    // ROUTES G8BPQ-2 2 100 !     - Set quality and toggle 'locked' flag


	ConvToAX25(ptr, AXCALL);

	ptr = strtok_s(NULL, " ", &Context);

	if (ptr)
		Port = atoi(ptr);

	if (Port == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Port Number Missing \r");
		goto SendReply;
	}

	Found = FindNeighbour(AXCALL, Port, &Routes);

	if (Context && Context[0] > 32)
	{
		// More Params

		ptr = strtok_s(NULL, " ", &Context);

		if (ptr)
		{
			// Adding

			memcpy(Routes->NEIGHBOUR_CALL, AXCALL, 7);	// In case Add
			Routes->NEIGHBOUR_PORT = Port;
			Found = TRUE;
		}

		if (strcmp(ptr, "!") == 0)
		{
			// Toggle Lock

			Routes->NEIGHBOUR_FLAG ^= LOCKEDBYSYSOP;	// FLIP LOCKED BIT
			goto Displayit;
		}

		if (strcmp(ptr, "Z") == 0)
		{
			// Clear Counts

			Routes->NBOUR_IFRAMES = 0;
			Routes->NBOUR_RETRIES = 0;
			goto Displayit;
		}

		Routes->NEIGHBOUR_QUAL = atoi(ptr);

		if (Context && Context[0] == '!')
		{
			// Toggle Lock

			Routes->NEIGHBOUR_FLAG ^= LOCKEDBYSYSOP;	// FLIP LOCKED BIT
			goto Displayit;
		}
	}

Displayit:

	// Just display

	if (Found)
		Bufferptr = DisplayRoute(Session, Bufferptr, Routes, 1);
	else
		Bufferptr = Cmdprintf(Session, Bufferptr, "Not Found\r");

SendReply:

	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}


VOID LISTENCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	// PROCESS LISTEN COMMAND

	// for monitoring a remote ax.25 port

	int Port = 0, index =0;
	uint64_t ListenMask = 0;
	char * ptr, *Context;
	struct PORTCONTROL * PORT = NULL;
	char ListenPortList[128] = "";
	int len = 0;

	ptr = strtok_s(CmdTail, " ,", &Context);

	// Now accepts a list of ports

	if (ptr == 0 || memcmp(ptr, "OFF", 3) == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Listening disabled\r");
		Session->LISTEN = 0;
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	while (ptr)
	{
		Port = atoi(ptr);

		if (Port == 0 && NUMBEROFPORTS == 1)
			Port = 1;

		ptr = strtok_s(NULL, ", ", &Context);		// Get port String

		if (Port)
			PORT = GetPortTableEntryFromPortNum(Port);

		if (PORT == NULL)
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port %d\r", Port);
			continue;
		}

		if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0)
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port %d is not an ax.25 port\r", Port);
			continue;
		}

		if (PORT->PORTL3FLAG)
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port %d is for internode traffic only\r", Port);
			continue;
		}
	
		if (Session->L4CIRCUITTYPE == L2LINK + UPLINK)
		{
			if (Session->L4TARGET.LINK->LINKPORT->PORTNUMBER == Port)
			{
				Bufferptr = Cmdprintf(Session, Bufferptr, "You can't Listen to the port you are connected on\r");
				continue;
			}
		}

		len += sprintf(&ListenPortList[len], " %d", Port);

		ListenMask |= ((uint64_t)1 << (Port - 1));
	}

	Session->LISTEN = ListenMask;

	if (ListenMask)
	{
		if (CountBits64(ListenMask) == 1)
			Bufferptr = Cmdprintf(Session, Bufferptr, "Listening on port%s. Use CQ to send a beacon, LIS to disable\r", ListenPortList);
		else
			Bufferptr = Cmdprintf(Session, Bufferptr, "Listening on ports%s. Use LIS to disable\r", ListenPortList);
	}

	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}


VOID UNPROTOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	// PROCESS UNPROTO COMMAND

	int Port = 0, index =0;
	char * ptr, *Context;
	struct PORTCONTROL * PORT = NULL;
	UCHAR axcalls[64];
	BOOL Stay, Spy;
	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
		Port = atoi(ptr);

	if (Port == 0 && NUMBEROFPORTS == 1)
		Port = 1;
	else
		ptr = strtok_s(NULL, " ", &Context);		// Get Unproto String

	if (Port)
		PORT = GetPortTableEntryFromPortNum(Port);

	if (PORT == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (ptr == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Destination missing\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}
	ptr[strlen(ptr)] = ' ';				// Put param back together

	if (DecodeCallString(ptr, &Stay, &Spy, &axcalls[0]) == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (PORT->PORTL3FLAG)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for internode traffic only\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	//	Copy Address Info to Session Record

	Session->UNPROTO = Port;
	Session->UAddrLen = (int)strlen(axcalls);
	memcpy(Session->UADDRESS, axcalls, 63);

	Bufferptr = Cmdprintf(Session, Bufferptr, "Unproto Mode - enter ctrl/z or /ex to exit\r");
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}

VOID CALCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	// PROCESS CAL COMMAND

	int Port = 0, index = 0, Count = 0;
	char * ptr, *Context;
	struct PORTCONTROL * PORT = NULL;

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
		Port = atoi(ptr);

	if (Port == 0 && NUMBEROFPORTS == 1)
		Port = 1;
	else
		ptr = strtok_s(NULL, " ", &Context);		// Get Unproto String

	if (Port)
		PORT = GetPortTableEntryFromPortNum(Port);

	if (PORT == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (ptr == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Count Missing\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	Count = atoi(ptr);

	ptr = strtok_s(NULL, " ", &Context);		// Get Unproto String

	Bufferptr = Cmdprintf(Session, Bufferptr, "Ok\r");
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}



VOID CQCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	// Send a CQ Beacon on a radio port. Must be in LISTEN state

	DIGIMESSAGE Msg;
	int Port = 0;
	int OneBits = 0;
	uint64_t MaskCopy = Session->LISTEN;
	int Len;
	UCHAR CQCALL[7];
	char Empty[] = "";
	char * ptr1 = &OrigCmdBuffer[3];
	UCHAR * axptr = &Msg.DIGIS[0][0];
	char * ptr2, *Context;

	while (MaskCopy)
	{
		if (MaskCopy & 1)
			OneBits++;
			
		Port++;
		MaskCopy = MaskCopy >> 1;
	}

	if (OneBits == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "You must enter LISTEN before calling CQ\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (OneBits > 1)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "You can't call CQ if LISTENing on more than one port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}


	Len = (int)strlen(OrigCmdBuffer) - 3;

	if (Len < 0)
		Len = 0;

	memset(&Msg, 0, sizeof(Msg));

	Msg.PORT = Port;
	Msg.CTL = 3;			// UI

	// see if a Via specified

	if (_memicmp(ptr1, "via ", 4) == 0)
	{
		ptr2 = strtok_s(ptr1 + 4, ",", &Context);

		while (ptr2)
		{
			if (ConvToAX25(ptr2, axptr) == 0)
			{
				Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid via string\r");
				SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
				return;
			}

			axptr += 7;

			if (axptr == &Msg.DIGIS[7][0])
			{
				Bufferptr = Cmdprintf(Session, Bufferptr, "Too many digis\r");
				SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
				return;
			}
			ptr1 = ptr2;
			ptr2 = strtok_s(NULL, ",", &Context);
		}
		
		//	ptr1 is start of last digi call. We need to position to data

		ptr1 = strchr(ptr1, ' ');

		if (ptr1 == NULL)
			ptr1 = Empty;
		else
			ptr1++	;			// to message

		Len = (int)strlen(ptr1);

	}

	ConvToAX25("CQ", CQCALL);
	memcpy(Msg.DEST, CQCALL, 7);
	Msg.DEST[6] |= 0x80;			// set Command Bit
	memcpy(Msg.ORIGIN, Session->L4USER, 7);
	Msg.ORIGIN[6] ^= 0x1e;					// Flip SSID
	Msg.PID = 0xf0;							// Data PID
	memcpy(&Msg.L2DATA, ptr1, Len);
	
	Send_AX_Datagram(&Msg, Len + 2, Port);		// Len is Payload ie CTL, PID and Data

	Bufferptr = Cmdprintf(Session, Bufferptr, "CQ sent\r");
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;

}


TRANSPORTENTRY * SetupNewSession(TRANSPORTENTRY * Session, char * Bufferptr)
{
	TRANSPORTENTRY * NewSess = L4TABLE;
	int Index = 0;
	
	while (Index < MAXCIRCUITS)
	{
		if (NewSess->L4USER[0] == 0)
		{
			// Got One

			Session->L4CROSSLINK = NewSess;
			NewSess->L4CROSSLINK = Session;

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

			NewSess->CIRCUITINDEX = Index;				//OUR INDEX
			NewSess->CIRCUITID = NEXTID;

			NEXTID++;
			if (NEXTID == 0)
				NEXTID++;								// kEEP nON-ZERO

			NewSess->SESSIONT1 = Session->SESSIONT1;
			NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW;

			return NewSess;
		}
		Index++;
		NewSess++;
	}

	if (Bufferptr)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - System Tables Full\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	}
	
	return NULL;
}


VOID DoNetromConnect(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest, BOOL Spy, int Service)
{
	TRANSPORTENTRY * NewSess;
	
	NewSess = SetupNewSession(Session, Bufferptr);

	if (NewSess == NULL)
		return;						// Tables Full

	NewSess->Service = Service;

	NewSess->L4CIRCUITTYPE = SESSION + DOWNLINK;

	NewSess->L4TARGET.DEST = Dest;
	NewSess->L4STATE = 2;		// CONNECTING

	NewSess->SPYFLAG = Spy;

	if (Service == -1)
		ReleaseBuffer((UINT *)REPLYBUFFER);

	SENDL4CONNECT(NewSess, Service);

	L4CONNECTSOUT++;
	return;
}

BOOL CheckLink(UCHAR * LinkCall, UCHAR * OurCall, int Port)
{
	// Check if a link exists betwwn a pair of calls on a port. Return TRUE if found

	struct _LINKTABLE * LINK = LINKS;
	int n = MAXLINKS;

	while (n--)
	{
		if (LINK->LINKCALL[0] == 0)		// Spare
		{		
			LINK++;
			continue;
		}

		if ((LINK->LINKPORT->PORTNUMBER == Port) && CompareCalls(LINK->LINKCALL, LinkCall) && CompareCalls(LINK->OURCALL, OurCall))
			return TRUE;

		LINK++;
	}

	//	ENTRY NOT FOUND 

	return FALSE;
}

BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK)
{
	struct _LINKTABLE * LINK = LINKS;
	struct _LINKTABLE * FIRSTSPARE = NULL;
	int n = MAXLINKS;

	while (n--)
	{
		if (LINK->LINKCALL[0] == 0)		// Spare
		{
			if (FIRSTSPARE == NULL)
				FIRSTSPARE = LINK;
		
			LINK++;
			continue;
		}

		if ((LINK->LINKPORT->PORTNUMBER == Port) && CompareCalls(LINK->LINKCALL, LinkCall) && CompareCalls(LINK->OURCALL, OurCall))
		{
			*REQLINK = LINK;
			return TRUE;
		}

		LINK++;
	}

	//	ENTRY NOT FOUND - FIRSTSPARE HAS FIRST FREE ENTRY, OR ZERO IF TABLE FULL

	*REQLINK = FIRSTSPARE;
	return FALSE;
}

VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD);

VOID CMDC00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	//	PROCESS CONNECT COMMAND
	
	TRANSPORTENTRY * NewSess;

	int CONNECTPORT, Port;
	BOOL CallEvenIfInNodes = FALSE;
	char * ptr, *Context;
	UCHAR axcalls[64];
	UCHAR ourcall[7];					// Call we are using (may have SSID bits inverted
	int ret;
	struct PORTCONTROL * PORT = PORTTABLE;
	struct _LINKTABLE * LINK;
	int CQFLAG = 0;			// NOT CQ CALL
	BOOL Stay, Spy;
	int n;
	char TextCall[10];
	int TextCallLen;
	char PortString[10];
	char cmdCopy[256];
	struct _EXTPORTDATA * EXTPORT = (struct _EXTPORTDATA *)PORT;
	int Service = -1;
	int haveService = 0;
	int i = 0;

#ifdef EXCLUDEBITS

	if (CheckExcludeList(Session->L4USER) == FALSE)
	{
		//	CONNECTS FROM THIS STATION ARE NOT ALLOWED

		ReleaseBuffer((UINT *)REPLYBUFFER);
		return;
	}

#endif

	if (Session->LISTEN)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Can't connect while listening\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	CONNECTPORT = 0;			// NO PORT SPECIFIED

	ptr = strtok_s(CmdTail, " ", &Context);

	strcpy(cmdCopy, Context);	// Save in case Telnet Connect

	if (ptr == 0)
	{
		// No param

		if (CFLAG)				// C Command Disabled ?
		{
			// Convert to HOST (appl 32) command

		//MOV	_CMDPTR,OFFSET32 _HOSTCMD
		//MOV	_ALIASPTR,OFFSET32 _HOSTCMD + 32 * 31

		//MOV	_APPLMASK, 80000000H	; Internal Term

		}

		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	// See if NETROMX connect c service@node

	if (strchr(ptr, '@'))
	{
		// We now use service@node, unlike xr

		char * Context1;
		
		char * Cmd = strtok_s(ptr, "@", &Context1);
		char * Node = strtok_s(NULL, " ", &Context1);
		int i;
		int Stay = 0;

		if (Context1 && Context1[0] == 'S')
			Session->STAYFLAG = Stay;
				
		for (i = 0; i < NUMBEROFSSERVICES; i++)
		{
			if (strcmp(Cmd, SERVICES[i].ServiceName) == 0)
			{
				if (Node)
				{
					ConnecttoService(Session, Bufferptr, SERVICES[i].ServiceNo, Node, Stay);
					return;
				}
			}
		}

		// May be numeric service

		for (i = 0; i < strlen(Cmd); i++)
		{
			if (!isdigit(Cmd[i]))
				break;
		}

		if (i == strlen(Cmd))
		{
			if (Node)
			{
				ConnecttoService(Session, Bufferptr, atoi(Cmd), Node, Stay);
				return;
			}
		}

		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid NetromX command\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	Port = atoi(ptr);
	
	if (Port)
	{
		//	IF THERE IS NOTHING FOLLOWING THE NUMBER, ASSUME IT IS A
		//	NUMERIC ALIAS INSTEAD OF A PORT

		sprintf(PortString, "%d", Port);

		if (strlen(PortString) < (int)strlen(ptr))
			goto NoPort;

		PORT = GetPortTableEntryFromPortNum(Port);

		if (PORT == NULL)
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}

		EXTPORT = (struct _EXTPORTDATA *)PORT;

		ptr = strtok_s(NULL, " ", &Context);

		if (ptr == 0)
		{
			// No param

			Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r");
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}
		
		CONNECTPORT = Port;

		if (strcmp(ptr, "CMS") == 0 || strcmp(ptr, "HOST") == 0)	// In case someeone has CMS or HOST as an alias
			goto Downlink;
	
	}

NoPort:

	ptr[strlen(ptr)] = ' ';				// Put param back together

	if (ptr[0] == '!')
	{
		CallEvenIfInNodes = TRUE;
		ptr++;
	}

	if (memcmp(ptr, "RELAY ", 5) == 0 || memcmp(ptr, "SYNC ", 5) == 0)
	{
		// c p relay with extra parms

		goto Downlink;
	}

	// Skip call validation if using a ptc to allow 1:call, 2:call format

	if (Port && PORT->PROTOCOL == 10 && memcmp(EXTPORT->PORT_DLL_NAME, "SCSPACTOR", 9) == 0)
	{
		char * p;

		if (p = strstr(cmdCopy, " S "))
		{
			Stay = TRUE;
			p++;
			*p = ' ';
		}

		if (p = strstr(cmdCopy, " Z "))
		{
			Spy = TRUE;
			p++;
			*p = ' ';
		}
		
		goto Downlink;
	}
	else
	{
		if (DecodeCallString(ptr, &Stay, &Spy, &axcalls[0]) == 0)
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r");
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}
	}

	Session->STAYFLAG = Stay;

	TextCallLen = ConvFromAX25(axcalls, TextCall);

	if (CallEvenIfInNodes)
		goto Downlink;

	//	SEE IF CALL TO ANY OF OUR HOST SESSIONS - UNLESS DIGIS SPECIFIED

	if (axcalls[7] == 0)
	{
		//	If this connect is as a result of a command alias, don't check appls or we will loop

		if (ALIASINVOKED == 0)
		{
			APPLCALLS * APPL = APPLCALLTABLE;
			int n = NumberofAppls;
			APPLMASK = 1;

			while (n--)
			{
				if (memcmp(axcalls, APPL->APPLALIAS, 6) == 0 || CompareCalls(axcalls, APPL->APPLCALL))
				{
					// Call to an appl

					//	Convert to an APPL command, so any alias is actioned

					memcpy(Session->APPL,APPL->APPLCMD, 12);


					//	SEE IF THERE IS AN ALIAS DEFINDED FOR THIS COMMAND

					if (APPL->APPLHASALIAS && APPL->APPLALIASVAL[0] != 0x20)
					{
						//	COPY ALIAS TO COMMAND _BUFFER, THEN REENTER COMMAND HANDLER

						memcpy(COMMANDBUFFER, APPL->APPLALIASVAL, ALIASLEN);
						COMMANDBUFFER[80] = 0;
						_strupr(COMMANDBUFFER);
						memcpy(OrigCmdBuffer, APPL->APPLALIASVAL, ALIASLEN);	// In case original case version needed

						ALIASINVOKED = TRUE;			//	 To prevent Alias Loops 	
					}
					else
					{	
						
						// Copy Appl Command to Command Buffer. Ensure doesn't contain old command

						memset(COMMANDBUFFER, ' ', 72);
						memcpy(COMMANDBUFFER, APPL->APPLCMD, 12);
					}
					DoTheCommand(Session);
					return;
				}
				APPL++;
				APPLMASK <<= 1;
			}
		}
	}

	if (axcalls[7] == 0)
	{
		//	SEE IF CALL TO ANOTHER NODE 

		struct DEST_LIST * Dest = DESTS;
		int n = MAXDESTS;
	
		if (axcalls[6] == 0x60)		// if SSID, dont check aliases
		{	
			while (n--)
			{
				if (memcmp(Dest->DEST_ALIAS, TextCall, 6) == 0)	
				{
					DoNetromConnect(Session, Bufferptr, Dest, Spy, Service);
					return;
				}
				Dest++;
			}
		}

		Dest = DESTS;
		n = MAXDESTS;
			
		while (n--)
		{
			if (CompareCalls(Dest->DEST_CALL, axcalls))	
			{
				DoNetromConnect(Session, Bufferptr, Dest, Spy, Service);
				return;
			}
			Dest++;
		}
	}

	// Must be Downlink Connect

Downlink:

	if (CONNECTPORT == 0 && NUMBEROFPORTS > 1)
	{
		//	L2 NEEDS PORT NUMBER

		Bufferptr = Cmdprintf(Session, Bufferptr, "Downlink connect needs port number - C P CALLSIGN\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	//	ENSURE PORT IS AVAILABLE FOR L2 USE

	if (PORT->PROTOCOL >= 10)			// Pactor=-style port?
	{
		int count;

		// 	if Via PACTOR ARDOP WINMOR or VARA, convert to attach and call = Digi's are in AX25STRING (+7)

		if (memcmp(&axcalls[7], &WINMOR[0], 6) == 0 ||
			memcmp(&axcalls[7], &ARDOP[0], 6) == 0 ||
			memcmp(&axcalls[7], &VARA[0], 6) == 0 ||
			memcmp(&axcalls[7], &PACTORCALL[0], 6) == 0)
		{
			char newcmd[80];

			TextCall[TextCallLen] = 0;
			sprintf(newcmd, "%s %s", CmdTail, TextCall);

			ATTACHCMD(Session, Bufferptr, newcmd, NULL);
			return;
		}
	
		//	If on a KAM or SCS with ax.25 on port 2, do an Attach command, then pass on connect

		if (EXTPORT->MAXHOSTMODESESSIONS <= 1)
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r");
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}

		//	Only Allow Attach VHF from Secure Applications or if PERMITGATEWAY is set

		if (EXTPORT->PERMITGATEWAY == 0 && Session->Secure_Session == 0)
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, you are not allowed to use this port\r");
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}

		count = EXTPORT->MAXHOSTMODESESSIONS;
		count--;								// First is Pactor Stream, count is now last ax.25 session
	
		while (count)
		{
			if (EXTPORT->ATTACHEDSESSIONS[count] == 0)
			{
				int Paclen, PortPaclen;
				struct DATAMESSAGE * Buffer;
				struct DATAMESSAGE Message = {0};
				char Callstring[80];
				int len;
				
				//	Found a free one - use it

				//	See if TNC is OK

				Message.PORT = count;

				ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT);
			
				if ((ret & 0xff00) == 0)
				{
					Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r");
					SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
					return;
				}

				//	GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK
	
				NewSess = SetupNewSession(Session, Bufferptr);
				if (NewSess == NULL)
					return;

				// if a UZ7HO port, and the uplink is L2 or Uz7HO invert SSID bits

				// We only get here if multisession

				if (memcmp(EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0)
					goto noFlip;

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

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

					if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0)
						goto noFlip;
					else
						NewSess->L4USER[6] ^= 0x1e;		// UZ7HO Uplink - flip
				}
				else
					
					// Must be L2 uplink - flip
				
					NewSess->L4USER[6] ^= 0x1e;									// Flip SSID
noFlip:
				EXTPORT->ATTACHEDSESSIONS[count] = NewSess;

				NewSess->KAMSESSION = count;
				
				//	Set paclen to lower of incoming and outgoing

				Paclen = Session->SESSPACLEN;	// Incoming PACLEN

				if (Paclen == 0)
					Paclen = 256;				// 0 = 256

				PortPaclen = PORT->PORTPACLEN;

				if (PortPaclen == 0)
					PortPaclen = 256;				// 0 = 256

				if (PortPaclen < Paclen)
					Paclen = PortPaclen;

				NewSess->SESSPACLEN = Paclen;
				Session->SESSPACLEN = Paclen;

				NewSess->L4STATE = 5;
				NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR;
				NewSess->L4TARGET.PORT = PORT;

				// Send the connect command to the TNC

				Buffer = REPLYBUFFER;

				Buffer->PORT = count;
				Buffer->PID = 0xf0;

				// if on Telnet Port convert use original cmd tail

				// Why just on telnet - what not all ports?? 

				if (memcmp(EXTPORT->PORT_DLL_NAME, "TELNET", 6) == 0 || memcmp(EXTPORT->PORT_DLL_NAME, "SCSPACTOR", 9) == 0)
				{
					NewSess->Secure_Session = Session->Secure_Session;
					len = sprintf(Callstring,"C %s", cmdCopy);
				}
				else
				{
					TextCall[TextCallLen] = 0;

					len = sprintf(Callstring,"C %s", TextCall);

					if (axcalls[7])
					{
						int digi = 7;

						// we have digis

						len += sprintf(&Callstring[len], " via");

						while (axcalls[digi])
						{
							TextCall[ConvFromAX25(&axcalls[digi], TextCall)] = 0;
							len += sprintf(&Callstring[len], " %s", TextCall);
							digi += 7;
						}
					}
				}
				Callstring[len++] = 13;
				Callstring[len] = 0;
						
				Buffer->LENGTH = len + MSGHDDRLEN + 1;
				memcpy(Buffer->L2DATA, Callstring, len);
				C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer);

				return;
			}
			count--;
		}

		Bufferptr = Cmdprintf(Session, Bufferptr, "Error - No free streams on this port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if ((Session->L4CIRCUITTYPE & BPQHOST) == 0 && PORT->PORTL3FLAG)
	{
		//Port only for L3 
	
		Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for internode traffic only\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (PORT->PortUIONLY)
	{
		//Port only for UI
	
		Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for UI traffic only\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	ret = CheckKissInterlock(PORT, TRUE);

	if (ret)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, Interlocked port %d is in use\r", ret);
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	
	if (Session->L4USER[6] == 0x42 || Session->L4USER[6] == 0x44)
	{	
		Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - Can't make ax.25 calls with SSID of T or R\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	// Get Session Entry for Downlink

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

	NewSess->L4CIRCUITTYPE = L2LINK + DOWNLINK;
	
	//	FORMAT LINK TABLE ENTRY FOR THIS CONNECTION

	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, Port, &LINK);
	
	if (LINK == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - System Tables Full\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		
		// Should release NewSess

		return;
	}

	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 (CMD->String[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;

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

BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls)
{
	//	CONVERT CALL + OPTIONAL DIGI STRING TO AX25, RETURN 
	//	CONVERTED STRING IN AXCALLS. Return FALSE if invalied

	char * axptr = AXCalls;
	char * ptr, *Context;
	int CQFLAG = 0;					// NOT CQ CALL
	int n = 8;						// Max digis

	*Stay = 0;
	*Spy = 0;

	memset(AXCalls, 0, 64);

	ptr = strtok_s(Calls, " ,", &Context);

	if (ptr == NULL)
		return FALSE;

	// First field is Call

	if (ConvToAX25(ptr, axptr) == 0)
		return FALSE;

	axptr += 7;

	ptr = strtok_s(NULL, " ,", &Context);

	while (ptr && n--)
	{
		// NEXT FIELD = COULD BE CALLSIGN, VIA, OR S (FOR STAY)

		if (strcmp(ptr, "S") == 0)
			*Stay = TRUE;
		else if (strcmp(ptr, "Z") == 0)
			*Spy = TRUE;
		else if (memcmp(ptr, "VIA", (int)strlen(ptr)) == 0)
		{
		}	//skip via
		else					
		{
			// Convert next digi

			if (ConvToAX25(ptr, axptr) == 0)
				return FALSE;

			axptr += 7;
		}

		ptr = strtok_s(NULL, " ,", &Context);
	}

	return TRUE;
}


VOID LINKCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	// PROCESS *** LINKED to CALLSIGN

	char * ptr, *Context;
	UCHAR axcall[7];
	int ret;

	if (LINKEDFLAG == 'Y' ||			// UNCONDITIONAL?
		(LINKEDFLAG == 'A' && 
			((Session->L4CIRCUITTYPE & BPQHOST) || Session->Secure_Session || Session->PASSWORD == 0xffff)))
	{
		ptr = strtok_s(CmdTail, " ", &Context);
		if (ptr) 
			ptr = strtok_s(NULL, " ", &Context);
		
		if (ptr)
		{
			ret = ConvToAX25Ex(ptr, axcall);

			if (ret)
			{
				memcpy(Session->L4USER, axcall, 7);
				strcpy(Bufferptr, OKMSG);
				Bufferptr += (int)strlen(OKMSG);
				SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
				return;
			}
		}
	
		strcpy(Bufferptr, BADMSG);
		Bufferptr += (int)strlen(BADMSG);
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	memcpy(Bufferptr, PASSWORDMSG, LPASSMSG);
	Bufferptr += LPASSMSG;
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

char * DoOneNode(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest)
{
	char Normcall[10];
	char Alias[10];
	struct NR_DEST_ROUTE_ENTRY * NRRoute;
	struct INP3_DEST_ROUTE_ENTRY * Route;
	struct ROUTE * Neighbour;
	int i, Active, len;

	Alias[6] = 0;

	memcpy(Alias, Dest->DEST_ALIAS, 6);
	strlop(Alias, ' ');

	Normcall[ConvFromAX25(Dest->DEST_CALL, Normcall)] = 0;

	Bufferptr = Cmdprintf(Session, Bufferptr, "Routes to: %s:%s", Alias, Normcall);

	if (Dest->DEST_COUNT)
		Bufferptr = Cmdprintf(Session, Bufferptr, " RTT=%4.2f FR=%d %c %.1d\r",
			Dest->DEST_RTT /1000.0, Dest->DEST_COUNT, 
			(Dest->DEST_STATE & 0x40)? 'B':' ', (Dest->DEST_STATE & 63));
	else
		Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	NRRoute = &Dest->NRROUTE[0];

	Active = Dest->DEST_ROUTE;

	for (i = 1; i < 4; i++)
	{
		Neighbour = NRRoute->ROUT_NEIGHBOUR;

		if (Neighbour)
		{
			len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall);
			Normcall[len] = 0;

			Bufferptr = Cmdprintf(Session, Bufferptr, "%c %d %d %d %s\r",
				(Active == i)?'>':' ',NRRoute->ROUT_QUALITY, NRRoute->ROUT_OBSCOUNT, Neighbour->NEIGHBOUR_PORT, Normcall);
		}
		NRRoute++;
	}

	//	DISPLAY  INP3 ROUTES

	Route = &Dest->INP3ROUTE[0];

	Active = Dest->DEST_ROUTE;

	for (i = 1; i < 4; i++)
	{
		Neighbour = Route->ROUT_NEIGHBOUR;

		if (Neighbour)
		{
			double srtt = Route->STT/100.0;

			len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall);
			Normcall[len] = 0;

			Bufferptr = Cmdprintf(Session, Bufferptr, "%c %d %4.2fs %d %s\r",
				(Active == i + 3)?'>':' ',Route->Hops, srtt, Neighbour->NEIGHBOUR_PORT, Normcall);
		}
		Route++;
	}

	return Bufferptr;
}


int DoViaEntry(struct DEST_LIST * Dest, int n, char * line, int cursor)
{
	char Portcall[10];
	int len;

	if (Dest->NRROUTE[n].ROUT_NEIGHBOUR != 0)
	{
		len=ConvFromAX25(Dest->NRROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall);
		Portcall[len]=0;

		len=sprintf(&line[cursor],"%s %d %d ",
			Portcall,
			Dest->NRROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT,
			Dest->NRROUTE[n].ROUT_QUALITY);

		cursor+=len;

		if (Dest->NRROUTE[n].ROUT_OBSCOUNT > 127)
		{
			len=sprintf(&line[cursor],"! ");
			cursor+=len;
		}
	}
	return cursor;
}

int DoINP3ViaEntry(struct DEST_LIST * Dest, int n, char * line, int cursor)
{
	char Portcall[10];
	int len;
	double srtt;

	if (Dest->INP3ROUTE[n].ROUT_NEIGHBOUR != 0)
	{
		srtt = Dest->INP3ROUTE[n].STT/100.0;

		len=ConvFromAX25(Dest->INP3ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall);
		Portcall[len]=0;

		len=sprintf(&line[cursor],"%s %d %d %4.2fs ",
			Portcall,
			Dest->INP3ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT,
			Dest->INP3ROUTE[n].Hops, srtt);

		cursor+=len;

		if (Dest->NRROUTE[n].ROUT_OBSCOUNT > 127)
		{
			len=sprintf(&line[cursor],"! ");
			cursor+=len;
		}
	}
	return cursor;
}

int WildCmp(char * pattern, char * string)
{
	// Check if string is at end or not.
	
	if (*pattern == '\0')
		return *string == '\0';

	// Check for single character missing or match

	if (*pattern == '?' || *pattern == *string)
		return *string != '\0' && WildCmp(pattern + 1, string + 1);

	if (*pattern == '*')
	{
		// Check for multiple character missing

		return WildCmp(pattern + 1, string) || (*string != '\0' && WildCmp(pattern, string + 1));
	}

	return 0;
}

VOID CMDN00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	struct DEST_LIST * Dest = DESTS;
	int count = MAXDESTS, i;
	char Normcall[10];
	char Alias[10];
	int Width = 4;
	int x = 0, n = 0;
	struct DEST_LIST * List[1000];
	char Param = 0;
	char * ptr, * param2,* Context;
	char Nodeline[21];
	char AXCALL[7];
	char * Call;
	char * Qualptr;
	int Qual;
	char line[160];
	int cursor, len;
	UCHAR axcall[7];
	int SavedOBSINIT = OBSINIT;
	struct ROUTE * ROUTE = NULL;
	char Pattern[80] = "";
	char * firststar;
	int minqual = 0;

	ptr = strtok_s(CmdTail, " ", &Context);
	param2 = strtok_s(NULL, " ", &Context);

	if (ptr)
	{
		if (strcmp(ptr, "ADD") == 0)
			goto NODE_ADD;
	
		if (strcmp(ptr, "DEL") == 0)
			goto NODE_DEL;

		if (strcmp(ptr, "VIA") == 0)
			goto NODE_VIA;
	}
	
	if (ptr)
	{
		// Could be C or a pattern. Accept C pattern or pattern C

		if ((int)strlen(ptr) > 1)
		{
			strcpy(Pattern, ptr);
			if (param2 && param2[0] == 'C')
				Param = 'C';
		}
		else
		{
			Param = ptr[0];
			if (param2)
				strcpy(Pattern, param2);
		}
	}

	// Pattern >nnn selects nodes with at least that quality

	if (Pattern[0] == '>')
	{
		minqual = atoi(&Pattern[1]);
		Pattern[0] = 0;
	}

	// We need to pick out CALL or CALL* from other patterns (as call use detail display)

	firststar = strchr(Pattern, '*');

	if ((firststar && *(firststar + 1) != 0)|| strchr(Pattern, '?'))  //(* not on end)

		// definitely pattern

		goto DoNodePattern;

	// If it works as CALL*, process, else drop through 
	
	if (Pattern[0])
	{
		UCHAR AXCall[8];
		int count;
		int paramlen = (int)strlen(ptr);
		char parampadded[20];
		int n = 0;

		Alias[8] = 0;
		strcpy(parampadded, Pattern);
		strcat(parampadded, "     ");

		ConvToAX25(Pattern, AXCall);

		//	if * on end, list all ssids

		if (firststar)
		{
			AXCall[6] = 0;

			Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
		
			while (AXCall[6] < 32)
			{
				Dest = DESTS;

				for (count = 0; count < MAXDESTS; count++)
				{
					if (memcmp(Dest->DEST_ALIAS, parampadded, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall))
					{
						break;
					}
					Dest++;
				}

				if (count < MAXDESTS)
				{
					Bufferptr = DoOneNode(Session, Bufferptr, Dest);
					n++;
				}

				AXCall[6] += 2;
			}

			if (n)		// Found Some
			{
				SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
				return;
			}

			Dest = DESTS;				// Reset

			// Drop through to try as pattern
		}
		else
		{
			// process as just call

			for (count = 0; count < MAXDESTS; count++)
			{
				if (memcmp(Dest->DEST_ALIAS, parampadded, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall))
				{
					break;
				}
				Dest++;
			}

			if (count == MAXDESTS)
			{
				Bufferptr = Cmdprintf(Session, Bufferptr, "Not found\r");
				SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
				return;
			}

			Bufferptr = DoOneNode(Session, Bufferptr, Dest);

			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}
	}

DoNodePattern:

	Bufferptr = Cmdprintf(Session, Bufferptr, "Nodes\r");

	while (count--)
	{
		if (Dest->DEST_CALL[0] != 0)
		{
			if (Dest->NRROUTE->ROUT_QUALITY >= minqual)
				if (Param != 'T' || Dest->DEST_COUNT)
					List[n++] = Dest;

			if (n > 999)
				break;
		}
		Dest++;
	}

	if (Param == 'C') 
		qsort(List, n, sizeof(void *), CompareNode);
	else
		qsort(List, n, sizeof(void *), CompareAlias);
	

	for (i = 0; i < n; i++)
	{
		int len = ConvFromAX25(List[i]->DEST_CALL, Normcall);
		Normcall[len]=0;
		
		memcpy(Alias, List[i]->DEST_ALIAS, 6);
		Alias[6] = 0; 
		strlop(Alias, ' ');

		if (strlen(Alias))
			strcat(Alias, ":");

		if (Alias[0] == '#' && HIDENODES == 1 && Param != '*')	// Hidden Node and not N * command
			continue;

		if (Pattern[0])
			if (!WildCmp(Pattern, Normcall) && !WildCmp(Pattern, Alias))
				continue;

		if (Param == 'T')
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s RTT=%4.2f Frames = %d %c %.1d\r",
				Alias, Normcall, List[i]->DEST_RTT /1000.0, List[i]->DEST_COUNT, 
				(List[i]->DEST_STATE & 0x40)? 'B':' ', (List[i]->DEST_STATE & 63));
		}
		else
		{
			len = sprintf(Nodeline, "%s%s", Alias, Normcall);
			memset(&Nodeline[len], ' ', 20 - len);
			Nodeline[20] = 0;
			Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Nodeline);

			if (++x == Width)
			{
				x = 0;
				Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
			}
		}
	}

	if (x)
		Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	goto SendReply;


NODE_VIA:

	// List Nodes reachable via a neighbour

	ptr = param2;

	if (ptr == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Call\r");
		goto SendReply;
	}

	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	ConvToAX25(ptr, AXCALL);

	Dest = DESTS;

	Dest-=1;

	for (count=0; count<MAXDESTS; count++)
	{
		Dest+=1;

		if (Dest->NRROUTE[0].ROUT_NEIGHBOUR == 0 && Dest->INP3ROUTE[0].ROUT_NEIGHBOUR == 0)
			continue;


		if ((Dest->NRROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))
			|| (Dest->NRROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))
			|| (Dest->NRROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))

			|| (Dest->INP3ROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->INP3ROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))
			|| (Dest->INP3ROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->INP3ROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))
			|| (Dest->INP3ROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->INP3ROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)))
		{
			len=ConvFromAX25(Dest->DEST_CALL,Normcall);

			Normcall[len]=0;

			memcpy(Alias,Dest->DEST_ALIAS,6);

			Alias[6]=0;

			for (i=0;i<6;i++)
			{
				if (Alias[i] == ' ')
					Alias[i] = 0;
			}

			cursor=sprintf(line,"%s:%s ", Alias,Normcall);

			cursor = DoViaEntry(Dest, 0, line, cursor);
			cursor = DoViaEntry(Dest, 1, line, cursor);
			cursor = DoViaEntry(Dest, 2, line, cursor);
			cursor = DoINP3ViaEntry(Dest, 0, line, cursor);
			cursor = DoINP3ViaEntry(Dest, 1, line, cursor);
			cursor = DoINP3ViaEntry(Dest, 2, line, cursor);

			line[cursor++]='\r';
			line[cursor++]=0;

			Bufferptr = Cmdprintf(Session, Bufferptr, "%s", line);
		}
		}


	goto SendReply;

NODE_ADD:

	//	FORMAT IS NODE ADD ALIAS:CALL QUAL ROUTE PORT


	if (Session->PASSWORD  != 0xFFFF)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG);
		goto SendReply;
	}

	ptr = param2;

	if (ptr == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Alias:Call\r");
		goto SendReply;
	}

	Call = strlop(ptr, ':');

	if (Call == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Alias:Call\r");
		goto SendReply;
	}


	ConvToAX25(Call, AXCALL);

	Qualptr = strtok_s(NULL, " ", &Context);

	if (Qualptr == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Quality missing\r");
		goto SendReply;
	}

	Qual = atoi(Qualptr);

	if (Qual < MINQUAL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Quality is below MINQUAL\r");
		goto SendReply;
	}

	if (FindDestination(AXCALL, &Dest))
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Node already in Table\r");
		goto SendReply;
	}

	if (Dest == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Node Table Full\r");
		goto SendReply;
	}

	memcpy(Dest->DEST_CALL, AXCALL, 7);		
	memcpy(Dest->DEST_ALIAS, ptr, 6);

	NUMBEROFNODES++;

	ptr = strtok_s(NULL, " ", &Context);
	
	if (ptr == NULL || ptr[0] == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Neighbour missing\r");
		goto SendReply;
	}

	if (ConvToAX25(ptr, axcall) == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Neighbour\r");
		goto SendReply;
	}
	else
	{
		int Port;

		ptr = strtok_s(NULL, " ", &Context);
		if (ptr == NULL)
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "Port missing\r");
			goto SendReply;
		}

		Port = atoi(ptr);

		if (Context[0] == '!')
		{
			OBSINIT = 255;			//; SPECIAL FOR LOCKED
		}
		
		if (FindNeighbour(axcall, Port, &ROUTE))
		{
			PROCROUTES(Dest, ROUTE, Qual);
		}

		OBSINIT = SavedOBSINIT;

		Bufferptr = Cmdprintf(Session, Bufferptr, "Node Added\r");
		goto SendReply;
	}



	
/*
PNODE48:


;	GET NEIGHBOURS FOR THIS DESTINATION
;
	CALL	CONVTOAX25
	JNZ SHORT BADROUTE
;
	CALL	GETVALUE
	MOV	SAVEPORT,AL		; SET PORT FOR _FINDNEIGHBOUR

	CALL	GETVALUE
	MOV	ROUTEQUAL,AL
;
	MOV	ESI,OFFSET32 AX25CALL

	PUSH	EBX			; SAVE DEST
	CALL	_FINDNEIGHBOUR
	MOV	EAX,EBX			; ROUTE TO AX
	POP	EBX

	JZ SHORT NOTBADROUTE

	JMP SHORT BADROUTE

NOTBADROUTE:
;
;	UPDATE ROUTE LIST FOR THIS DEST
;
	MOV	ROUT1_NEIGHBOUR[EBX],EAX
	MOV	AL,ROUTEQUAL
	MOV	ROUT1_QUALITY[EBX],AL
	MOV	ROUT1_OBSCOUNT[EBX],255	; LOCKED
;
	POP	EDI
	POP	EBX
	
	INC	_NUMBEROFNODES

	JMP	SENDOK

BADROUTE:
;
;	KILL IT
;
	MOV	ECX,TYPE DEST_LIST
	MOV	EDI,EBX
	MOV	AL,0
	REP STOSB

	JMP	BADROUTECMD	

*/
	
	goto SendReply;


NODE_DEL:
	
	if (Session->PASSWORD  != 0xFFFF)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG);
		goto SendReply;
	}

	ptr = param2;

	if (ptr == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Call\r");
		goto SendReply;
	}

	if (strcmp(ptr, "ALL") == 0)
	{
		struct DEST_LIST * DEST = DESTS;
		int n = MAXDESTS;

		while (n--)
		{
			if (DEST->DEST_CALL[0] && ((DEST->DEST_STATE & 0x80) == 0))			// Don't delete appl node
					REMOVENODE(DEST);

			DEST++;
		}

		ClearNodes();

		Bufferptr = Cmdprintf(Session, Bufferptr, "All Nodes Deleted\r");
		goto SendReply;
	}

	ConvToAX25(ptr, AXCALL);

	if (FindDestination(AXCALL, &Dest) == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Not Found\r");
		goto SendReply;
	}

	if (Dest->DEST_STATE & 0x80)
		Bufferptr = Cmdprintf(Session, Bufferptr, "APPL Node - Can't delete\r");
	else
	{
		REMOVENODE(Dest);
		Bufferptr = Cmdprintf(Session, Bufferptr, "Node Deleted\r");
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "Node Deleted\r");

SendReply:

	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

VOID CMDQUERY(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD)
{
	//	DISPLAY AVAILABLE COMMANDS

	int n;
	char * ptr;
	char ApplList[2048];
	char * out = ApplList;

	struct CMDX * CMD = &COMMANDS[APPL1];

	for (n = 0; n < NumberofAppls; n++)
	{
		ptr = &CMD->String[0];
		if (*(ptr) != '*')
		{
			while (*ptr != ' ')
			{
				*(out++) = *(ptr++);
			}
			*(out++) = ' ';
		}
		CMD++;
	}

	*(out) = 0;

	n = CMDLISTLEN;

	if (NEEDMH == 0)
		n -= 7;					// Dont show MH

	Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s\r", ApplList, CMDLIST);

	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

char * FormatMH(MHSTRUC * MH, char Format);

VOID MHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	//	DISPLAY HEARD LIST
	
	int Port = 0, sess = 0;
	char * ptr, *Context, *pattern;
	struct PORTCONTROL * PORT = NULL;
	MHSTRUC * MH;
	int count = MHENTRIES;
	int n;
	char Normcall[20];
	char From[10];
	char DigiList[100];
	char * Output;
	int len;
	char Digi = 0;

	if (checkifService(Session, Bufferptr, CmdTail, CMD))		// See can be used remotely
		return;

	// Note that the MHDIGIS field may contain rubbish. You have to check End of Address bit to find
	// how many digis there are

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr == NULL  || ptr[0] == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Port Number needed eg MH 1\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (ptr)
		Port = atoi(ptr);

	if (Port)
		PORT = GetPortTableEntryFromPortNum(Port);

	if (PORT == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	pattern = strtok_s(NULL, " ", &Context);

	if (pattern)
		_strupr(pattern);			// Optional filter

	MH = PORT->PORTMHEARD;

	if (MH == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "MHEARD not enabled on that port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (pattern && strstr(pattern, "CLEAR"))
	{
		if (Session->Secure_Session)
		{
			memset(MH, 0, MHENTRIES * sizeof(MHSTRUC));
			SaveMH();
			Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d Cleared\r", Port);
		}
		else
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "MH Clear needs SYSOP status\r");
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}
	}
	else
	{
		if (CMD->String[2] == 'V' || CMD->String[3] == 'V')		// MHV
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "MHeard List %s for Port %d\r", MYNODECALL, Port);
			Bufferptr = Cmdprintf(Session, Bufferptr, "Callsign   Last heard     Pkts RX    via Digi ;) \r");
			Bufferptr = Cmdprintf(Session, Bufferptr, "---------  -----------    -------    ------------------------------------------\r");
		}
		else
			if (pattern)
				Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d filtered by %s\r", Port, pattern);
			else
				Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d\r", Port);
	}
	while (count--)
	{
		if (MH->MHCALL[0] == 0)
			break;
		
		Digi = 0;
		
		len = ConvFromAX25(MH->MHCALL, Normcall);

		Normcall[len++] = MH->MHDIGI;
		Normcall[len++] = 0;

		if (pattern && strstr(Normcall, pattern) == 0)
		{
			MH++;
			continue;
		}

		n = 8;					// Max number of digi-peaters

		ptr = &MH->MHCALL[6];	// End of Address bit

		Output = &DigiList[0];

		if ((*ptr & 1) == 0)
		{
			// at least one digi

			strcpy(Output, "via ");
			Output += 4;
		
			while ((*ptr & 1) == 0)
			{
				//	MORE TO COME
	
				From[ConvFromAX25(ptr + 1, From)] = 0;
				Output += sprintf((char *)Output, "%s", From);
	
				ptr += 7;
				n--;

				if (n == 0)
					break;

				// See if digi actioned - put a * on last actioned

				if (*ptr & 0x80)
				{
					if (*ptr & 1)						// if last address, must need *
					{
						*(Output++) = '*';
						Digi = '*';
					}

					else
						if ((ptr[7] & 0x80) == 0)		// Repeased by next?
						{
							*(Output++) = '*';			// No, so need *
							Digi = '*';
						}
					
}
				*(Output++) = ',';
			}		
			*(--Output) = 0;							// remove last comma
		}
		else 
			*(Output) = 0;

		// if we used a digi set * on call and display via string


		if (Digi)
			Normcall[len++] = Digi;
		else
			DigiList[0] = 0;	// Dont show list if not used

		Normcall[len++] = 0;


		ptr = FormatMH(MH, CMD->String[2]);
		
		if (CMD->String[2] == 'V')		// MHV
			Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s %-10s %-10d %-30s\r",
				Normcall, ptr, MH->MHCOUNT, DigiList);
		else
			Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s %s %s\r", Normcall, ptr, DigiList);

		MH++;
	}

	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

int Rig_Command(TRANSPORTENTRY * Session, char * Command);

VOID RADIOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD)
{
	char * ptr;
	
 	if (Rig_Command(Session, CmdTail))
	{
		ReleaseBuffer((UINT *)REPLYBUFFER);
		return;
	}
	
	// Error Message is in buffer

	ptr = strchr(CmdTail, 13);
	
	if (ptr)
	{
		int len = (int)(++ptr - CmdTail);
				
		memcpy(Bufferptr, CmdTail, len);
		Bufferptr += len;
	}
	
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}


VOID SendNRRecordRoute(struct DEST_LIST * DEST, TRANSPORTENTRY * Session);


VOID NRRCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD)
{
	//	PROCESS 'NRR - Netrom Record Route' COMMAND

	char * ptr, *Context;
	struct DEST_LIST * Dest = DESTS;
	int count = MAXDESTS;

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
	{	
		UCHAR AXCall[8];
		int count;

		ConvToAX25(ptr, AXCall);
		strcat(ptr, "      ");

		for (count = 0; count < MAXDESTS; count++)
		{
			if (memcmp(Dest->DEST_ALIAS, ptr, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall))
			{
				SendNRRecordRoute(Dest, Session);
				memcpy(Bufferptr, OKMSG, 3);
				Bufferptr += 3;
				SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	
				return;
			}
			Dest++;
		}
	}
	Bufferptr = Cmdprintf(Session, Bufferptr, "Not found\r");
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}

int CHECKINTERLOCK(struct PORTCONTROL * OURPORT)
{
	//	See if any Interlocked ports are Busy

	struct PORTCONTROL * PORT = PORTTABLE;
	struct _EXTPORTDATA * EXTPORT;
	
	int n = NUMBEROFPORTS;
	int ourgroup = OURPORT->PORTINTERLOCK;

	while (PORT)
	{
		if (PORT != OURPORT)
		{
			if (PORT->PORTINTERLOCK == ourgroup)
			{
				// Same Group - is it busy

				int i = 0;

				EXTPORT = (struct _EXTPORTDATA *)PORT;

				while (i < 27)
					if (EXTPORT->ATTACHEDSESSIONS[i++])
						return PORT->PORTNUMBER;
			}
		}
		PORT = PORT->PORTPOINTER;
	}

	return 0;
}

VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD)
{
	//	ATTACH to a PACTOR or similar port

	TRANSPORTENTRY * NewSess;
	struct _EXTPORTDATA * EXTPORT;
	struct TNCINFO * TNC = 0;
	
	int Port = 0, sess = 0;
	char * ptr, *Context;
	int ret;
	struct PORTCONTROL * PORT = NULL;
	struct DATAMESSAGE Message = {0};
	int Paclen, PortPaclen;
	struct DATAMESSAGE * Buffer;

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
		Port = atoi(ptr);

	if (Port)
		PORT = GetPortTableEntryFromPortNum(Port);

	if (PORT == NULL || PORT->PROTOCOL < 10)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	// If attach on telnet port, find a free stream

	EXTPORT = (struct _EXTPORTDATA *)PORT;

	if (strstr(EXTPORT->PORT_DLL_NAME, "TELNET"))
	{
		int count = EXTPORT->MAXHOSTMODESESSIONS;
		count--;								// First is Pactor Stream, count is now last ax.25 session
	
		while (count)
		{
			if (EXTPORT->ATTACHEDSESSIONS[count] == 0)
			{
				int Paclen, PortPaclen;
				struct DATAMESSAGE Message = {0};
				
				//	Found a free one - use it

				//	See if TNC is OK

				Message.PORT = count;

				ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT);
			
				if ((ret & 0xff00) == 0)
				{
					Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r");
					SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
					return;
				}

				//	GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK
	
				NewSess = SetupNewSession(Session, Bufferptr);

				if (NewSess == NULL)
					return;

				EXTPORT->ATTACHEDSESSIONS[count] = NewSess;

				NewSess->Secure_Session = Session->Secure_Session;

				NewSess->KAMSESSION = count;
				
				//	Set paclen to lower of incoming and outgoing

				Paclen = Session->SESSPACLEN;	// Incoming PACLEN

				if (Paclen == 0)
					Paclen = 256;				// 0 = 256

				PortPaclen = PORT->PORTPACLEN;

				if (PortPaclen == 0)
					PortPaclen = 256;				// 0 = 256

				if (PortPaclen < Paclen)
					Paclen = PortPaclen;

				NewSess->SESSPACLEN = Paclen;
				Session->SESSPACLEN = Paclen;

				NewSess->L4STATE = 5;
				NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR;
				NewSess->L4TARGET.PORT = PORT;

				ptr = strtok_s(NULL, " ", &Context);
				sess = count;

				// Replace command tail with original (before conversion to upper case

				Context = Context + (OrigCmdBuffer - COMMANDBUFFER);

				goto checkattachandcall;


				memcpy(Bufferptr, OKMSG, 3);
				Bufferptr += 3;
				SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));

				return;
			}
			count--;
		}

		Bufferptr = Cmdprintf(Session, Bufferptr, "Error - No free streams on this port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	Message.PORT = 0;

	ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT);
			
	if ((ret & 0xff00) == 0)
	{
 		Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	// See if "Attach and Call" (for VHF ports)

	ptr = strtok_s(NULL, " ", &Context);

	if (ptr && strcmp(ptr, "S") == 0)
	{
		Session->STAYFLAG = TRUE;
		ptr = strtok_s(NULL, " ", &Context);
	}

	if (ptr)
	{
		// we have another param

		// if it is a single char it is a channel number for vhf attach

		if (strlen(ptr) == 1)
		{
			//	Only Allow Attach VHF from Secure Applications or if PERMITGATEWAY is set

			if (EXTPORT->PERMITGATEWAY == 0 && Session->Secure_Session == 0)
			{
				Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, you are not allowed to use this port\r");
				SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
				return;
			}

			sess = ptr[0] - '@';

			if (sess < 1 || sess > EXTPORT->MAXHOSTMODESESSIONS)
			{
				Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Invalid Channel\r");
				SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
				return;
			}

			ptr = strtok_s(NULL, " ", &Context);

			if (ptr && strcmp(ptr, "S") == 0)
			{
				Session->STAYFLAG = TRUE;
				ptr = strtok_s(NULL, " ", &Context);
			}
		}
	}

	if (ret & 0x8000)			// Disconnecting
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port in use (Disconnecting)\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	// Check Interlock. Only ports with a TNC record can be interlocked

	TNC = PORT->TNC;

	if (TNC)
	{
		// See if any interlocked ports are in use

		struct TNCINFO * OtherTNC;
		int i;
		int rxInterlock = TNC->RXRadio;
		int txInterlock = TNC->TXRadio;

		if (rxInterlock || txInterlock)
		{
			for (i=1; i <= MAXBPQPORTS; i++)
			{
				OtherTNC = TNCInfo[i];

				if (OtherTNC == NULL)
					continue;

				if (OtherTNC == TNC)
					continue;

				if (rxInterlock && rxInterlock == OtherTNC->RXRadio || txInterlock && txInterlock == OtherTNC->TXRadio)	// Same Group	
				{
					int n;

					for (n = 0; n <= 26; n++)
					{
						if (OtherTNC->PortRecord->ATTACHEDSESSIONS[n])
						{			
							Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, interlocked port %d is in use\r", i);
							SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
							return;
						}
					}
				}
			}
		}
	}




	if (EXTPORT->ATTACHEDSESSIONS[sess])
	{
		// In use

		Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port in use (Session Attached\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (PORT->PortSuspended)
	{
		// In use

		Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port Suspended\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}


	//	GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK
	
	NewSess = SetupNewSession(Session, Bufferptr);

	if (NewSess == NULL)
		return;

	// if a UZ7HO port, and the uplink is L2 or Uz7HO and multisession,
	//	invert SSID bits

	if (memcmp(EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0)
		goto noFlip1;

	if (EXTPORT->MAXHOSTMODESESSIONS < 2)	// Not multisession	
		goto noFlip1;

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

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

		if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0)
			goto noFlip1;
		else
			NewSess->L4USER[6] ^= 0x1e;		// UZ7HO Uplink - flip
	}
	else
		
		// Must be L2 uplink - flip
				
		NewSess->L4USER[6] ^= 0x1e;									// Flip SSID
noFlip1:

	EXTPORT->ATTACHEDSESSIONS[sess] = NewSess;

	NewSess->KAMSESSION = sess;
				
	//	Set paclen to lower of incoming and outgoing

	Paclen = Session->SESSPACLEN;	// Incoming PACLEN

	if (Paclen == 0)
		Paclen = 256;				// 0 = 256

	PortPaclen = PORT->PORTPACLEN;

	if (PortPaclen == 0)
		PortPaclen = 256;				// 0 = 256

	if (PortPaclen < Paclen)
		Paclen = PortPaclen;

	NewSess->SESSPACLEN = Paclen;
	Session->SESSPACLEN = Paclen;
	NewSess->L4STATE = 5;
	NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR;
	NewSess->L4TARGET.PORT = PORT;

checkattachandcall:

	// If set freq on attach is defined, do it

	if (TNC && TNC->ActiveRXFreq && TNC->RXRadio)
	{
		char Msg[128];

		sprintf(Msg, "R%d %f", TNC->RXRadio, TNC->ActiveRXFreq);
		Rig_Command( (TRANSPORTENTRY *) -1, Msg);
	}

	if (TNC && TNC->ActiveTXFreq && TNC->TXRadio && TNC->TXRadio != TNC->RXRadio)
	{
		char Msg[128];

		sprintf(Msg, "R%d %f", TNC->TXRadio, TNC->ActiveTXFreq);
		Rig_Command( (TRANSPORTENTRY *) -1, Msg);
	}

	if (ptr)
	{
		// we have a call to connect to

		char Callstring[80];
		int len;

		Buffer = REPLYBUFFER;
		Buffer->PORT = sess;
		Buffer->PID = 0xf0;

		len = sprintf(Callstring,"C %s", ptr);

		ptr = strtok_s(NULL, " ", &Context);

		while (ptr)				// if any other params (such as digis) copy them
		{
			if (strcmp(ptr, "S") == 0)
			{
				Session->STAYFLAG = TRUE;
			}
			else
				len += sprintf(&Callstring[len], " %s", ptr);
			
			ptr = strtok_s(NULL, " ", &Context);
		}
	
		Callstring[len++] = 13;
		Callstring[len] = 0;
						
		Buffer->LENGTH = len + MSGHDDRLEN + 1;
		memcpy(Buffer->L2DATA, Callstring, len);
		C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer);
						
		return;
	}

	memcpy(Bufferptr, OKMSG, 3);
	Bufferptr += 3;
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));

	return;
}

//	SYSOP COMMANDS

struct CMDX COMMANDS[] =
{
//	"SAVENODES   ",8, SAVENODES(struct _TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD), 0,
	"SAVENODES   ",8, &SAVENODES, 0,
	"TELRECONFIG ",4, &RECONFIGTELNET, 0,
	"SAVEMH      ",6, &SAVEMHCMD, 0,
	"REBOOT      ",6, &REBOOT, 0,
	"RIGRECONFIG ",8, &RIGRECONFIG, 0,
	"RESTART     ",7, &RESTART,0,
	"RESTARTTNC  ",10,&RESTARTTNC,0,
	"POLLNODES   ",8, &POLLNODES,0,
	"SENDNODES   ",8, &SENDNODES,0,
	"SENDRIF     ",7, &SENDRIF,0,
	"EXTRESTART  ",10, EXTPORTVAL, offsetof(EXTPORTDATA, EXTRESTART),
	"TXDELAY     ",3, PORTVAL, offsetof(PORTCONTROLX, PORTTXDELAY),
	"MAXFRAME    ",3, PORTVAL, offsetof(PORTCONTROLX, PORTWINDOW),
	"RETRIES     ",3, PORTVAL, offsetof(PORTCONTROLX, PORTN2),
	"FRACK       ",3,PORTVAL, offsetof(PORTCONTROLX, PORTT1),
	"RESPTIME    ",3,PORTVAL, offsetof(PORTCONTROLX, PORTT2),
	"PPACLEN     ",3,PORTVAL, offsetof(PORTCONTROLX, PORTPACLEN),
	"QUALITY     ",3,PORTVAL, offsetof(PORTCONTROLX, PORTQUALITY),
	"PERSIST     ",2,PORTVAL, offsetof(PORTCONTROLX, PORTPERSISTANCE),
	"TXTAIL      ",3,PORTVAL, offsetof(PORTCONTROLX, PORTTAILTIME),
	"XMITOFF     ",7,PORTVAL, offsetof(PORTCONTROLX, PORTDISABLED),
	"DIGIFLAG    ",5,PORTVAL, offsetof(PORTCONTROLX, DIGIFLAG),
	"DIGIPORT    ",5,PORTVAL, offsetof(PORTCONTROLX, DIGIPORT),
	"MAXUSERS    ",4,PORTVAL, offsetof(PORTCONTROLX, USERS),
	"L3ONLY      ",6,PORTVAL, offsetof(PORTCONTROLX, PORTL3FLAG),
	"BBSALIAS    ",4,PORTVAL, offsetof(PORTCONTROLX, PORTBBSFLAG),
	"INP3ONLY    ",8,PORTVAL, offsetof(PORTCONTROLX, INP3ONLY),
	"ALLOWINP3   ",9,PORTVAL, offsetof(PORTCONTROLX, ALLOWINP3),
	"ENABLEINP3  ",10,PORTVAL, offsetof(PORTCONTROLX, ENABLEINP3),
	"MONTOFILE   ",9,SWITCHVAL,(size_t)&MONTOFILEFLAG,
	"VALIDCALLS  ",5,VALNODES,0,
	"WL2KSYSOP   ",5,WL2KSYSOP,0,
	"STOPPORT    ",4,STOPPORT,0,
	"STARTPORT   ",5,STARTPORT,0,
	"STOPCMS     ",7,STOPCMS,0,
	"STARTCMS    ",8,STARTCMS,0,
	"STOPROUTE   ",9,STOPROUTE,0,
	"STARTROUTE  ",10,STARTROUTE,0,

	"FINDBUFFS   ",4,FINDBUFFS,0,
	"KISS        ",4,KISSCMD,0,
	"LORA        ",4,LORACMD,0,
	"GETPORTCTEXT",9,GetPortCTEXT, 0,


#ifdef EXCLUDEBITS

	"EXCLUDE     ",4,ListExcludedCalls,0,

#endif

	"FULLDUP     ",4,PORTVAL, offsetof(PORTCONTROLX, FULLDUPLEX),
	"SOFTDCD     ",4,PORTVAL, offsetof(PORTCONTROLX, SOFTDCDFLAG),
	"OBSINIT     ",7,SWITCHVAL,(size_t)&OBSINIT,
	"OBSMIN      ",6,SWITCHVAL,(size_t)&OBSMIN,
	"NODESINT    ",8,SWITCHVAL,(size_t)&L3INTERVAL,
	"L3TTL       ",5,SWITCHVAL,(size_t)&L3LIVES,
	"L4RETRIES   ",5,SWITCHVAL,(size_t)&L4N2,
	"L4TIMEOUT   ",5,SWITCHVALW,(size_t)&L4T1,
	"T3          ",2,SWITCHVALW,(size_t)&T3,
	"NODEIDLETIME",8,SWITCHVALW,(size_t)&L4LIMIT,
	"LINKEDFLAG  ",10,SWITCHVAL,(size_t)&LINKEDFLAG,
	"IDINTERVAL  ",5,SWITCHVAL,(size_t)&IDINTERVAL,
	"MINQUAL     ",7,SWITCHVAL,(size_t)&MINQUAL,
	"FULLCTEXT   ",6,SWITCHVAL,(size_t)&FULL_CTEXT,
	"HIDENODES   ",8,SWITCHVAL,(size_t)&HIDENODES,
	"L4DELAY     ",7,SWITCHVAL,(size_t)&L4DELAY,
	"L4WINDOW    ",6,SWITCHVAL,(size_t)&L4DEFAULTWINDOW,
	"BTINTERVAL  ",5,SWITCHVAL,(size_t)&BTINTERVAL,
	"DEBUGINP3   ",8,SWITCHVAL,(size_t)&DEBUGINP3,
	"RIFINTERVAL ",11,SWITCHVALW,(size_t)&RIFInterval,
	"MAXHOPS     ",7,SWITCHVAL,(size_t)&MaxHops,
	"PREFERINP3  ",10,SWITCHVAL,(size_t)&PREFERINP3ROUTES,
	"MAXRTT      ",6,SWITCHVALW,(size_t)&MAXRTT,
	"MAXTT       ",6,SWITCHVALW,(size_t)&MAXRTT,
	"PASSWORD    ", 8, PWDCMD, 0,

	"************", 12, APPLCMD, 0,
	"************", 12, APPLCMD, 0,
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,		
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,		
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,	
	"************", 12, APPLCMD, 0,			// Apppl 32 is internal Terminal on Windows
	"*** LINKED  ",10,LINKCMD,0,
	"CQ          ",2,CQCMD,0,
	"CONNECT     ",1,CMDC00,0,
	"NC          ",2,CMDC00,0,
	"BYE         ",1,BYECMD,0,
	"QUIT        ",1,BYECMD,0,
	"INFO        ",1,CMDI00,0,
	"HELP        ",1,HELPCMD,0,
	"VERSION     ",1,CMDV00,0,
	"NODES       ",1,CMDN00,0,
	"LINKS       ",1,CMDL00,0,
	"LISTEN      ",3,LISTENCMD,0,
	"L4T1        ",2,CMDT00,0,
	"PORTS       ",1,CMDP00,0,
	"PACLEN      ",3,CMDPAC,0,
	"IDLETIME    ",4,CMDIDLE,0,
	"ROUTES      ",1,CMDR00,0,
	"STATS       ",1,CMDSTATS,0,
	"USERS       ",1,CMDS00,0,
	"STREAMS     ",6,CMDSTREAMS,0,
	"RHP         ",3,RHPCMD,0,
	"UNPROTO     ",2,UNPROTOCMD,0,
	"?           ",1,CMDQUERY,0,
	"DUMP        ",4,DUMPCMD,0,
	"MHU         ",3,MHCMD,0,		// UTC Times
	"MHL         ",3,MHCMD,0,		// Local Times
	"MHV         ",3,MHCMD,0,
	"MHUV        ",3,MHCMD,0,		// UTC Times
	"MHLV        ",3,MHCMD,0,		// Local Times
	"MHEARD      ",1,MHCMD,0,
	"APRS        ",2,APRSCMD,0,
	"ATTACH      ",1,ATTACHCMD,0,
	"RADIO       ",3,RADIOCMD,0,
	"AXRESOLVER  ",3,AXRESOLVER,0,
	"AXMHEARD    ",3,AXMHEARD,0,
	"TELSTATUS   ",3,SHOWTELNET,0,
	"NRR         ",1,NRRCMD,0,
	"PING        ",2,PING,0,
	"AGWSTATUS   ",3,SHOWAGW,0,
	"ARP         ",3,SHOWARP,0,
	"NAT         ",3,SHOWNAT,0,
	"IPROUTE     ",3,SHOWIPROUTE,0,
	"UZ7HO       ",5,UZ7HOCMD,0,
	"QTSM        ",4,QTSMCMD,0,

	"..FLMSG     ",7,FLMSG,0
};

struct CMDX * CMD = NULL;

int NUMBEROFCOMMANDS = sizeof(COMMANDS)/sizeof(struct CMDX);

char * ReplyPointer;			// Pointer into reply buffer

int DecodeNodeName(char * NodeName, char * ptr)
{
	// NodeName is TABLE ENTRY WITH AX25 CALL AND ALIAS

	// Copyies 20 byte 20 DECODED NAME IN FORM ALIAS:CALL to ptr
	// Returns significant length of string

	int len;
	char Normcall[10];
	char * alias = &NodeName[7];
	int n = 6;
	char * start = ptr;

	memset(ptr, ' ', 20);

	len = ConvFromAX25(NodeName, Normcall);

	if (*(alias) > ' ')		// Does alias start with a null or a space ?
	{
		while (*(alias) > ' ' && n--)
		{
			*ptr++ = *alias++;
		}
		*ptr++ = ':';
	}

	memcpy(ptr, Normcall, len);
	ptr += len;

	return (int)(ptr - start);
}

char * SetupNodeHeader(struct DATAMESSAGE * Buffer)
{
	char Header[20];
	int len;

	char * ptr = &Buffer->L2DATA[0];

	len = DecodeNodeName(MYCALLWITHALIAS, Header);

	memcpy (ptr, Header, len);
	ptr += len;

	(*ptr++) = HEADERCHAR;
	(*ptr++) = ' ';
	
	return ptr;
}

VOID SendCommandReply(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer, int Len)
{
	if (Len == (4 + sizeof(void *)))			// Null Packet
	{
		ReleaseBuffer((UINT *)Buffer);
		return;
	}

	Buffer->LENGTH = Len;

	C_Q_ADD(&Session->L4TX_Q, (UINT *)Buffer);

	PostDataAvailable(Session);
}


VOID CommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer)
{
	// ignore frames with single NULL (Keepalive)

	if (Buffer->LENGTH == sizeof(void *) + 5 && Buffer->L2DATA[0] == 0)
	{
		ReleaseBuffer(Buffer);
		return;
	}

	if (Buffer->LENGTH > 100)
	{
//		Debugprintf("BPQ32 command too long %s", Buffer->L2DATA);
		ReleaseBuffer(Buffer);
		return;
	}

InnerLoop:

	InnerCommandHandler(Session, Buffer);
	
//	See if any more commands in buffer

	if (Session->PARTCMDBUFFER)
	{	
		char * ptr1, * ptr2;
		int len;
		
		Buffer = Session->PARTCMDBUFFER;

		//	Check that message has a CR, if not save buffer and exit

		len = Buffer->LENGTH - (4 + sizeof(void *));
		ptr1 = &Buffer->L2DATA[0];

		ptr2 = memchr(ptr1, 13, len);
	
		if (ptr2 == NULL)
			return;

		Session->PARTCMDBUFFER = NULL;

		goto InnerLoop;
	}
}


VOID InnerCommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer)
{
	char * ptr1, * ptr2, *ptr3;
	int len, oldlen, newlen, rest, n;
	struct DATAMESSAGE * OldBuffer;
	struct DATAMESSAGE * SaveBuffer;
	char c;

	//	If a partial command is stored, append this data to it.
	
	if (Session->PARTCMDBUFFER)
	{
		len = Buffer->LENGTH - (sizeof(void *) + 4);
		ptr1 = &Buffer->L2DATA[0];
	
		OldBuffer = Session->PARTCMDBUFFER;			// Old Data

		if (OldBuffer == Buffer)
		{
			// something has gone horribly wrong

			Session->PARTCMDBUFFER = NULL;
			return;
		}

		oldlen = OldBuffer->LENGTH;

		newlen = len + oldlen;

		if (newlen > 200)
		{
			// Command far too long - ignore previous
	
			OldBuffer->LENGTH = oldlen = sizeof(void *) + 4;
		}

		OldBuffer->LENGTH += len;
		memcpy(&OldBuffer->L2DATA[oldlen - (sizeof(void *) + 4)], Buffer->L2DATA, len);
	
		ReleaseBuffer((UINT *)Buffer);
		
		Buffer = OldBuffer;
		
		Session->PARTCMDBUFFER = NULL;
	}

	//	Check that message has a CR, if not save buffer and exit

	len = Buffer->LENGTH - (sizeof(void *) + 4);
	ptr1 = &Buffer->L2DATA[0];

	// Check for sending YAPP to Node

	if (len == 2 && ptr1[0] == 5 && ptr1[1] == 1)
	{
		ptr1[0] = 0x15;				// NAK

		ptr1[1] = sprintf(&ptr1[2], "Node doesn't support YAPP Transfers");
				
		Buffer->LENGTH += ptr1[1];
		
		C_Q_ADD(&Session->L4TX_Q, (UINT *)Buffer);
		PostDataAvailable(Session);
		return;
	}


	ptr2 = memchr(ptr1, ';', len);
	
	if (ptr2 == 0)
	{
		ptr2 = memchr(ptr1, 13, len);
	
		if (ptr2 == 0)
		{
			//	No newline

			Session->PARTCMDBUFFER = Buffer;
			return;
		}
	}

	ptr2++;

	rest = len - (int)(ptr2 - ptr1);

	if (rest)
	{
		// there are chars beyond the cr in the buffer

		// see if LF after CR
	
		if ((*ptr2) == 10)			// LF
		{
			ptr2++;
			rest--;
		}

		if (rest)					// May only have had LF
		{
			//	Get a new buffer, and copy extra data to it.

			SaveBuffer = (struct DATAMESSAGE *)GetBuff();
	
			if (SaveBuffer)		//`Just ignore if no buffers
			{
				SaveBuffer->LENGTH = rest + MSGHDDRLEN + 1;
				SaveBuffer->PID = 0xf0;
				memcpy(&SaveBuffer->L2DATA[0], ptr2, rest);
				Session->PARTCMDBUFFER = SaveBuffer;
			}
		}
	}

	//	GET PACLEN FOR THIS CONNECTION

	CMDPACLEN = Session->SESSPACLEN;

	if (CMDPACLEN == 0)
		CMDPACLEN = PACLEN;		// Use default if no Session PACLEN

	// If sesion is in UNPROTO Mode, send message as a UI message

	if (Session->UNPROTO)
	{
//		char LongMsg[512] =
//			"VeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessage"
//			"VeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessage";

		DIGIMESSAGE Msg;
		int Port = Session->UNPROTO;
		int Len = Buffer->LENGTH - (MSGHDDRLEN -1);		// Need PID 

		//	First check for UNPROTO exit - ctrl/z or /ex

		if (Buffer->L2DATA[0] == 26 || (Len == 6 && _memicmp(&Buffer->L2DATA[0], "/ex", 3) == 0))		// CTRL/Z or /ex
		{
			REPLYBUFFER = Buffer;
	
			Session->UNPROTO = 0;
			memset(Session->UADDRESS, 0, 64);

			// SET UP HEADER

			Buffer->PID = 0xf0;
			ptr1 = SetupNodeHeader(Buffer);
			memcpy(ptr1, OKMSG, 3);
			ptr1 += 3;
			SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer));

			return;
		}
		
		memset(&Msg, 0, sizeof(Msg));

		Msg.PORT = Port;
		Msg.CTL = 3;			// UI
		memcpy(Msg.DEST, Session->UADDRESS, 7);
		Msg.DEST[6] |= 0x80;			// set Command Bit
		memcpy(Msg.ORIGIN, Session->L4USER, 7);
		memcpy(Msg.DIGIS, &Session->UADDRESS[7], Session->UAddrLen - 7);
		memcpy(&Msg.PID, &Buffer->PID, Len);
		Send_AX_Datagram(&Msg, Len, Port);		// Len is Payload - CTL, PID and Data
	
//		memcpy(&Msg.PID + 1, LongMsg, 260);
//		Send_AX_Datagram(&Msg, 241, Port);		// Len is Payload - CTL, PID and Data
	
	
//		SendUIModeFrame(Session, (PMESSAGE)Buffer, Session->UNPROTO);

		ReleaseBuffer((UINT *)Buffer);			// Not using buffer for reply

		// Assume we don't allow multiple lines in buffer with UI

		if (Session->PARTCMDBUFFER)
		{		
			Buffer = Session->PARTCMDBUFFER;
			ReleaseBuffer((UINT *)Buffer);			// Not using buffer for reply
			Session->PARTCMDBUFFER = NULL;
		}
		return;
	}

	memset(COMMANDBUFFER, 32, 80);		// Clear to spaces
	
	ptr1 = &Buffer->L2DATA[0];
	ptr2 = &COMMANDBUFFER[0];
	ptr3 = &OrigCmdBuffer[0];

	memset(OrigCmdBuffer, 0, 80);
	n = 80;
	
	while (n--)
	{
		c = *(ptr1++) & 0x7f;			// Mask paritu
		
		if (c == 13 || c == ';')
			break;						// CR
	
		*(ptr3++) = c;					// Original Case	
	
		c = toupper(c);
		*(ptr2++) = c;
	}

	
	//	USE INPUT MESSAGE _BUFFER FOR REPLY

	REPLYBUFFER = Buffer;

	// SET UP HEADER

	Buffer->PID = 0xf0;
	ptr1 = SetupNodeHeader(Buffer);

	ReplyPointer = ptr1;

	ALIASINVOKED = 0;				// Clear "Invoked by APPL ALIAS flag"

	DoTheCommand(Session);					// We also call DotheCommand when we need to reprocess - eg for alias handling
}

VOID DoTheCommand(TRANSPORTENTRY * Session)
{
	struct DATAMESSAGE * Buffer = REPLYBUFFER;
	char * ptr1, * ptr2;
	int n;
	int i, Service = -1;
	char * Cmd, *Node, *Context;
	int Stay = 0;

	ptr1 = &COMMANDBUFFER[0];		//

	n = 10;
	
	while ((*ptr1 == ' ' || *ptr1 == 0) && n--)
		ptr1++;						// STRIP LEADING SPACES and nulls (from keepalive)

	if (n == -1)
	{
		// Null command

		ReleaseBuffer((UINT *)Buffer);
		return;
	}

	ptr2 = ptr1;				// Save


	CMD = &COMMANDS[0];
	n = 0;
	
	for (n = 0; n < NUMBEROFCOMMANDS; n++)
	{
		int CL = CMD->CMDLEN;

		ptr1 = ptr2;

		CMDPTR = CMD;

		if (n == APPL1)		// First APPL command
		{
			APPLMASK = 1;			// FOR APPLICATION ATTACH REQUESTS
			ALIASPTR = &CMDALIAS[0][0];
		}

		// ptr1 is input command

		if (memcmp(CMD->String, ptr1, CL) == 0)
		{
			// Found match so far - check rest
		
			char * ptr2 = &CMD->String[CL];
			
			ptr1 += CL;

			if (*(ptr1) != ' ')
			{
				while(*(ptr1) == *ptr2 && *(ptr1) != ' ')
				{
					ptr1++;
					ptr2++;
				}
			}

			if (*(ptr1) == ' ')
			{
				Session->BADCOMMANDS = 0;			 // RESET ERROR COUNT
	
				// SEE IF SYSOP COMMAND, AND IF SO IF PASSWORD HAS BEEN ENTERED

				if (n < PASSCMD)
				{
					//NEEDS PASSWORD FOR SYSOP COMMANDS
					
					if (Session->PASSWORD  != 0xFFFF)
					{
						ptr1 = ReplyPointer;

						memcpy(ptr1, PASSWORDMSG, LPASSMSG);
						ptr1 += LPASSMSG;

						SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer));
						return;
					}
				}
//				VALNODESFLAG = 0;				//  NOT VALID NODES COMMAND

				ptr1++;						// Skip space

				CMD->CMDPROC(Session, ReplyPointer, ptr1, CMD);
				return;
			}
		}
		
		APPLMASK <<= 1;
		ALIASPTR += ALIASLEN;

		CMD++;
	
	}

	// See if a NETROMX Service

	// We now use service@node, unlike xr

	if (strchr(ptr1, '@'))
	{
		Cmd = strtok_s(ptr1, "@", &Context);
		Node = strtok_s(NULL, " ", &Context);

		if (Cmd && Node)
		{
			if (Context && Context[0] == 'S')
				Session->STAYFLAG = Stay;

			for (i = 0; i < NUMBEROFSSERVICES; i++)
			{
				if (strcmp(Cmd, SERVICES[i].ServiceName) == 0)
				{
					ConnecttoService(Session, ReplyPointer, SERVICES[i].ServiceNo, Node, Stay);
					return;
				}
			}

			// May be numeric service

			for (i = 0; i < strlen(Cmd); i++)
			{
				if (!isdigit(Cmd[i]))
					break;
			}

			if (i == strlen(Cmd))
			{
				ConnecttoService(Session, ReplyPointer, atoi(Cmd), Node, Stay);
				return;
			}
		}

		ReplyPointer = Cmdprintf(Session, ReplyPointer, "Invalid NetromX command\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(ReplyPointer - (char *)REPLYBUFFER));
		return;

	}
	Session->BADCOMMANDS++;

	if (Session->BADCOMMANDS > 6)			// TOO MANY ERRORS
	{
		ReleaseBuffer((UINT *)Buffer);
		Session->STAYFLAG = 0;
		CLOSECURRENTSESSION(Session);
		return;
	}

	ptr1 = ReplyPointer;

	memcpy(ptr1, CMDERRMSG, CMDERRLEN);
	ptr1 += CMDERRLEN;

	SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer));
	}


	VOID StatsTimer()
	{
		struct PORTCONTROL * PORT = PORTTABLE;
		uint64_t sum, sum2;

		// Interval is 60 secs

		while(PORT)
		{
			int index = PORT->StatsPointer++;

		if (index == 1439)
			PORT->StatsPointer = 0;		// Cyclic through 24 hours (1440 Mins)

		if (PORT->TNC)
		{
			struct TNCINFO * TNC = PORT->TNC;
			if (TNC->Hardware == H_ARDOP || TNC->Hardware == H_VARA)
			{
				sum = TNC->PTTActivemS / 600;		// ms but  want %
				PORT->AVSENDING = (UCHAR)sum;
				TNC->PTTActivemS = 0;

				sum2 = TNC->BusyActivemS / 600;		// ms but  want %
				PORT->AVACTIVE = (UCHAR)(sum + sum2);
				TNC->BusyActivemS = 0;
			}
		}
		else
		{
			// if KISS port using QtSM Average is already updated

			struct KISSINFO * KISS = (struct KISSINFO *)PORT;

			if (PORT->PORTNUMBER == 17)
			{
				int x = 17;
			}
			
			if ((void *)PORT->PORTTXROUTINE == (void *)KISSTX && (KISS->QtSMStats || KISS->FIRSTPORT->PORT.QtSMPort))			// KISS Port QtSM Stats
			{
			}
			else
			{
				sum = PORT->SENDING / 11;
				PORT->AVSENDING = (UCHAR)sum;

				sum = (PORT->SENDING + PORT->ACTIVE) /11;
				PORT->AVACTIVE = (UCHAR)sum;
			}
		}

		if (PORT->TX == NULL && PORT->AVACTIVE)
		{
			PORT->TX = zalloc(1440);	// Keep 1 day history
			PORT->BUSY = zalloc(1440);	
		}
		if (PORT->TX)
		{
			PORT->TX[index] = PORT->AVSENDING;
			PORT->BUSY[index] = PORT->AVACTIVE;
		}

		PORT->SENDING = 0;
		PORT->ACTIVE = 0;
	
		PORT = PORT->PORTPOINTER;
	}
}



extern struct AXIPPORTINFO * Portlist[];

#define TCPConnected 4


VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	//	DISPLAY AXIP Resolver info
	
	int Port = 0, index =0;
	char * ptr, *Context;
	struct PORTCONTROL * PORT = NULL;
	struct AXIPPORTINFO * AXPORT;
	char Normcall[11];
	char Flags[10];
	struct arp_table_entry * arp;

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
		Port = atoi(ptr);

	if (Port)
		PORT = GetPortTableEntryFromPortNum(Port);

	if (PORT == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	AXPORT = Portlist[Port];

	if (AXPORT == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Not an AXIP port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	Bufferptr = Cmdprintf(Session, Bufferptr, "AXIP Resolver info for Port %d\r", Port);

	while (index < AXPORT->arp_table_len)
	{
		arp = &AXPORT->arp_table[index];

		if (arp->ResolveFlag && arp->error != 0)
		{
				// resolver error - Display Error Code
			sprintf(AXPORT->hostaddr, "Error %d", arp->error);
		}
		else
		{
			if (arp->IPv6)	
				Format_Addr((unsigned char *)&arp->destaddr6.sin6_addr, AXPORT->hostaddr, TRUE);
			else
				Format_Addr((unsigned char *)&arp->destaddr.sin_addr, AXPORT->hostaddr, FALSE);
		}
				
		ConvFromAX25(arp->callsign, Normcall);

		Flags[0] = 0;
		
		if (arp->BCFlag)
			strcat(Flags, "B ");

		if (arp->TCPState == TCPConnected)
			strcat(Flags, "C ");

		if (arp->AutoAdded)
			strcat(Flags, "A");
								
		if (arp->port == arp->SourcePort)
			Bufferptr = Cmdprintf(Session, Bufferptr,"%.10s = %.64s %d = %-.42s %s\r",
				Normcall,
				arp->hostname,
				arp->port,
				AXPORT->hostaddr,
				Flags);

		else
			Bufferptr = Cmdprintf(Session, Bufferptr,"%.10s = %.64s %d<%d = %-.42s %s\r",
				Normcall,
				arp->hostname,
				arp->port,
				arp->SourcePort,
				AXPORT->hostaddr,
				Flags);

		index++;
	}
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

VOID AXMHEARD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	//	DISPLAY AXIP Mheard info
	
	int Port = 0, index = 0;
	char * ptr, *Context;
	struct PORTCONTROL * PORT = NULL;
	struct AXIPPORTINFO * AXPORT;
	int n = MHENTRIES;
	char Normcall[11];

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
		Port = atoi(ptr);

	if (Port)
		PORT = GetPortTableEntryFromPortNum(Port);

	if (PORT == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	AXPORT = Portlist[Port];

	if (AXPORT == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Not an AXIP port\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	Bufferptr = Cmdprintf(Session, Bufferptr, "AXIP Mheard for Port %d\r", Port);
	
	while (index < MaxMHEntries)
	{	
		if (AXPORT->MHTable[index].proto != 0)
		{
			char Addr[80];
				
			Format_Addr((unsigned char *)&AXPORT->MHTable[index].ipaddr6, Addr, AXPORT->MHTable[index].IPv6);

			Normcall[ConvFromAX25(AXPORT->MHTable[index].callsign, Normcall)] = 0;

			Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s%-15s %c %-6d %-25s%c\r", Normcall,
						Addr,
						AXPORT->MHTable[index].proto,
						AXPORT->MHTable[index].port,
						asctime(gmtime( &AXPORT->MHTable[index].LastHeard )),
						(AXPORT->MHTable[index].Keepalive == 0) ? ' ' : 'K');

			Bufferptr[-3] =  ' ';			// Clear CR returned by asctime
		}

		index++;
	}
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

#pragma pack()

extern char WL2KCall[10];
extern char WL2KLoc[7];

BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER);
BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL);

VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char _REPLYBUFFER[1000] = "";

	char LastUpdated[100];
	char Name[100] = "";
	char Addr1[100] = "";
	char Addr2[100] = "";
	char City[100] = "";
	char State[100] = "";
	char Country[100] = "";
	char PostCode[100] = "";
	char Email[100] = "";
	char Website[100] = "";
	char Phone[100] = "";
	char Data[100] = "";
	char LOC[100] = "";
	BOOL Exists = TRUE;
	time_t LastUpdateSecs = 0;
	char * ptr1, * ptr2;

	SOCKET sock;
			
	int Len;
	char Message[2048];

	if (WL2KCall[0] < 33)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Winlink reporting is not configured\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}


	if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER) == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Failed to connect to WL2K Database\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (strstr(_REPLYBUFFER, "\"ErrorMessage\":"))
		Exists = FALSE;

	GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Name);	
	GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Addr1);	
	GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Addr2);	
	GetJSONValue(_REPLYBUFFER, "\"City\":", City);	
	GetJSONValue(_REPLYBUFFER, "\"State\":", State);	
	GetJSONValue(_REPLYBUFFER, "\"Country\":", Country);	
	GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", PostCode);	
	GetJSONValue(_REPLYBUFFER, "\"Email\":", Email);	
	GetJSONValue(_REPLYBUFFER, "\"Website\":", Website);	
	GetJSONValue(_REPLYBUFFER, "\"Phones\":", Phone);	
	GetJSONValue(_REPLYBUFFER, "\"Comments\":", Data);	
	GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", LOC);	
	GetJSONValue(_REPLYBUFFER, "\"Timestamp\":", LastUpdated);	

	ptr1 = strchr(LastUpdated, '(');

	if (ptr1)
	{
		ptr2 = strchr(++ptr1, ')');

		if (ptr2)
		{
			*(ptr2 - 3) = 0; // remove millisecs
			LastUpdateSecs = atoi(ptr1);

			FormatTime3(LastUpdated, LastUpdateSecs);
		}
	}

	if (_memicmp(CmdTail, "SET ", 4) == 0)
	{
		if (Exists)
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "Record already exists in WL2K Database\r");
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}

	// Set New Values. Any other params are values to set, separated by |

//		ptr1 = strtok_s(&CmdTail[4], ",", &Context);

//		if (ptr1 == NULL)
//			goto DoReplace;

//		strcpy(Name, ptr1);

//DoReplace:

		Len = sprintf(Message,
				"\"Callsign\":\"%s\","
				"\"GridSquare\":\"%s\","
				"\"SysopName\":\"%s\","
				"\"StreetAddress1\":\"%s\","
				"\"StreetAddress2\":\"%s\","
				"\"City\":\"%s\","
				"\"State\":\"%s\","
				"\"Country\":\"%s\","
				"\"PostalCode\":\"%s\","
				"\"Email\":\"%s\","
				"\"Phones\":\"%s\","
				"\"Website\":\"%s\","
				"\"Comments\":\"%s\",",

			WL2KCall, WL2KLoc, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data);
		
		Debugprintf("Sending %s", Message);

		sock = OpenWL2KHTTPSock();

		if (sock)
			SendHTTPRequest(sock, "api.winlink.org", 80, 
				"/sysop/add", Message, Len, NULL);
	
		closesocket(sock);

		Bufferptr = Cmdprintf(Session, Bufferptr, "Database Updated\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (Exists)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "\rWL2K SYSOP Info for %s\r", WL2KCall);
		Bufferptr = Cmdprintf(Session, Bufferptr, "Grid Square: %s\r", LOC);
		Bufferptr = Cmdprintf(Session, Bufferptr, "Name: %s\r", Name);
		Bufferptr = Cmdprintf(Session, Bufferptr, "Addr Line 1: %s\r", Addr1);
		Bufferptr = Cmdprintf(Session, Bufferptr, "Addr Line 2: %s\r", Addr2);
		Bufferptr = Cmdprintf(Session, Bufferptr, "City: %s\r", City);
		Bufferptr = Cmdprintf(Session, Bufferptr, "State: %s\r", State);
		Bufferptr = Cmdprintf(Session, Bufferptr, "Country: %s\r", Country);
		Bufferptr = Cmdprintf(Session, Bufferptr, "PostCode: %s\r", PostCode);
		Bufferptr = Cmdprintf(Session, Bufferptr, "Email Address: %s\r", Email);
		Bufferptr = Cmdprintf(Session, Bufferptr, "Website: %s\r", Website);
		Bufferptr = Cmdprintf(Session, Bufferptr, "Phone: %s\r", Phone);
		Bufferptr = Cmdprintf(Session, Bufferptr, "Additional Data: %s\r", Data);
		Bufferptr = Cmdprintf(Session, Bufferptr, "Last Updated: %s\r", LastUpdated);
	}
	else
		Bufferptr = Cmdprintf(Session, Bufferptr, "No SYSOP record for %s\r", WL2KCall);


	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}

VOID CloseKISSPort(struct PORTCONTROL * PortVector);

VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char _REPLYBUFFER[1000] = "";
	char * ptr, * Context;

	int portno;

	struct TNCINFO * TNC;
	struct TCPINFO * TCP;
	struct PORTCONTROL * PORT = PORTTABLE;
	int n = NUMBEROFPORTS;

	// Get port number

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
	{
		portno = atoi (ptr);

		if (portno)
		{
			while (n--)
			{
				if (PORT->PORTNUMBER == portno)
				{
					TNC = TNCInfo[portno];

					if (!TNC || !TNC->TCPInfo)
					{
						Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet Port\r");
						SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
						return;
					}

					TCP = TNC->TCPInfo;
			
					TCP->CMS = 0;
					TCP->CMSOK = FALSE;
#ifndef LINBPQ
					CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3);
					SetWindowText(TCP->hCMSWnd, "CMS Off"); 
#endif
					Bufferptr = Cmdprintf(Session, Bufferptr, "CMS Server Disabled\r");
					SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
					return;
				}
				PORT = PORT->PORTPOINTER;
			}
		}
	}

	// Bad port

	strcpy(Bufferptr, BADPORT);
	Bufferptr += (int)strlen(BADPORT);
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}


VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char _REPLYBUFFER[1000] = "";
	char * ptr, * Context;

	int portno;

	struct TNCINFO * TNC;
	struct TCPINFO * TCP;
	struct PORTCONTROL * PORT = PORTTABLE;
	int n = NUMBEROFPORTS;

	// Get port number

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
	{
		portno = atoi (ptr);

		if (portno)
		{
			while (n--)
			{
				if (PORT->PORTNUMBER == portno)
				{
					TNC = TNCInfo[portno];

					if (!TNC || !TNC->TCPInfo)
					{
						Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet Port\r");
						SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
						return;
					}

					TCP = TNC->TCPInfo;
					TCP->CMS = 1;
#ifndef LINBPQ
					CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3);
#endif	
					CheckCMS(TNC);
	
					Bufferptr = Cmdprintf(Session, Bufferptr, "CMS Server Enabled\r");
					SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
					return;
				}
				PORT = PORT->PORTPOINTER;
			}
		}
	}

	// Bad port

	strcpy(Bufferptr, BADPORT);
	Bufferptr += (int)strlen(BADPORT);
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}


VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char _REPLYBUFFER[1000] = "";
	char * ptr, * Context;

	int portno;
	struct PORTCONTROL * PORT = PORTTABLE;
	int n = NUMBEROFPORTS;

	// Get port number

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
	{
		portno = atoi (ptr);

		if (portno)
		{
			while (n--)
			{
				if (PORT->PORTNUMBER == portno)
				{
					struct KISSINFO * KISS;

					if (PORT->PORTSTOPCODE)
					{
						// Port has Close Routine

						PORT->PortStopped = TRUE;

						if (PORT->PORTSTOPCODE(PORT))
							Bufferptr = Cmdprintf(Session, Bufferptr, "Port Closed\r");
						else
							Bufferptr = Cmdprintf(Session, Bufferptr, "Port Close Failed\r");

	
						SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
						return;
					}
					if (PORT->PORTTYPE != 0)
					{
						Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r");
						SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
						return;
					}

					if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE)
					{
						Bufferptr = Cmdprintf(Session, Bufferptr, "Not a serial port\r");
						SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
						return;
					}

					KISS = (struct KISSINFO *) PORT;

					if (KISS->FIRSTPORT != KISS)
					{
						Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r");
						SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
						return;
					}

					CloseKISSPort(PORT); 
					PORT->PortStopped = TRUE;
					Bufferptr = Cmdprintf(Session, Bufferptr, "Port Closed\r");
					SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));

					return;
				}
				PORT = PORT->PORTPOINTER;
			}
		}
	}

	// Bad port

	strcpy(Bufferptr, BADPORT);
	Bufferptr += (int)strlen(BADPORT);
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}


VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char _REPLYBUFFER[1000] = "";
	char * ptr, * Context;

	int portno;
	struct PORTCONTROL * PORT = PORTTABLE;
	int n = NUMBEROFPORTS;

	// Get port number

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
	{
		portno = atoi (ptr);

		if (portno)
		{
			while (n--)
			{
				if (PORT->PORTNUMBER == portno)
				{
					struct KISSINFO * KISS;

					if (PORT->PORTSTARTCODE)
					{
						// Port has Open Routine

						PORT->PortStopped = FALSE;

						if (PORT->PORTSTARTCODE(PORT))
							Bufferptr = Cmdprintf(Session, Bufferptr, "Port Opened\r");
						else
							Bufferptr = Cmdprintf(Session, Bufferptr, "Port Open Failed\r");

						SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
						return;
					}


					if (PORT->PORTTYPE != 0)
					{
						Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r");
						SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
						return;
					}

					if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE)
					{
						Bufferptr = Cmdprintf(Session, Bufferptr, "Not a serial port\r");
						SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
						return;
					}

					KISS = (struct KISSINFO *) PORT;

					if (KISS->FIRSTPORT != KISS)
					{
						Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r");
						SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
						return;
					}

					if (OpenConnection(PORT))
						Bufferptr = Cmdprintf(Session, Bufferptr, "Port Opened\r");
					else
						Bufferptr = Cmdprintf(Session, Bufferptr, "Port Open Failed\r");

					PORT->PortStopped = FALSE;
	
					SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
					return;
				}
				PORT = PORT->PORTPOINTER;
			}
		}
	}

	// Bad port

	strcpy(Bufferptr, BADPORT);
	Bufferptr += (int)strlen(BADPORT);
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}

BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE);
VOID DecayNETROMRoutes(struct ROUTE * Route);
VOID DeleteINP3Routes(struct ROUTE * Route);
void NETROMCloseTCP(struct ROUTE * Route);

VOID STOPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char _REPLYBUFFER[1000] = "";
	char * ptr, * Context, * Call;
	int portno;
	unsigned char Neighbour[7];
	struct ROUTE * Route;

	// STOPROUTE PORT CALL

	// Get port number and call 

	ptr = strtok_s(CmdTail, " ", &Context);
	Call = strtok_s(NULL, " \r", &Context);

	if (ptr && Call && Call[0])
	{
		portno = atoi (ptr);
		ConvToAX25(Call, Neighbour);
	}
	else
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Params - Should be STOPROUTE PORT CALL\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (FindNeighbour(Neighbour, portno, &Route) == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Route not found\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (Route->NEIGHBOUR_LINK == 0 || Route->NEIGHBOUR_LINK->LINKPORT == 0)
	{
		// Not active

		Bufferptr = Cmdprintf(Session, Bufferptr, "Route not active\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	Route->Stopped = 1;
	DecayNETROMRoutes(Route);
	DeleteINP3Routes(Route);

	Route->Status = 0;	// Down

	// close the link

	if (Route->TCPPort == 0)	// NetromTCP doesn't have a real link
	{
		Route->NEIGHBOUR_LINK->KILLTIMER = 0;
		Route->NEIGHBOUR_LINK->L2TIMER = 1;		// TO FORCE DISC
		Route->NEIGHBOUR_LINK->L2STATE = 4;		// DISCONNECTING
	}
	else
	{
		// but we should reset the TCP connection

		NETROMCloseTCP(Route);
	}


	Route->BCTimer = 999999;		// Wait a while before retrying

	Bufferptr = Cmdprintf(Session, Bufferptr, "Route Closed\r");
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}

VOID STARTROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char _REPLYBUFFER[1000] = "";
	char * ptr, * Context, * Call;
	int portno;
	unsigned char Neighbour[7];
	struct ROUTE * Route;

	// STARTROUTE PORT CALL

	// Get port number and call 

	ptr = strtok_s(CmdTail, " ", &Context);
	Call = strtok_s(NULL, " \r", &Context);

	if (ptr && Call && Call[0])
	{
		portno = atoi (ptr);
		ConvToAX25(Call, Neighbour);
	}
	else
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Params - Should be STARTROUTE PORT CALL\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (FindNeighbour(Neighbour, portno, &Route) == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Route not found\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (Route->Stopped == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Route not stopped\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	Route->Stopped = 0;
	Route->BCTimer = 0;	

	Bufferptr = Cmdprintf(Session, Bufferptr, "Route Enabled\r");
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}



int ASYSEND(struct PORTCONTROL * PortVector, char * buffer, int count);
int	KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len);

VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char _REPLYBUFFER[1000] = "";
	char * ptr, * Context;

	int portno = 0;
	struct PORTCONTROL * PORT = PORTTABLE;
	int n = NUMBEROFPORTS;
	UCHAR KissString[128];
	UCHAR ENCBUFF[256];
	int KissLen = 0;
	unsigned char * Kissptr = KissString;

	// Send KISS Command to TNC

	// Get port number

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
	{
		portno = atoi (ptr);
		ptr = strtok_s(NULL, " ", &Context);

		while (ptr && ptr[0] && KissLen < 120)
		{
			*(Kissptr++) = atoi (ptr);
			KissLen++;
			ptr = strtok_s(NULL, " ", &Context);

		}
	}

	if (portno == 0 || KissLen == 0)
	{
		strcpy(Bufferptr, BADMSG);
		Bufferptr += (int)strlen(BADMSG);
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	while (n--)
	{
		if (PORT->PORTNUMBER == portno)
		{
			struct KISSINFO * KISS;

			if (PORT->PORTTYPE != 0 && PORT->PORTTYPE != 22)
			{
				Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r");
				SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
				return;
			}

			KISS = (struct KISSINFO *) PORT;

			// Set Channel bits

			KissString[0] |= KISS->OURCTRL;

			// Send Command

			PORT->Session = Session;
			PORT->LastKISSCmdTime = time(NULL);

			PORT = (struct PORTCONTROL *)KISS->FIRSTPORT;			// ALL FRAMES GO ON SAME Q
			KissLen = KissEncode(KissString, ENCBUFF, KissLen);
			ASYSEND(PORT, ENCBUFF, KissLen);

			Bufferptr = Cmdprintf(Session, Bufferptr, "Command Sent\r");
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}
		PORT = PORT->PORTPOINTER;
	}


	// Bad port

	strcpy(Bufferptr, BADPORT);
	Bufferptr += (int)strlen(BADPORT);
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}

VOID LORACMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char _REPLYBUFFER[1000] = "";
	char * ptr, * Context;

	int portno = 0;
	struct PORTCONTROL * PORT = PORTTABLE;
	int n = NUMBEROFPORTS;
	UCHAR KissString[128];
	UCHAR ENCBUFF[256];
	int KissLen = 0;
	unsigned char * Kissptr = KissString;
	int Param[4];
	int i = 0;

	// Send CONFIG Command to LORA TNC

	// Get port number

	ptr = strtok_s(CmdTail, " ", &Context);

	if (ptr)
	{
		portno = atoi(ptr);
		ptr = strtok_s(NULL, " ", &Context);

		while (ptr && ptr[0] && KissLen < 120)
		{
			Param[i++] = atoi(ptr);
			ptr = strtok_s(NULL, " ", &Context);
		}
	}

	if (portno == 0 || i != 4)
	{
		char ErrMsg[] = "Invalid params - format is LORA port freq bandwidth  spreading factor coding rate";
		strcpy(Bufferptr, ErrMsg);
		Bufferptr += (int)strlen(ErrMsg);
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}


	while (n--)
	{
		if (PORT->PORTNUMBER == portno)
		{
			KissLen = sprintf(KissString, "%cLORA=%d, %d, %d, %d", 6, Param[0], Param[1], Param[2], Param[3]);
			KissLen = KissEncode(KissString, ENCBUFF, KissLen);

			PORT->Session = Session;
			PORT->LastKISSCmdTime = time(NULL);

			ASYSEND(PORT, ENCBUFF, KissLen);

			Bufferptr = Cmdprintf(Session, Bufferptr, "Command Sent\r");
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}

		PORT = PORT->PORTPOINTER;
	}

	// Bad port

	strcpy(Bufferptr, BADPORT);
	Bufferptr += (int)strlen(BADPORT);
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;

}



VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	FindLostBuffers();

#ifdef WIN32
	Bufferptr = Cmdprintf(Session, Bufferptr, "Lost buffer info dumped to Debugview\r");
#else
	Bufferptr = Cmdprintf(Session, Bufferptr, "Lost buffer info dumped to syslog\r");
#endif
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

VOID FLMSG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD)
{
	// Telnet Connection from FLMSG
	CLOSECURRENTSESSION(Session);		// Kills any crosslink, plus local link
	ReleaseBuffer((UINT *)REPLYBUFFER);
}

BOOL CheckExcludeList(UCHAR * Call)
{
	UCHAR * ptr1 = ExcludeList;

	while (*ptr1)
	{
		if (memcmp(Call, ptr1, 6) == 0)
			return FALSE;

		ptr1 += 7;
	}

	return TRUE;
}


void ListExcludedCalls(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{

	UCHAR * ptr = ExcludeList;
	char Normcall[10] = "";
	UCHAR AXCall[8] = "";

	if (*CmdTail == ' ')
		goto DISPLIST;

	if (*CmdTail == 'Z')
	{
		// CLEAR LIST

		memset(ExcludeList, 0, 70);
		goto DISPLIST;
	}

	ConvToAX25(CmdTail, AXCall);

	if (strlen(ExcludeList) < 70)
		strcat(ExcludeList, AXCall);
	
DISPLIST:

	while (*ptr)
	{
		Normcall[ConvFromAX25(ptr, Normcall)] = 0;
		Bufferptr = Cmdprintf(Session, Bufferptr, "%s ", Normcall);
		ptr += 7;
	}

	*(Bufferptr++) = '\r';
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

BOOL isSYSOP(TRANSPORTENTRY * Session, char * Bufferptr)
{
	if (Session->PASSWORD  != 0xFFFF)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG);
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));

		return FALSE;
	}

	return TRUE;
}

VOID HELPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	int FileSize;
	char MsgFile[MAX_PATH];
	FILE * hFile;
	char * MsgBytes;
	struct stat STAT;
	char * ptr1, * ptr, * ptr2;
 
	sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s", BPQDirectory, "NodeHelp.txt");

	if (stat(MsgFile, &STAT) == -1)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Help file not found\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	FileSize = STAT.st_size;

	hFile = fopen(MsgFile, "rb");

	if (hFile == NULL)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Help file not found\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	MsgBytes = malloc(FileSize+1);

	fread(MsgBytes, 1, FileSize, hFile); 

	fclose(hFile);

	MsgBytes[FileSize] = 0;

	ptr1 = MsgBytes;

	// Replace LF or CRLF with CR

	// First remove cr from crlf

	while(ptr2 = strstr(ptr1, "\r\n"))
	{
		memmove(ptr2, ptr2 + 1, strlen(ptr2));
	}

	// Now replace lf with cr

	ptr1 = MsgBytes;

	while (*ptr1)
	{
		if (*ptr1 == '\n')
			*(ptr1) = '\r';

		ptr1++;
	}
	
	ptr = ptr1 = MsgBytes;

	Bufferptr = Cmdprintf(Session, Bufferptr, "\r");

	// Read and send a line at a time, converting any line endings into CR

	while (*ptr1)
	{
		if (*ptr1 == '\r')
		{
			*(ptr1++) = 0;

			Bufferptr = Cmdprintf(Session, Bufferptr, "%s\r", ptr);

			ptr = ptr1;
		}
		else
			ptr1++;
	}

	free(MsgBytes);
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}

int UZ7HOSetFreq(int port, struct TNCINFO * TNC, struct AGWINFO * AGW, PDATAMESSAGE buff, PMSGWITHLEN buffptr);
int UZ7HOSetModem(int port, struct TNCINFO * TNC, struct AGWINFO * AGW, PDATAMESSAGE buff, PMSGWITHLEN buffptr);
int UZ7HOSetFlags(int port, struct TNCINFO * TNC, struct AGWINFO * AGW, PDATAMESSAGE buff, PMSGWITHLEN buffptr);


VOID UZ7HOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	char * Cmd;
	int port;
	struct TNCINFO * TNC;
	struct AGWINFO * AGW = 0;
	PDATAMESSAGE buff;
	PMSGWITHLEN buffptr;

	CmdTail = CmdTail + (OrigCmdBuffer - COMMANDBUFFER); // Replace with original case version

	Cmd = strlop(CmdTail, ' ');
	port = atoi(CmdTail);

	if (Cmd == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Missing params - usage is UZ7HO port Command\r", port);
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	// remove trailing spaces 

	while(strlen(Cmd) && Cmd[strlen(Cmd) - 1] == ' ')
		Cmd[strlen(Cmd) - 1] = 0;

	TNC = TNCInfo[port];

	if (TNC)
		AGW = TNC->AGWInfo; 

	if (TNC == 0 || AGW == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Error - %d is not UZ7HO port\r", port);
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (_memicmp(Cmd, "FREQ", 4) == 0 || _memicmp(Cmd, "MODEM", 5) == 0 || _memicmp(Cmd, "FLAGS", 5) == 0)
	{
		// Pass to procesing code in UZ7HO driver. This expects command in a PDATAMESSAGE amd places response in a PMSGWITHLEN buffer

		buff = (PDATAMESSAGE)GetBuff();
		buffptr = (PMSGWITHLEN)GetBuff();

		if (buffptr == 0)
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "UZ7HO Command Failed - no buffers\r");
			if (buff)
				ReleaseBuffer(buff);
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}



		buff->LENGTH = sprintf(buff->L2DATA, "%s\r", Cmd) + MSGHDDRLEN + 1;

		if (_memicmp(Cmd, "FREQ", 4) == 0)
			UZ7HOSetFreq(port, TNC, AGW, buff, buffptr);
		else if (_memicmp(Cmd, "FLAGS", 5) == 0)
			UZ7HOSetFlags(port, TNC, AGW, buff, buffptr);
		else
			UZ7HOSetModem(port, TNC, AGW, buff, buffptr);


		Bufferptr = Cmdprintf(Session, Bufferptr, buffptr->Data);

		ReleaseBuffer(buff);
		ReleaseBuffer(buffptr);
	}
	else
		Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid UZ7HO Command (not Freq Modem or FLAGS)\r");
	
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}

#define QTSMKISSCMD 7
#define MODEMREPORT 1		// QTSM Modem Info subcommand
#define SETMODEMPARAMS 2	// QtSM Moden change settings


VOID QTSMCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	int port;
	struct PORTCONTROL * PORT;
	struct KISSINFO * KISS;
	NPASYINFO Port;
	char * ptr, * context;
	UCHAR KissString[128] = "";
	UCHAR ENCBUFF[256];
	int KissLen = 0;
	unsigned char * Data = KissString;
	int fx25_mode = 0;
	int il2p_mode = 0;
	int il2p_crc = 0;
	int i;


	CmdTail = CmdTail + (OrigCmdBuffer - COMMANDBUFFER); // Replace with original case version

	ptr = strtok_s(CmdTail, " ,\r", &context);

	if (!ptr || _stricmp(ptr, "HELP") == 0)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "QTSM portno displays QTSM configuration info (if avaliable)\r", ptr);
		Bufferptr = Cmdprintf(Session, Bufferptr, "Modem, Centre Freq, fx25 flags and il2p flags can be changed if you have sysop access. All prameters are optional.\r", ptr);
		Bufferptr = Cmdprintf(Session, Bufferptr, "The Modem name (if used) must come first. Others can be entered in any order. Values should be comma separated.\r", ptr);
		Bufferptr = Cmdprintf(Session, Bufferptr, "For example QTSM 1 AFSK AX.25 1200bd, 1700, fx25 none, il2p only\r", ptr);
		Bufferptr = Cmdprintf(Session, Bufferptr, "            QTSM 1 il2p rx+tx\r", ptr);

		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	port = atoi(ptr);

	PORT = GetPortTableEntryFromPortNum(port);

	if (PORT == NULL || (void *)PORT->PORTTXROUTINE != (void *)KISSTX)			// Must be a kiss like port
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port %d is not a KISS port\r", port);
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	KISS = (struct KISSINFO *)PORT;

	Port = KISSInfo[PORT->PORTNUMBER];

	if (Port)
	{
		// Check if connected

		if (PORT->KISSTCP)
		{
			if (Port->Connected == 0)
			{
				Bufferptr = Cmdprintf(Session, Bufferptr, "Error - QTSM not connected\r", port);
				SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
				return;
			}
		}		
	}

	if (context == 0 || context[0] == 0)
	{
		// Display 

		if (PORT->QtSMFreq)
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "Modem %s Centre frequency %d fx25 %s il2p %s %s\r",
				(PORT->QtSMModem[0]) ? PORT->QtSMModem : "Not Available", PORT->QtSMFreq, 
				FX25Modes[PORT->fx25Flags], IL2PModes[PORT->il2pFlags], PORT->il2pcrc?"CRC":"");

			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));	
			return;
		}

		if (KISS->QtSMModem == 0)
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port %d has no QtSM information\r", port);
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}

		Bufferptr = Cmdprintf(Session, Bufferptr, "Modem %s Centre frequency %d\r", 
			(KISS->QtSMModem) ? KISS->QtSMModem : "Not Available", KISS->QtSMFreq);

		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	// Set params. Layout is

	/*
	0  QTSMKISSCMD
	1  SETMODEMPARAMS
	2  Freq
	4  Modem
	24 Reserved
	29 set flags indicator (2)
	30 fx25
	31 il2p
	32 crc
	*/

	if (Session->PASSWORD  != 0xFFFF)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "Update requires SYSOP status - enter password\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	if (_memicmp(context, "fx25", 4) == 0 || _memicmp(context, "il2p", 4) == 0)   // These have space separated params
		ptr = strtok_s(NULL, " ,\r", &context);
	else
		ptr = strtok_s(NULL, ",\r", &context);

	Data[0] = QTSMKISSCMD;
	Data[1] = SETMODEMPARAMS;


	while (ptr && ptr[0])
	{
		uint16_t freq = atoi(ptr);

		if (freq)
			memcpy(&Data[2], &freq, 2);


		else if (strlen(ptr) > 8)
			strcpy(&Data[4], ptr);

		else if (_stricmp(ptr, "fx25") == 0)
		{
			ptr = strtok_s(NULL, " ,\r", &context);
			i = 4;

			if (ptr && ptr[0])
			{
				for (i = 0; i < 4; i++)
				{
					if (_stricmp(FX25Modes[i], ptr) == 0)
					{
						fx25_mode = i;
						Data[29] = 2;
						break;
					}
				}
			}

			if (i == 4)
			{
				Bufferptr = Cmdprintf(Session, Bufferptr, "QTSM} Failed - fx25 flags invalid\r");
				SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
				return;
			}
		}
		else if (_stricmp(ptr, "il2p") == 0)
		{
			ptr = strtok_s(NULL, " ,\r", &context);
			i = 4;

			if (ptr && ptr[0])
			{
				for (i = 0; i < 4; i++)
				{
					if (_stricmp(IL2PModes[i], ptr) == 0)
					{
						il2p_mode = i;
						Data[29] = 2;
						break;
					}
				}
			}

			if (i == 4)
			{
				Bufferptr = Cmdprintf(Session, Bufferptr, "QTSM} Failed - il2p flags invalid\r");
				SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
				return;

			}
		}
		else if (_stricmp(ptr, "crc") == 0)
		{
			il2p_crc = 1;
			Data[29] = 2;
		}
		else if (_stricmp(ptr, "nocrc") == 0)
		{
			il2p_crc = 0;
			Data[29] = 2;
		}
		else
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "QTSM} Failed - invalid param %s. Enter QTSM HELP for more info\r", ptr);
			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
			return;
		}

		ptr = strtok_s(NULL, " ,\r", &context);
	}

	Data[30] = fx25_mode;
	Data[31] = il2p_mode;
	Data[32] = il2p_crc;
	
	KissLen = 33;

	// Send Command

	PORT->Session = Session;
	PORT->LastKISSCmdTime = time(NULL);
	KissString[0] |= KISS->OURCTRL;

	PORT = (struct PORTCONTROL *)KISS->FIRSTPORT;			// ALL FRAMES GO ON SAME Q

	KissLen = KissEncode(KissString, ENCBUFF, KissLen);
	ASYSEND(PORT, ENCBUFF, KissLen);

	//			Bufferptr = Cmdprintf(Session, Bufferptr, "Command Sent\r");
	//			SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));


	Bufferptr = Cmdprintf(Session, Bufferptr, "OK\r");
	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;

}


DllExport int APIENTRY Get_APPLMASK(int Stream);
DllExport int APIENTRY GetStreamPID(int Stream);
DllExport int APIENTRY GetApplFlags(int Stream);
DllExport int APIENTRY GetApplNum(int Stream);
DllExport int APIENTRY GetApplMask(int Stream);
DllExport BOOL APIENTRY GetAllocationState(int Stream);
int Check_Timer();

DllExport int APIENTRY GetCallsignNoSem(int stream, char * callsign)
{
	//	Returns call connected on stream (BPQHOST function 8 (part)).

	BPQVECSTRUC * SESS;
	TRANSPORTENTRY * L4;
	TRANSPORTENTRY * Partner;
	UCHAR  Call[11] = "SWITCH    ";
	UCHAR * AXCall = NULL;
	Check_Timer();

	stream--;						// API uses 1 - 64

	if (stream < 0 || stream > 63)
		return 0;

	SESS = &BPQHOSTVECTOR[stream];
	L4 = SESS->HOSTSESSION;

	if (L4 == 0)
	{
		return 0;
	}

	Partner = L4->L4CROSSLINK;

	if (Partner)
	{
		//	CONNECTED OUT - GET TARGET SESSION

		if (Partner->L4CIRCUITTYPE & BPQHOST)
		{
			AXCall = &Partner->L4USER[0];
		}
		else if (Partner->L4CIRCUITTYPE & L2LINK)
		{
			struct _LINKTABLE * LINK = Partner->L4TARGET.LINK;

			if (LINK)
				AXCall = LINK->LINKCALL;

			if (Partner->L4CIRCUITTYPE & UPLINK)
			{
				// IF UPLINK, SHOULD USE SESSION CALL, IN CASE *** LINKED HAS BEEN USED

				AXCall = &Partner->L4USER[0];
			}
		}
		else if (Partner->L4CIRCUITTYPE & PACTOR)
		{
			//	PACTOR Type - Frames are queued on the Port Entry

			EXTPORTDATA * EXTPORT = Partner->L4TARGET.EXTPORT;

			if (EXTPORT)
				AXCall = &EXTPORT->ATTACHEDSESSIONS[Partner->KAMSESSION]->L4USER[0];

		}
		else
		{
			//	MUST BE NODE SESSION

			//	ANOTHER NODE

			//	IF THE HOST IS THE UPLINKING STATION, WE NEED THE TARGET CALL

			if (L4->L4CIRCUITTYPE & UPLINK)
			{
				struct DEST_LIST *DEST = Partner->L4TARGET.DEST;

				if (DEST)
					AXCall = &DEST->DEST_CALL[0];
			}
			else
				AXCall = Partner->L4USER;
		}
		if (AXCall)
			ConvFromAX25(AXCall, Call);
	}

	memcpy(callsign, Call, 10);
	return 0;
}


VOID CMDSTREAMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	int i;
	char callsign[12] = "";
	char flag[3];
	UINT Mask, MaskCopy;
	int Flags;
	int AppNumber;
	int OneBits;
	Bufferptr = Cmdprintf(Session, Bufferptr, "\r|   | RX | TX | MON |App|Flg| Callsign |    Program    |\r");

	for (i=1; i <=BPQHOSTSTREAMS; i++)
	{		
		callsign[0]=0;

		if (GetAllocationState(i) == 0)
			continue;

		strcpy(flag,"*");

		GetCallsignNoSem(i,callsign);

		Mask = MaskCopy = Get_APPLMASK(i);

		// if only one bit set, convert to number

		AppNumber = 0;
		OneBits = 0;

		while (MaskCopy)
		{
			if (MaskCopy & 1)
				OneBits++;

			AppNumber++;
			MaskCopy = MaskCopy >> 1;
		}

		Flags=GetApplFlags(i);

		if (OneBits > 1)
			Bufferptr = Cmdprintf(Session, Bufferptr, "|%2d%s|%4d|%4d|%5d|%3x|%3x|%10s|%-15s|\r", i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName);
		else
			Bufferptr = Cmdprintf(Session, Bufferptr, "|%2d%s|%4d|%4d|%5d|%3d|%3x|%10s|%-15s|\r",i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName);

	}

	SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
	return;
}







