/*
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
*/	

// DRATS support code

#define _CRT_SECURE_NO_DEPRECATE

#include "cheaders.h"

#include "bpq32.h"
#include "telnetserver.h"


/*
The header is the first 23 bytes of of the frame. The payload is the rest of the frame.

Byte 1 is a "magic" number. It is 0xDD if the payload is zlib compressed before being yencoded.
Bytes 2 and 3 is a 16 bit sequence number.
Byte 4 is a session number.
Byte 5 is a type. Still don't know the types.
Bytes 6 and 7, 16 bits, is the checksum of the data after any compression.
Bytes 8 and 9, 16 bits, is the length of the data.
bytes 10-18, 8 bits, are the source call sign.
bytes 19-25, 8 bits, are the destination call sign.
If a call sign is less than 8 characters, it is padded to fill the space with the "~" tilde character.

Frame types: (Some from sessions/chat.py)

0 - T_DEF
1 - T_PING_REQ - Ping request (Used in Test frame)
2 - T_PING_RSP - Ping response
3 - T_PING_ERS - Ping error status?
4 - T_STATUS - Status frame
5 - File Transfer
8 - Used in Test frame
254 - Apparently a warm up frame.

	T_DEF = 0
    T_PNG_REQ = 1
    T_PNG_RSP = 2
    T_PNG_ERQ = 3
    T_PNG_ERS = 4
    T_STATUS  = 5
For a station status frame, the first byte of the message is an ASCII station status value.

'0' - Unknown
'1' - Online
'2' - Unattended
'9' - Offline
*/

#define	T_DEF 0
#define T_PNG_REQ 1
#define T_PNG_RSP 2
#define T_PNG_ERQ 3
#define T_PNG_ERS 4
#define T_STATUS 5

#pragma pack(1)

// shorts are big-endian

struct DRATSHeader
{
	unsigned char Magic;
	unsigned short Seq;
	unsigned char Sessno;
	unsigned char Type;
	unsigned short CheckSum;
	unsigned short Length;
	char CallFrom[8];
	char CallTo[8];
	unsigned char Message[2048];
};

#pragma pack()

struct DRATSSession
{
	struct ConnectionInfo * sockptr;
	unsigned int Seq;
	unsigned int Sessno;
	char CallFrom[8];
	char CallTo[8];
	int Stream;					// BPQ Stream
	int StreamState;
	struct DRATSQueue * Queue;
	struct DRATSSession * Next; 
};

struct DRATSQueue
{
	// Queue of messages to be sent to node from background (ie not under semaphore)

	int Stream;
	int Len;
	unsigned char * Msg;
	struct DRATSQueue * Next;
};


struct DRATSSession * DRATSSessions = NULL; 


char peer0_2[] = { /* Packet 17 */
0x5b, 0x53, 0x4f, 0x42, 0x5d, 0xdd, 0x3d, 0x40, 
0x3d, 0x40, 0x01, 0x05, 0x45, 0x78, 0x3d, 0x40, 
0x18, 0x47, 0x38, 0x42, 0x50, 0x51, 0x7e, 0x7e, 
0x7e, 0x43, 0x51, 0x43, 0x51, 0x43, 0x51, 0x7e, 
0x7e, 0x78, 0xda, 0x33, 0xf4, 0xcf, 0xcb, 0xc9, 
0xcc, 0x4b, 0x55, 0xd0, 0x70, 0xd1, 0x0d, 0x72, 
0x0c, 0x09, 0xd6, 0x04, 0x3d, 0x40, 0x2a, 0x8c, 
0x04, 0xb3, 0x5b, 0x45, 0x4f, 0x42, 0x5d };


void processDRATSFrame(unsigned char * Message, int Len, struct ConnectionInfo * sockptr);

int testDRATS()
{
//	processDRATSFrame(peer0_1, sizeof(peer0_1), 0);
//	processDRATSFrame(peer0_2, sizeof(peer0_2), 0);
//	processDRATSFrame(peer1_1, sizeof(peer1_1), 0);
//	processDRATSFrame(peer1_20, sizeof(peer1_20));
//	processDRATSFrame(peer0_20, sizeof(peer0_20));
//	processDRATSFrame(peer1_21, sizeof(peer1_21));

	return 0;
}


