/*
Copyright 2001-2022 John Wiseman G8BPQ

This file is part of LinBPQ/BPQ32.

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

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

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

/*

 AGWPE emulation Interface for BPQ32
 
 Based on AGWtoBPQ
 	
*/
#define _CRT_SECURE_NO_DEPRECATE

#include "cheaders.h"

#include "bpq32.h"

// Internal AGW Interface

//#define VVICON 400

struct AGWHeader
{
    uint8_t Port;
	uint8_t filler1[3];
	unsigned char DataKind;
    unsigned char filler2;
	unsigned char PID;
    unsigned char filler3;
    unsigned char callfrom[10];
    unsigned char callto[10];
    unsigned int DataLength;
    int reserved;
};

struct AGWSocketConnectionInfo
{
	int Number;					// Number of record - for AGWConnections display
    SOCKET socket;
	SOCKADDR_IN sin;  
	BOOL SocketActive;
    BOOL RawFlag;
    BOOL MonFlag;
    BOOL useLocalTime;
    BOOL doNodes;
    unsigned char CallSign1[10];
    unsigned char CallSign2[10];
    BOOL GotHeader;
    int MsgDataLength;
    struct AGWHeader AGWRXHeader; 
	unsigned char * MsgData; 
};

struct BPQConnectionInfo
{    
    struct AGWSocketConnectionInfo * SocketIndex;
    int BPQStream;
    unsigned char  CallKey[21];					// Port + two calls
    BOOL Connecting;					// Set while waiting for connection to complete
    BOOL Listening;
    int ApplMask;   
} ConInfoRec;


char AGWPorts[1000];

struct AGWHeader AGWTXHeader;

char SessionList[100];

struct BPQConnectionInfo AGWConnections[65];

#define MaxSockets 64

static struct AGWSocketConnectionInfo Sockets[MaxSockets+1];

int CurrentConnections;

static int CurrentSockets=0;

int AGWPort = 0;
int AGWSessions = 0;
int	AGWMask = 0;

BOOL LoopMonFlag = FALSE;
BOOL Loopflag = FALSE;

extern char pgm[256];	

SOCKET agwsock;

extern BPQVECSTRUC * AGWMONVECPTR;

extern int SemHeldByAPI;

char szBuff[80];

static int VisiblePortToRealPort[MaxBPQPortNo + 1];

int SetUpHostSessions();
int DisplaySessions();
int AGWDoStateChange(int Stream);
int AGWDoReceivedData(int Stream);
int AGWDoMonitorData();
int AGWConnected(struct BPQConnectionInfo * Con, int Stream);
int AGWDisconnected(struct BPQConnectionInfo * Con, int Stream);
int DeleteConnection(struct BPQConnectionInfo * Con);
int SendConMsgtoAppl(BOOL Incomming, struct BPQConnectionInfo * Con, char * CallSign);
int SendDisMsgtoAppl(char * Msg, struct AGWSocketConnectionInfo * sockptr);
int AGWSocket_Accept(SOCKET SocketId);
int Socket_Data(int SocketId,int error, int eventcode);
int AGWDataSocket_Read(struct AGWSocketConnectionInfo * sockptr, SOCKET sock);
int DataSocket_Write(struct AGWSocketConnectionInfo * sockptr, SOCKET sock);
int AGWGetSessionKey(char * key, struct AGWSocketConnectionInfo * sockptr);
int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr);
int SendDataToAppl(int Stream, byte * Buffer, int Length);
int InternalAGWDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, int * FrameType, int useLocalTime, int doNodes);
int AGWDataSocket_Disconnect( struct AGWSocketConnectionInfo * sockptr);
int SendRawPacket(struct AGWSocketConnectionInfo * sockptr, char *txmsg, int Length);
int ShowApps();
int Terminate();
int SendtoSocket(SOCKET sock,char * Msg);
char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...);

VOID Poll_AGW()
{
	int state, change, i;
	int Stream;
	struct BPQConnectionInfo * Con;
	struct AGWSocketConnectionInfo * sockptr;

	// Look for incoming connects

	fd_set readfd, writefd, exceptfd;
	struct timeval timeout;
	int retval;
	int n;
	int Active = 0;
	SOCKET maxsock;

	timeout.tv_sec = 0;
	timeout.tv_usec = 0;				// poll
		
	FD_ZERO(&readfd);

	FD_SET(agwsock, &readfd);

	retval = select((int)agwsock + 1, &readfd, NULL, NULL, &timeout);

	if (retval == -1)
	{
		retval = WSAGetLastError();
		perror("listen select");
	}

	if (retval)
		if (FD_ISSET((int)agwsock, &readfd))
			AGWSocket_Accept(agwsock);

	// look for data on any active sockets

	maxsock = 0;

	FD_ZERO(&readfd);
	FD_ZERO(&writefd);
	FD_ZERO(&exceptfd);

	// Check for data on active streams
	
	for (i = 0; i < CurrentConnections; i++)
	{
		Con = &AGWConnections[i];
		Stream = Con->BPQStream;

		SessionState(Stream, &state, &change);
	
		if (change == 1)
		{
			if (state == 1)
	
			// Connected
			
				AGWConnected(Con, Stream);	
			else
				AGWDisconnected(Con, Stream);
		}


		if (Con->SocketIndex)		// Active Session
		{
			AGWDoReceivedData(Stream);

			//	Get current Session State. Any state changed is ACK'ed
			//	automatically. See BPQHOST functions 4 and 5.
			
			SessionState(Stream, &state, &change);
	
			if (change == 1)
			{
				if (state == 1)
	
				// Connected
			
					AGWConnected(Con, Stream);	
				else
					AGWDisconnected(Con, Stream);
			}
		}
	}
	for (n = 1; n <= MaxSockets; n++)
	{
		sockptr=&Sockets[n];
		
		if (sockptr->SocketActive)
		{
			SOCKET sock = sockptr->socket;

			FD_SET(sock, &readfd);
			FD_SET(sock, &exceptfd);
				
			Active++;
			if (sock > maxsock)
				maxsock = sock;		
		}
	}

	if (Active)
	{
		retval = select((int)maxsock + 1, &readfd, &writefd, &exceptfd, &timeout);

		if (retval == -1)
		{				
			perror("data select yy");
			Debugprintf("Select Error %d", WSAGetLastError());
		}
		else
		{
			if (retval)
			{
				// see who has data

				for (n = 1; n <= MaxSockets; n++)
				{
					sockptr=&Sockets[n];
		
					if (sockptr->SocketActive)
					{
						SOCKET sock = sockptr->socket;

						if (FD_ISSET(sock, &exceptfd))
							AGWDataSocket_Disconnect(sockptr);

						if (FD_ISSET(sock, &readfd))
							AGWDataSocket_Read(sockptr, sock);

					}
				}
			}		
		}
	}
	
	AGWDoMonitorData();
}