extern char pgm[256];	
extern char TextVerstring[50];

int HeaderLen = offsetof(struct DRATSHeader, Message);

int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen);
int dratscrc(unsigned char *ptr, int count);
int FindFreeStreamNoSem();
void sendDRATSFrame(struct ConnectionInfo * sockptr, struct DRATSHeader * Header);
int yEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned);


int AllocateDRATSStream(struct DRATSSession * Sess)
{
	int Stream;

	strcpy(pgm, "DRATS");

	Stream = FindFreeStreamNoSem();

	strcpy(pgm, "bpq32.exe");

	if (Stream == 255) return 0;

	if (memcmp(Sess->CallTo, "NODE", 4) == 0)
	{
		//  Just connect to command level on switch
	}

	return Stream;
}

void ProcessDRATSPayload(struct DRATSHeader * Header, struct DRATSSession * Sess)
{
	struct DRATSQueue * QEntry;
	BPQVECSTRUC * HOST;

	if (Sess->Stream == 0)
	{
		Sess->Stream = AllocateDRATSStream(Sess);
	}

	if (Sess->StreamState == 0)
	{
		unsigned char AXCall[10];

		Connect(Sess->Stream);				// Connect
		ConvToAX25(Sess->CallFrom, AXCall);
		ChangeSessionCallsign(Sess->Stream, AXCall);		// Prevent triggering incoming connect code

		// Clear State Changed bits (cant use SessionState under semaphore)

		HOST = &BPQHOSTVECTOR[Sess->Stream -1]; // API counts from 1
		HOST->HOSTFLAGS &= 0xFC;		  // Clear Change Bits
		Sess->StreamState = 1;
	}

	strcat(Header->Message, "\r");

	// Need to Queue to Background as we can't use SendMsg under semaphore

	QEntry = zalloc(sizeof(struct DRATSQueue));
	QEntry->Len = strlen(Header->Message);
	QEntry->Msg = malloc(QEntry->Len);
	memcpy(QEntry->Msg, Header->Message, QEntry->Len);

	// Add to queue

	if (Sess->Queue)
	{
		struct DRATSQueue * End = Sess->Queue;

		// Add on end
		while (End->Next)
			End = End->Next;

		End->Next = QEntry;
	}
	else
		Sess->Queue = QEntry;

}

// Called under semaphore