static SOCKADDR_IN local_sin;  /* Local socket - internet style */

static PSOCKADDR_IN psin;


BOOL AGWAPIInit()
{	
	struct PORTCONTROL * PORT = PORTTABLE;
	int i = 1;
	int v = 0;
	char * ptr;
	BOOL opt=TRUE;

	if (AGWPort == 0)
		return FALSE;

//	Create listening socket

	agwsock = socket( AF_INET, SOCK_STREAM, 0);

    if (agwsock == INVALID_SOCKET)
	{
        sprintf(szBuff, "socket() failed error %d", WSAGetLastError());
		WritetoConsole(szBuff);
		return FALSE;        
	}

	setsockopt (agwsock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&opt,4);

	psin=&local_sin;

	psin->sin_family = AF_INET;
	psin->sin_addr.s_addr = INADDR_ANY;
    psin->sin_port = htons(AGWPort);        /* Convert to network ordering */

    if (bind(agwsock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR)
	{
        sprintf(szBuff, "bind(sock) failed Port %d Error %d", AGWPort, WSAGetLastError());
		WritetoConsole(szBuff);
        closesocket(agwsock);

		return FALSE;
	}

    if (listen(agwsock, 5) < 0)
	{
		sprintf(szBuff, "listen(sock) failed Error %d", WSAGetLastError());
		WritetoConsole(szBuff);

		return FALSE;
	} 

	SetUpHostSessions();

	// Set up port List

	ptr = &AGWPorts[0];

	ptr += sprintf(ptr, "%d", NUMBEROFPORTS);
				
	*(ptr)=';';
	*(++ptr)=0;

	while (PORT)
	{
		if (PORT->Hide == 0)
		{
			VisiblePortToRealPort[v++] = i - 1;
			memcpy(ptr,"Port",4);
			ptr += sprintf(ptr, "%d", i);
			memcpy(ptr, " with ", 6);
			ptr+=6;
			memcpy(ptr, PORT->PORTDESCRIPTION, 29);		// ";"
			ptr+=29;

			while (*(--ptr) == ' ') {}

			ptr++;

			*(ptr++)=';';
		}
		i++;
		PORT=PORT->PORTPOINTER;
	}

	*(ptr)=0;

	AGWMONVECPTR->HOSTAPPLFLAGS = 0x80;		// Requext Monitoring

	return TRUE;
}
int SetUpHostSessions()
{
	int Stream, i;

	if (AGWMask == 0) return 0;

	for (i = 1; i <= AGWSessions; i++)
	{ 
		strcpy(pgm, "AGW");

		Stream = FindFreeStream();
  
		if (Stream == 255) break;
    	
		SetAppl(Stream, 2, AGWMask);
    
		strcpy(pgm, "bpq32.exe");

		AGWConnections[CurrentConnections].CallKey[0] = 0;
		AGWConnections[CurrentConnections].BPQStream = Stream;
		AGWConnections[CurrentConnections].SocketIndex = 0;
		AGWConnections[CurrentConnections].Connecting = FALSE;
		AGWConnections[CurrentConnections].Listening = TRUE;
		AGWConnections[CurrentConnections].ApplMask = AGWMask;
          
		CurrentConnections++;
	}

	return 0;
}

extern struct DATAMESSAGE * REPLYBUFFER;
extern BOOL AGWActive;

VOID SHOWAGW(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
	//	DISPLAY AGW Session Status
	
	int i, con;
	struct BPQConnectionInfo * Con;
	byte key[21];

	struct AGWSocketConnectionInfo * sockptr;
	char IPAddr[20];

	if (AGWActive == FALSE)
	{
		Bufferptr = Cmdprintf(Session, Bufferptr, "\rAGW Interface is not enabled\r");
		SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
		return;
	}

	Bufferptr = Cmdprintf(Session, Bufferptr, "\rSockets\r");
	for (i = 1; i <= CurrentSockets; i++)
	{
		sockptr=&Sockets[i];

		if (sockptr->SocketActive)
		{
			unsigned char work[4];
			memcpy(work, &sockptr->sin.sin_addr.s_addr, 4);
		
			sprintf(IPAddr, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]);

			Bufferptr = Cmdprintf(Session, Bufferptr, "%2d   %-16s %5d %-10s %-10s\r", i, IPAddr, htons(sockptr->sin.sin_port), &sockptr->CallSign1,  &sockptr->CallSign2);
		}
		else
		{
			Bufferptr = Cmdprintf(Session, Bufferptr, "%2d   Idle\r", 1);
		}
	}
		
	Bufferptr = Cmdprintf(Session, Bufferptr, "\rPort    Calls        Stream Socket Connecting Listening Mask\r");

	for (con = 0; con < CurrentConnections; con++)
	{
		Con = &AGWConnections[con];

		memcpy(key,Con->CallKey,21);

		if (key[0] == 0) key[0] = 32;

		key[10]=0;
		key[20]=0;

		Bufferptr = Cmdprintf(Session, Bufferptr, "%2c ", key[0]);
		Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s%-10s ",&key[1],&key[11]);

		Bufferptr = Cmdprintf(Session, Bufferptr, "%2d     %2d      %s     %s    %X\r",
			Con->BPQStream,
			(Con->SocketIndex == 0) ? 0 : AGWConnections[con].SocketIndex->Number,
			(Con->Connecting == 0) ? "FALSE" : "TRUE ",
			(Con->Listening == 0) ? "FALSE" : "TRUE ",
			Con->ApplMask);

	}

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



int DisplaySessions()
{
/*	char * ptr;
	int i, con;

	byte key[21]; // As String, char As String

	strcpy (SessionList,"   Port    Calls         Stream Socket Connecting Listening Mask");
  	
	SendDlgItemMessage(MainWnd,IDC_TEXTBOX3,LB_RESETCONTENT,0,0);
	SendDlgItemMessage(MainWnd,IDC_TEXTBOX3,LB_ADDSTRING,0,(LPARAM) SessionList);

	for (con = 0; con < CurrentConnections; con++)
	{
		memcpy(key,AGWConnections[con].CallKey,21);

		if (key[0] == 0) key[0] = 32;


		key[10]=0;
		key[20]=0;

		ptr=&SessionList[0];

		i=sprintf(ptr,"%2d %2c ",con,key[0]);

		ptr+=i;

		i=sprintf(ptr,"%-10s%-10s ",&key[1],&key[11]);

		ptr+=i;

		i=sprintf(ptr,"%2d     %2d      %s     %s    %x",
			AGWConnections[con].BPQStream,
			(AGWConnections[con].SocketIndex == 0) ? 0 : AGWConnections[con].SocketIndex->Number,
			(AGWConnections[con].Connecting == 0) ? "FALSE" : "TRUE ",
			(AGWConnections[con].Listening == 0) ? "FALSE" : "TRUE ",
			AGWConnections[con].ApplMask);

		SendDlgItemMessage(MainWnd,IDC_TEXTBOX3,LB_ADDSTRING,0,(LPARAM) SessionList);
	}
*/
	return (0);

}

int AGWConnected(struct BPQConnectionInfo * Con, int Stream)
{
	byte ConnectingCall[10];
	byte * ApplCallPtr;
	byte * keyptr;
	byte ApplCall[10]="";
	int i;

	int ApplNum;
	struct AGWSocketConnectionInfo * sockptr;

	GetCallsign(Stream, ConnectingCall);

	for (i=9;i>0;i--)
		if (ConnectingCall[i]==32)
			ConnectingCall[i]=0;

	ApplNum = GetApplNum(Stream);

	if (ApplNum == 0) 
	{
		return 0; // Cant be an incomming connect
	}
	ApplCallPtr = GetApplCall(ApplNum);

	if (ApplCallPtr != 0) memcpy(ApplCall,ApplCallPtr,10);

	// Convert trailing spaces to nulls
	
	for (i=9;i>0;i--)
		if (ApplCall[i]==32)
			ApplCall[i]=0;

//   See if incomming connection

	if (Con->Listening)
	{    
		//	Allocate Session and send "c" Message
		//
		//	Find an AGW session
                          
		for (sockptr=&Sockets[1]; sockptr <= &Sockets[CurrentSockets]; sockptr++)
		{
			if (sockptr->SocketActive &&
				(memcmp(sockptr->CallSign1, ApplCall, 10) == 0) || (memcmp(sockptr->CallSign2, ApplCall, 10) == 0))
			{
				// Create Key

				char callsign[10];
				int port;
				int sesstype;
				int paclen;
				int maxframe;
				int l4window;

           
				keyptr=(byte *)&Con->CallKey;

				// Try using the BPQ Port Number if a L2 connect, first free port number if not

				GetConnectionInfo(Stream, callsign,
										 &port, &sesstype, &paclen,
										 &maxframe, &l4window);


				if (port == 0)
					port = 64;

				*(keyptr++)='0' + port;
				memcpy(keyptr, ApplCall, 10);
				keyptr+=10;
				memcpy(keyptr,ConnectingCall, 10);
                        		
				// Make sure key is not already in use

				for (i = 0; i < CurrentConnections; i++)
				{
					if (Con->BPQStream == Stream)
						continue;		// Dont Check ourself!

					if (AGWConnections[i].SocketIndex == sockptr &&
							memcmp(&Con->CallKey, &AGWConnections[i].CallKey,21) == 0)
					{
						SendMsg(Stream, "AGWtoBPQ - Callsign is already connected\r", 43);
						Sleep (500);
						Disconnect(Stream);
						Con->CallKey[0]=0;

						return 0;
					}
				}

				Con->Listening = FALSE;
				Con->SocketIndex = sockptr; 

				DisplaySessions();
                      
				SendConMsgtoAppl(TRUE, Con, ConnectingCall);

				return 0;
   			}
		}
			
		SendMsg(Stream, "No AGWPE Host Sessions available\r", 33);
		Sleep (500);
		Disconnect(Stream);   // disconnect
		return (0);
	}
	
	// Not listening ??

	OutputDebugString("Inbound Connection on Outgoing Stream");

	SendMsg(Stream, "AGWtoBPQ - Inbound Connection on Outgoing Stream\r", 49);
	Sleep (500);
	Disconnect(Stream);   // disconnect
	return (0);
}

int AGWDisconnected(struct BPQConnectionInfo * Con, int Stream)
{
	struct AGWSocketConnectionInfo * sockptr;	
	char key[21];

	memcpy(key,Con->CallKey,21);

	sockptr = Con->SocketIndex;
        
	if (sockptr != 0)
	{  
		AGWTXHeader.Port = key[0] - 49;
		
		memcpy(&AGWTXHeader.callfrom,&key[11],10);
		memcpy(&AGWTXHeader.callto,&key[1],10);

		//   Send a "d" Message
        
		// DisMsg = "*** DISCONNECTED From Station "

 		SendDisMsgtoAppl("*** DISCONNECTED From Station ", sockptr);
		
	}

	if (Con->ApplMask != 0)
	{
		Con->Listening = TRUE;
		Con->SocketIndex = 0;
		memset(&Con->CallKey ,0 ,21);
            
		DisplaySessions();
	}
	else
	{
		DeleteConnection(Con);
	}
		
	return 0;

}
int AGWDoReceivedData(int Stream)
{
	byte Buffer[400];
	int len,count;

//Dim n As Integer, i As Integer, j As Integer, portcount As Integer
//Dim start As Integer

	do
	{ 
		GetMsg(Stream, Buffer,&len, &count);

		if (len > 0)
			SendDataToAppl(Stream, Buffer,len);
    
	}
	while (count > 0);
	
	return 0;
}

int AGWDoMonitorData()
{
	byte Buffer[500];
	int RawLen, Length;
	byte Port;
	struct AGWSocketConnectionInfo * sockptr;	
	byte AGWBuffer[1000];
	int n;
	int Frametype;
	BOOL RXFlag;
	time_t Stamp;

	// Look for Monitor Data

	while (AGWMONVECPTR->HOSTTRACEQ)
	{
		MESSAGE * monbuff;

		GetSemaphore(&Semaphore, 99);
		
		monbuff = Q_REM((void *)&AGWMONVECPTR->HOSTTRACEQ);

		RawLen = monbuff->LENGTH;

		if (RawLen < MSGHDDRLEN || RawLen > 350)
		{	
			ReleaseBuffer(monbuff);
			FreeSemaphore(&Semaphore);
			return 0;
		}

		Stamp = monbuff->Timestamp;

		memcpy(Buffer, monbuff, RawLen);

		ReleaseBuffer(monbuff);

		FreeSemaphore(&Semaphore);

		// Set monbuff to point to the copy

		monbuff = (MESSAGE *)Buffer;
	
//'   4 byte chain
//'   1 byte port - top bit = transmit
//'   2 byte length (LO-HI)

		Port = monbuff->PORT;

		if (Port > 127)
		{
			RXFlag = FALSE;
			Port = Port - 128;
		}
		else
		{
		    RXFlag = TRUE;
		}

		if (Port == 0)
		{
			Debugprintf("AGWMON Port number is zero");
			return 0;
		}

		// Can now have different mon flags per connection, so need to run decode for each socket

		for (n = 1; n<= CurrentSockets; n++)
		{
			sockptr = &Sockets[n];

			if (sockptr->SocketActive && sockptr->MonFlag && (RXFlag || LoopMonFlag))
			{
				Length = InternalAGWDecodeFrame(monbuff, AGWBuffer, Stamp, &Frametype, sockptr->useLocalTime, sockptr->doNodes);

				if (Length > 0)
				{
					AGWTXHeader.Port = Port - 1;       // AGW Ports start from 0

					/*
					* Patch by D. van der Locht      09-10-2020
					* Added support for sending 'T' frames to AGW clients if LoopMonFlag is set.
					*/

					if (RXFlag)
					{
						if (Frametype == 3)
						{
							AGWTXHeader.DataKind = 'U';
						}
						else
						{
							if (Frametype && 1 == 0)
							{
								AGWTXHeader.DataKind = 'I';
							}
							else
							{
								AGWTXHeader.DataKind = 'S';
							}
						}
					}
					else
					{
						AGWTXHeader.DataKind = 'T';
					}

					AGWTXHeader.DataLength = Length;

					memset(AGWTXHeader.callfrom, 0,10);
					ConvFromAX25(monbuff->ORIGIN, AGWTXHeader.callfrom);
       
					SendRawPacket(sockptr, AGWBuffer, Length);
				}
			}
		}

		RawLen = RawLen - (MSGHDDRLEN - 1);		// One more for KISS control
 
		if (RXFlag || Loopflag) // Send transmitted frames if requested
		{

        //
        //  Send raw data to any sockets that have requested Raw frames
        //

		// Format is ax.25 packet prceeded by a KISS command byte 00 for channel 1 0x10 for channel 2 etc
		// As this is an application API I think all should go as Port 1


			Buffer[MSGHDDRLEN - 1] = 0;			// Just in case big-endian
       
			AGWTXHeader.Port = Port - 1;       // AGW Ports start from 0
			AGWTXHeader.DataKind = 'K';
        
			AGWTXHeader.DataLength = RawLen;
         
	 		for (n = 1; n<= CurrentSockets; n++)
			{
				sockptr=&Sockets[n];
       
				if (sockptr->SocketActive && sockptr->RawFlag)
					SendRawPacket(sockptr, &Buffer[MSGHDDRLEN - 1], RawLen);
        
			}
		}		
   }
   return 0;
}

int DeleteConnection(struct BPQConnectionInfo * Con)
{
	int i;
	int con;

	//
	//	remove specified session
	//

    SetAppl(Con->BPQStream, 0, 0);

	Disconnect(Con->BPQStream);
 
    DeallocateStream(Con->BPQStream);

//   move all down one

	con = (int)(Con - &AGWConnections[0]);
	
	for (i = con; i <= CurrentConnections - 1; i++)
	{
	    memcpy(&AGWConnections[i],&AGWConnections[i + 1],sizeof ConInfoRec);  
	}
    
	CurrentConnections--;

	return 0;
}

int SendConMsgtoAppl(BOOL Incomming, struct BPQConnectionInfo * Con, char * CallSign)
{
	char key[21];
	char ConMsg[80]="*** CONNECTED ";
	struct AGWSocketConnectionInfo * sockptr;


    memcpy(key,&Con->CallKey,21);
        
    sockptr = Con->SocketIndex;
        
    AGWTXHeader.Port = key[0] - 49;
            
    memcpy(AGWTXHeader.callfrom, &key[11],10);
        
    memcpy(AGWTXHeader.callto, &key[1],10);
            
/*    '
    '   Send a "C" Message
    '
'01 00 00 00 43 00 00 00 47 4D 38 42 50 51 2D 34    C   GM8BPQ-4
'00 EA 47 4D 38 42 50 51 2D 34 00 FF 25 00 00 00  êGM8BPQ-4 ÿ%
'00 00 00 00 2A 2A 2A 20 43 4F 4E 4E 45 43 54 45     *** CONNECTE
'44 20 57 69 74 68 20 53 74 61 74 69 6F 6E 20 47 D With Station G
'4D 38 42 50 51 2D 34 0D 00 M8BPQ-4
*/

    AGWTXHeader.DataKind = 'C';

    AGWTXHeader.PID = 0;
                    
    if (Incomming)	
		strcat(ConMsg,"To");
	else
		strcat(ConMsg,"With");
    
    strcat(ConMsg," Station ");
    strcat(ConMsg,CallSign);
                
    AGWTXHeader.DataLength = (int)strlen(ConMsg)+1;
               
	SendtoSocket(sockptr->socket, ConMsg);
            
	return 0;

}




int SendDisMsgtoAppl(char * Msg, struct AGWSocketConnectionInfo * sockptr)
{
    byte DisMsg[100];
            
    strcpy(DisMsg,Msg);
	strcat(DisMsg,(const char *)&AGWTXHeader.callfrom);
            
    AGWTXHeader.Port = sockptr->AGWRXHeader.Port;
    AGWTXHeader.DataKind = 'd';
 
    strcat(DisMsg,"\r");
                                
    AGWTXHeader.DataLength = (int)strlen(DisMsg)+1;
            
	SendtoSocket(sockptr->socket, DisMsg);

	return 0;

}



int AGWSocket_Accept(SOCKET SocketId)
{
	int n,addrlen;
	struct AGWSocketConnectionInfo * sockptr;
	SOCKET sock;

//   Find a free Socket

	for (n = 1; n <= MaxSockets; n++)
	{
		sockptr=&Sockets[n];
		
		if (sockptr->SocketActive == FALSE)
		{
			addrlen=sizeof(struct sockaddr);
		
			memset(sockptr, 0, sizeof(struct AGWSocketConnectionInfo));

			sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen);

			if (sock == INVALID_SOCKET)
			{
				sprintf(szBuff, "AGW accept() failed Error %d\r", WSAGetLastError());
				WritetoConsole(szBuff);
				return FALSE;
			}

			sockptr->socket = sock;
			sockptr->SocketActive = TRUE;
			sockptr->GotHeader = FALSE;
			sockptr->MsgDataLength = 0;
			sockptr->Number = n;

			if (CurrentSockets < n) CurrentSockets=n;  //Record max used to save searching all entries

			ShowApps();

			return 0;
		}
	}

	// Should accept, then immediately close

	return 0;
}

int SendtoSocket(SOCKET sock, char * Msg)
{
	int len;
	int n;
	char * ptr;

	len = AGWTXHeader.DataLength;

	// Make sure calls are null terminated

	n = 10;
	ptr = &AGWTXHeader.callfrom[9];
	while (n-- && *(ptr) == ' ')
	{
		*(ptr) = 0;
		ptr--;
	}

	n = 10;
	ptr = &AGWTXHeader.callto[9];
	while (n-- && *(ptr) == ' ')
	{
		*(ptr) = 0;
		n--;
	}

	send(sock,(char *)&AGWTXHeader, 36,0);
	if (len > 0) send(sock, Msg, len,0);

	return 0;
}

int AGWDataSocket_Read(struct AGWSocketConnectionInfo * sockptr, SOCKET sock)
{
	int i;
	int DataLength;
	struct AGWHeader * AGW = &sockptr->AGWRXHeader;

	ioctlsocket(sock,FIONREAD,&DataLength);

	if (DataLength == SOCKET_ERROR || DataLength == 0)
	{
		// Failed or closed - clear connection

		AGWDataSocket_Disconnect(sockptr);
		return 0;
	}

	if (DataLength < 36)		//         A header
	{
		// If we don't get a header within a few ms assume a rogue connection and close it

		int n = 50;

		while (n--)
		{
			Sleep(10);
			ioctlsocket(sock,FIONREAD,&DataLength);
			
			if (DataLength >= 36)
				break;
		}

		if (n < 1)
		{
			Debugprintf("Corrupt AGW Packet Received");
			AGWDataSocket_Disconnect(sockptr);
			return 0;			
		}
	}

	// Have a header

	i=recv(sock,(char *)&sockptr->AGWRXHeader, 36, 0);
            
	if (i == SOCKET_ERROR)
	{
		i=WSAGetLastError();
		AGWDataSocket_Disconnect(sockptr);
	}
	
	sockptr->MsgDataLength = sockptr->AGWRXHeader.DataLength;

	// Validate packet to protect against accidental (or malicious!) connects from a non-agw application

	if (AGW->Port > 64 || AGW->filler2 != 0 || AGW->filler3 != 0 || AGW->DataLength > 400)
	{
		Debugprintf("Corrupt AGW Packet Received");
		AGWDataSocket_Disconnect(sockptr);
		return 0;
	}
	
	if (sockptr->MsgDataLength == 0)
	   ProcessAGWCommand (sockptr);
	else
		sockptr->GotHeader = TRUE;			// Wait for data

	ioctlsocket(sock,FIONREAD,&DataLength);	// See if more data
			
	if (sockptr->GotHeader)
	{
		// Received a header, without sufficient data bytes
   
		if (DataLength < sockptr->MsgDataLength)
		{
			// Fiddle - seem to be problems somtimes with un-Neagled hosts so wait a few ms
			// if we don't get a full packet assume a rogue connection and close it

			int n = 50;

			while (n--)
			{
				Sleep(10);
				ioctlsocket(sock,FIONREAD,&DataLength);
			
				if (DataLength >= sockptr->MsgDataLength)
					break;
			}

			if (n < 1)
			{
				Debugprintf("Corrupt AGW Packet Received");
				AGWDataSocket_Disconnect(sockptr);
				return 0;			
			}
		}
		
		if (DataLength >= sockptr->MsgDataLength)
		{
			//   Read Data and Process Command

			sockptr->MsgData = malloc(sockptr->MsgDataLength);
    
			i = recv(sock, sockptr->MsgData, sockptr->MsgDataLength, 0);

			ProcessAGWCommand (sockptr);
			free(sockptr->MsgData);
			sockptr->GotHeader = FALSE;
		}
	}
	return 0;
}