void processDRATSFrame(unsigned char * Message, int Len, struct ConnectionInfo * sockptr)
{
	unsigned char * Payload;
	unsigned char * ptr;
	unsigned char dest[2048];
	struct DRATSHeader * Header;
	int outLen;
	struct DRATSSession * Sess = DRATSSessions;
	unsigned short crc, savecrc;
	char CallFrom[10] = "";
	char CallTo[10] = "";

	Message[Len] = 0;
	Debugprintf(Message);

	Payload = strstr(Message, "[SOB]");
	
	if (Payload == 0)
		return;

	ptr = strstr(Message, "[EOB]");

	if (ptr == 0)
		return;

	ptr[0] = 0;

	Payload += 5;

	Header = (struct DRATSHeader *)Payload;

	// Undo = transparency

	ptr = Payload;

	while (ptr = strchr(ptr, '='))
	{
		memmove(ptr, ptr + 1, Len);
		ptr[0] -= 64;
		ptr++;
	}

	// Check CRC

	savecrc = htons(Header->CheckSum);
	Header->CheckSum = 0;

	crc = dratscrc(Payload, htons(Header->Length) + HeaderLen);

	if (crc != savecrc)
	{
		Debugprintf(" DRARS CRC Error %x %x", crc, savecrc);		// Good CRC
		return;
	}

	Header->Length = htons(Header->Length);		// convert to machine order

	if (Header->Magic == 0xdd)		// Zlib compressed
	{
		doinflate(Header->Message, dest,  Header->Length, 2048, &outLen);
		memcpy(Header->Message, dest, outLen + 1);
		Header->Length = outLen;
	}
	Debugprintf(Header->Message);

	// Look for a matching From/To/Session

	memcpy(CallFrom, Header->CallFrom, 8);
	memcpy(CallTo, Header->CallTo, 8);

	strlop(CallFrom, '~');
	strlop(CallTo, '~');

	if (Header->Type == T_STATUS)
	{
		// Status frame ?? What to do with it ??

		return;
	}

	if (Header->Type == T_PNG_REQ)
	{
		// "Ping Request"

		// if to "NODE" reply to it

		if (strcmp(CallTo, "NODE") == 0)
		{
			// Reuse incoming message

			strcpy(Header->CallFrom, CallTo);
			strcpy(Header->CallTo, CallFrom);
			Header->Type = T_PNG_RSP;
			Header->Length = sprintf(Header->Message, "Running BPQ32 Version %s", TextVerstring);

			sendDRATSFrame(sockptr, Header);
			return;
		}

		// Not to us - do we route it ??

		return;
	}

	if (Header->Type == T_PNG_RSP)
	{
		// Reponse is PNG_RSP then Status - 1Online (D-RATS)
		// "Running D-RATS 0.3.9  (Windows 8->10 (6, 2, 9200, 2, ''))"
		// "Running D-RATS 0.3.10 beta 4  (Linux - Raspbian GNU/Linux 9)"

		return;
	}

	if (Header->Type != T_DEF)
	{
		return;
	}

	// ?? Normal Data

	if (strcmp(CallTo, "NODE") != 0)
	{
		// Not not Node - should we route it ??

		return;
	}

	while (Sess)
	{
		if (Sess->Sessno == Header->Sessno && memcmp(Sess->CallFrom, CallFrom, 8) == 0
			&& memcmp(Sess->CallTo, CallTo, 8) == 0 && Sess->sockptr == sockptr)
		{
			ProcessDRATSPayload(Header, Sess);
			return;
		}
		Sess = Sess->Next;
	}

	// Allocate a new one

	Sess = zalloc(sizeof(struct DRATSSession));

	Sess->Sessno = Header->Sessno;
	memcpy(Sess->CallFrom, CallFrom, 8);
	memcpy(Sess->CallTo, CallTo, 8);
	Sess->sockptr = sockptr;

	if (DRATSSessions)
	{
		// Add to front of Chain

		Sess->Next = DRATSSessions;
	}
	
	DRATSSessions = Sess;	

	ProcessDRATSPayload(Header, Sess);
	return;

}

void DRATSPoll()
{
	struct DRATSSession * Sess = DRATSSessions;
	int Stream, state, change;
	int count;
	struct DRATSHeader Header;
	struct DRATSQueue * QEntry;
	struct DRATSQueue * Save;

	while (Sess)
	{
		Stream = Sess->Stream;	
		SessionState(Stream, &state, &change);
	
		if (change == 1)
		{
			if (state == 1)
			{
				// Connected - do we need anything ??
			}
			else
			{
				// Send a disconnected message

				char From[10] = "~~~~~~~~~";
				char To[10] = "~~~~~~~~~";
		
				Sess->StreamState = 0;

				Header.Length = sprintf(Header.Message, "*** Disconnected from Node");


				memcpy(To, Sess->CallFrom, strlen(Sess->CallFrom));
				memcpy(From, Sess->CallTo, strlen(Sess->CallTo));

				memcpy(Header.CallFrom, From, 8);
				memcpy(Header.CallTo, To, 8);

				Header.Magic = 0x22;
				Header.Type = 0;
				Header.Seq = 0;
				Header.Sessno = Sess->Sessno;

				sendDRATSFrame(Sess->sockptr, &Header);


			}
		}

		do
		{ 
			int Len;
			
			GetMsg(Stream, (char *)Header.Message, &Len, &count);
			Header.Length = Len;
	
			if (Header.Length)
			{
				char From[10] = "~~~~~~~~~";
				char To[10] = "~~~~~~~~~";

				memcpy(To, Sess->CallFrom, strlen(Sess->CallFrom));
				memcpy(From, Sess->CallTo, strlen(Sess->CallTo));

				memcpy(Header.CallFrom, From, 8);
				memcpy(Header.CallTo, To, 8);

				Header.Magic = 0x22;
				Header.Type = 0;
				Header.Seq = 0;
				Header.Sessno = Sess->Sessno;

				sendDRATSFrame(Sess->sockptr, &Header);
			}
		}
		while (count > 0);

		// See if anything to send to node

		QEntry = Sess->Queue;

		while (QEntry)
		{
			SendMsg(Sess->Stream, QEntry->Msg, QEntry->Len);
			Save = QEntry;	
			QEntry = QEntry->Next;
			free(Save->Msg);
			free(Save);
		}

		Sess->Queue = 0;
		Sess = Sess->Next;
	}
}