int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr)
{
	int AGWVersion[2]={2003,999};
	byte AGWPortCaps[12] = { 0, 255, 30, 10, 63, 10, 4, 0, 1, 0, 0, 0 };
	char AGWRegReply[1];
	struct BPQConnectionInfo * Connection;
	int Stream;
	char AXCall[10];
	char RegCall[10];
	unsigned char TXMessage[500];
	int Digis,MsgStart,j;
	byte * TXMessageptr;
	char key[21];
	char ToCall[10];
	char ConnectMsg[128];
	int con,conport;
	int AGWYReply = 0;
	int state, change;
	int n;

	// if we have hidden some ports then the port in the AGW packet will be an index into the visible ports,
	// not the real port number

	switch (sockptr->AGWRXHeader.DataKind)
	{
	case 'C':
	case 'v':

        //   Connect or Connect with Digis
        
        //   Create Session Key from port and callsign pair

		AGWGetSessionKey(key, sockptr);
            
        memcpy(ToCall, &key[11],10);

		strcpy(pgm, "AGW");
           
        Stream = FindFreeStream();

		strcpy(pgm, "bpq32.exe");

        if (Stream == 255) return 0;

		Connection=&AGWConnections[CurrentConnections];

        memcpy(&Connection->CallKey,key,21);
        Connection->BPQStream = Stream;
        Connection->SocketIndex = sockptr;
        Connection->Connecting = TRUE;
        
        Connect(Stream);				// Connect
		SessionState(Stream, &state, &change);
        
		ConvToAX25(sockptr->CallSign1, AXCall);
		ChangeSessionCallsign(Stream, AXCall);		// Prevent triggering incoming connect code

        DisplaySessions();
        
        if (memcmp(ToCall,"SWITCH",6) == 0)
		{
			//  Just connect to command level on switch

			SendConMsgtoAppl(FALSE, Connection, ToCall);
			Connection->Connecting = FALSE;
		} 
		else
		{

			// Need to convert port index (used by AGW) to port number

			conport=GetPortNumber(VisiblePortToRealPort[key[0]-49] + 1);

			n = sprintf(ConnectMsg,"C %d %s",conport,ToCall);

			// if 'v' command add digis

			if (sockptr->AGWRXHeader.DataLength)
			{
				// Have digis

				char * Digis = sockptr->MsgData;
				int nDigis = Digis[0];

				Digis ++;

				while(nDigis--)
				{
					n += sprintf(&ConnectMsg[n], " %s", Digis);
					Digis += 10;
				}
			}

			strcat(ConnectMsg, "\r");

			// Send C command to Node

			SendMsg(Stream, ConnectMsg, (int)strlen(ConnectMsg));
		}

        CurrentConnections++;

        DisplaySessions();

		return 0;
        
	case 'D':
   
        //   Send Data
        //
        //   Create Session Key from port and callsign pair
		
        AGWGetSessionKey(key, sockptr);

        for (con = 0; con < CurrentConnections; con++)
		{
			if (memcmp(AGWConnections[con].CallKey,key,21) == 0)
			{
				SendMsg(AGWConnections[con].BPQStream, sockptr->MsgData, sockptr->MsgDataLength);
				return 0;
			}
		}

		return 0;
  
	case 'd':

    //   Disconnect
            
        memcpy(AGWTXHeader.callto,sockptr->AGWRXHeader.callfrom,10);
        memcpy(AGWTXHeader.callfrom,sockptr->AGWRXHeader.callto,10);
        
        SendDisMsgtoAppl("*** DISCONNECTED RETRYOUT With ", sockptr);
   
        AGWGetSessionKey(key, sockptr);
 
        for (con = 0; con < CurrentConnections; con++)
		{
			if (memcmp(AGWConnections[con].CallKey,key,21) == 0)
			{ 
                Disconnect(AGWConnections[con].BPQStream);
				return 0;
			}
		}

		// There is confusion about the correct ordring of calls in the "d" packet. AGW appears to accept either,
		//	so I will too.

		memset(&key[1],0,20);
		strcpy(&key[1],sockptr->AGWRXHeader.callto);
		strcpy(&key[11],sockptr->AGWRXHeader.callfrom);

		for (con = 0; con < CurrentConnections; con++)
		{
			if (memcmp(AGWConnections[con].CallKey,key,21) == 0)
			{ 
                Disconnect(AGWConnections[con].BPQStream);
				return 0;
			}
		}

		return 0;


	case 'R':
    
    //   Version
    
        memset(&AGWTXHeader,0,36);
        AGWTXHeader.DataKind = 'R';
        AGWTXHeader.DataLength = 8;       // Length
    
        SendtoSocket(sockptr->socket, (char *)&AGWVersion[0]);

		return 0;
    

	case 'G':

        //   Port info. String is in AGWPorts
        
        
        memset(&AGWTXHeader,0,36);
        AGWTXHeader.DataKind = 'G';
        AGWTXHeader.DataLength =(int)strlen(AGWPorts)+1;     // Length
    
        SendtoSocket(sockptr->socket, AGWPorts);

		return 0;

		     
	case 'g':

        //   Port capabilities. Currently hard-coded.

		AGWTXHeader.Port = sockptr->AGWRXHeader.Port;
		AGWTXHeader.DataKind = 'g';
		AGWTXHeader.DataLength = 12;

		SendtoSocket(sockptr->socket, (char *)&AGWPortCaps[0]);

		return 0;


    
	case 'k':

       //   Toggle Raw receive

        sockptr->RawFlag = !sockptr->RawFlag;
        
		return 0;

	case 'K':

        // Send Raw Frame

		SendRaw(sockptr->AGWRXHeader.Port+1,&sockptr->MsgData[1], sockptr->MsgDataLength - 1);
        
		return 0;

	case 'm':
     
       //   Toggle Monitor receive

		if (sockptr->AGWRXHeader.DataLength == 12)			// QtTermTCP monitor info
		{
//				Msg[AGWHDDRRLEN] = AGWUsers->MonSess->mlocaltime;
//	Msg[AGWHDDRRLEN + 1] = AGWUsers->MonSess->MonitorNODES;
	//Msg[AGWHDDRRLEN + 2] = AGWUsers->MonSess->MonitorColour;
//	Msg[AGWHDDRRLEN + 3] = AGWUsers->MonSess->mtxparam;
//	memcpy(&Msg[AGWHDDRRLEN + 4], (void *)&AGWUsers->MonSess->portmask, 8);
			sockptr->useLocalTime = sockptr->MsgData[0];
			sockptr->doNodes =  sockptr->MsgData[1];
			sockptr->MonFlag = 1;
		}
		else
			sockptr->MonFlag = !sockptr->MonFlag;

		return 0;
    
  
	case 'M':
	case 'V':         // Send UNProto Frame "V" includes Via string
  
   
        ConvToAX25(sockptr->AGWRXHeader.callto,TXMessage);
		ConvToAX25(sockptr->AGWRXHeader.callfrom,&TXMessage[7]);

		Digis=0;
        MsgStart = 0;

        if (sockptr->AGWRXHeader.DataKind == 'V')	// Unproto with VIA string
		{        
            Digis = sockptr->MsgData[0];                 // Number of digis
                    
			for (j = 1; j<= Digis; j++)
			{
				ConvToAX25(&sockptr->MsgData[(j - 1) * 10 + 1],&TXMessage[7+(j*7)]);      // No "last" bit
			}

			// set end of call 

           MsgStart = Digis * 10 + 1;                // UI Data follows digis in message
 		}
   
		TXMessageptr=&TXMessage[13+(Digis*7)];

		*(TXMessageptr++) |= 1;		// set last bit
        
		*(TXMessageptr++) = 3;     // UI

        if (sockptr->AGWRXHeader.PID == 0)

            *(TXMessageptr++) = 240;		 // PID
		else
            *(TXMessageptr++) = sockptr->AGWRXHeader.PID; 
   
        memcpy(TXMessageptr,&sockptr->MsgData[MsgStart], sockptr->MsgDataLength - MsgStart);
        
		TXMessageptr += (sockptr->MsgDataLength - MsgStart);

        SendRaw(sockptr->AGWRXHeader.Port + 1, TXMessage, (int)(TXMessageptr-&TXMessage[0]));

		return 0;

	case 'X':
 
        //   Register Callsign
        
		memset(&AGWTXHeader,0,36);
		
		memset(RegCall,0,10);
		memcpy(RegCall, sockptr->AGWRXHeader.callfrom, strlen(sockptr->AGWRXHeader.callfrom));  

		if (sockptr->CallSign1[0] == 0)
			memcpy(sockptr->CallSign1, RegCall, 10);  
		else
			memcpy(sockptr->CallSign2, RegCall, 10); 

        AGWTXHeader.DataKind = 'X';
        
        memcpy(&AGWTXHeader.callfrom, RegCall, 10);

        AGWTXHeader.DataLength = 1;      // Length
             
        AGWRegReply[0] = 1;

        SendtoSocket(sockptr->socket, AGWRegReply);

		ShowApps();


		return 0;

   
	case 'Y':
    
        //   Session Status
        
        //   Create Session Key from port and callsign pair
        
        AGWGetSessionKey(key, sockptr);

        for (con = 0; con < CurrentConnections; con++)
		{
			if (memcmp(AGWConnections[con].CallKey,key,21) == 0)
			{
				memcpy(&AGWTXHeader,&sockptr->AGWRXHeader,36);

				AGWYReply = CountFramesQueuedOnStream(AGWConnections[con].BPQStream);
				AGWTXHeader.DataLength = 4;      // Length
				SendtoSocket(sockptr->socket, (char *)&AGWYReply);

				return 0;
			}
		}

		return 0;


	default:
    
        //If Debugging Then Print #10, "Unknown Message "; Chr$(Sockets(Index).AGWRXHeader(4))
       // Debug.Print "Unknown Message "; Chr$(Sockets(Index).AGWRXHeader(4))
        
		return 0;

		}

	return 0;
   
}

int AGWGetSessionKey(char * key, struct AGWSocketConnectionInfo * sockptr)
{

//   Create Session Key from port and callsign pair
        
  

	key[0] = sockptr->AGWRXHeader.Port + '1'; 
        
	memset(&key[1],0,20);
	strcpy(&key[1],sockptr->AGWRXHeader.callfrom);
	strcpy(&key[11],sockptr->AGWRXHeader.callto);

	return 0;

}
int SendDataToAppl(int Stream, byte * Buffer, int Length)
{
	int con;
	char * i;
	char ConMsg[80];
	char DisMsg[80];
	char key[21];
	struct AGWSocketConnectionInfo * sockptr;

//Dim i As Long, Length As Long, con As Long, key As String, hilen As Long, lolen As Long
//Dim Index As Integer, ConMsg As String, DisMsg As String
//Dim BytesSent As Long


	//'   Find Connection number and call pair

	for (con = 0; con < CurrentConnections; con++)
	{
		if (AGWConnections[con].BPQStream == Stream)
		{
			memcpy(key,&AGWConnections[con].CallKey,21);

			if (key[0] == 32)
			{
				//Debug.Print "Data on Unconnected Session"

				Disconnect(Stream);
				return (0);
			}
        
			sockptr = AGWConnections[con].SocketIndex;
			
			if (sockptr == 0)
			{
				// No connection, but have a key - wot's going on!!
				// Probably best to clear out connection

				Disconnect(Stream);

				return (0);
			}

			AGWTXHeader.Port = key[0] - 49;

			memcpy(AGWTXHeader.callfrom,&key[11],10);
			memcpy(AGWTXHeader.callto,&key[1],10);

			if (AGWConnections[con].Connecting)
			{

            //   See if *** Connected message

				i = strstr(Buffer, "Connected to");
            
				if (i != 0)
				{  
					AGWConnections[con].Connecting = FALSE;
                
					DisplaySessions();

					AGWTXHeader.DataKind = 'C';
	                AGWTXHeader.PID = 0;
                
		            strcpy(ConMsg,"*** CONNECTED With Station ");
					strcat(ConMsg, AGWTXHeader.callfrom);
					strcat(ConMsg,"\r");
                
					AGWTXHeader.DataLength = (int)strlen(ConMsg)+1;
           
					SendtoSocket(sockptr->socket, ConMsg);
            
					return (0);

				}
            
				i = strstr(Buffer, "Failure with");
            
				if (i != 0)
				{   
					AGWConnections[con].Connecting = FALSE;
                               
					strcpy(DisMsg,"*** DISCONNECTED RETRYOUT With ");
        
					SendDisMsgtoAppl(DisMsg, sockptr);

					DeleteConnection(&AGWConnections[con]);
                
		            return 0;

				}
				
				i = strstr(Buffer, "Busy from");
            
				if (i != 0)
				{   
					AGWConnections[con].Connecting = FALSE;
                               
					strcpy(DisMsg,"*** DISCONNECTED RETRYOUT With ");
        
					SendDisMsgtoAppl(DisMsg, sockptr);
					
					DeleteConnection(&AGWConnections[con]);
                
		            return 0;

				}
			}

	        AGWTXHeader.DataKind = 'D';
		    AGWTXHeader.PID = 0xF0;
			AGWTXHeader.DataLength = Length;              
        
			SendtoSocket(sockptr->socket, Buffer);
		}
	}

	return 0;
 }