unsigned char BANNED[] = {'=', 0x11, 0x13, 0x1A, 0xFD, 0xFE, 0xFF, 0};


void sendDRATSFrame(struct ConnectionInfo * sockptr, struct DRATSHeader * Header)
{
	unsigned short crc;
	int len;
	unsigned char out[2048] = "[SOB]";
	int packetLen = Header->Length + HeaderLen;

	// Length is in host order

	Header->Length = htons(Header->Length);

	Header->CheckSum = 0;

	crc = dratscrc((unsigned char *)Header, packetLen);
	Header->CheckSum = htons(crc);

	len = yEncode((unsigned char *)Header, out + 5, packetLen, BANNED);

	memcpy(&out[len + 5], "[EOB]", 5);
	Debugprintf(out);
	send(sockptr->socket, out, len + 10, 0);
}

void DRATSConnectionLost(struct ConnectionInfo * sockptr)
{
	// Disconnect any sessions, then free Stream and Sess record
	
	struct DRATSSession * Sess = DRATSSessions;
	struct DRATSSession * Save = 0;
	BPQVECSTRUC * HOST;

	while (Sess)
	{
		if (Sess->sockptr == sockptr)
		{
			if (Sess->StreamState == 1)	// COnnected
			{
				Disconnect(Sess->Stream);
				HOST = &BPQHOSTVECTOR[Sess->Stream -1]; // API counts from 1
				HOST->HOSTFLAGS &= 0xFC;		  // Clear Change Bits
			}
			DeallocateStream(Sess->Stream);

			// We must unhook from chain

			if (Save)
				Save->Next = Sess->Next;
			else
				DRATSSessions = Sess->Next;

			// Should really Free any Queue, but unlikely to be any
			
			free(Sess);

			if (Save)
				Sess = Save->Next;
			else
				Sess = DRATSSessions;
		}
		else
		{
			Save = Sess;
			Sess = Sess->Next;
		}
	}
}




#ifdef WIN32
#define ZEXPORT __stdcall
#endif

#include <zlib.h>


int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen)
{
	int ret;
	z_stream strm;

	strm.zalloc = Z_NULL;
	strm.zfree = Z_NULL;
	strm.opaque = Z_NULL;
	strm.avail_in = 0;
	strm.next_in = Z_NULL;

	ret = inflateInit(&strm);

	if (ret != Z_OK)
		return ret;

	strm.avail_in = Len;
	strm.next_in = source;

	strm.avail_out = destlen;
	strm.next_out = dest;

	ret = inflate(&strm, Z_NO_FLUSH);

	inflateEnd(&strm);

	dest[strm.total_out] = 0;

	*outLen = strm.total_out;

    return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}

// No idea what this CRC is, but it works! (converted from DRATS python code)

int update_crc(int c, int crc)
{
	int i;
	int v;

	for (i = 0; i < 8; i++)
	{
		if ((c & 0x80))
			v = 1;
		else
			v = 0;

		if (crc & 0x8000)
		{
			crc <<= 1;
			crc += v;
			crc ^= 0x1021;
		}
		else
		{
			crc <<= 1;
			crc += v;
		}

		c <<= 1;
	}

	crc &= 0xFFFF;
	return crc;

}

int dratscrc(unsigned char *ptr, int count)
{
	int i;	
	int checksum = 0;
    
	for (i = 0; i < count; i++)
        checksum = update_crc(ptr[i], checksum);

    checksum = update_crc(0, checksum);
    checksum = update_crc(0, checksum);
    return checksum;
}

#define OFFSET 64

int yEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned)
{
	unsigned char * ptr = out;
	unsigned char c;

	while (len--)
	{
		c = *(in++);

		if (strchr(&Banned[0], c))
		{
			*(out++) = '=';
			*(out++) = (c + OFFSET) & 0xFF;
		}
		else
			*(out++) = c;
	}

	return (out - ptr);
}