int AGWDataSocket_Disconnect(struct AGWSocketConnectionInfo * sockptr)
{
	int con;

	closesocket(sockptr->socket);

	for (con = 0; con < CurrentConnections; con++)
	{
		if (AGWConnections[con].SocketIndex == sockptr)
			Disconnect(AGWConnections[con].BPQStream);
	}

	sockptr->SocketActive = FALSE;
	sockptr->RawFlag = FALSE;
	sockptr->MonFlag = FALSE;
	
	ShowApps();


	return 0;
}

int SendRawPacket(struct AGWSocketConnectionInfo * sockptr, char *txmsg, int Length)
{
	SendtoSocket(sockptr->socket, txmsg);

	return 0;
}

int ShowApps()
{
/*
	struct AGWSocketConnectionInfo * sockptr;
	int i;
	char Msg[80];
	char IPAddr[20];

	if (ConnWnd == 0) return 0; // Not on display
	
	SendDlgItemMessage(ConnWnd,IDC_CONNECTIONS_LIST,LB_RESETCONTENT,0,0);

	for (i = 1; i <= CurrentSockets; i++)
	{
		sockptr=&Sockets[i];

		if (sockptr->SocketActive)
		{
			sprintf(IPAddr,"%d.%d.%d.%d",
				sockptr->sin.sin_addr.S_un.S_un_b.s_b1,
				sockptr->sin.sin_addr.S_un.S_un_b.s_b2,
				sockptr->sin.sin_addr.S_un.S_un_b.s_b3,
				sockptr->sin.sin_addr.S_un.S_un_b.s_b4);

			sprintf(Msg,"%2d   %-16s %5d %-10s",i,IPAddr,htons(sockptr->sin.sin_port),&sockptr->CallSign);
		}
		else
		{
			sprintf(Msg,"%2d   Idle",i);
		}

		SendDlgItemMessage(ConnWnd,IDC_CONNECTIONS_LIST,LB_ADDSTRING,0,(LPARAM) Msg);
	}
*/
	return 0;
}


int LocalSessionState(int stream, int * state, int * change, BOOL ACK);

int AGWAPITerminate()
{
	int con, State, Change, n;
	struct BPQConnectionInfo * Connection;
	struct AGWSocketConnectionInfo * sockptr;
//
//   Release all streams
//
	for (con = 0; con < CurrentConnections; con++)
	{
		Connection=&AGWConnections[con];

        SetAppl(Connection->BPQStream, 0, 0);

        Disconnect(Connection->BPQStream);

        DeallocateStream(Connection->BPQStream);
    
        LocalSessionState(Connection->BPQStream, &State, &Change, TRUE);

		memset(Connection, 0, sizeof(struct AGWSocketConnectionInfo));
		    
	}

	CurrentConnections = 0;

	// Close Listening socket and any connections
	
	shutdown(agwsock, 2);
	closesocket(agwsock);

	for (n = 1; n <= MaxSockets; n++)
	{
		sockptr=&Sockets[n];
		
		if (sockptr->SocketActive)
		{
			SOCKET sock = sockptr->socket;

			shutdown(sock, 2);
			closesocket(sock);
		}
		memset(sockptr, 0, sizeof(struct BPQConnectionInfo));
	}

	return 0;
}

