diff --git a/.svn/entries b/.svn/entries new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/.svn/entries @@ -0,0 +1 @@ +12 diff --git a/.svn/format b/.svn/format new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/.svn/format @@ -0,0 +1 @@ +12 diff --git a/.svn/pristine/00/0091dc3ec6a47e062a4f7f1c098c31c1e17d2eae.svn-base b/.svn/pristine/00/0091dc3ec6a47e062a4f7f1c098c31c1e17d2eae.svn-base new file mode 100644 index 0000000..6c6b3fc --- /dev/null +++ b/.svn/pristine/00/0091dc3ec6a47e062a4f7f1c098c31c1e17d2eae.svn-base @@ -0,0 +1,1849 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// Interface to allow G8BPQ switch to use HSMODEM TNC + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifndef WIN32 +#ifndef MACBPQ +#include +#endif +#endif + + +#include "cheaders.h" + +#pragma pack(1) + +struct BroadcastMsg +{ + unsigned char Type; + unsigned char initialVolTX; + unsigned char initialVolRX; + unsigned char AudioTimespan; + unsigned char intialVolSpeaker; + unsigned char initalVolMic; + unsigned char Retransmits; + unsigned char SendAudio; + unsigned char RTTYAutoSync; + unsigned char Speed; + char playbackDevice[100]; + char captureDevice[100]; + char Callsign[20]; + char Locator[10]; + char Name[20]; +}; + +struct FileHeader +{ + unsigned char Type; + unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only + char filename[50]; + unsigned short CRC; // of filename = transfer id + unsigned char Size[3]; // Big endian + unsigned char Data[164]; +}; + +struct FileData +{ + unsigned char Type; + unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only + unsigned char Data[219]; +}; + +#pragma pack() + +struct HSFILEINFO +{ + struct HSFILEINFO * Next; // May want to chain entries for partial files + + char fileName[50]; + unsigned short CRC; // Used as a transfer ID + int fileSize; + int Sequence; + int State; + int Type; + time_t LastRX; + unsigned char goodBlocks[1024]; + unsigned char * Data; + int dataPointer; + int lastBlock; + int lostBlocks; + unsigned char * txData; + int txSize; + int txLeft; +}; + + +struct HSMODEMINFO +{ + struct HSFILEINFO * File; + + int Mode; + char * Capture; // Capture Device Name + char * Playback; // Playback Device Name + int Seq; // To make CRC more Unique + int txFifo; + int rxFifo; + int Sync; + int DCD; +}; + + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + +#include "bpq32.h" + +#include "tncinfo.h" + +static int Socket_Data(int sock, int error, int eventcode); + +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); +BOOL HSMODEMWriteCommBlock(struct TNCINFO * TNC); +void HSMODEMCheckRX(struct TNCINFO * TNC); +int HSMODEMSendData(struct TNCINFO * TNC, UCHAR * data, int txlen); +int HSMODEMSendSingleData(struct TNCINFO * TNC, UCHAR * FN, UCHAR * data, int txlen); +int HSMODEMSendCommand(struct TNCINFO * TNC, UCHAR * data); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int HSMODEMGetLine(char * buf); +int ProcessEscape(UCHAR * TXMsg); +BOOL KAMStartPort(struct PORTCONTROL * PORT); +BOOL KAMStopPort(struct PORTCONTROL * PORT); +void SendPoll(struct TNCINFO * TNC); +void SendMode(struct TNCINFO * TNC); + +static char ClassName[]="HSMODEMSTATUS"; +static char WindowTitle[] = "HSMODEM"; +static int RigControlRow = 205; + +#ifndef LINBPQ +#include +#endif + +extern int SemHeldByAPI; + +static RECT Rect; + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC = TNCInfo[Port]; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if (ptr == NULL) return (TRUE); + + if (*ptr =='#') return (TRUE); // comment + + if (*ptr ==';') return (TRUE); // comment + + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->HSModemInfo = zalloc(sizeof(struct HSMODEMINFO)); + TNC->HSModemInfo->File = zalloc(sizeof(struct HSFILEINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->TCPPort = WINMORport; + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport + 2); // We only receive on Port + 2 + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "CAPTURE", 7) == 0) + { + TNC->HSModemInfo->Capture = _strdup(&buf[8]); + strlop(TNC->HSModemInfo->Capture, 13); + } + else if (_memicmp(buf, "PLAYBACK", 8) == 0) + { + TNC->HSModemInfo->Playback = _strdup(&buf[9]); + strlop(TNC->HSModemInfo->Playback, 13); + } + else if (_memicmp(buf, "MODE ", 5) == 0) + TNC->HSModemInfo->Mode = atoi(&buf[5]); + else if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + else + strcat (TNC->InitScript, buf); + } + + return (TRUE); +} + +static char * Config; +static char * ptr1, * ptr2; + +int HSMODEMGetLine(char * buf) +{ +loop: + + if (ptr2 == NULL) + return 0; + + memcpy(buf, ptr1, ptr2 - ptr1 + 2); + buf[ptr2 - ptr1 + 2] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + if (buf[0] < 0x20) goto loop; + if (buf[0] == '#') goto loop; + if (buf[0] == ';') goto loop; + + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + buf[strlen(buf)] = 13; + + return 1; +} + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static time_t ltime; + + + +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // HSMODEM mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +VOID HSMODEMChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + + datalen = sprintf(TXMsg, "MYCALL %s\r", Call); + HSMODEMSendCommand(TNC, TXMsg); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int bytes,txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + + if (TNC->CONNECTED) + { + TNC->CONNECTED--; + + if (TNC->CONNECTED == 0) + { + sprintf(TNC->WEB_COMMSSTATE, "Connection to HSMODEM lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + } + + TNC->PollDelay++; + + if (TNC->PollDelay > 20) + { + TNC->PollDelay = 0; + + SendPoll(TNC); + } + + return 0; + + case 1: // poll + + HSMODEMCheckRX(TNC); + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + int CallLen; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + +/* if (TNC->CONNECTED == 0 || + TNC->Streams[0].Connecting || + TNC->Streams[0].Connected) + { + // discard if TNC not connected or sesison active + + ReleaseBuffer(buffptr); + continue; + } +*/ + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + // Frame has ax.25 format header. Convert to Text + + CallLen = ConvFromAX25(Buffer + 7, Call); // Origin + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + *ptr++ = '!'; + + CallLen = ConvFromAX25(Buffer, Call); // Dest + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + Buffer += 14; // TO Digis + datalen -= 14; + + while ((Buffer[-1] & 1) == 0) + { + *ptr++ = ','; + CallLen = ConvFromAX25(Buffer, Call); + memcpy(ptr, Call, CallLen); + ptr += CallLen; + Buffer += 7; // End of addr + datalen -= 7; + } + + *ptr++ = '_'; + *ptr++ = 'U'; // UI Frame + *ptr++ = 0; // delimit calls + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + } + + HSMODEMSendSingleData(TNC, FECMsg, Buffer, datalen); + + ReleaseBuffer(buffptr); + } + + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + } + } + + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + HSMODEMSendCommand(TNC, "DISCONNECT\r"); + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("HSMODEM New Attach Stream %d", Stream); + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + + HSMODEMChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + //sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + //MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + STREAM->bytesTXed += txlen; + + bytes=HSMODEMSendData(TNC, data, txlen); + WritetoTrace(TNC, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 21; + memcpy(&buffptr->Data[0], "No Connection to TNC\r", 21); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + TXMsg = &buff->L2DATA[0]; + TXMsg[txlen] = 0; + + // for now just send, but allow sending control + // characters with \\ or ^ escape + + if (STREAM->Connected) + { + STREAM->PacketsSent++; + + bytes=HSMODEMSendData(TNC, TXMsg, txlen); + TNC->Streams[Stream].BytesOutstanding += bytes; // So flow control works - will be updated by BUFFER response + STREAM->bytesTXed += bytes; +// WritetoTrace(TNC, &buff->L2DATA[0], txlen); + + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + char cmd[56]; + + strcpy(cmd, &buff->L2DATA[6]); + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, cmd); + + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "HSMODEM} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "MODE ", 5) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->HSModemInfo->Mode = atoi(&buff->L2DATA[5]); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "HSMODEM} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + SendMode(TNC); + + return 0; + + } + + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + txlen = sprintf(Connect, "C %s\r", &buff->L2DATA[2]); + + HSMODEMChangeMYC(TNC, TNC->Streams[0].MyCall); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + HSMODEMSendCommand(TNC, Connect); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = TRUE; + return 0; + + } + + // Normal data. Send to TNC + + + HSMODEMSendData(TNC, TXMsg, txlen); + + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // I think we should check buffer space for all comms modes + + { + int Queued; + int Outstanding = TNC->Streams[Stream].BytesOutstanding; + + if (Stream == 0) + Queued = TNC->Streams[13].FramesQueued; // ARDOP Native Mode Send Queue + else + Queued = TNC->Streams[Stream].FramesQueued; + + Outstanding = Queued = 0; + + if (Queued > 4 || Outstanding > 8500) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + + if (TNC->Streams[Stream].Attached == 0) + return (TNC->CONNECTED != 0) << 8 | 1; + + return ((TNC->CONNECTED != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + + case 4: // reinit7 + + return 0; + + case 5: // Close + + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on ARDOP"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + HSMODEMSendCommand(TNC, "CONOK OFF"); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + HSMODEMSendCommand(TNC, "CONOK ON"); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID HSMODEMReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + HSMODEMChangeMYC(TNC, TNC->NodeCall); + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID HSMODEMSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ + HSMODEMSendCommand(TNC, "CONOK OFF\r"); +} + +VOID HSMODEMReleasePort(struct TNCINFO * TNC) +{ + HSMODEMSendCommand(TNC, "CONOK ON\r"); +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "VARA Status" + "

HSMODEM Status" + "

", + TNC->Port); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +#ifndef LINBPQ + +#define BGCOLOUR RGB(236,233,216) +HBRUSH RedBrush = NULL; +HBRUSH GreenBrush; +HBRUSH BlueBrush; +static HBRUSH bgBrush = NULL; + +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + + + +static LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + PAINTSTRUCT ps; + HDC hdc; + + int i; + struct TNCINFO * TNC; + + HKEY hKey; + char Key[80]; + int retCode, disp; + + for (i=0; i<41; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->hDlg == hWnd) + break; + } + + if (TNC == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) { + + case WM_CREATE: + + break; + + case WM_PAINT: + + hdc = BeginPaint(hWnd, &ps); + + TextOut(hdc, 10, 162, "RX", 4); + TextOut(hdc, 10, 182, "TX", 4); + + if (TNC->HSModemInfo->Sync) + TextOut(hdc, 305, 162, "Sync", 4); + +// SelectObject(ps.hdc, RedBrush); + SelectObject(ps.hdc, GreenBrush); +// SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH)); + + Rectangle(ps.hdc, 40, 165, TNC->HSModemInfo->rxFifo + 42, 175); + SelectObject(ps.hdc, RedBrush); + Rectangle(ps.hdc, 40, 185, (TNC->HSModemInfo->txFifo * 10) + 42, 195); + + EndPaint(hWnd, &ps); + break; + + case WM_GETMINMAXINFO: + + if (TNC->ClientHeight) + { + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = TNC->ClientWidth; + mmi->ptMaxSize.y = TNC->ClientHeight; + mmi->ptMaxTrackSize.x = TNC->ClientWidth; + mmi->ptMaxTrackSize.y = TNC->ClientHeight; + } + + break; + + + case WM_MDIACTIVATE: + { + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + + if (TNC->hMenu) + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + +// SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + // call DrawMenuBar after the menu items are set + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)TNC->hMenu) + { + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") || strstr(TNC->ProgramPath, "VARA")) + { + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED); + + break; + } + } + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED); + } + + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case WINMOR_KILL: + + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + KillTNC(TNC); + RestartTNC(TNC); + break; + + case WINMOR_RESTARTAFTERFAILURE: + + TNC->RestartAfterFailure = !TNC->RestartAfterFailure; + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4); + RegCloseKey(hKey); + } + break; + + case ARDOP_ABORT: + + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SIZING: + case WM_SIZE: + + MoveWindows(TNC); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + + case SC_RESTORE: + + TNC->Minimized = FALSE; + break; + + case SC_MINIMIZE: + + TNC->Minimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +#endif + + + +VOID * HSMODEMExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + u_long param = 1; + int ret; + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + +#ifndef LINBPQ + + if (bgBrush == NULL) + { + bgBrush = CreateSolidBrush(BGCOLOUR); + RedBrush = CreateSolidBrush(RGB(255,0,0)); + GreenBrush = CreateSolidBrush(RGB(0,255,0)); + BlueBrush = CreateSolidBrush(RGB(0,0,255)); + } + +#endif + + Consoleprintf("HSMODEM Host %s %d", TNC->HostName, TNC->TCPPort); + + TNC->Port = port; + TNC->PortRecord = PortEntry; + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_HSMODEM; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (TNC->PacketChannels > 1) + TNC->PacketChannels = 1; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = HSMODEMSuspendPort; + TNC->ReleasePortProc = HSMODEMReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + // Set MYCALL + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,20,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + + + // Open and bind UDP socket + + TNC->TCPSock = socket(AF_INET,SOCK_DGRAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + WritetoConsole("Failed to create UDP socket for HSMODEM"); + ret = WSAGetLastError(); + } + else + ioctl (TNC->TCPSock, FIONBIO, ¶m); + + ret = bind(TNC->TCPSock, (struct sockaddr *) &TNC->destaddr, sizeof(struct sockaddr_in)); + + if (ret != 0) + { + // Bind Failed + + ret = WSAGetLastError(); + sprintf(Msg, "Bind Failed for UDP port %d - error code = %d", TNC->TCPPort + 2, ret); + WritetoConsole(Msg); + } + + +// SendInitScript(TNC); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[Stream].BytesOutstanding == 0) + HSMODEMSendCommand(TNC, "DISCONNECT\r"); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + HSMODEMSendCommand(TNC, "DISCONNECT\r"); +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + HSMODEMReleaseTNC(TNC); + } +} + +VOID HSMODEMAbort(struct TNCINFO * TNC) +{ + HSMODEMSendCommand(TNC, "ABORT\r"); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID HSMODEMDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + return; + } + + if (TNC->ReinitState == 3) + { + return; + } +} + +RECT Rect1 = {30, 160, 400, 195}; + + +VOID HSMODEMProcessTNCMessage(struct TNCINFO * TNC, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FileHeader * FH; + struct HSMODEMINFO * Modem = TNC->HSModemInfo; + struct HSFILEINFO * Info = Modem->File; + int fileLen, Seq, Offset; + + // Any message indicates Ok + + Msg[Len] = 0; + + if (TNC->CONNECTED == 0) + { + // Just come up + + sprintf(TNC->WEB_COMMSSTATE, "Connected to HSMODEM"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + SendMode(TNC); + } + + TNC->CONNECTED = 100; // time out after 10 secs + + /* + 3: responses to broadcast messages (see: GUI Interface: UDP/IP/Initialization) + 1: received payload data + 4: FFT data for a spectrum monitor + 5: IQ data for a constellation display + 6: received RTTY characters + */ + switch (Msg[0]) + { + case 1: + /* + Byte 0 ... 0x01 + Byte 1 ... frame type (which was inserted by the sender) + Byte 2 ... frame counter MSB + Byte 3 ... frame counter LSB (10 bits used) + Byte 4 ... frame information (which was inserted by the sender) + Byte 5 ... unused + Byte 6 ... measured line speed MSB + Byte 7 ... measured line speed LSB + Bytes 8-10 ... unused + Bytes 11-229 ... 219 bytes payload return; + + 1 … BER Test Pattern + 2 … Image + 3 … Ascii File + 4 … HTML File + 5 … Binary File + 6 … Voice Audio (for Codec 2 or Opus) + 7 … UserInfo + */ + + Seq = Msg[2] << 8 | Msg[3]; + + switch (Msg[1]) + { + case 1: + case 6: + case 7: + + Debugprintf("%d %d %02x %s %s %s", Msg[1], Seq, Msg[4], &Msg[11], &Msg[31], &Msg[41]); + return; + + case 2: + case 3: + case 4: + case 5: + + // File transfer types + + switch (Msg[4]) + { + case 0: + case 3: + + // File Header + + FH = (struct FileHeader *) &Msg[9]; + + if (FH->CRC == Info->CRC) + { + Debugprintf("Dup Header %X", Info->CRC); + return; + } + + Info->CRC = FH->CRC; + + fileLen = FH->Size[0] * 65536 + FH->Size[1] * 256 + FH->Size[2]; + + Info->Data = zalloc(fileLen + 512); + + if (Info->Data == NULL) + return; + + Info->fileSize = fileLen; + strcpy(Info->fileName, FH->filename); + + memset(Info->goodBlocks, 0, 1024); + Info->goodBlocks[0] = 1; + + Info->lastBlock = 0; + Info->lostBlocks = 0; + Info->LastRX = time(NULL); + + Debugprintf("%d %d %04X %02x %s %d %s", Msg[1], Seq, FH->CRC, Msg[4], + FH->filename, fileLen, FH->Data); + + memcpy(Info->Data, FH->Data, 164); + Info->dataPointer = 164; + break; + + case 1: + case 2: + + // Data Frame + + if (Seq == Info->lastBlock) + { + Debugprintf("Duplicate data frame %d", Seq); + return; + } + + Info->lastBlock++; + + if (Info->lastBlock != Seq) + Info->lostBlocks += Seq - Info->lastBlock; + + Info->goodBlocks[Seq] = 1; + + Offset = (Seq - 1) * 221 + 164; + + memcpy(&Info->Data[Offset], &Msg[11], 221); + + Debugprintf("%d %d %02x %s %d %s", Msg[1], Seq, Msg[4], &Msg[11]); + break; + + default: + + Debugprintf("%d %d %02x %s %d %s", Msg[1], Seq, Msg[4], &Msg[11]); + return; + + } + + // End of Data Frame Case + + if (Msg[4] == 2 || Msg[4] == 3) + { + // Last Frame - check file + + if (Info->lostBlocks == 0) + { + // filename is encoding of calls and frame type + + struct _MESSAGE * buffptr; + +// FILE * fp1 = fopen(Info->fileName, "wb"); +// int WriteLen; + +// if (fp1) +// { +// WriteLen = (int)fwrite(Info->Data, 1, Info->fileSize, fp1); +// fclose(fp1); +// } + + if (strchr(Info->fileName, '!')) + { + // Callsigns encoded in filename + + + + char * Origin = &Info->fileName[0]; + char * Type = strlop(Origin, '_'); + char * Dest = strlop(Origin, '!'); + unsigned char * Packet; + + + // Convert to ax.25 format + + buffptr = GetBuff(); + + // Convert to ax.25 format + + if (buffptr == 0) + return; // No buffers, so ignore + + Type = strlop(Origin, '_'); + + Packet = &buffptr->ORIGIN[0]; + + buffptr->PORT = TNC->Port; + buffptr->LENGTH = 16 + MSGHDDRLEN + Info->fileSize; + + ConvToAX25(Origin, buffptr->ORIGIN); + ConvToAX25(Dest, buffptr->DEST); + + + while (strchr(Dest, ',')) + { + Dest = strlop(Dest, ','); // Next digi + Packet += 7; + ConvToAX25(Dest, Packet); + buffptr->LENGTH += 7; + } + + Packet[6] |= 1; // Set end of address + + Packet += 7; + + *(Packet++) = 3; + *(Packet++) = 0xF0; + + memcpy(Packet, Info->Data, Info->fileSize); + time(&buffptr->Timestamp); + + BPQTRACE((MESSAGE *)buffptr, TRUE); + } + } + + return; + } + } + + return; + + case 4: // FFT data for a spectrum monitor + + Modem->txFifo = Msg[1]; + Modem->rxFifo = Msg[2]; + Modem->DCD = Msg[3]; + Modem->Sync = Msg[4]; + +#ifndef LINBPQ + InvalidateRect(TNC->hDlg, &Rect1, TRUE); +#endif + +// if (Info->Sync || Info->txFifo) +// Debugprintf("%d %d %d %d", Info->txFifo, Info->rxFifo, Info->DCD, Info->Sync); + /* + Byte 0 ... 0x04 + Byte 1 ... usage of the TX fifo (used by the transmitter to sync its data + output to the modem). This is a value between 0..255. During + an active transmission keep it above 4. + Byte 2 ... usage of RX fifo (not important, but can be displayed to the + user). A very high RX fifo usage indicates the the computer + is too slow for HSmodem. + Byte 3 ... 0 or 1. Indicates that an RF level was detected + Byte 4 ... 0 or 1. Indicates that the HSmodem receiver is synchronized + with a signal + Byte 5 ... maximum audio level (0..100%) of the audio input from the + transceiver. Can be used to detect clipping. + Byte 6 ... maximum audio level (0..100%) of the audio output to the + transceiver. Can be used to detect clipping. + Byte 7 ... in RTTY mode this is the auto-locked RTTY frequency MSB + Byte 8 ... and LSB + Byte 9 ... RTTY: 0=tx off, 1=txon + Byte 10 to the end ... FFT spectrum data, beginning at 0 Hz to 4kHz with + a resolution of 10 Hz +*/ + return; + + + case 5: // IQ data for a constellation display + return; + + case 6: //received RTTY characters + return; + } + + return; + + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", TNC->RXBuffer); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + +} + +extern char LOC[7]; + +void SendMode(struct TNCINFO * TNC) +{ + unsigned char Msg[221] = ""; + int ret; + + Msg[0] = 16; + Msg[1] = TNC->HSModemInfo->Mode; + + TNC->destaddr.sin_port = htons(TNC->TCPPort + 1); // Data Port + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + ret = sendto(TNC->TCPSock, (char *)&Msg, 221, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); + + return; +} + +void SendPoll(struct TNCINFO * TNC) +{ + struct BroadcastMsg PollMsg = {0x3c, 100, 100, 0, 50, 50, 1, 0, 0, 9}; + int ret; + + strcpy(&PollMsg.captureDevice[0], TNC->HSModemInfo->Capture); + strcpy(&PollMsg.playbackDevice[0], TNC->HSModemInfo->Playback); +// strcpy(&PollMsg.playbackDevice[0], "CABLE Input (VB-Audio Virtual Cable)"); + + strcpy(&PollMsg.Callsign[0], TNC->NodeCall); + strcpy(&PollMsg.Locator[0], LOC); + strcpy(&PollMsg.Name[0], "1234567890"); + + TNC->destaddr.sin_port = htons(TNC->TCPPort); // Command Port + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + ret = sendto(TNC->TCPSock, (char *)&PollMsg, 260, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); + + return; +} +/* + unsigned char Type; + unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only + char filename[50]; + unsigned short CRC; // of filename = transfer id + unsigned char Size[3]; // Big endian + unsigned char Data[163]; +*/ + +unsigned short int compute_crc(unsigned char *buf,int len); + +int HSMODEMSendSingleData(struct TNCINFO * TNC, UCHAR * FN, UCHAR * data, int txlen) +{ + struct FileHeader Msg; + unsigned short int crc; + int ret, fragLen = txlen; + struct HSMODEMINFO * Modem = TNC->HSModemInfo; + struct HSFILEINFO * Info = Modem->File; + + char Seq[60] = ""; + + sprintf(Seq, "%04X%s", Modem->Seq++, FN); + + crc = compute_crc(Seq, 60); + + crc ^= 0xffff; + + memset(&Msg, 0, sizeof(struct FileHeader)); + + Msg.Type = 5; // Binary Data + Msg.Info = 3; // Only Fragment + + if (txlen > 163) + { + // Need to send as multiple fragments + + fragLen = 164; + Info->txData = malloc(txlen + 512); + memcpy(Info->txData, data, txlen); + Info->txSize = txlen; + Info->txLeft = txlen - 164; + Msg.Info = 0; // First Fragment + } + + strcpy(Msg.filename, FN); + memcpy(Msg.Data, data, txlen); + memcpy(&Msg.CRC, &crc, 2); + Msg.Size[0] = txlen >> 16; + Msg.Size[1] = txlen >> 8;; + Msg.Size[2] = txlen; + + TNC->destaddr.sin_port = htons(TNC->TCPPort + 1); // Data Port + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + ret = sendto(TNC->TCPSock, (char *)&Msg, 221, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); + memset(&Msg, 0, sizeof(struct FileHeader)); + + return ret; +} + +int HSMODEMSendData(struct TNCINFO * TNC, UCHAR * data, int txlen) +{ + struct FileHeader Msg; + + memset(&Msg, 0, sizeof(struct FileHeader)); + + Msg.Type = 5; // Binary Data + Msg.Info = 3; // Only Fragment + + + return 0; +} + +void HSMODEMCheckRX(struct TNCINFO * TNC) +{ + int Len = 0; + unsigned char Buff[2000]; + + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + + Len = recvfrom(TNC->TCPSock, Buff, 2000, 0, (struct sockaddr *)&rxaddr, &addrlen); + + while (1) + { + if (Len == -1) + { +// Debugprintf("%d", GetLastError()); + Len = 0; + return; + } + TNC->RXLen = Len; + HSMODEMProcessTNCMessage(TNC, Buff, Len); + + Len = recvfrom(TNC->TCPSock, Buff, 2000, 0, (struct sockaddr *)&rxaddr, &addrlen); + + } + return; + + return; +} + +int HSMODEMWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + return 0; +} + + +int HSMODEMSendCommand(struct TNCINFO * TNC, UCHAR * data) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, data, (int)strlen(data)); + + return 0; +} + + \ No newline at end of file diff --git a/.svn/pristine/01/0166e0c783c5b8e5a6c55b1369dfe5459e2f71f2.svn-base b/.svn/pristine/01/0166e0c783c5b8e5a6c55b1369dfe5459e2f71f2.svn-base new file mode 100644 index 0000000..5a18429 --- /dev/null +++ b/.svn/pristine/01/0166e0c783c5b8e5a6c55b1369dfe5459e2f71f2.svn-base @@ -0,0 +1,243 @@ +/* +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 +*/ + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#define _CRT_SECURE_NO_DEPRECATE + +#include "compatbits.h" +#include +#include "asmstrucs.h" +#include "tncinfo.h" + +VOID __cdecl Debugprintf(const char * format, ...); + +#ifndef WIN32 + +#define APIENTRY +#define DllExport +#define VOID void + +#else +#include +#endif + +extern BOOL EventsEnabled; +void MQTTReportSession(char * Msg); +extern int MQTT; + + +extern char Modenames[19][10]; + +// Runs use specified routine on certain event +#ifndef WIN32 + +void RunEventProgram(char * Program, char * Param) +{ + char * arg_list[] = {Program, NULL, NULL}; + pid_t child_pid; + + if (EventsEnabled == 0) + return; + + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + if (Param && Param[0]) + arg_list[1] = Param; + + // Fork and Exec Specified program + + // Duplicate this process. + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("Event fork() Failed\n"); + return; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + // The execvp function returns only if an error occurs. + + printf ("Failed to run %s\n", arg_list[0]); + exit(0); // Kill the new process + } + +#else + +DllExport void APIENTRY RunEventProgram(char * Program, char * Param) +{ + int n = 0; + char cmdLine[256]; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + + if (EventsEnabled == 0) + return; + + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + sprintf(cmdLine, "%s %s", Program, Param); + + if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo)) + Debugprintf("Failed to Start %s Error %d ", Program, GetLastError()); + +#endif + + return; +} + +void hookL2SessionAccepted(int Port, char * remotecall, char * ourcall, struct _LINKTABLE * LINK) +{ + // Incoming SABM + + LINK->ConnectTime = time(NULL); + LINK->bytesTXed = LINK->bytesRXed = 0; + + strcpy(LINK->callingCall, remotecall); + strcpy(LINK->receivingCall, ourcall); + strcpy(LINK->Direction, "In"); +} + +void hookL2SessionDeleted(struct _LINKTABLE * LINK) +{ + // calculate session time and av bytes/min in and out + + if (LINK->ConnectTime) + { + if (LINK->bytesTXed == 0 && LINK->bytesRXed == 0) + { + // assume failed connect and ignore for now - maybe log later + + } + else + { + char Msg[256]; + char timestamp[16]; + time_t sessionTime = time(NULL) - LINK->ConnectTime; + double avBytesSent = LINK->bytesTXed / (sessionTime / 60.0); + double avBytesRXed = LINK->bytesRXed / (sessionTime / 60.0); + time_t Now = time(NULL); + struct tm * TM = localtime(&Now); + + sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + if (sessionTime == 0) + sessionTime = 1; // Or will get divide by zero error + + Debugprintf("KISS Session Stats Port %d %s %s %d secs Bytes Sent %d BPM %4.2f Bytes Received %d %4.2f BPM ", + LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime, LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp); + + + sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d," + "\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}", + "KISS", LINK->Direction, LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime, + LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp); + + if (MQTT) + MQTTReportSession(Msg); + } + + LINK->ConnectTime = 0; + } + + if (LINK->Sent && LINK->Received && (LINK->SentAfterCompression || LINK->ReceivedAfterExpansion)) + Debugprintf("L2 Compression Stats %s %s TX %d %d %d%% RX %d %d %d%%", LINK->callingCall, LINK->receivingCall, + LINK->Sent, LINK->SentAfterCompression, ((LINK->Sent - LINK->SentAfterCompression) * 100) / LINK->Sent, + LINK->Received, LINK->ReceivedAfterExpansion, ((LINK->ReceivedAfterExpansion - LINK->Received) * 100) / LINK->Received); + +} + +void hookL2SessionAttempt(int Port, char * ourcall, char * remotecall, struct _LINKTABLE * LINK) +{ + LINK->ConnectTime = time(NULL); + LINK->bytesTXed = LINK->bytesRXed = 0; + + strcpy(LINK->callingCall, ourcall); + strcpy(LINK->receivingCall, remotecall); + strcpy(LINK->Direction, "Out"); +} + +void hookL4SessionAttempt(struct STREAMINFO * STREAM, char * remotecall, char * ourcall) +{ + // Outgoing Connect + + STREAM->ConnectTime = time(NULL); + STREAM->bytesTXed = STREAM->bytesRXed = 0; + + strcpy(STREAM->callingCall, ourcall); + strcpy(STREAM->receivingCall, remotecall); + strcpy(STREAM->Direction, "Out"); +} + +void hookL4SessionAccepted(struct STREAMINFO * STREAM, char * remotecall, char * ourcall) +{ + // Incoming Connect + + STREAM->ConnectTime = time(NULL); + STREAM->bytesTXed = STREAM->bytesRXed = 0; + + strcpy(STREAM->callingCall, remotecall); + strcpy(STREAM->receivingCall, ourcall); + strcpy(STREAM->Direction, "In"); +} + +void hookL4SessionDeleted(struct TNCINFO * TNC, struct STREAMINFO * STREAM) +{ + char Msg[256]; + + char timestamp[16]; + + if (STREAM->ConnectTime) + { + time_t sessionTime = time(NULL) - STREAM->ConnectTime; + double avBytesRXed = STREAM->bytesRXed / (sessionTime / 60.0); + double avBytesSent = STREAM->bytesTXed / (sessionTime / 60.0); + time_t Now = time(NULL); + struct tm * TM = localtime(&Now); + sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + if (sessionTime == 0) + sessionTime = 1; // Or will get divide by zero error + + sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d," + "\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}", + Modenames[TNC->Hardware - 1], STREAM->Direction, TNC->Port, STREAM->callingCall, STREAM->receivingCall, sessionTime, + STREAM->bytesTXed, avBytesSent, STREAM->bytesRXed, avBytesRXed, timestamp); + + if (MQTT) + MQTTReportSession(Msg); + + STREAM->ConnectTime = 0; + } +} + + diff --git a/.svn/pristine/01/01ea0d74258aeb1aaca3b61c3205e84a4ae6371b.svn-base b/.svn/pristine/01/01ea0d74258aeb1aaca3b61c3205e84a4ae6371b.svn-base new file mode 100644 index 0000000..34ec376 --- /dev/null +++ b/.svn/pristine/01/01ea0d74258aeb1aaca3b61c3205e84a4ae6371b.svn-base @@ -0,0 +1,111 @@ +#ifndef TELNETSERVER +#define TELNETSERVER + +#ifndef LINBPQ +//#include "resource.h" +#endif +#define WSA_ACCEPT WM_USER + 1 +#define WSA_CONNECT WM_USER + 2 +#define WSA_DATA WM_USER + 3 + +#define InputBufferLen 100000 + +struct ConnectionInfo +{ + int Number; // Number of record - for Connections display + SOCKET socket; + union + { + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + }; + BOOL SocketActive; + int BPQStream; + char Callsign[10]; + BOOL GotHeader; + unsigned char InputBuffer[InputBufferLen]; + int InputLen; + struct UserRec * UserPointer; + int Retries; + int LoginState; // 1 = user ok, 2 = password ok + BOOL DoingCommand; // Processing Telnet Command + BOOL DoEcho; // Telnet Echo option accepted + BOOL CMSSession; // Set if connect to CMS + BOOL FBBMode; // Pure TCP for FBB forwarding + BOOL NeedLF; // FBB mode, but with cr > crlf outbound + BOOL RelayMode; // Pure TCP for RMS Relay Emulation forwarding + BOOL DRATSMode; // HTML Terminal Emulator + BOOL SyncMode; // RMS Relay Sync + BOOL HTTPMode; // HTTP Server + BOOL APIMode; // REST API Server + BOOL TriMode; // Trimode emulation + BOOL TriModeConnected; // Set when remote session is connected - now send data to DataSock + SOCKET TriModeDataSock; // Data Socket + BOOL Auth; // Set if User is flagged as a Secure User + BOOL BPQTermMode; // Set if connected to BPQTermTCP + BOOL ClientSession; // Set if acting as a client (ie Linux HOST Mode) + BOOL MonitorNODES; // Monitor Control Flags + unsigned long long MMASK; + BOOL MCOM; + BOOL MonitorColour; + BOOL MTX; + BOOL MUIOnly; + int CMSIndex; // Pointer to CMS used for this connect + UCHAR * FromHostBuffer; // Somewhere to store msg from CMS - it sends the whole message at once + int FromHostBufferSize; + int FromHostBuffPutptr; + int FromHostBuffGetptr; + + struct TNCINFO * TNC; // Used to pass TCP struct to http code (for passwood list) + + time_t ConnectTime; + BOOL UTF8; // Set if BPQTerminal in UTF8 Mode + BOOL RelaySession; // Set if connection to RMS Relay + BOOL LogonSent; // To ignore second callsign: prompt + char Signon[100]; // User/Pass/Appl for Outgoing Connects + BOOL Keepalive; // For HOST (esp CCC) Keepalives + time_t LastSendTime; + BOOL NoCallsign; // Don't Send Callsign to host if no Signon + UCHAR * ResendBuffer; // Used if send() returns EWOULDBLOCK + int ResendLen; // Len to resend + + struct ADIF * ADIF; // ADIF Logging info + int WebSocks; + char WebURL[32]; // URL for WebSocket Connection + int WebSecure; // Set if secure session +}; + + +#define Disconnect(stream) SessionControl(stream,2,0) +#define Connect(stream) SessionControl(stream,1,0) + +#define SE 240 // End of subnegotiation parameters +#define NOP 241 //No operation +#define xDM 242 //Data mark Indicates the position of a Synch event within the data stream. This should always be accompanied by a TCP urgent notification. +#define BRK 243 //Break Indicates that the "break" or "attention" key was hi. +#define IPx 244 //Suspend Interrupt or abort the process to which the NVT is connected. +#define AO 245 //Abort output Allows the current process to run to completion but does not send its output to the user. +#define AYT 246 //Are you there Send back to the NVT some visible evidence that the AYT was received. +#define EC 247 //Erase character The receiver should delete the last preceding undeleted character from the data stream. +#define EL 248 //Erase line Delete characters from the data stream back to but not including the previous CRLF. +#define GA 249 //Go ahead Under certain circumstances used to tell the other end that it can transmit. +#define SB 250 //Subnegotiation Subnegotiation of the indicated option follows. +#define WILL 251 //will Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. +#define WONT 252 //wont Indicates the refusal to perform, or continue performing, the indicated option. +#define DOx 253 //do Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. +#define DONT 254 //dont Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. +#define IAC 255 + +#define suppressgoahead 3 //858 +#define xStatus 5 //859 +#define echo 1 //857 +#define timingmark 6 //860 +#define terminaltype 24 //1091 +#define windowsize 31 //1073 +#define terminalspeed 32 //1079 +#define remoteflowcontrol 33 //1372 +#define linemode 34 //1184 +#define environmentvariables 36 //1408 + +#endif + diff --git a/.svn/pristine/03/037b7a71a9a62b41bdee83bd30d4b81006bcecf1.svn-base b/.svn/pristine/03/037b7a71a9a62b41bdee83bd30d4b81006bcecf1.svn-base new file mode 100644 index 0000000..387582e --- /dev/null +++ b/.svn/pristine/03/037b7a71a9a62b41bdee83bd30d4b81006bcecf1.svn-base @@ -0,0 +1,1560 @@ +/* +Copyright 2001-2018 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 Fvoideven 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 +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// White Pages Database Support Routines + +#include "bpqmail.h" + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); + +int CurrentWPIndex; +char CurrentWPCall[10]; + +time_t LASTWPSendTime; + + +VOID DoWPUpdate(WPRec *WP, char Type, char * Name, char * HA, char * QTH, char * ZIP, time_t WPDate); +VOID Do_Save_WPRec(HWND hDlg); +VOID SaveInt64Value(config_setting_t * group, char * name, long long value); +VOID SaveIntValue(config_setting_t * group, char * name, int value); +VOID SaveStringValue(config_setting_t * group, char * name, char * value); +BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen); +void MQTTMessageEvent(void* message); + +WPRec * AllocateWPRecord() +{ + WPRec * WP = zalloc(sizeof (WPRec)); + + GetSemaphore(&AllocSemaphore, 0); + + WPRecPtr=realloc(WPRecPtr,(++NumberofWPrecs+1) * sizeof(void *)); + WPRecPtr[NumberofWPrecs]= WP; + + FreeSemaphore(&AllocSemaphore); + + return WP; +} + +int BadCall(char * Call) +{ + if (_stricmp(Call, "RMS") == 0) + return 1; + + if (_stricmp(Call, "SYSTEM") == 0) + return 1; + + if (_stricmp(Call, "SWITCH") == 0) + return 1; + + if (_stricmp(Call, "SYSOP") == 0) + return 1; + + if (_memicmp(Call, "SMTP", 4) == 0) + return 1; + + if (_memicmp(Call, "SMTP:", 5) == 0) + return 1; + + if (_stricmp(Call, "AMPR") == 0) + return 1; + + if (_stricmp(Call, "FILE") == 0) + return 1; + + if (_memicmp(Call, "MCAST", 5) == 0) + return 1; + + if (_memicmp(Call, "SYNC", 5) == 0) + return 1; + + return 0; +} + +extern config_t cfg; + +VOID GetWPDatabase() +{ + WPRec WPRec; + FILE * Handle; + int ReadLen; + WPRecP WP; + char CfgName[MAX_PATH]; + long long val; + config_t wpcfg; + config_setting_t * group, * wpgroup; + int i = 1; + struct stat STAT; + + // If WP info is in main config file, use it + + group = config_lookup (&cfg, "WP"); + + if (group) + { + // Set up control record + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = zalloc(sizeof(WPRec)); + NumberofWPrecs = 0; + + while (1) + { + char Key[16]; + char Record[1024]; + char * ptr, * ptr2; + unsigned int n; + + sprintf(Key, "R%d", i++); + + GetStringValue(group, Key, Record, 1024); + + if (Record[0] == 0) // End of List + return; + + memset(&WPRec, 0, sizeof(WPRec)); + + WP = &WPRec; + + ptr = Record; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->callsign[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->name[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) WP->Type = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) WP->changed = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) WP->seen = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->first_homebbs[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->secnd_homebbs[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->first_zip[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->secnd_zip[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) continue; + + if (strlen(ptr) > 30) + ptr[30] = 0; + + strcpy(&WP->first_qth[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) continue; + + if (strlen(ptr) > 30) + ptr[30] = 0; + + strcpy(&WP->secnd_qth[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr) WP->last_modif = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr) + { + WP->last_seen = atol(ptr); + + // Check Call + + for (n = 1; n < strlen(WP->callsign); n++) // skip first which may also be digit + { + if (isdigit(WP->callsign[n])) + { + // Has a digit. Check Last is not digit + + if (isalpha(WP->callsign[strlen(WP->callsign) - 1])) + { + WP = LookupWP(WPRec.callsign); + if (WP == NULL) + WP = AllocateWPRecord(); + + memcpy(WP, &WPRec, sizeof(WPRec)); + goto WPOK; + } + } + } + Debugprintf("Bad WP Call %s", WP->callsign); + } +WPOK:; + } + return; + } + + // If text format exists use it + + strcpy(CfgName, WPDatabasePath); + strlop(CfgName, '.'); + strcat(CfgName, ".cfg"); + + if (stat(CfgName, &STAT) == -1) + goto tryOld; + + config_init(&wpcfg); + + if (!config_read_file(&wpcfg, CfgName)) + { + char Msg[256]; + sprintf(Msg, "Config File %s Line %d - %s\n", CfgName, + config_error_line(&wpcfg), config_error_text(&wpcfg)); + + printf("%s", Msg); + config_destroy(&wpcfg); + goto tryOld; + } + + // Set up control record + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = zalloc(sizeof(WPRec)); + NumberofWPrecs = 0; + + while (1) + { + char Key[16]; + char Temp[128]; + + sprintf(Key, "R%d", i++); + + wpgroup = config_lookup(&wpcfg, Key); + + if (wpgroup == NULL) // End of List + { + config_destroy(&wpcfg); + return; + } + + memset(&WPRec, 0, sizeof(WPRec)); + + GetStringValue(wpgroup, "c", WPRec.callsign, 6); + GetStringValue(wpgroup, "n", WPRec.name, 12); + + WPRec.Type = GetIntValue(wpgroup, "T"); + WPRec.changed = GetIntValue(wpgroup, "ch"); + WPRec.seen = GetIntValue(wpgroup, "s"); + + GetStringValue(wpgroup, "h", WPRec.first_homebbs, 40); + GetStringValue(wpgroup, "sh", WPRec.secnd_homebbs, 40); + GetStringValue(wpgroup, "z", WPRec.first_zip, 8); + GetStringValue(wpgroup, "sz", WPRec.secnd_zip, 8); + + GetStringValue(wpgroup, "q", Temp, 30); + Temp[30] = 0; + strcpy(WPRec.first_qth, Temp); + + GetStringValue(wpgroup, "sq", Temp, 30); + Temp[30] = 0; + strcpy(WPRec.secnd_qth, Temp); + + val = GetIntValue(wpgroup, "m"); + WPRec.last_modif = val; + val = GetIntValue(wpgroup, "ls"); + WPRec.last_seen = val; + + _strupr(WPRec.callsign); + + strlop(WPRec.callsign, ' '); + + if (strlen(WPRec.callsign) > 2) + { + if (strchr(WPRec.callsign, ':')) + continue; + + if (BadCall(WPRec.callsign)) + continue; + + WP = LookupWP(WPRec.callsign); + + if (WP == NULL) + WP = AllocateWPRecord(); + + memcpy(WP, &WPRec, sizeof(WPRec)); + } + } + +tryOld: + + Handle = fopen(WPDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = malloc(sizeof(WPRec)); + memset(WPRecPtr[0], 0, sizeof(WPRec)); + NumberofWPrecs = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&WPRec, 1, sizeof(WPRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&WPRec, 0, sizeof(WPRec)); + } + + // Set up control record + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = malloc(sizeof(WPRec)); + memcpy(WPRecPtr[0], &WPRec, sizeof(WPRec)); + + NumberofWPrecs = 0; + +Next: + + ReadLen = fread(&WPRec, 1, sizeof(WPRec), Handle); + + if (ReadLen == sizeof(WPRec)) + { + _strupr(WPRec.callsign); + + strlop(WPRec.callsign, ' '); + + if (strlen(WPRec.callsign) > 2) + { + if (strchr(WPRec.callsign, ':')) + goto Next; + + if (BadCall(WPRec.callsign)) + goto Next; + + WP = LookupWP(WPRec.callsign); + + if (WP == NULL) + WP = AllocateWPRecord(); + + memcpy(WP, &WPRec, sizeof(WPRec)); + } + goto Next; + } + + fclose(Handle); + SaveWPDatabase(); +} + +VOID CopyWPDatabase() +{ + char Backup[MAX_PATH]; + char Orig[MAX_PATH]; + + return; + + strcpy(Backup, WPDatabasePath); + strcat(Backup, ".bak"); + + CopyFile(WPDatabasePath, Backup, FALSE); + + strcpy(Backup, WPDatabasePath); + strlop(Backup, '.'); + strcat(Backup, ".cfg.bak"); + + strcpy(Orig, WPDatabasePath); + strlop(Orig, '.'); + strcat(Orig, ".cfg"); + CopyFile(Orig, Backup, FALSE); +} + +VOID SaveWPDatabase() +{ +// SaveConfig(ConfigName); // WP config is now in main config file + + int i; + config_setting_t *root, *group; + config_t cfg; + char Key[16]; + WPRec * WP; + char CfgName[MAX_PATH]; + long long val; + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + root = config_root_setting(&cfg); + + for (i = 0; i <= NumberofWPrecs; i++) + { + WP = WPRecPtr[i]; + sprintf(Key, "R%d", i); + + group = config_setting_add(root, Key, CONFIG_TYPE_GROUP); + + SaveStringValue(group, "c", &WP->callsign[0]); + SaveStringValue(group, "n", &WP->name[0]); + SaveIntValue(group, "T", WP->Type); + SaveIntValue(group, "ch", WP->changed); + SaveIntValue(group, "s", WP->seen); + SaveStringValue(group, "h", &WP->first_homebbs[0]); + SaveStringValue(group, "sh", &WP->secnd_homebbs[0]); + SaveStringValue(group, "z", &WP->first_zip[0]); + SaveStringValue(group, "sz", &WP->secnd_zip[0]); + SaveStringValue(group, "q", &WP->first_qth[0]); + SaveStringValue(group, "sq", &WP->secnd_qth[0]); + val = WP->last_modif; + SaveInt64Value(group, "m", val); + val = WP->last_seen; + SaveInt64Value(group, "ls", val); + } + + strcpy(CfgName, WPDatabasePath); + strlop(CfgName, '.'); + strcat(CfgName, ".cfg"); + + Debugprintf("Saving WP Database to %s\n", CfgName); + + config_write_file(&cfg, CfgName); + config_destroy(&cfg); + +} + +WPRec * LookupWP(char * Call) +{ + WPRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + if (_stricmp(ptr->callsign, Call) == 0) return ptr; + } + + return NULL; +} + +char * FormatWPDate(time_t Datim) +{ + struct tm *tm; + static char Date[]="xx-xxx-xx "; + + tm = gmtime(&Datim); + + if (tm) + { + if (tm->tm_year >= 100) + sprintf_s(Date, sizeof(Date), "%02d-%3s-%02d", + tm->tm_mday, month[tm->tm_mon], tm->tm_year - 100); + else + sprintf_s(Date, sizeof(Date), ""); + } + return Date; +} + +#ifndef LINBPQ + +int Do_WP_Sel_Changed(HWND hDlg) +{ + // Update WP display with newly selected rec + + WPRec * WP; + int Sel = SendDlgItemMessage(hDlg, IDC_WP, CB_GETCURSEL, 0, 0); + char Type[] = " "; + + if (Sel == -1) + SendDlgItemMessage(hDlg, IDC_WP, WM_GETTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentWPCall); + else + SendDlgItemMessage(hDlg, IDC_WP, CB_GETLBTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentWPCall); + + for (CurrentWPIndex = 1; CurrentWPIndex <= NumberofWPrecs; CurrentWPIndex++) + { + WP = WPRecPtr[CurrentWPIndex]; + + if (_stricmp(WP->callsign, CurrentWPCall) == 0) + { + + SetDlgItemText(hDlg, IDC_WPNAME, WP->name); + SetDlgItemText(hDlg, IDC_HOMEBBS1, WP->first_homebbs); + SetDlgItemText(hDlg, IDC_HOMEBBS2, WP->secnd_homebbs); + SetDlgItemText(hDlg, IDC_QTH1, WP->first_qth); + SetDlgItemText(hDlg, IDC_QTH2, WP->secnd_qth); + SetDlgItemText(hDlg, IDC_ZIP1, WP->first_zip); + SetDlgItemText(hDlg, IDC_ZIP2, WP->secnd_zip); + Type[0] = WP->Type; + SetDlgItemText(hDlg, IDC_TYPE, Type); + SetDlgItemInt(hDlg, IDC_CHANGED, WP->changed, FALSE); + SetDlgItemInt(hDlg, IDC_SEEN, WP->seen, FALSE); + + SetDlgItemText(hDlg, IDC_LASTSEEN, FormatWPDate(WP->last_seen)); + SetDlgItemText(hDlg, IDC_LASTMODIFIED, FormatWPDate(WP->last_modif)); + + return 0; + } + } + + CurrentWPIndex = -1; + + return 0; +} + +INT_PTR CALLBACK InfoDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + + +VOID Do_Save_WPRec(HWND hDlg) +{ + WPRec * WP; + char Type[] = " "; + BOOL OK1; + + if (CurrentWPIndex == -1) + { + sprintf(InfoBoxText, "Please select a WP Record to save"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + WP = WPRecPtr[CurrentWPIndex]; + + if (strcmp(CurrentWPCall, WP->callsign) != 0) + { + sprintf(InfoBoxText, "Inconsistancy detected - record not saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + GetDlgItemText(hDlg, IDC_WPNAME, WP->name, 13); + GetDlgItemText(hDlg, IDC_HOMEBBS1, WP->first_homebbs, 41); + GetDlgItemText(hDlg, IDC_HOMEBBS2, WP->secnd_homebbs, 41); + GetDlgItemText(hDlg, IDC_QTH1, WP->first_qth, 31); + GetDlgItemText(hDlg, IDC_QTH2, WP->secnd_qth, 31); + GetDlgItemText(hDlg, IDC_ZIP1, WP->first_zip, 31); + GetDlgItemText(hDlg, IDC_ZIP2, WP->secnd_zip, 31); + WP->last_modif = time(NULL); + WP->seen = GetDlgItemInt(hDlg, IDC_SEEN, &OK1, FALSE); + + WP->Type = 'U'; + WP->changed = 1; + + SaveWPDatabase(); + + sprintf(InfoBoxText, "WP information saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); +} + +VOID Do_Delete_WPRec(HWND hDlg) +{ + WPRec * WP; + int n; + + if (CurrentWPIndex == -1) + { + sprintf(InfoBoxText, "Please select a WP Record to delete"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + WP = WPRecPtr[CurrentWPIndex]; + + if (strcmp(CurrentWPCall, WP->callsign) != 0) + { + sprintf(InfoBoxText, "Inconsistancy detected - record not deleted"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + for (n = CurrentWPIndex; n < NumberofWPrecs; n++) + { + WPRecPtr[n] = WPRecPtr[n+1]; // move down all following entries + } + + NumberofWPrecs--; + + SendDlgItemMessage(hDlg, IDC_WP, CB_RESETCONTENT, 0, 0); + + for (n = 1; n <= NumberofWPrecs; n++) + { + SendDlgItemMessage(hDlg, IDC_WP, CB_ADDSTRING, 0, (LPARAM)WPRecPtr[n]->callsign); + } + + + sprintf(InfoBoxText, "WP record for %s deleted", WP->callsign); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + + free(WP); + + SaveWPDatabase(); + + return; + +} + +INT_PTR CALLBACK WPEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Command, n; + + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + + case WM_INITDIALOG: + + for (n = 1; n <= NumberofWPrecs; n++) + { + SendDlgItemMessage(hDlg, IDC_WP, CB_ADDSTRING, 0, (LPARAM)WPRecPtr[n]->callsign); + } + + return (INT_PTR)TRUE; + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_COMMAND: + + Command = LOWORD(wParam); + + switch (Command) + { + + case IDOK: + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case IDC_WP: + + // Msg Selection Changed + + Do_WP_Sel_Changed(hDlg); + + return TRUE; + + case IDC_SAVEWP: + + Do_Save_WPRec(hDlg); + return TRUE; + + case IDC_DELETEWP: + + Do_Delete_WPRec(hDlg); + return TRUE; + } + break; + } + + return (INT_PTR)FALSE; +} +#endif + +VOID GetWPBBSInfo(char * Rline) +{ + // Update WP with /I records for each R: Line + + // R:111206/1636Z 29130@N9PMO.#SEWI.WI.USA.NOAM [Racine, WI] FBB7.00i + + struct tm rtime; + time_t RLineTime; + int Age; + + WPRec * WP; + char ATBBS[200]; + char Call[200]; + char QTH[200] = ""; + int RLen; + + char * ptr1; + char * ptr2; + + + memset(&rtime, 0, sizeof(struct tm)); + + if (Rline[10] == '/') + { + // Dodgy 4 char year + + sscanf(&Rline[2], "%04d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + rtime.tm_year -= 1900; + rtime.tm_mon--; + } + else if (Rline[8] == '/') + { + sscanf(&Rline[2], "%02d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + + if (rtime.tm_year < 90) + rtime.tm_year += 100; // Range 1990-2089 + rtime.tm_mon--; + } + + // Otherwise leave date as zero, which should be rejected + + if ((RLineTime = mktime(&rtime)) != (time_t)-1 ) + { + Age = (time(NULL) - RLineTime)/86400; + + if ( Age < -1) + return; // in the future + + if (Age > BidLifetime || Age > MaxAge) + return; // Too old + } + + ptr1 = strchr(Rline, '@'); + ptr2 = strchr(Rline, '\r'); + + if (!ptr1) + return; // Duff + + if (*++ptr1 == ':') + ptr1++; // Format 2 + + + if (ptr2 == NULL) + return; // No CR on end + + RLen = ptr2 - ptr1; + + if (RLen > 200) + return; + + memcpy(ATBBS, ptr1, RLen); + ATBBS[RLen] = 0; + + ptr2 = strchr(ATBBS, ' '); + + if (ptr2) + *ptr2 = 0; + + strcpy(Call, ATBBS); + strlop(Call, '.'); + + ptr2 = memchr(ptr1, '[', RLen); + + if (ptr2) + { + ptr1= memchr(ptr2, ']', RLen); + if (ptr1) + memcpy(QTH, ptr2 + 1, ptr1 - ptr2 - 1); + } + + if (BadCall(Call)) + return; + + WP = LookupWP(Call); + + if (!WP) + { + // Not Found + + WP = AllocateWPRecord(); + + strcpy(WP->callsign, Call); + strcpy(WP->first_homebbs, ATBBS); + strcpy(WP->secnd_homebbs, ATBBS); + + if (QTH[0]) + { + strcpy(WP->first_qth, QTH); + strcpy(WP->secnd_qth, QTH); + } + + WP->last_modif = RLineTime; + WP->last_seen = RLineTime; + + WP->Type = 'I'; + WP->changed = TRUE; + + return; + } + + if (WP->last_modif >= RLineTime || WP->Type != 'I') + return; + + // Update 2nd if changed + + if (strcmp(WP->secnd_homebbs , ATBBS) != 0) + { + strcpy(WP->secnd_homebbs, ATBBS); + WP->last_modif = RLineTime; + } + + if (QTH[0] && strcmp(WP->secnd_qth , QTH) != 0) + { + strcpy(WP->secnd_qth, QTH); + WP->last_modif = RLineTime; + } + + return; +} + + + + +VOID GetWPInfoFromRLine(char * From, char * FirstRLine, time_t RLineTime) +{ + /* The /G suffix denotes that the information in this line has been gathered by examining + the header of a message to GUESS at which BBS the sender is registered. The HomeBBS of the User + is assumed to be the BBS shown in the first R: header line. The date associated with this + information is the date shown on this R: header line. + */ + + // R:930101/0000 1530@KA6FUB.#NOCAL.CA.USA.NOAM + + // R:930101/0000 @:KA6FUB.#NOCAL.CA.USA.NOAM #:1530 + + // The FirstRLine pointer points to the message, so shouldnt be changed + + WPRec * WP; + char ATBBS[200]; + int RLen; + + char * ptr1 = strchr(FirstRLine, '@'); + char * ptr2 = strchr(FirstRLine, '\r'); + + if (BadCall(From)) + return; + + if (!ptr1) + return; // Duff + + if (*++ptr1 == ':') + ptr1++; // Format 2 + + RLen = ptr2 - ptr1; + + if (RLen > 200) + return; + + memcpy(ATBBS, ptr1, RLen); + ATBBS[RLen] = 0; + + ptr2 = strchr(ATBBS, ' '); + + if (ptr2) + *ptr2 = 0; + + if (strlen(ATBBS) > 40) + ATBBS[40] = 0; + + WP = LookupWP(From); + + if (!WP) + { + // Not Found + + WP = AllocateWPRecord(); + + strcpy(WP->callsign, From); + strcpy(WP->first_homebbs, ATBBS); + strcpy(WP->secnd_homebbs, ATBBS); + + WP->last_modif = RLineTime; + WP->last_seen = RLineTime; + + WP->Type = 'G'; + WP->changed = TRUE; + + return; + } + + if (WP->last_modif >= RLineTime) + return; + + // Update 2nd if changed + + if (strcmp(WP->secnd_homebbs , ATBBS) != 0) + { + strcpy(WP->secnd_homebbs, ATBBS); + WP->last_modif = RLineTime; + } + + return; +} + +VOID ProcessWPMsg(char * MailBuffer, int Size, char * FirstRLine) +{ + char * ptr1 = MailBuffer; + char * ptr2; + WPRec * WP; + char WPLine[200]; + int WPLen; + + ptr1[Size] = 0; + + ptr1 = FirstRLine; + + if (ptr1 == NULL) + return; + + while (*ptr1) + { + ptr2 = strchr(ptr1, '\r'); + + if (ptr2 == 0) // No CR in buffer + return; + + WPLen = ptr2 - ptr1; + + if (WPLen > 128) + return; + + if ((memcmp(ptr1, "On ", 3) == 0) && (WPLen < 200)) + { + char * Date; + char * Call; + char * AT; + char * HA; + char * zip; + char * ZIP; + char * Name; + char * QTH = NULL; + char * Context; + char seps[] = " \r"; + + // Make copy of string, as strtok messes with it + + memcpy(WPLine, ptr1, WPLen); + WPLine[WPLen] = 0; + + Date = strtok_s(WPLine+3, seps, &Context); + Call = strtok_s(NULL, seps, &Context); + AT = strtok_s(NULL, seps, &Context); + HA = strtok_s(NULL, seps, &Context); + zip = strtok_s(NULL, seps, &Context); + ZIP = strtok_s(NULL, seps, &Context); + Name = strtok_s(NULL, seps, &Context); + QTH = strtok_s(NULL, "\r", &Context); // QTH may have spaces + + if (Date == 0 || Call == 0 || AT == 0 || ZIP == 0 || Name == 0 || QTH == 0) + return; + + if (strlen(HA) > 40) + return; + if (strlen(ZIP) > 8) + return; + if (strlen(Name) > 12) + return; + if (strlen(QTH) > 30) + return; + + if (AT[0] == '@' && (QTH)) + { + struct tm rtime; + time_t WPDate; + char Type; + char * TypeString; + + if (memcmp(Name, "?", 2) == 0) Name = NULL; + if (memcmp(ZIP, "?", 2) == 0) ZIP = NULL; + if (memcmp(QTH, "?", 2) == 0) QTH = NULL; + + memset(&rtime, 0, sizeof(struct tm)); + + sscanf(Date, "%02d%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday); + + rtime.tm_year += 100; + rtime.tm_mon--; + +/* +This process freshens the database, following receipt of the new or changed information detailed above. + +The update subroutine will first look for an entry in the database for the callsign which matches the received information. +If it does not exist then a completely new record will be created in the database and the information be used to fill what +fields it can, in both the active and the temporary components. The date will be then changed to the one associated with the +update information. + +If the record does already exist, then the unknown fields of both the temporary and active fields will be filled in, and +those fields already known in the temporary part will be replaced by the new information if the date new information is +younger than that already on file. The date will then be +adjusted such that it is consistent with the updated information. + +If the new information is of the /U category, then the current fields will be replaced by the new information in both +the primary and secondary (Active and Temporary) parts of the record, as this information has been input directly from +the user. If the information was of another category then only the secondary (Temporary) part of the record will be +updated, so the Active or primary record will remain unchanged at this time. + +If a field is changed, a flag giving the update request type is then validated. If the /U flag is already validated, +it will not be replaced. This flag will be used in case the WP update messages are validated. +*/ + if ((WPDate = mktime(&rtime)) != (time_t)-1 ) + { + WPDate -= (time_t)_MYTIMEZONE; + TypeString = strlop(Call, '/'); + + if (strlen(Call) < 3 || strlen(Call) > 6) + return; + + if (TypeString) + Type = TypeString[0]; + else + Type = 'G'; + + if (strchr(Call, ':')) + break; + + if (BadCall(Call)) + break; + + WP = LookupWP(Call); + + if (WP) + { + // Found, so update + + DoWPUpdate(WP, Type, Name, HA, QTH, ZIP, WPDate); + } + else + { + WP = AllocateWPRecord(); + + strcpy(WP->callsign, Call); + if (Name) strcpy(WP->name, Name); + strcpy(WP->first_homebbs, HA); + strcpy(WP->secnd_homebbs, HA); + + if (QTH) + { + strcpy(WP->first_qth, QTH); + strcpy(WP->secnd_qth, QTH);; + } + if (ZIP) + { + strcpy(WP->first_zip, ZIP); + strcpy(WP->secnd_zip, ZIP); + } + + WP->Type = Type; + WP->last_modif = WPDate; + WP->last_seen = WPDate; + WP->changed = TRUE; + WP->seen++; + } + } + } + } + + ptr1 = ++ptr2; + if (*ptr1 == '\n') + ptr1++; + } + + SaveWPDatabase(); + + return; +} + +VOID DoWPUpdate(WPRec * WP, char Type, char * Name, char * HA, char * QTH, char * ZIP, time_t WPDate) +{ + // First Update any unknown field + + if(Name) + if (WP->name == NULL) {strcpy(WP->name, Name); WP->last_modif = WPDate; WP->changed = TRUE;} + + if (QTH) + { + if (WP->first_qth == NULL) {strcpy(WP->first_qth, QTH); WP->last_modif = WPDate; WP->changed = TRUE;} + if (WP->secnd_qth == NULL) {strcpy(WP->secnd_qth, QTH); WP->last_modif = WPDate; WP->changed = TRUE;} + } + if (ZIP) + { + if (WP->first_zip == NULL) {strcpy(WP->first_zip, ZIP); WP->last_modif = WPDate; WP->changed = TRUE;} + if (WP->secnd_zip == NULL) {strcpy(WP->secnd_zip, ZIP); WP->last_modif = WPDate; WP->changed = TRUE;} + } + + WP->last_seen = WPDate; + WP->seen++; + + // Now Update Temp Fields if update is newer than original + + if (WP->last_modif >= WPDate) + return; + + if (Type == 'U') // Definitive Update + { + if (strcmp(WP->first_homebbs , HA) != 0) + { + strcpy(WP->first_homebbs, HA); strcpy(WP->secnd_homebbs, HA); WP->last_modif = WPDate; WP->changed = TRUE; + } + + if (Name) + { + if (strcmp(WP->name , Name) != 0) + { + strcpy(WP->name, Name); + WP->last_modif = WPDate; + WP->changed = TRUE; + } + } + + if (QTH) + { + if (strcmp(WP->first_qth , QTH) != 0) + { + strcpy(WP->first_qth, QTH); + strcpy(WP->secnd_qth, QTH); + WP->last_modif = WPDate; + WP->changed = TRUE; + } + } + + if (ZIP) + { + if (strcmp(WP->first_zip , ZIP) != 0) + { + strcpy(WP->first_zip, ZIP); + strcpy(WP->secnd_zip, ZIP); + WP->last_modif = WPDate; + WP->changed = TRUE; + } + } + + WP->Type = Type; + + return; + } + + // Non-Definitive - only update second copy + + if (strcmp(WP->secnd_homebbs , HA) != 0) {strcpy(WP->secnd_homebbs, HA); WP->last_modif = WPDate; WP->Type = Type;} + + if (Name) + if (strcmp(WP->name , Name) != 0) {strcpy(WP->name, Name); WP->last_modif = WPDate; WP->Type = Type;} + + if (QTH) + if (strcmp(WP->secnd_qth , QTH) != 0) {strcpy(WP->secnd_qth, QTH); WP->last_modif = WPDate; WP->Type = Type;} + + if (ZIP) + if (strcmp(WP->secnd_zip , ZIP) != 0) {strcpy(WP->secnd_zip, ZIP); WP->last_modif = WPDate; WP->Type = Type;} + + return; +} + +VOID UpdateWPWithUserInfo(struct UserInfo * user) +{ + WPRec * WP = LookupWP(user->Call); + + if (strchr(user->Call, ':')) + return; + + if (BadCall(user->Call)) + return; + + if (!WP) + { + WP = AllocateWPRecord(); + strcpy(WP->callsign, user->Call); + } + + // Update Record + + if (user->HomeBBS[0]) + { + strcpy(WP->first_homebbs, user->HomeBBS); + strcpy(WP->secnd_homebbs, user->HomeBBS); + } + + if (user->Address[0]) + { + char Temp[127]; + strcpy(Temp, user->Address); + Temp[30] = 0; + + strcpy(WP->first_qth, Temp); + strcpy(WP->secnd_qth, Temp); + } + + if (user->ZIP[0]) + { + strcpy(WP->first_zip, user->ZIP); + strcpy(WP->secnd_zip, user->ZIP ); + } + + if (user->Name[0]) + strcpy(WP->name, user->Name); + + WP->last_modif = WP->last_seen = time(NULL); + + WP->changed = TRUE; + WP->Type = 'U'; + + SaveWPDatabase(); + +} + +VOID DoWPLookup(ConnectionInfo * conn, struct UserInfo * user, char Type, char *Param) +{ + // Process the I call command + + WPRec * ptr = NULL; + int i; + char ATBBS[100]; + char * HA; + char * Rest; + + _strupr(Param); + Type = toupper(Type); + + switch (Type) + { + case 0: + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + if (wildcardcompare(ptr->callsign, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, + ptr->name, ptr->first_zip, ptr->first_qth); + } + } + + return; + + case'@': // AT BBS + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + strcpy(ATBBS, ptr->first_homebbs); + strlop(ATBBS,'.'); + + if (wildcardcompare(ATBBS, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); + } + } + + case'H': // Hierarchic Element + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + strcpy(ATBBS, ptr->first_homebbs); + + Rest = strlop(ATBBS,'.'); + + if (Rest == 0) + continue; + + HA = strtok_s(Rest, ".", &Rest); + + while (HA) + { + if (wildcardcompare(HA, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); + } + + HA = strtok_s(NULL, ".", &Rest); + } + } + return; + + case'Z': // ZIP + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + if (ptr->first_zip[0] == 0) + continue; + + if (wildcardcompare(ptr->first_zip, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); + } + } + return; + } + nodeprintf(conn, "Invalid I command option %c\r", Type); +} +/* +On 111120 N4ZKF/I @ N4ZKF.#NFL.FL.USA.NOAM zip 32118 Dave 32955 +On 111120 F6IQF/I @ F6IQF.FRPA.FRA.EU zip ? ? f6iqf.dyndns.org +On 111121 W9JUN/I @ W9JUN.W9JUN.#SEIN.IN.US.NOAM zip 47250 Don NORTH VERNON, IN +On 111120 KR8ZY/U @ KR8ZY zip ? john ? +On 111120 N0DEC/G @ N0ARY.#NCA.CA.USA.NOAM zip ? ? ? + +From: N0JAL +To: WP +Type/Status: B$ +Date/Time: 22-Nov 10:15Z +Bid: 95F7N0JAL +Title: WP Update + +R:111122/1500Z 35946@KD6PGI.OR.USA.NOAM BPQ1.0.4 +R:111122/1020 16295@K7ZS.OR.USA.NOAM +R:111122/1015 38391@N0JAL.OR.USA.NOAM + +On 111121 N0JAL @ N0JAL.OR.USA.NOAM zip ? Sai Damascus, Oregon CN85sj + +*/ + +VOID UpdateWP() +{ + // If 2nd copy of info has been unchanged for 30 days, copy to active fields + + WPRec * ptr = NULL; + int i; + char * via = NULL; + int MsgLen = 0; + char MailBuffer[100100]; + char * Buffptr = MailBuffer; + int WriteLen=0; + char HDest[61] = "WP"; + char WPMsgType = 'P'; + time_t NOW = time(NULL); + time_t UpdateLimit = NOW - (86400 * 30); // 30 days + LASTWPSendTime = NOW - (86400 * 5); // 5 days max + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + // DO we have a new field, and if so is it different? + + if ((ptr->secnd_homebbs[0] && _stricmp(ptr->first_homebbs, ptr->secnd_homebbs)) + || (ptr->secnd_qth[0] && _stricmp(ptr->first_qth, ptr->secnd_qth)) + || (ptr->secnd_zip[0] && _stricmp(ptr->first_zip, ptr->secnd_zip))) + { + // Have new data + + if (ptr->last_modif < UpdateLimit) + { + // Stable for 30 days + + if (ptr->secnd_homebbs[0]) + strcpy(ptr->first_homebbs, ptr->secnd_homebbs); + if (ptr->secnd_qth[0]) + strcpy(ptr->first_qth, ptr->secnd_qth); + if (ptr->secnd_zip[0]) + strcpy(ptr->first_zip, ptr->secnd_zip); + + ptr->last_modif = NOW; + + } + } + } +} + +int CreateWPMessage() +{ + // Include all messages with Type of U whach have changed since LASTWPSendTime + + WPRec * ptr = NULL; + int i; + struct tm *tm; + struct MsgInfo * Msg; + char * via = NULL; + char BID[13]; + BIDRec * BIDRec; + int MsgLen = 0; + char MailBuffer[100100]; + char * Buffptr = MailBuffer; + char MsgFile[MAX_PATH]; + FILE * hFile; + int WriteLen=0; + char ** To = SendWPAddrs; + + LASTWPSendTime = time(NULL) - (86400 * 5); // 5 days max + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + +// if (ptr->last_modif > LASTWPSendTime && ptr->Type == 'U' && ptr->first_homebbs[0]) + if (ptr->changed && ptr->last_modif > LASTWPSendTime && ptr->first_homebbs[0]) + { + tm = gmtime((time_t *)&ptr->last_modif); + MsgLen += sprintf(Buffptr, "On %02d%02d%02d %s/%c @ %s zip %s %s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, + ptr->callsign, ptr->Type, ptr->first_homebbs, + (ptr->first_zip[0]) ? ptr->first_zip : "?", + (ptr->name[0]) ? ptr->name : "?", + (ptr->first_qth[0]) ? ptr->first_qth : "?"); + + Buffptr = &MailBuffer[MsgLen]; + + ptr->changed = FALSE; + + if (MsgLen > 100000) + break; + } + } + + if (MsgLen == 0) + return TRUE; + + while(To[0]) + { + char TO[256]; + char * VIA; + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + Msg->length = MsgLen; + MsgnotoMsg[Msg->number] = Msg; + + strcpy(Msg->from, BBSName); + + strcpy(TO, To[0]); + + VIA = strlop(TO, '@'); + + if (VIA) + { + if (strlen(VIA) > 40) + VIA[40] = 0; + strcpy(Msg->via, VIA); + } + strcpy(Msg->to, TO); + + strcpy(Msg->title, "WP Update"); + + Msg->type = (SendWPType) ? 'P' : 'B'; + Msg->status = 'N'; + + sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName); + + strcpy(Msg->bid, BID); + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, 0); + + BuildNNTPList(Msg); // Build NNTP Groups list + +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(Msg); +#endif + To++; + } + + SaveMessageDatabase(); + SaveBIDDatabase(); + + return TRUE; +} + +VOID CreateWPReport() +{ + int i; + char Line[200]; + int len; + char File[100]; + FILE * hFile; + WPRec * WP = NULL; + + sprintf_s(File, sizeof(File), "%s/wp.txt", BaseDir); + + hFile = fopen(File, "wb"); + + if (hFile == NULL) + return; + +// len = sprintf(Line, " Call Connects Connects Messages Messages Bytes Bytes Rejected Rejected\r\n"); +// WriteFile(hFile, Line, len, &written, NULL); +// len = sprintf(Line, " In Out Rxed Sent Rxed Sent In Out\r\n\r\n"); +// WriteFile(hFile, Line, len, &written, NULL); + + + for (i=1; i <= NumberofWPrecs; i++) + { + WP = WPRecPtr[i]; + + len = sprintf(Line, "%-7s,%c,%s,%s,%s,%s,%s,%s,%s,%d,%s,%s\r\n", + WP->callsign, WP->Type, WP->first_homebbs, WP->first_qth, WP->first_zip, + WP->secnd_homebbs, WP->secnd_qth, WP->secnd_zip, WP->name, WP->changed, + FormatWPDate((time_t)WP->last_modif), + FormatWPDate((time_t)WP->last_seen)); + + fwrite(Line, 1, len, hFile); + } + fclose(hFile); +} + + + + + + diff --git a/.svn/pristine/03/0381960f1d4a4876281c5e925e69f5634d57b798.svn-base b/.svn/pristine/03/0381960f1d4a4876281c5e925e69f5634d57b798.svn-base new file mode 100644 index 0000000..8c35faa --- /dev/null +++ b/.svn/pristine/03/0381960f1d4a4876281c5e925e69f5634d57b798.svn-base @@ -0,0 +1,1573 @@ + +/* pngpread.c - read a png file in push mode + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + +/* push model modes */ +#define PNG_READ_SIG_MODE 0 +#define PNG_READ_CHUNK_MODE 1 +#define PNG_READ_IDAT_MODE 2 +#define PNG_SKIP_MODE 3 +#define PNG_READ_tEXt_MODE 4 +#define PNG_READ_zTXt_MODE 5 +#define PNG_READ_DONE_MODE 6 +#define PNG_READ_iTXt_MODE 7 +#define PNG_ERROR_MODE 8 + +void PNGAPI +png_process_data(png_structp png_ptr, png_infop info_ptr, + png_bytep buffer, png_size_t buffer_size) +{ + png_push_restore_buffer(png_ptr, buffer, buffer_size); + + while (png_ptr->buffer_size) + { + png_process_some_data(png_ptr, info_ptr); + } +} + +/* What we do with the incoming data depends on what we were previously + * doing before we ran out of data... + */ +void /* PRIVATE */ +png_process_some_data(png_structp png_ptr, png_infop info_ptr) +{ + switch (png_ptr->process_mode) + { + case PNG_READ_SIG_MODE: + { + png_push_read_sig(png_ptr, info_ptr); + break; + } + case PNG_READ_CHUNK_MODE: + { + png_push_read_chunk(png_ptr, info_ptr); + break; + } + case PNG_READ_IDAT_MODE: + { + png_push_read_IDAT(png_ptr); + break; + } +#if defined(PNG_READ_tEXt_SUPPORTED) + case PNG_READ_tEXt_MODE: + { + png_push_read_tEXt(png_ptr, info_ptr); + break; + } +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + case PNG_READ_zTXt_MODE: + { + png_push_read_zTXt(png_ptr, info_ptr); + break; + } +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + case PNG_READ_iTXt_MODE: + { + png_push_read_iTXt(png_ptr, info_ptr); + break; + } +#endif + case PNG_SKIP_MODE: + { + png_push_crc_finish(png_ptr); + break; + } + default: + { + png_ptr->buffer_size = 0; + break; + } + } +} + +/* Read any remaining signature bytes from the stream and compare them with + * the correct PNG signature. It is possible that this routine is called + * with bytes already read from the signature, either because they have been + * checked by the calling application, or because of multiple calls to this + * routine. + */ +void /* PRIVATE */ +png_push_read_sig(png_structp png_ptr, png_infop info_ptr) +{ + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + if (png_ptr->buffer_size < num_to_check) + { + num_to_check = png_ptr->buffer_size; + } + + png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), + num_to_check); + png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes+num_to_check); + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + else + { + if (png_ptr->sig_bytes >= 8) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } + } +} + +void /* PRIVATE */ +png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; + PNG_IDAT; + PNG_IEND; + PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_sCAL; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_sRGB; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_sPLT; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + /* First we make sure we have enough data for the 4 byte chunk name + * and the 4 byte chunk length before proceeding with decoding the + * chunk data. To fully decode each of these chunks, we also make + * sure we have enough data in the buffer for the 4 byte CRC at the + * end of every chunk (except IDAT, which is handled separately). + */ + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + } + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); + } + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); + + png_ptr->process_mode = PNG_READ_DONE_MODE; + png_push_have_end(png_ptr, info_ptr); + } +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + } + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); + } + else if (!png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4)) + { + /* If we reach an IDAT chunk, this means we have read all of the + * header chunks, and we can start reading the image (or if this + * is called after the image has been read - we have an error). + */ + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + { + if (png_ptr->push_length == 0) + return; + + if (png_ptr->mode & PNG_AFTER_IDAT) + png_error(png_ptr, "Too many IDAT's found"); + } + + png_ptr->idat_size = png_ptr->push_length; + png_ptr->mode |= PNG_HAVE_IDAT; + png_ptr->process_mode = PNG_READ_IDAT_MODE; + png_push_have_info(png_ptr, info_ptr); + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + png_ptr->zstream.next_out = png_ptr->row_buf; + return; + } +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif + else + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + } + + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; +} + +void /* PRIVATE */ +png_push_crc_skip(png_structp png_ptr, png_uint_32 skip) +{ + png_ptr->process_mode = PNG_SKIP_MODE; + png_ptr->skip_length = skip; +} + +void /* PRIVATE */ +png_push_crc_finish(png_structp png_ptr) +{ + if (png_ptr->skip_length && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->save_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->skip_length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->current_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->skip_length) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } +} + +void PNGAPI +png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) +{ + png_bytep ptr; + + ptr = buffer; + if (png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->save_buffer_size) + save_size = length; + else + save_size = png_ptr->save_buffer_size; + + png_memcpy(ptr, png_ptr->save_buffer_ptr, save_size); + length -= save_size; + ptr += save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->current_buffer_size) + save_size = length; + else + save_size = png_ptr->current_buffer_size; + + png_memcpy(ptr, png_ptr->current_buffer_ptr, save_size); + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } +} + +void /* PRIVATE */ +png_push_save_buffer(png_structp png_ptr) +{ + if (png_ptr->save_buffer_size) + { + if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) + { + png_size_t i,istop; + png_bytep sp; + png_bytep dp; + + istop = png_ptr->save_buffer_size; + for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer; + i < istop; i++, sp++, dp++) + { + *dp = *sp; + } + } + } + if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > + png_ptr->save_buffer_max) + { + png_size_t new_max; + png_bytep old_buffer; + + if (png_ptr->save_buffer_size > PNG_SIZE_MAX - + (png_ptr->current_buffer_size + 256)) + { + png_error(png_ptr, "Potential overflow of save_buffer"); + } + new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; + old_buffer = png_ptr->save_buffer; + png_ptr->save_buffer = (png_bytep)png_malloc(png_ptr, + (png_uint_32)new_max); + png_memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); + png_free(png_ptr, old_buffer); + png_ptr->save_buffer_max = new_max; + } + if (png_ptr->current_buffer_size) + { + png_memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, + png_ptr->current_buffer_ptr, png_ptr->current_buffer_size); + png_ptr->save_buffer_size += png_ptr->current_buffer_size; + png_ptr->current_buffer_size = 0; + } + png_ptr->save_buffer_ptr = png_ptr->save_buffer; + png_ptr->buffer_size = 0; +} + +void /* PRIVATE */ +png_push_restore_buffer(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + png_ptr->current_buffer = buffer; + png_ptr->current_buffer_size = buffer_length; + png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size; + png_ptr->current_buffer_ptr = png_ptr->current_buffer; +} + +void /* PRIVATE */ +png_push_read_IDAT(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + + if (png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4)) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_error(png_ptr, "Not enough compressed data"); + return; + } + + png_ptr->idat_size = png_ptr->push_length; + } + if (png_ptr->idat_size && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->save_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + /* check for overflow */ + if((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->idat_size && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->current_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + /* check for overflow */ + if((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->idat_size) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; + png_ptr->mode |= PNG_AFTER_IDAT; + } +} + +void /* PRIVATE */ +png_process_IDAT_data(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + int ret; + + if ((png_ptr->flags & PNG_FLAG_ZLIB_FINISHED) && buffer_length) + png_error(png_ptr, "Extra compression data"); + + png_ptr->zstream.next_in = buffer; + png_ptr->zstream.avail_in = (uInt)buffer_length; + for(;;) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK) + { + if (ret == Z_STREAM_END) + { + if (png_ptr->zstream.avail_in) + png_error(png_ptr, "Extra compressed data"); + if (!(png_ptr->zstream.avail_out)) + { + png_push_process_row(png_ptr); + } + + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + else if (ret == Z_BUF_ERROR) + break; + else + png_error(png_ptr, "Decompression Error"); + } + if (!(png_ptr->zstream.avail_out)) + { + if (( +#if defined(PNG_READ_INTERLACING_SUPPORTED) + png_ptr->interlaced && png_ptr->pass > 6) || + (!png_ptr->interlaced && +#endif + png_ptr->row_number == png_ptr->num_rows)) + { + if (png_ptr->zstream.avail_in) + png_warning(png_ptr, "Too much data in IDAT chunks"); + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + png_push_process_row(png_ptr); + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + png_ptr->zstream.next_out = png_ptr->row_buf; + } + else + break; + } +} + +void /* PRIVATE */ +png_push_process_row(png_structp png_ptr) +{ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, + png_ptr->rowbytes + 1); + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* blow up interlaced rows to full size */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) +/* old interface (pre-1.0.9): + png_do_read_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + switch (png_ptr->pass) + { + case 0: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 0; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); /* updates png_ptr->pass */ + } + if (png_ptr->pass == 2) /* pass 1 might be empty */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + if (png_ptr->pass == 4 && png_ptr->height <= 4) + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + if (png_ptr->pass == 6 && png_ptr->height <= 4) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 1: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 1; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 2) /* skip top 4 generated rows */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 2: + { + int i; + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 4) /* pass 3 might be empty */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 3: + { + int i; + for (i = 0; i < 4 && png_ptr->pass == 3; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 4) /* skip top two generated rows */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 4: + { + int i; + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 6) /* pass 5 might be empty */ + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 5: + { + int i; + for (i = 0; i < 2 && png_ptr->pass == 5; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 6) /* skip top generated row */ + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 6: + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + if (png_ptr->pass != 6) + break; + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + } + else +#endif + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } +} + +void /* PRIVATE */ +png_read_push_finish_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + const int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + const int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + const int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + const int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + + /* Width of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h + const int FARDATA png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; + */ + + /* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h + const int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; + */ +#endif + + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset_check(png_ptr, png_ptr->prev_row, 0, + png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if ((png_ptr->pass == 1 && png_ptr->width < 5) || + (png_ptr->pass == 3 && png_ptr->width < 3) || + (png_ptr->pass == 5 && png_ptr->width < 2)) + png_ptr->pass++; + + if (png_ptr->pass > 7) + png_ptr->pass--; + if (png_ptr->pass >= 7) + break; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + + if (png_ptr->transformations & PNG_INTERLACE) + break; + + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + + } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0); + } +} + +#if defined(PNG_READ_tEXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place tEXt"); + /* to quiet some compiler warnings */ + if(info_ptr == NULL) return; + } + +#ifdef PNG_MAX_MALLOC_64K + png_ptr->skip_length = 0; /* This may not be necessary */ + + if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ + { + png_warning(png_ptr, "tEXt chunk too large to fit in memory"); + png_ptr->skip_length = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_tEXt_MODE; +} + +void /* PRIVATE */ +png_push_read_tEXt(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp text; + png_charp key; + int ret; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + +#if defined(PNG_MAX_MALLOC_64K) + if (png_ptr->skip_length) + return; +#endif + + key = png_ptr->current_text; + + for (text = key; *text; text++) + /* empty loop */ ; + + if (text != key + png_ptr->current_text_size) + text++; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; +#endif + text_ptr->text = text; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + png_ptr->current_text = NULL; + + if (ret) + png_warning(png_ptr, "Insufficient memory to store text chunk."); + } +} +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place zTXt"); + /* to quiet some compiler warnings */ + if(info_ptr == NULL) return; + } + +#ifdef PNG_MAX_MALLOC_64K + /* We can't handle zTXt chunks > 64K, since we don't have enough space + * to be able to store the uncompressed data. Actually, the threshold + * is probably around 32K, but it isn't as definite as 64K is. + */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "zTXt chunk too large to fit in memory"); + png_push_crc_skip(png_ptr, length); + return; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_zTXt_MODE; +} + +void /* PRIVATE */ +png_push_read_zTXt(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < (png_uint_32)png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp text; + png_charp key; + int ret; + png_size_t text_size, key_size; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + + key = png_ptr->current_text; + + for (text = key; *text; text++) + /* empty loop */ ; + + /* zTXt can't have zero text */ + if (text == key + png_ptr->current_text_size) + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + return; + } + + text++; + + if (*text != PNG_TEXT_COMPRESSION_zTXt) /* check compression byte */ + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + return; + } + + text++; + + png_ptr->zstream.next_in = (png_bytep )text; + png_ptr->zstream.avail_in = (uInt)(png_ptr->current_text_size - + (text - key)); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + key_size = text - key; + text_size = 0; + text = NULL; + ret = Z_STREAM_END; + + while (png_ptr->zstream.avail_in) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + png_ptr->current_text = NULL; + png_free(png_ptr, key); + png_free(png_ptr, text); + return; + } + if (!(png_ptr->zstream.avail_out) || ret == Z_STREAM_END) + { + if (text == NULL) + { + text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out + + key_size + 1)); + png_memcpy(text + key_size, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + png_memcpy(text, key, key_size); + text_size = key_size + png_ptr->zbuf_size - + png_ptr->zstream.avail_out; + *(text + text_size) = '\0'; + } + else + { + png_charp tmp; + + tmp = text; + text = (png_charp)png_malloc(png_ptr, text_size + + (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out + + 1)); + png_memcpy(text, tmp, text_size); + png_free(png_ptr, tmp); + png_memcpy(text + text_size, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; + *(text + text_size) = '\0'; + } + if (ret != Z_STREAM_END) + { + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else + { + break; + } + + if (ret == Z_STREAM_END) + break; + } + + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + if (ret != Z_STREAM_END) + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + png_free(png_ptr, text); + return; + } + + png_ptr->current_text = NULL; + png_free(png_ptr, key); + key = text; + text += key_size; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = PNG_TEXT_COMPRESSION_zTXt; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; +#endif + text_ptr->text = text; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + + if (ret) + png_warning(png_ptr, "Insufficient memory to store text chunk."); + } +} +#endif + +#if defined(PNG_READ_iTXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place iTXt"); + /* to quiet some compiler warnings */ + if(info_ptr == NULL) return; + } + +#ifdef PNG_MAX_MALLOC_64K + png_ptr->skip_length = 0; /* This may not be necessary */ + + if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ + { + png_warning(png_ptr, "iTXt chunk too large to fit in memory"); + png_ptr->skip_length = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_iTXt_MODE; +} + +void /* PRIVATE */ +png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr) +{ + + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp key; + int comp_flag; + png_charp lang; + png_charp lang_key; + png_charp text; + int ret; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + +#if defined(PNG_MAX_MALLOC_64K) + if (png_ptr->skip_length) + return; +#endif + + key = png_ptr->current_text; + + for (lang = key; *lang; lang++) + /* empty loop */ ; + + if (lang != key + png_ptr->current_text_size) + lang++; + + comp_flag = *lang++; + lang++; /* skip comp_type, always zero */ + + for (lang_key = lang; *lang_key; lang_key++) + /* empty loop */ ; + lang_key++; /* skip NUL separator */ + + for (text = lang_key; *text; text++) + /* empty loop */ ; + + if (text != key + png_ptr->current_text_size) + text++; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = comp_flag + 2; + text_ptr->key = key; + text_ptr->lang = lang; + text_ptr->lang_key = lang_key; + text_ptr->text = text; + text_ptr->text_length = 0; + text_ptr->itxt_length = png_strlen(text); + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_ptr->current_text = NULL; + + png_free(png_ptr, text_ptr); + if (ret) + png_warning(png_ptr, "Insufficient memory to store iTXt chunk."); + } +} +#endif + +/* This function is called when we haven't found a handler for this + * chunk. If there isn't a problem with the chunk itself (ie a bad chunk + * name or a critical chunk), the chunk is (currently) silently ignored. + */ +void /* PRIVATE */ +png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + png_uint_32 skip=0; + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + + if (!(png_ptr->chunk_name[0] & 0x20)) + { +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + && png_ptr->read_user_chunk_fn == NULL +#endif + ) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + + /* to quiet compiler warnings about unused info_ptr */ + if (info_ptr == NULL) + return; + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) + { + png_unknown_chunk chunk; + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "unknown chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_strcpy((png_charp)chunk.name, (png_charp)png_ptr->chunk_name); + chunk.data = (png_bytep)png_malloc(png_ptr, length); + png_crc_read(png_ptr, chunk.data, length); + chunk.size = length; +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + if(png_ptr->read_user_chunk_fn != NULL) + { + /* callback to user unknown chunk handler */ + if ((*(png_ptr->read_user_chunk_fn)) (png_ptr, &chunk) <= 0) + { + if (!(png_ptr->chunk_name[0] & 0x20)) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS) + png_chunk_error(png_ptr, "unknown critical chunk"); + } + png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); + } + else +#endif + png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); + png_free(png_ptr, chunk.data); + } + else +#endif + skip=length; + png_push_crc_skip(png_ptr, skip); +} + +void /* PRIVATE */ +png_push_have_info(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->info_fn != NULL) + (*(png_ptr->info_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_end(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->end_fn != NULL) + (*(png_ptr->end_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr->row_fn != NULL) + (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number, + (int)png_ptr->pass); +} + +void PNGAPI +png_progressive_combine_row (png_structp png_ptr, + png_bytep old_row, png_bytep new_row) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + const int FARDATA png_pass_dsp_mask[7] = + {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; +#endif + if (new_row != NULL) /* new_row must == png_ptr->row_buf here. */ + png_combine_row(png_ptr, old_row, png_pass_dsp_mask[png_ptr->pass]); +} + +void PNGAPI +png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn) +{ + png_ptr->info_fn = info_fn; + png_ptr->row_fn = row_fn; + png_ptr->end_fn = end_fn; + + png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer); +} + +png_voidp PNGAPI +png_get_progressive_ptr(png_structp png_ptr) +{ + return png_ptr->io_ptr; +} +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ diff --git a/.svn/pristine/03/03939e911e3c5083483e599efbc794dcbc354e8c.svn-base b/.svn/pristine/03/03939e911e3c5083483e599efbc794dcbc354e8c.svn-base new file mode 100644 index 0000000..7f870ff --- /dev/null +++ b/.svn/pristine/03/03939e911e3c5083483e599efbc794dcbc354e8c.svn-base @@ -0,0 +1,43 @@ +# LinBPQ Makefile + +# To exclude i2c support run make noi2c + +OBJS = pngwtran.o pngrtran.o pngset.o pngrio.o pngwio.o pngtrans.o pngrutil.o pngwutil.o\ + pngread.o pngwrite.o png.o pngerror.o pngget.o pngmem.o APRSIconData.o AISCommon.o\ + upnp.o APRSStdPages.o HSMODEM.o WinRPR.o KISSHF.o TNCEmulators.o bpqhdlc.o SerialPort.o\ + adif.o WebMail.o utf8Routines.o VARA.o LzFind.o Alloc.o LzmaDec.o LzmaEnc.o LzmaLib.o \ + Multicast.o ARDOP.o IPCode.o FLDigi.o linether.o CMSAuth.o APRSCode.o BPQtoAGW.o KAMPactor.o\ + AEAPactor.o HALDriver.o MULTIPSK.o BBSHTMLConfig.o ChatHTMLConfig.o BBSUtilities.o bpqaxip.o\ + BPQINP3.o BPQNRR.o cMain.o Cmd.o CommonCode.o HTMLCommonCode.o compatbits.o config.o datadefs.o \ + FBBRoutines.o HFCommon.o Housekeeping.o HTTPcode.o kiss.o L2Code.o L3Code.o L4Code.o lzhuf32.o \ + MailCommands.o MailDataDefs.o LinBPQ.o MailRouting.o MailTCP.o MBLRoutines.o md5.o Moncode.o \ + NNTPRoutines.o RigControl.o TelnetV6.o WINMOR.o TNCCode.o UZ7HODrv.o WPRoutines.o \ + SCSTrackeMulti.o SCSPactor.o SCSTracker.o HanksRT.o UIRoutines.o AGWAPI.o AGWMoncode.o \ + DRATS.o FreeDATA.o base64.o Events.o nodeapi.o mailapi.o mqtt.o RHP.o + +# Configuration: + +CC = gcc + +all: CFLAGS = -DLINBPQ -MMD -g -rdynamic -fcommon +all: LDFLAGS = -l:libpaho-mqtt3a.a -l:libjansson.a +all: linbpq + + +nomqtt: CFLAGS = -DLINBPQ -MMD -fcommon -g -rdynamic -DNOMQTT +nomqtt: linbpq + +noi2c: CFLAGS = -DLINBPQ -MMD -DNOI2C -g -rdynamic -fcommon +noi2c: linbpq + + +linbpq: $(OBJS) + gcc $(OBJS) -Xlinker -Map=output.map -l:libminiupnpc.a -lrt -lm -lz $(LDFLAGS) -lpthread -lconfig -lpcap -o linbpq + sudo setcap "CAP_NET_ADMIN=ep CAP_NET_RAW=ep CAP_NET_BIND_SERVICE=ep" linbpq + +-include *.d + +clean : + rm *.d + rm linbpq $(OBJS) + diff --git a/.svn/pristine/03/03b559519d295623ca3bf53396185358ecec2f30.svn-base b/.svn/pristine/03/03b559519d295623ca3bf53396185358ecec2f30.svn-base new file mode 100644 index 0000000..58afaa7 --- /dev/null +++ b/.svn/pristine/03/03b559519d295623ca3bf53396185358ecec2f30.svn-base @@ -0,0 +1,232 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {8EFA1E59-8654-4A23-8102-AA77A074D57C} + CBPQ32 + Win32Proj + 10.0.17763.0 + + + + DynamicLibrary + v141 + NotSet + false + + + DynamicLibrary + v141 + MultiByte + + + + + + + + + + + + + + <_ProjectFileVersion>15.0.28127.55 + + + $(SolutionDir)$(Configuration)\ + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\ + true + + + $(SolutionDir)$(Configuration)\ + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\ + false + + + + 3 + ..\CInclude + true + + + Disabled + ..\CInclude;..\CommonSource;..\CKernel;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;BPQ32_EXPORTS;MDIKERNEL;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + + All + c:\devprogs\bpq32\listings\debug\ + true + Level3 + EditAndContinue + + + /section:_BPQDATA,srw %(AdditionalOptions) + WS2_32.Lib;winmm.lib;DbgHelp.lib;comctl32.lib;Iphlpapi.lib;setupapi.lib;..\lib\libconfig.lib;miniupnpc.lib;zlibstat.lib;%(AdditionalDependencies) + c:\DevProgs\BPQ32\bpq32.dll + false + LIBCMTD.lib;%(IgnoreSpecificDefaultLibraries) + ..\CommonSource\bpq32.def + true + true + c:\DevProgs\BPQ32\bpqdev.map + true + Windows + 8000000 + 4000000 + false + + 0x42000000 + ..\lib\bpq32.lib + MachineX86 + false + + + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\$(ProjectName).bsc + + + "C:\Program Files\7-Zip\7z.exe" a C:\DevProgs\BPQ32\bpq32.zip C:\DevProgs\BPQ32\bpq32.dll && myxcopy /y c:\DevProgs\BPQ32\bpq32.dll c:\windows\SysWOW64\bpq32.dll && del C:\DevProgs\BPQ32\bpq32.dll + + + + + 3 + $(IntDir)$(ProjectName) + ..\CInclude + true + true + true + true + + + /D "MDIKERNEL" %(AdditionalOptions) + Disabled + false + ..\CInclude;..\CommonSource;..\CKernel;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;BPQ32_EXPORTS;MDIKERNEL;_USE_32BIT_TIME_T;%(PreprocessorDefinitions) + MultiThreaded + + All + c:\devprogs\bpq32\listings\ + Level3 + ProgramDatabase + + + /section:_BPQDATA,srw %(AdditionalOptions) + WS2_32.Lib;winmm.lib;DbgHelp.lib;comctl32.lib;setupapi.lib;..\lib\libconfig.lib;miniupnpc.lib;zlibstat.lib;%(AdditionalDependencies) + C:\DevProgs\BPQ32\bpq32.dll + ..\CommonSource\bpq32.def + true + c:\DevProgs\BPQ32\bpq32.pdb + true + c:\DevProgs\BPQ32\bpqpdn.map + true + Windows + true + true + + 0x42000000 + C:\Dev\Msdev2005\Projects\BPQ32\lib\bpq32.lib + MachineX86 + + + C:\Dev\Msdev2005\Intermed\$(SolutionName)\$(Configuration)\$(ProjectName).bsc + + + "C:\Program Files\7-Zip\7z.exe" a C:\DevProgs\BPQ32\bpq32.zip C:\DevProgs\BPQ32\bpq32.dll && myxcopy /y c:\DevProgs\BPQ32\bpq32.dll c:\windows\SysWOW64\bpq32.dll && del C:\DevProgs\BPQ32\bpq32.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.svn/pristine/04/048651e525d8fb5c544330e2b48b108f54a00e3d.svn-base b/.svn/pristine/04/048651e525d8fb5c544330e2b48b108f54a00e3d.svn-base new file mode 100644 index 0000000..2682195 --- /dev/null +++ b/.svn/pristine/04/048651e525d8fb5c544330e2b48b108f54a00e3d.svn-base @@ -0,0 +1,71 @@ + +//Microsoft Developer Studio generated resource script. +// +//#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + + +#define BPQICON 400 + +BPQICON ICON DISCARDABLE "..\\bpqicon.ico" + +// +// Version +// +#define TEXTVER "2. 0. 1. 1\0" +#define BINVER 2, 0, 1, 1 + +VS_VERSION_INFO VERSIONINFO + FILEVERSION BINVER + PRODUCTVERSION BINVER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "Comments", "Program to hold BPQ32.dll in memory\0" + VALUE "CompanyName", " \0" + VALUE "FileDescription", "bpq32\0" + VALUE "FileVersion", TEXTVER + VALUE "InternalName", "bpq32\0" + VALUE "LegalCopyright", "Copyright © 2006-2011 G8BPQ\0" + VALUE "OriginalFilename", "bpq32.exe\0" + VALUE "ProductName", " bpq32\0" + VALUE "ProductVersion", TEXTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1200 + END +END + + +#endif // not APSTUDIO_INVOKED diff --git a/.svn/pristine/04/04f5a5d5a7082c3a1451a77d1359fa064d327499.svn-base b/.svn/pristine/04/04f5a5d5a7082c3a1451a77d1359fa064d327499.svn-base new file mode 100644 index 0000000..940a7fc --- /dev/null +++ b/.svn/pristine/04/04f5a5d5a7082c3a1451a77d1359fa064d327499.svn-base @@ -0,0 +1,3903 @@ +/* pngvcrd.c - mixed C/assembler version of utilities to read a PNG file + * + * For Intel x86 CPU and Microsoft Visual C++ compiler + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * Copyright (c) 1998, Intel Corporation + * + * Contributed by Nirav Chhatrapati, Intel Corporation, 1998 + * Interface to libpng contributed by Gilles Vollant, 1999 + * + * + * In png_do_read_interlace() in libpng versions 1.0.3a through 1.0.4d, + * a sign error in the post-MMX cleanup code for each pixel_depth resulted + * in bad pixels at the beginning of some rows of some images, and also + * (due to out-of-range memory reads and writes) caused heap corruption + * when compiled with MSVC 6.0. The error was fixed in version 1.0.4e. + * + * [png_read_filter_row_mmx_avg() bpp == 2 bugfix, GRR 20000916] + * + * [runtime MMX configuration, GRR 20010102] + * + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_USE_PNGVCRD) + +static int mmx_supported=2; + + +int PNGAPI +png_mmx_support(void) +{ + int mmx_supported_local = 0; + _asm { + push ebx //CPUID will trash these + push ecx + push edx + + pushfd //Save Eflag to stack + pop eax //Get Eflag from stack into eax + mov ecx, eax //Make another copy of Eflag in ecx + xor eax, 0x200000 //Toggle ID bit in Eflag [i.e. bit(21)] + push eax //Save modified Eflag back to stack + + popfd //Restored modified value back to Eflag reg + pushfd //Save Eflag to stack + pop eax //Get Eflag from stack + push ecx // save original Eflag to stack + popfd // restore original Eflag + xor eax, ecx //Compare the new Eflag with the original Eflag + jz NOT_SUPPORTED //If the same, CPUID instruction is not supported, + //skip following instructions and jump to + //NOT_SUPPORTED label + + xor eax, eax //Set eax to zero + + _asm _emit 0x0f //CPUID instruction (two bytes opcode) + _asm _emit 0xa2 + + cmp eax, 1 //make sure eax return non-zero value + jl NOT_SUPPORTED //If eax is zero, mmx not supported + + xor eax, eax //set eax to zero + inc eax //Now increment eax to 1. This instruction is + //faster than the instruction "mov eax, 1" + + _asm _emit 0x0f //CPUID instruction + _asm _emit 0xa2 + + and edx, 0x00800000 //mask out all bits but mmx bit(24) + cmp edx, 0 // 0 = mmx not supported + jz NOT_SUPPORTED // non-zero = Yes, mmx IS supported + + mov mmx_supported_local, 1 //set return value to 1 + +NOT_SUPPORTED: + mov eax, mmx_supported_local //move return value to eax + pop edx //CPUID trashed these + pop ecx + pop ebx + } + + //mmx_supported_local=0; // test code for force don't support MMX + //printf("MMX : %u (1=MMX supported)\n",mmx_supported_local); + + mmx_supported = mmx_supported_local; + return mmx_supported_local; +} + +/* Combines the row recently read in with the previous row. + This routine takes care of alpha and transparency if requested. + This routine also handles the two methods of progressive display + of interlaced images, depending on the mask value. + The mask value describes which pixels are to be combined with + the row. The pattern always repeats every 8 pixels, so just 8 + bits are needed. A one indicates the pixel is to be combined; a + zero indicates the pixel is to be skipped. This is in addition + to any alpha or transparency value associated with the pixel. If + you want all pixels to be combined, pass 0xff (255) in mask. */ + +/* Use this routine for x86 platform - uses faster MMX routine if machine + supports MMX */ + +void /* PRIVATE */ +png_combine_row(png_structp png_ptr, png_bytep row, int mask) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1,"in png_combine_row_asm\n"); + + if (mmx_supported == 2) { +#if !defined(PNG_1_0_X) + /* this should have happened in png_init_mmx_flags() already */ + png_warning(png_ptr, "asm_flags may not have been initialized"); +#endif + png_mmx_support(); + } + + if (mask == 0xff) + { + png_memcpy(row, png_ptr->row_buf + 1, + (png_size_t)PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->width)); + } + /* GRR: add "else if (mask == 0)" case? + * or does png_combine_row() not even get called in that case? */ + else + { + switch (png_ptr->row_info.pixel_depth) + { + case 1: + { + png_bytep sp; + png_bytep dp; + int s_inc, s_start, s_end; + int m; + int shift; + png_uint_32 i; + + sp = png_ptr->row_buf + 1; + dp = row; + m = 0x80; +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 7; + s_inc = 1; + } + else +#endif + { + s_start = 7; + s_end = 0; + s_inc = -1; + } + + shift = s_start; + + for (i = 0; i < png_ptr->width; i++) + { + if (m & mask) + { + int value; + + value = (*sp >> shift) & 0x1; + *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + + case 2: + { + png_bytep sp; + png_bytep dp; + int s_start, s_end, s_inc; + int m; + int shift; + png_uint_32 i; + int value; + + sp = png_ptr->row_buf + 1; + dp = row; + m = 0x80; +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 6; + s_inc = 2; + } + else +#endif + { + s_start = 6; + s_end = 0; + s_inc = -2; + } + + shift = s_start; + + for (i = 0; i < png_ptr->width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0x3; + *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + + case 4: + { + png_bytep sp; + png_bytep dp; + int s_start, s_end, s_inc; + int m; + int shift; + png_uint_32 i; + int value; + + sp = png_ptr->row_buf + 1; + dp = row; + m = 0x80; +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 4; + s_inc = 4; + } + else +#endif + { + s_start = 4; + s_end = 0; + s_inc = -4; + } + shift = s_start; + + for (i = 0; i < png_ptr->width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0xf; + *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + + case 8: + { + png_bytep srcptr; + png_bytep dstptr; + png_uint_32 len; + int m; + int diff, unmask; + + __int64 mask0=0x0102040810204080; + +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && mmx_supported */ ) +#else + if (mmx_supported) +#endif + { + srcptr = png_ptr->row_buf + 1; + dstptr = row; + m = 0x80; + unmask = ~mask; + len = png_ptr->width &~7; //reduce to multiple of 8 + diff = png_ptr->width & 7; //amount lost + + _asm + { + movd mm7, unmask //load bit pattern + psubb mm6,mm6 //zero mm6 + punpcklbw mm7,mm7 + punpcklwd mm7,mm7 + punpckldq mm7,mm7 //fill register with 8 masks + + movq mm0,mask0 + + pand mm0,mm7 //nonzero if keep byte + pcmpeqb mm0,mm6 //zeros->1s, v versa + + mov ecx,len //load length of line (pixels) + mov esi,srcptr //load source + mov ebx,dstptr //load dest + cmp ecx,0 //lcr + je mainloop8end + +mainloop8: + movq mm4,[esi] + pand mm4,mm0 + movq mm6,mm0 + pandn mm6,[ebx] + por mm4,mm6 + movq [ebx],mm4 + + add esi,8 //inc by 8 bytes processed + add ebx,8 + sub ecx,8 //dec by 8 pixels processed + + ja mainloop8 +mainloop8end: + + mov ecx,diff + cmp ecx,0 + jz end8 + + mov edx,mask + sal edx,24 //make low byte the high byte + +secondloop8: + sal edx,1 //move high bit to CF + jnc skip8 //if CF = 0 + mov al,[esi] + mov [ebx],al +skip8: + inc esi + inc ebx + + dec ecx + jnz secondloop8 +end8: + emms + } + } + else /* mmx not supported - use modified C routine */ + { + register unsigned int incr1, initial_val, final_val; + png_size_t pixel_bytes; + png_uint_32 i; + register int disp = png_pass_inc[png_ptr->pass]; + int offset_table[7] = {0, 4, 0, 2, 0, 1, 0}; + + pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]* + pixel_bytes; + dstptr = row + offset_table[png_ptr->pass]*pixel_bytes; + initial_val = offset_table[png_ptr->pass]*pixel_bytes; + final_val = png_ptr->width*pixel_bytes; + incr1 = (disp)*pixel_bytes; + for (i = initial_val; i < final_val; i += incr1) + { + png_memcpy(dstptr, srcptr, pixel_bytes); + srcptr += incr1; + dstptr += incr1; + } + } /* end of else */ + + break; + } // end 8 bpp + + case 16: + { + png_bytep srcptr; + png_bytep dstptr; + png_uint_32 len; + int unmask, diff; + __int64 mask1=0x0101020204040808, + mask0=0x1010202040408080; + +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && mmx_supported */ ) +#else + if (mmx_supported) +#endif + { + srcptr = png_ptr->row_buf + 1; + dstptr = row; + + unmask = ~mask; + len = (png_ptr->width)&~7; + diff = (png_ptr->width)&7; + _asm + { + movd mm7, unmask //load bit pattern + psubb mm6,mm6 //zero mm6 + punpcklbw mm7,mm7 + punpcklwd mm7,mm7 + punpckldq mm7,mm7 //fill register with 8 masks + + movq mm0,mask0 + movq mm1,mask1 + + pand mm0,mm7 + pand mm1,mm7 + + pcmpeqb mm0,mm6 + pcmpeqb mm1,mm6 + + mov ecx,len //load length of line + mov esi,srcptr //load source + mov ebx,dstptr //load dest + cmp ecx,0 //lcr + jz mainloop16end + +mainloop16: + movq mm4,[esi] + pand mm4,mm0 + movq mm6,mm0 + movq mm7,[ebx] + pandn mm6,mm7 + por mm4,mm6 + movq [ebx],mm4 + + movq mm5,[esi+8] + pand mm5,mm1 + movq mm7,mm1 + movq mm6,[ebx+8] + pandn mm7,mm6 + por mm5,mm7 + movq [ebx+8],mm5 + + add esi,16 //inc by 16 bytes processed + add ebx,16 + sub ecx,8 //dec by 8 pixels processed + + ja mainloop16 + +mainloop16end: + mov ecx,diff + cmp ecx,0 + jz end16 + + mov edx,mask + sal edx,24 //make low byte the high byte +secondloop16: + sal edx,1 //move high bit to CF + jnc skip16 //if CF = 0 + mov ax,[esi] + mov [ebx],ax +skip16: + add esi,2 + add ebx,2 + + dec ecx + jnz secondloop16 +end16: + emms + } + } + else /* mmx not supported - use modified C routine */ + { + register unsigned int incr1, initial_val, final_val; + png_size_t pixel_bytes; + png_uint_32 i; + register int disp = png_pass_inc[png_ptr->pass]; + int offset_table[7] = {0, 4, 0, 2, 0, 1, 0}; + + pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]* + pixel_bytes; + dstptr = row + offset_table[png_ptr->pass]*pixel_bytes; + initial_val = offset_table[png_ptr->pass]*pixel_bytes; + final_val = png_ptr->width*pixel_bytes; + incr1 = (disp)*pixel_bytes; + for (i = initial_val; i < final_val; i += incr1) + { + png_memcpy(dstptr, srcptr, pixel_bytes); + srcptr += incr1; + dstptr += incr1; + } + } /* end of else */ + + break; + } // end 16 bpp + + case 24: + { + png_bytep srcptr; + png_bytep dstptr; + png_uint_32 len; + int unmask, diff; + + __int64 mask2=0x0101010202020404, //24bpp + mask1=0x0408080810101020, + mask0=0x2020404040808080; + + srcptr = png_ptr->row_buf + 1; + dstptr = row; + + unmask = ~mask; + len = (png_ptr->width)&~7; + diff = (png_ptr->width)&7; + +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && mmx_supported */ ) +#else + if (mmx_supported) +#endif + { + _asm + { + movd mm7, unmask //load bit pattern + psubb mm6,mm6 //zero mm6 + punpcklbw mm7,mm7 + punpcklwd mm7,mm7 + punpckldq mm7,mm7 //fill register with 8 masks + + movq mm0,mask0 + movq mm1,mask1 + movq mm2,mask2 + + pand mm0,mm7 + pand mm1,mm7 + pand mm2,mm7 + + pcmpeqb mm0,mm6 + pcmpeqb mm1,mm6 + pcmpeqb mm2,mm6 + + mov ecx,len //load length of line + mov esi,srcptr //load source + mov ebx,dstptr //load dest + cmp ecx,0 + jz mainloop24end + +mainloop24: + movq mm4,[esi] + pand mm4,mm0 + movq mm6,mm0 + movq mm7,[ebx] + pandn mm6,mm7 + por mm4,mm6 + movq [ebx],mm4 + + + movq mm5,[esi+8] + pand mm5,mm1 + movq mm7,mm1 + movq mm6,[ebx+8] + pandn mm7,mm6 + por mm5,mm7 + movq [ebx+8],mm5 + + movq mm6,[esi+16] + pand mm6,mm2 + movq mm4,mm2 + movq mm7,[ebx+16] + pandn mm4,mm7 + por mm6,mm4 + movq [ebx+16],mm6 + + add esi,24 //inc by 24 bytes processed + add ebx,24 + sub ecx,8 //dec by 8 pixels processed + + ja mainloop24 + +mainloop24end: + mov ecx,diff + cmp ecx,0 + jz end24 + + mov edx,mask + sal edx,24 //make low byte the high byte +secondloop24: + sal edx,1 //move high bit to CF + jnc skip24 //if CF = 0 + mov ax,[esi] + mov [ebx],ax + xor eax,eax + mov al,[esi+2] + mov [ebx+2],al +skip24: + add esi,3 + add ebx,3 + + dec ecx + jnz secondloop24 + +end24: + emms + } + } + else /* mmx not supported - use modified C routine */ + { + register unsigned int incr1, initial_val, final_val; + png_size_t pixel_bytes; + png_uint_32 i; + register int disp = png_pass_inc[png_ptr->pass]; + int offset_table[7] = {0, 4, 0, 2, 0, 1, 0}; + + pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]* + pixel_bytes; + dstptr = row + offset_table[png_ptr->pass]*pixel_bytes; + initial_val = offset_table[png_ptr->pass]*pixel_bytes; + final_val = png_ptr->width*pixel_bytes; + incr1 = (disp)*pixel_bytes; + for (i = initial_val; i < final_val; i += incr1) + { + png_memcpy(dstptr, srcptr, pixel_bytes); + srcptr += incr1; + dstptr += incr1; + } + } /* end of else */ + + break; + } // end 24 bpp + + case 32: + { + png_bytep srcptr; + png_bytep dstptr; + png_uint_32 len; + int unmask, diff; + + __int64 mask3=0x0101010102020202, //32bpp + mask2=0x0404040408080808, + mask1=0x1010101020202020, + mask0=0x4040404080808080; + + srcptr = png_ptr->row_buf + 1; + dstptr = row; + + unmask = ~mask; + len = (png_ptr->width)&~7; + diff = (png_ptr->width)&7; + +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && mmx_supported */ ) +#else + if (mmx_supported) +#endif + { + _asm + { + movd mm7, unmask //load bit pattern + psubb mm6,mm6 //zero mm6 + punpcklbw mm7,mm7 + punpcklwd mm7,mm7 + punpckldq mm7,mm7 //fill register with 8 masks + + movq mm0,mask0 + movq mm1,mask1 + movq mm2,mask2 + movq mm3,mask3 + + pand mm0,mm7 + pand mm1,mm7 + pand mm2,mm7 + pand mm3,mm7 + + pcmpeqb mm0,mm6 + pcmpeqb mm1,mm6 + pcmpeqb mm2,mm6 + pcmpeqb mm3,mm6 + + mov ecx,len //load length of line + mov esi,srcptr //load source + mov ebx,dstptr //load dest + + cmp ecx,0 //lcr + jz mainloop32end + +mainloop32: + movq mm4,[esi] + pand mm4,mm0 + movq mm6,mm0 + movq mm7,[ebx] + pandn mm6,mm7 + por mm4,mm6 + movq [ebx],mm4 + + movq mm5,[esi+8] + pand mm5,mm1 + movq mm7,mm1 + movq mm6,[ebx+8] + pandn mm7,mm6 + por mm5,mm7 + movq [ebx+8],mm5 + + movq mm6,[esi+16] + pand mm6,mm2 + movq mm4,mm2 + movq mm7,[ebx+16] + pandn mm4,mm7 + por mm6,mm4 + movq [ebx+16],mm6 + + movq mm7,[esi+24] + pand mm7,mm3 + movq mm5,mm3 + movq mm4,[ebx+24] + pandn mm5,mm4 + por mm7,mm5 + movq [ebx+24],mm7 + + add esi,32 //inc by 32 bytes processed + add ebx,32 + sub ecx,8 //dec by 8 pixels processed + + ja mainloop32 + +mainloop32end: + mov ecx,diff + cmp ecx,0 + jz end32 + + mov edx,mask + sal edx,24 //make low byte the high byte +secondloop32: + sal edx,1 //move high bit to CF + jnc skip32 //if CF = 0 + mov eax,[esi] + mov [ebx],eax +skip32: + add esi,4 + add ebx,4 + + dec ecx + jnz secondloop32 + +end32: + emms + } + } + else /* mmx _not supported - Use modified C routine */ + { + register unsigned int incr1, initial_val, final_val; + png_size_t pixel_bytes; + png_uint_32 i; + register int disp = png_pass_inc[png_ptr->pass]; + int offset_table[7] = {0, 4, 0, 2, 0, 1, 0}; + + pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]* + pixel_bytes; + dstptr = row + offset_table[png_ptr->pass]*pixel_bytes; + initial_val = offset_table[png_ptr->pass]*pixel_bytes; + final_val = png_ptr->width*pixel_bytes; + incr1 = (disp)*pixel_bytes; + for (i = initial_val; i < final_val; i += incr1) + { + png_memcpy(dstptr, srcptr, pixel_bytes); + srcptr += incr1; + dstptr += incr1; + } + } /* end of else */ + + break; + } // end 32 bpp + + case 48: + { + png_bytep srcptr; + png_bytep dstptr; + png_uint_32 len; + int unmask, diff; + + __int64 mask5=0x0101010101010202, + mask4=0x0202020204040404, + mask3=0x0404080808080808, + mask2=0x1010101010102020, + mask1=0x2020202040404040, + mask0=0x4040808080808080; + +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && mmx_supported */ ) +#else + if (mmx_supported) +#endif + { + srcptr = png_ptr->row_buf + 1; + dstptr = row; + + unmask = ~mask; + len = (png_ptr->width)&~7; + diff = (png_ptr->width)&7; + _asm + { + movd mm7, unmask //load bit pattern + psubb mm6,mm6 //zero mm6 + punpcklbw mm7,mm7 + punpcklwd mm7,mm7 + punpckldq mm7,mm7 //fill register with 8 masks + + movq mm0,mask0 + movq mm1,mask1 + movq mm2,mask2 + movq mm3,mask3 + movq mm4,mask4 + movq mm5,mask5 + + pand mm0,mm7 + pand mm1,mm7 + pand mm2,mm7 + pand mm3,mm7 + pand mm4,mm7 + pand mm5,mm7 + + pcmpeqb mm0,mm6 + pcmpeqb mm1,mm6 + pcmpeqb mm2,mm6 + pcmpeqb mm3,mm6 + pcmpeqb mm4,mm6 + pcmpeqb mm5,mm6 + + mov ecx,len //load length of line + mov esi,srcptr //load source + mov ebx,dstptr //load dest + + cmp ecx,0 + jz mainloop48end + +mainloop48: + movq mm7,[esi] + pand mm7,mm0 + movq mm6,mm0 + pandn mm6,[ebx] + por mm7,mm6 + movq [ebx],mm7 + + movq mm6,[esi+8] + pand mm6,mm1 + movq mm7,mm1 + pandn mm7,[ebx+8] + por mm6,mm7 + movq [ebx+8],mm6 + + movq mm6,[esi+16] + pand mm6,mm2 + movq mm7,mm2 + pandn mm7,[ebx+16] + por mm6,mm7 + movq [ebx+16],mm6 + + movq mm7,[esi+24] + pand mm7,mm3 + movq mm6,mm3 + pandn mm6,[ebx+24] + por mm7,mm6 + movq [ebx+24],mm7 + + movq mm6,[esi+32] + pand mm6,mm4 + movq mm7,mm4 + pandn mm7,[ebx+32] + por mm6,mm7 + movq [ebx+32],mm6 + + movq mm7,[esi+40] + pand mm7,mm5 + movq mm6,mm5 + pandn mm6,[ebx+40] + por mm7,mm6 + movq [ebx+40],mm7 + + add esi,48 //inc by 32 bytes processed + add ebx,48 + sub ecx,8 //dec by 8 pixels processed + + ja mainloop48 +mainloop48end: + + mov ecx,diff + cmp ecx,0 + jz end48 + + mov edx,mask + sal edx,24 //make low byte the high byte + +secondloop48: + sal edx,1 //move high bit to CF + jnc skip48 //if CF = 0 + mov eax,[esi] + mov [ebx],eax +skip48: + add esi,4 + add ebx,4 + + dec ecx + jnz secondloop48 + +end48: + emms + } + } + else /* mmx _not supported - Use modified C routine */ + { + register unsigned int incr1, initial_val, final_val; + png_size_t pixel_bytes; + png_uint_32 i; + register int disp = png_pass_inc[png_ptr->pass]; + int offset_table[7] = {0, 4, 0, 2, 0, 1, 0}; + + pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]* + pixel_bytes; + dstptr = row + offset_table[png_ptr->pass]*pixel_bytes; + initial_val = offset_table[png_ptr->pass]*pixel_bytes; + final_val = png_ptr->width*pixel_bytes; + incr1 = (disp)*pixel_bytes; + for (i = initial_val; i < final_val; i += incr1) + { + png_memcpy(dstptr, srcptr, pixel_bytes); + srcptr += incr1; + dstptr += incr1; + } + } /* end of else */ + + break; + } // end 48 bpp + + default: + { + png_bytep sptr; + png_bytep dp; + png_size_t pixel_bytes; + int offset_table[7] = {0, 4, 0, 2, 0, 1, 0}; + unsigned int i; + register int disp = png_pass_inc[png_ptr->pass]; // get the offset + register unsigned int incr1, initial_val, final_val; + + pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + sptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]* + pixel_bytes; + dp = row + offset_table[png_ptr->pass]*pixel_bytes; + initial_val = offset_table[png_ptr->pass]*pixel_bytes; + final_val = png_ptr->width*pixel_bytes; + incr1 = (disp)*pixel_bytes; + for (i = initial_val; i < final_val; i += incr1) + { + png_memcpy(dp, sptr, pixel_bytes); + sptr += incr1; + dp += incr1; + } + break; + } + } /* end switch (png_ptr->row_info.pixel_depth) */ + } /* end if (non-trivial mask) */ + +} /* end png_combine_row() */ + + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + +void /* PRIVATE */ +png_do_read_interlace(png_structp png_ptr) +{ + png_row_infop row_info = &(png_ptr->row_info); + png_bytep row = png_ptr->row_buf + 1; + int pass = png_ptr->pass; + png_uint_32 transformations = png_ptr->transformations; +#ifdef PNG_USE_LOCAL_ARRAYS + const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1,"in png_do_read_interlace\n"); + + if (mmx_supported == 2) { +#if !defined(PNG_1_0_X) + /* this should have happened in png_init_mmx_flags() already */ + png_warning(png_ptr, "asm_flags may not have been initialized"); +#endif + png_mmx_support(); + } + + if (row != NULL && row_info != NULL) + { + png_uint_32 final_width; + + final_width = row_info->width * png_pass_inc[pass]; + + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp, dp; + int sshift, dshift; + int s_start, s_end, s_inc; + png_byte v; + png_uint_32 i; + int j; + + sp = row + (png_size_t)((row_info->width - 1) >> 3); + dp = row + (png_size_t)((final_width - 1) >> 3); +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)((row_info->width + 7) & 7); + dshift = (int)((final_width + 7) & 7); + s_start = 7; + s_end = 0; + s_inc = -1; + } + else +#endif + { + sshift = 7 - (int)((row_info->width + 7) & 7); + dshift = 7 - (int)((final_width + 7) & 7); + s_start = 0; + s_end = 7; + s_inc = 1; + } + + for (i = row_info->width; i; i--) + { + v = (png_byte)((*sp >> sshift) & 0x1); + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + + case 2: + { + png_bytep sp, dp; + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + + sp = row + (png_size_t)((row_info->width - 1) >> 2); + dp = row + (png_size_t)((final_width - 1) >> 2); +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (png_size_t)(((row_info->width + 3) & 3) << 1); + dshift = (png_size_t)(((final_width + 3) & 3) << 1); + s_start = 6; + s_end = 0; + s_inc = -2; + } + else +#endif + { + sshift = (png_size_t)((3 - ((row_info->width + 3) & 3)) << 1); + dshift = (png_size_t)((3 - ((final_width + 3) & 3)) << 1); + s_start = 0; + s_end = 6; + s_inc = 2; + } + + for (i = row_info->width; i; i--) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0x3); + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + + case 4: + { + png_bytep sp, dp; + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + + sp = row + (png_size_t)((row_info->width - 1) >> 1); + dp = row + (png_size_t)((final_width - 1) >> 1); +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (png_size_t)(((row_info->width + 1) & 1) << 2); + dshift = (png_size_t)(((final_width + 1) & 1) << 2); + s_start = 4; + s_end = 0; + s_inc = -4; + } + else +#endif + { + sshift = (png_size_t)((1 - ((row_info->width + 1) & 1)) << 2); + dshift = (png_size_t)((1 - ((final_width + 1) & 1)) << 2); + s_start = 0; + s_end = 4; + s_inc = 4; + } + + for (i = row_info->width; i; i--) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0xf); + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + + default: // This is the place where the routine is modified + { + __int64 const4 = 0x0000000000FFFFFF; + // __int64 const5 = 0x000000FFFFFF0000; // unused... + __int64 const6 = 0x00000000000000FF; + png_bytep sptr, dp; + png_uint_32 i; + png_size_t pixel_bytes; + int width = row_info->width; + + pixel_bytes = (row_info->pixel_depth >> 3); + + sptr = row + (width - 1) * pixel_bytes; + dp = row + (final_width - 1) * pixel_bytes; + // New code by Nirav Chhatrapati - Intel Corporation + // sign fix by GRR + // NOTE: there is NO MMX code for 48-bit and 64-bit images + + // use MMX routine if machine supports it +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE) + /* && mmx_supported */ ) +#else + if (mmx_supported) +#endif + { + if (pixel_bytes == 3) + { + if (((pass == 0) || (pass == 1)) && width) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width + sub edi, 21 // (png_pass_inc[pass] - 1)*pixel_bytes +loop_pass0: + movd mm0, [esi] ; X X X X X v2 v1 v0 + pand mm0, const4 ; 0 0 0 0 0 v2 v1 v0 + movq mm1, mm0 ; 0 0 0 0 0 v2 v1 v0 + psllq mm0, 16 ; 0 0 0 v2 v1 v0 0 0 + movq mm2, mm0 ; 0 0 0 v2 v1 v0 0 0 + psllq mm0, 24 ; v2 v1 v0 0 0 0 0 0 + psrlq mm1, 8 ; 0 0 0 0 0 0 v2 v1 + por mm0, mm2 ; v2 v1 v0 v2 v1 v0 0 0 + por mm0, mm1 ; v2 v1 v0 v2 v1 v0 v2 v1 + movq mm3, mm0 ; v2 v1 v0 v2 v1 v0 v2 v1 + psllq mm0, 16 ; v0 v2 v1 v0 v2 v1 0 0 + movq mm4, mm3 ; v2 v1 v0 v2 v1 v0 v2 v1 + punpckhdq mm3, mm0 ; v0 v2 v1 v0 v2 v1 v0 v2 + movq [edi+16] , mm4 + psrlq mm0, 32 ; 0 0 0 0 v0 v2 v1 v0 + movq [edi+8] , mm3 + punpckldq mm0, mm4 ; v1 v0 v2 v1 v0 v2 v1 v0 + sub esi, 3 + movq [edi], mm0 + sub edi, 24 + //sub esi, 3 + dec ecx + jnz loop_pass0 + EMMS + } + } + else if (((pass == 2) || (pass == 3)) && width) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width + sub edi, 9 // (png_pass_inc[pass] - 1)*pixel_bytes +loop_pass2: + movd mm0, [esi] ; X X X X X v2 v1 v0 + pand mm0, const4 ; 0 0 0 0 0 v2 v1 v0 + movq mm1, mm0 ; 0 0 0 0 0 v2 v1 v0 + psllq mm0, 16 ; 0 0 0 v2 v1 v0 0 0 + movq mm2, mm0 ; 0 0 0 v2 v1 v0 0 0 + psllq mm0, 24 ; v2 v1 v0 0 0 0 0 0 + psrlq mm1, 8 ; 0 0 0 0 0 0 v2 v1 + por mm0, mm2 ; v2 v1 v0 v2 v1 v0 0 0 + por mm0, mm1 ; v2 v1 v0 v2 v1 v0 v2 v1 + movq [edi+4], mm0 ; move to memory + psrlq mm0, 16 ; 0 0 v2 v1 v0 v2 v1 v0 + movd [edi], mm0 ; move to memory + sub esi, 3 + sub edi, 12 + dec ecx + jnz loop_pass2 + EMMS + } + } + else if (width) /* && ((pass == 4) || (pass == 5)) */ + { + int width_mmx = ((width >> 1) << 1) - 8; + if (width_mmx < 0) + width_mmx = 0; + width -= width_mmx; // 8 or 9 pix, 24 or 27 bytes + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 3 + sub edi, 9 +loop_pass4: + movq mm0, [esi] ; X X v2 v1 v0 v5 v4 v3 + movq mm7, mm0 ; X X v2 v1 v0 v5 v4 v3 + movq mm6, mm0 ; X X v2 v1 v0 v5 v4 v3 + psllq mm0, 24 ; v1 v0 v5 v4 v3 0 0 0 + pand mm7, const4 ; 0 0 0 0 0 v5 v4 v3 + psrlq mm6, 24 ; 0 0 0 X X v2 v1 v0 + por mm0, mm7 ; v1 v0 v5 v4 v3 v5 v4 v3 + movq mm5, mm6 ; 0 0 0 X X v2 v1 v0 + psllq mm6, 8 ; 0 0 X X v2 v1 v0 0 + movq [edi], mm0 ; move quad to memory + psrlq mm5, 16 ; 0 0 0 0 0 X X v2 + pand mm5, const6 ; 0 0 0 0 0 0 0 v2 + por mm6, mm5 ; 0 0 X X v2 v1 v0 v2 + movd [edi+8], mm6 ; move double to memory + sub esi, 6 + sub edi, 12 + sub ecx, 2 + jnz loop_pass4 + EMMS + } + } + + sptr -= width_mmx*3; + dp -= width_mmx*6; + for (i = width; i; i--) + { + png_byte v[8]; + int j; + + png_memcpy(v, sptr, 3); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, 3); + dp -= 3; + } + sptr -= 3; + } + } + } /* end of pixel_bytes == 3 */ + + else if (pixel_bytes == 1) + { + if (((pass == 0) || (pass == 1)) && width) + { + int width_mmx = ((width >> 2) << 2); + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub edi, 31 + sub esi, 3 +loop1_pass0: + movd mm0, [esi] ; X X X X v0 v1 v2 v3 + movq mm1, mm0 ; X X X X v0 v1 v2 v3 + punpcklbw mm0, mm0 ; v0 v0 v1 v1 v2 v2 v3 v3 + movq mm2, mm0 ; v0 v0 v1 v1 v2 v2 v3 v3 + punpcklwd mm0, mm0 ; v2 v2 v2 v2 v3 v3 v3 v3 + movq mm3, mm0 ; v2 v2 v2 v2 v3 v3 v3 v3 + punpckldq mm0, mm0 ; v3 v3 v3 v3 v3 v3 v3 v3 + punpckhdq mm3, mm3 ; v2 v2 v2 v2 v2 v2 v2 v2 + movq [edi], mm0 ; move to memory v3 + punpckhwd mm2, mm2 ; v0 v0 v0 v0 v1 v1 v1 v1 + movq [edi+8], mm3 ; move to memory v2 + movq mm4, mm2 ; v0 v0 v0 v0 v1 v1 v1 v1 + punpckldq mm2, mm2 ; v1 v1 v1 v1 v1 v1 v1 v1 + punpckhdq mm4, mm4 ; v0 v0 v0 v0 v0 v0 v0 v0 + movq [edi+16], mm2 ; move to memory v1 + movq [edi+24], mm4 ; move to memory v0 + sub esi, 4 + sub edi, 32 + sub ecx, 4 + jnz loop1_pass0 + EMMS + } + } + + sptr -= width_mmx; + dp -= width_mmx*8; + for (i = width; i; i--) + { + int j; + + /* I simplified this part in version 1.0.4e + * here and in several other instances where + * pixel_bytes == 1 -- GR-P + * + * Original code: + * + * png_byte v[8]; + * png_memcpy(v, sptr, pixel_bytes); + * for (j = 0; j < png_pass_inc[pass]; j++) + * { + * png_memcpy(dp, v, pixel_bytes); + * dp -= pixel_bytes; + * } + * sptr -= pixel_bytes; + * + * Replacement code is in the next three lines: + */ + + for (j = 0; j < png_pass_inc[pass]; j++) + *dp-- = *sptr; + sptr--; + } + } + else if (((pass == 2) || (pass == 3)) && width) + { + int width_mmx = ((width >> 2) << 2); + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub edi, 15 + sub esi, 3 +loop1_pass2: + movd mm0, [esi] ; X X X X v0 v1 v2 v3 + punpcklbw mm0, mm0 ; v0 v0 v1 v1 v2 v2 v3 v3 + movq mm1, mm0 ; v0 v0 v1 v1 v2 v2 v3 v3 + punpcklwd mm0, mm0 ; v2 v2 v2 v2 v3 v3 v3 v3 + punpckhwd mm1, mm1 ; v0 v0 v0 v0 v1 v1 v1 v1 + movq [edi], mm0 ; move to memory v2 and v3 + sub esi, 4 + movq [edi+8], mm1 ; move to memory v1 and v0 + sub edi, 16 + sub ecx, 4 + jnz loop1_pass2 + EMMS + } + } + + sptr -= width_mmx; + dp -= width_mmx*4; + for (i = width; i; i--) + { + int j; + + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp-- = *sptr; + } + sptr --; + } + } + else if (width) /* && ((pass == 4) || (pass == 5))) */ + { + int width_mmx = ((width >> 3) << 3); + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub edi, 15 + sub esi, 7 +loop1_pass4: + movq mm0, [esi] ; v0 v1 v2 v3 v4 v5 v6 v7 + movq mm1, mm0 ; v0 v1 v2 v3 v4 v5 v6 v7 + punpcklbw mm0, mm0 ; v4 v4 v5 v5 v6 v6 v7 v7 + //movq mm1, mm0 ; v0 v0 v1 v1 v2 v2 v3 v3 + punpckhbw mm1, mm1 ;v0 v0 v1 v1 v2 v2 v3 v3 + movq [edi+8], mm1 ; move to memory v0 v1 v2 and v3 + sub esi, 8 + movq [edi], mm0 ; move to memory v4 v5 v6 and v7 + //sub esi, 4 + sub edi, 16 + sub ecx, 8 + jnz loop1_pass4 + EMMS + } + } + + sptr -= width_mmx; + dp -= width_mmx*2; + for (i = width; i; i--) + { + int j; + + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp-- = *sptr; + } + sptr --; + } + } + } /* end of pixel_bytes == 1 */ + + else if (pixel_bytes == 2) + { + if (((pass == 0) || (pass == 1)) && width) + { + int width_mmx = ((width >> 1) << 1); + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 2 + sub edi, 30 +loop2_pass0: + movd mm0, [esi] ; X X X X v1 v0 v3 v2 + punpcklwd mm0, mm0 ; v1 v0 v1 v0 v3 v2 v3 v2 + movq mm1, mm0 ; v1 v0 v1 v0 v3 v2 v3 v2 + punpckldq mm0, mm0 ; v3 v2 v3 v2 v3 v2 v3 v2 + punpckhdq mm1, mm1 ; v1 v0 v1 v0 v1 v0 v1 v0 + movq [edi], mm0 + movq [edi + 8], mm0 + movq [edi + 16], mm1 + movq [edi + 24], mm1 + sub esi, 4 + sub edi, 32 + sub ecx, 2 + jnz loop2_pass0 + EMMS + } + } + + sptr -= (width_mmx*2 - 2); // sign fixed + dp -= (width_mmx*16 - 2); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 2; + png_memcpy(v, sptr, 2); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 2; + png_memcpy(dp, v, 2); + } + } + } + else if (((pass == 2) || (pass == 3)) && width) + { + int width_mmx = ((width >> 1) << 1) ; + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 2 + sub edi, 14 +loop2_pass2: + movd mm0, [esi] ; X X X X v1 v0 v3 v2 + punpcklwd mm0, mm0 ; v1 v0 v1 v0 v3 v2 v3 v2 + movq mm1, mm0 ; v1 v0 v1 v0 v3 v2 v3 v2 + punpckldq mm0, mm0 ; v3 v2 v3 v2 v3 v2 v3 v2 + punpckhdq mm1, mm1 ; v1 v0 v1 v0 v1 v0 v1 v0 + movq [edi], mm0 + sub esi, 4 + movq [edi + 8], mm1 + //sub esi, 4 + sub edi, 16 + sub ecx, 2 + jnz loop2_pass2 + EMMS + } + } + + sptr -= (width_mmx*2 - 2); // sign fixed + dp -= (width_mmx*8 - 2); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 2; + png_memcpy(v, sptr, 2); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 2; + png_memcpy(dp, v, 2); + } + } + } + else if (width) // pass == 4 or 5 + { + int width_mmx = ((width >> 1) << 1) ; + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 2 + sub edi, 6 +loop2_pass4: + movd mm0, [esi] ; X X X X v1 v0 v3 v2 + punpcklwd mm0, mm0 ; v1 v0 v1 v0 v3 v2 v3 v2 + sub esi, 4 + movq [edi], mm0 + sub edi, 8 + sub ecx, 2 + jnz loop2_pass4 + EMMS + } + } + + sptr -= (width_mmx*2 - 2); // sign fixed + dp -= (width_mmx*4 - 2); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 2; + png_memcpy(v, sptr, 2); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 2; + png_memcpy(dp, v, 2); + } + } + } + } /* end of pixel_bytes == 2 */ + + else if (pixel_bytes == 4) + { + if (((pass == 0) || (pass == 1)) && width) + { + int width_mmx = ((width >> 1) << 1) ; + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 4 + sub edi, 60 +loop4_pass0: + movq mm0, [esi] ; v3 v2 v1 v0 v7 v6 v5 v4 + movq mm1, mm0 ; v3 v2 v1 v0 v7 v6 v5 v4 + punpckldq mm0, mm0 ; v7 v6 v5 v4 v7 v6 v5 v4 + punpckhdq mm1, mm1 ; v3 v2 v1 v0 v3 v2 v1 v0 + movq [edi], mm0 + movq [edi + 8], mm0 + movq [edi + 16], mm0 + movq [edi + 24], mm0 + movq [edi+32], mm1 + movq [edi + 40], mm1 + movq [edi+ 48], mm1 + sub esi, 8 + movq [edi + 56], mm1 + sub edi, 64 + sub ecx, 2 + jnz loop4_pass0 + EMMS + } + } + + sptr -= (width_mmx*4 - 4); // sign fixed + dp -= (width_mmx*32 - 4); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 4; + png_memcpy(v, sptr, 4); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 4; + png_memcpy(dp, v, 4); + } + } + } + else if (((pass == 2) || (pass == 3)) && width) + { + int width_mmx = ((width >> 1) << 1) ; + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 4 + sub edi, 28 +loop4_pass2: + movq mm0, [esi] ; v3 v2 v1 v0 v7 v6 v5 v4 + movq mm1, mm0 ; v3 v2 v1 v0 v7 v6 v5 v4 + punpckldq mm0, mm0 ; v7 v6 v5 v4 v7 v6 v5 v4 + punpckhdq mm1, mm1 ; v3 v2 v1 v0 v3 v2 v1 v0 + movq [edi], mm0 + movq [edi + 8], mm0 + movq [edi+16], mm1 + movq [edi + 24], mm1 + sub esi, 8 + sub edi, 32 + sub ecx, 2 + jnz loop4_pass2 + EMMS + } + } + + sptr -= (width_mmx*4 - 4); // sign fixed + dp -= (width_mmx*16 - 4); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 4; + png_memcpy(v, sptr, 4); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 4; + png_memcpy(dp, v, 4); + } + } + } + else if (width) // pass == 4 or 5 + { + int width_mmx = ((width >> 1) << 1) ; + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 4 + sub edi, 12 +loop4_pass4: + movq mm0, [esi] ; v3 v2 v1 v0 v7 v6 v5 v4 + movq mm1, mm0 ; v3 v2 v1 v0 v7 v6 v5 v4 + punpckldq mm0, mm0 ; v7 v6 v5 v4 v7 v6 v5 v4 + punpckhdq mm1, mm1 ; v3 v2 v1 v0 v3 v2 v1 v0 + movq [edi], mm0 + sub esi, 8 + movq [edi + 8], mm1 + sub edi, 16 + sub ecx, 2 + jnz loop4_pass4 + EMMS + } + } + + sptr -= (width_mmx*4 - 4); // sign fixed + dp -= (width_mmx*8 - 4); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 4; + png_memcpy(v, sptr, 4); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 4; + png_memcpy(dp, v, 4); + } + } + } + + } /* end of pixel_bytes == 4 */ + + else if (pixel_bytes == 6) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, 6); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, 6); + dp -= 6; + } + sptr -= 6; + } + } /* end of pixel_bytes == 6 */ + + else + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr-= pixel_bytes; + } + } + } /* end of mmx_supported */ + + else /* MMX not supported: use modified C code - takes advantage + * of inlining of memcpy for a constant */ + { + if (pixel_bytes == 1) + { + for (i = width; i; i--) + { + int j; + for (j = 0; j < png_pass_inc[pass]; j++) + *dp-- = *sptr; + sptr--; + } + } + else if (pixel_bytes == 3) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr -= pixel_bytes; + } + } + else if (pixel_bytes == 2) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr -= pixel_bytes; + } + } + else if (pixel_bytes == 4) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr -= pixel_bytes; + } + } + else if (pixel_bytes == 6) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr -= pixel_bytes; + } + } + else + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr -= pixel_bytes; + } + } + + } /* end of MMX not supported */ + break; + } + } /* end switch (row_info->pixel_depth) */ + + row_info->width = final_width; + + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width); + } + +} + +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + + +// These variables are utilized in the functions below. They are declared +// globally here to ensure alignment on 8-byte boundaries. + +union uAll { + __int64 use; + double align; +} LBCarryMask = {0x0101010101010101}, + HBClearMask = {0x7f7f7f7f7f7f7f7f}, + ActiveMask, ActiveMask2, ActiveMaskEnd, ShiftBpp, ShiftRem; + + +// Optimized code for PNG Average filter decoder +void /* PRIVATE */ +png_read_filter_row_mmx_avg(png_row_infop row_info, png_bytep row + , png_bytep prev_row) +{ + int bpp; + png_uint_32 FullLength; + png_uint_32 MMXLength; + //png_uint_32 len; + int diff; + + bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel + FullLength = row_info->rowbytes; // # of bytes to filter + _asm { + // Init address pointers and offset + mov edi, row // edi ==> Avg(x) + xor ebx, ebx // ebx ==> x + mov edx, edi + mov esi, prev_row // esi ==> Prior(x) + sub edx, bpp // edx ==> Raw(x-bpp) + + xor eax, eax + // Compute the Raw value for the first bpp bytes + // Raw(x) = Avg(x) + (Prior(x)/2) +davgrlp: + mov al, [esi + ebx] // Load al with Prior(x) + inc ebx + shr al, 1 // divide by 2 + add al, [edi+ebx-1] // Add Avg(x); -1 to offset inc ebx + cmp ebx, bpp + mov [edi+ebx-1], al // Write back Raw(x); + // mov does not affect flags; -1 to offset inc ebx + jb davgrlp + // get # of bytes to alignment + mov diff, edi // take start of row + add diff, ebx // add bpp + add diff, 0xf // add 7 + 8 to incr past alignment boundary + and diff, 0xfffffff8 // mask to alignment boundary + sub diff, edi // subtract from start ==> value ebx at alignment + jz davggo + // fix alignment + // Compute the Raw value for the bytes upto the alignment boundary + // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2) + xor ecx, ecx +davglp1: + xor eax, eax + mov cl, [esi + ebx] // load cl with Prior(x) + mov al, [edx + ebx] // load al with Raw(x-bpp) + add ax, cx + inc ebx + shr ax, 1 // divide by 2 + add al, [edi+ebx-1] // Add Avg(x); -1 to offset inc ebx + cmp ebx, diff // Check if at alignment boundary + mov [edi+ebx-1], al // Write back Raw(x); + // mov does not affect flags; -1 to offset inc ebx + jb davglp1 // Repeat until at alignment boundary +davggo: + mov eax, FullLength + mov ecx, eax + sub eax, ebx // subtract alignment fix + and eax, 0x00000007 // calc bytes over mult of 8 + sub ecx, eax // drop over bytes from original length + mov MMXLength, ecx + } // end _asm block + // Now do the math for the rest of the row + switch ( bpp ) + { + case 3: + { + ActiveMask.use = 0x0000000000ffffff; + ShiftBpp.use = 24; // == 3 * 8 + ShiftRem.use = 40; // == 64 - 24 + _asm { + // Re-init address pointers and offset + movq mm7, ActiveMask + mov ebx, diff // ebx ==> x = offset to alignment boundary + movq mm5, LBCarryMask + mov edi, row // edi ==> Avg(x) + movq mm4, HBClearMask + mov esi, prev_row // esi ==> Prior(x) + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm2, [edi + ebx - 8] // Load previous aligned 8 bytes + // (we correct position in loop below) +davg3lp: + movq mm0, [edi + ebx] // Load mm0 with Avg(x) + // Add (Prev_row/2) to Average + movq mm3, mm5 + psrlq mm2, ShiftRem // Correct position Raw(x-bpp) data + movq mm1, [esi + ebx] // Load mm1 with Prior(x) + movq mm6, mm7 + pand mm3, mm1 // get lsb for each prev_row byte + psrlq mm1, 1 // divide prev_row bytes by 2 + pand mm1, mm4 // clear invalid bit 7 of each byte + paddb mm0, mm1 // add (Prev_row/2) to Avg for each byte + // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 1 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active + // byte + // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry + psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 3-5 + movq mm2, mm0 // mov updated Raws to mm2 + psllq mm2, ShiftBpp // shift data to position correctly + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 2 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active + // byte + + // Add 3rd active group (Raw(x-bpp)/2) to Average with LBCarry + psllq mm6, ShiftBpp // shift the mm6 mask to cover the last two + // bytes + movq mm2, mm0 // mov updated Raws to mm2 + psllq mm2, ShiftBpp // shift data to position correctly + // Data only needs to be shifted once here to + // get the correct x-bpp offset. + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 2 bytes to add to Avg + add ebx, 8 + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active + // byte + + // Now ready to write back to memory + movq [edi + ebx - 8], mm0 + // Move updated Raw(x) to use as Raw(x-bpp) for next loop + cmp ebx, MMXLength + movq mm2, mm0 // mov updated Raw(x) to mm2 + jb davg3lp + } // end _asm block + } + break; + + case 6: + case 4: + case 7: + case 5: + { + ActiveMask.use = 0xffffffffffffffff; // use shift below to clear + // appropriate inactive bytes + ShiftBpp.use = bpp << 3; + ShiftRem.use = 64 - ShiftBpp.use; + _asm { + movq mm4, HBClearMask + // Re-init address pointers and offset + mov ebx, diff // ebx ==> x = offset to alignment boundary + // Load ActiveMask and clear all bytes except for 1st active group + movq mm7, ActiveMask + mov edi, row // edi ==> Avg(x) + psrlq mm7, ShiftRem + mov esi, prev_row // esi ==> Prior(x) + movq mm6, mm7 + movq mm5, LBCarryMask + psllq mm6, ShiftBpp // Create mask for 2nd active group + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm2, [edi + ebx - 8] // Load previous aligned 8 bytes + // (we correct position in loop below) +davg4lp: + movq mm0, [edi + ebx] + psrlq mm2, ShiftRem // shift data to position correctly + movq mm1, [esi + ebx] + // Add (Prev_row/2) to Average + movq mm3, mm5 + pand mm3, mm1 // get lsb for each prev_row byte + psrlq mm1, 1 // divide prev_row bytes by 2 + pand mm1, mm4 // clear invalid bit 7 of each byte + paddb mm0, mm1 // add (Prev_row/2) to Avg for each byte + // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm7 // Leave only Active Group 1 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active + // byte + // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry + movq mm2, mm0 // mov updated Raws to mm2 + psllq mm2, ShiftBpp // shift data to position correctly + add ebx, 8 + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 2 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active + // byte + cmp ebx, MMXLength + // Now ready to write back to memory + movq [edi + ebx - 8], mm0 + // Prep Raw(x-bpp) for next loop + movq mm2, mm0 // mov updated Raws to mm2 + jb davg4lp + } // end _asm block + } + break; + case 2: + { + ActiveMask.use = 0x000000000000ffff; + ShiftBpp.use = 16; // == 2 * 8 [BUGFIX] + ShiftRem.use = 48; // == 64 - 16 [BUGFIX] + _asm { + // Load ActiveMask + movq mm7, ActiveMask + // Re-init address pointers and offset + mov ebx, diff // ebx ==> x = offset to alignment boundary + movq mm5, LBCarryMask + mov edi, row // edi ==> Avg(x) + movq mm4, HBClearMask + mov esi, prev_row // esi ==> Prior(x) + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm2, [edi + ebx - 8] // Load previous aligned 8 bytes + // (we correct position in loop below) +davg2lp: + movq mm0, [edi + ebx] + psrlq mm2, ShiftRem // shift data to position correctly [BUGFIX] + movq mm1, [esi + ebx] + // Add (Prev_row/2) to Average + movq mm3, mm5 + pand mm3, mm1 // get lsb for each prev_row byte + psrlq mm1, 1 // divide prev_row bytes by 2 + pand mm1, mm4 // clear invalid bit 7 of each byte + movq mm6, mm7 + paddb mm0, mm1 // add (Prev_row/2) to Avg for each byte + // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 1 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte + // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry + psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 2 & 3 + movq mm2, mm0 // mov updated Raws to mm2 + psllq mm2, ShiftBpp // shift data to position correctly + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 2 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte + + // Add rdd active group (Raw(x-bpp)/2) to Average with LBCarry + psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 4 & 5 + movq mm2, mm0 // mov updated Raws to mm2 + psllq mm2, ShiftBpp // shift data to position correctly + // Data only needs to be shifted once here to + // get the correct x-bpp offset. + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 2 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte + + // Add 4th active group (Raw(x-bpp)/2) to Average with LBCarry + psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 6 & 7 + movq mm2, mm0 // mov updated Raws to mm2 + psllq mm2, ShiftBpp // shift data to position correctly + // Data only needs to be shifted once here to + // get the correct x-bpp offset. + add ebx, 8 + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 2 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte + + cmp ebx, MMXLength + // Now ready to write back to memory + movq [edi + ebx - 8], mm0 + // Prep Raw(x-bpp) for next loop + movq mm2, mm0 // mov updated Raws to mm2 + jb davg2lp + } // end _asm block + } + break; + + case 1: // bpp == 1 + { + _asm { + // Re-init address pointers and offset + mov ebx, diff // ebx ==> x = offset to alignment boundary + mov edi, row // edi ==> Avg(x) + cmp ebx, FullLength // Test if offset at end of array + jnb davg1end + // Do Paeth decode for remaining bytes + mov esi, prev_row // esi ==> Prior(x) + mov edx, edi + xor ecx, ecx // zero ecx before using cl & cx in loop below + sub edx, bpp // edx ==> Raw(x-bpp) +davg1lp: + // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2) + xor eax, eax + mov cl, [esi + ebx] // load cl with Prior(x) + mov al, [edx + ebx] // load al with Raw(x-bpp) + add ax, cx + inc ebx + shr ax, 1 // divide by 2 + add al, [edi+ebx-1] // Add Avg(x); -1 to offset inc ebx + cmp ebx, FullLength // Check if at end of array + mov [edi+ebx-1], al // Write back Raw(x); + // mov does not affect flags; -1 to offset inc ebx + jb davg1lp +davg1end: + } // end _asm block + } + return; + + case 8: // bpp == 8 + { + _asm { + // Re-init address pointers and offset + mov ebx, diff // ebx ==> x = offset to alignment boundary + movq mm5, LBCarryMask + mov edi, row // edi ==> Avg(x) + movq mm4, HBClearMask + mov esi, prev_row // esi ==> Prior(x) + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm2, [edi + ebx - 8] // Load previous aligned 8 bytes + // (NO NEED to correct position in loop below) +davg8lp: + movq mm0, [edi + ebx] + movq mm3, mm5 + movq mm1, [esi + ebx] + add ebx, 8 + pand mm3, mm1 // get lsb for each prev_row byte + psrlq mm1, 1 // divide prev_row bytes by 2 + pand mm3, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 + psrlq mm2, 1 // divide raw bytes by 2 + pand mm1, mm4 // clear invalid bit 7 of each byte + paddb mm0, mm3 // add LBCarrys to Avg for each byte + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm0, mm1 // add (Prev_row/2) to Avg for each byte + paddb mm0, mm2 // add (Raw/2) to Avg for each byte + cmp ebx, MMXLength + movq [edi + ebx - 8], mm0 + movq mm2, mm0 // reuse as Raw(x-bpp) + jb davg8lp + } // end _asm block + } + break; + default: // bpp greater than 8 + { + _asm { + movq mm5, LBCarryMask + // Re-init address pointers and offset + mov ebx, diff // ebx ==> x = offset to alignment boundary + mov edi, row // edi ==> Avg(x) + movq mm4, HBClearMask + mov edx, edi + mov esi, prev_row // esi ==> Prior(x) + sub edx, bpp // edx ==> Raw(x-bpp) +davgAlp: + movq mm0, [edi + ebx] + movq mm3, mm5 + movq mm1, [esi + ebx] + pand mm3, mm1 // get lsb for each prev_row byte + movq mm2, [edx + ebx] + psrlq mm1, 1 // divide prev_row bytes by 2 + pand mm3, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 + psrlq mm2, 1 // divide raw bytes by 2 + pand mm1, mm4 // clear invalid bit 7 of each byte + paddb mm0, mm3 // add LBCarrys to Avg for each byte + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm0, mm1 // add (Prev_row/2) to Avg for each byte + add ebx, 8 + paddb mm0, mm2 // add (Raw/2) to Avg for each byte + cmp ebx, MMXLength + movq [edi + ebx - 8], mm0 + jb davgAlp + } // end _asm block + } + break; + } // end switch ( bpp ) + + _asm { + // MMX acceleration complete now do clean-up + // Check if any remaining bytes left to decode + mov ebx, MMXLength // ebx ==> x = offset bytes remaining after MMX + mov edi, row // edi ==> Avg(x) + cmp ebx, FullLength // Test if offset at end of array + jnb davgend + // Do Paeth decode for remaining bytes + mov esi, prev_row // esi ==> Prior(x) + mov edx, edi + xor ecx, ecx // zero ecx before using cl & cx in loop below + sub edx, bpp // edx ==> Raw(x-bpp) +davglp2: + // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2) + xor eax, eax + mov cl, [esi + ebx] // load cl with Prior(x) + mov al, [edx + ebx] // load al with Raw(x-bpp) + add ax, cx + inc ebx + shr ax, 1 // divide by 2 + add al, [edi+ebx-1] // Add Avg(x); -1 to offset inc ebx + cmp ebx, FullLength // Check if at end of array + mov [edi+ebx-1], al // Write back Raw(x); + // mov does not affect flags; -1 to offset inc ebx + jb davglp2 +davgend: + emms // End MMX instructions; prep for possible FP instrs. + } // end _asm block +} + +// Optimized code for PNG Paeth filter decoder +void /* PRIVATE */ +png_read_filter_row_mmx_paeth(png_row_infop row_info, png_bytep row, + png_bytep prev_row) +{ + png_uint_32 FullLength; + png_uint_32 MMXLength; + //png_uint_32 len; + int bpp; + int diff; + //int ptemp; + int patemp, pbtemp, pctemp; + + bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel + FullLength = row_info->rowbytes; // # of bytes to filter + _asm + { + xor ebx, ebx // ebx ==> x offset + mov edi, row + xor edx, edx // edx ==> x-bpp offset + mov esi, prev_row + xor eax, eax + + // Compute the Raw value for the first bpp bytes + // Note: the formula works out to be always + // Paeth(x) = Raw(x) + Prior(x) where x < bpp +dpthrlp: + mov al, [edi + ebx] + add al, [esi + ebx] + inc ebx + cmp ebx, bpp + mov [edi + ebx - 1], al + jb dpthrlp + // get # of bytes to alignment + mov diff, edi // take start of row + add diff, ebx // add bpp + xor ecx, ecx + add diff, 0xf // add 7 + 8 to incr past alignment boundary + and diff, 0xfffffff8 // mask to alignment boundary + sub diff, edi // subtract from start ==> value ebx at alignment + jz dpthgo + // fix alignment +dpthlp1: + xor eax, eax + // pav = p - a = (a + b - c) - a = b - c + mov al, [esi + ebx] // load Prior(x) into al + mov cl, [esi + edx] // load Prior(x-bpp) into cl + sub eax, ecx // subtract Prior(x-bpp) + mov patemp, eax // Save pav for later use + xor eax, eax + // pbv = p - b = (a + b - c) - b = a - c + mov al, [edi + edx] // load Raw(x-bpp) into al + sub eax, ecx // subtract Prior(x-bpp) + mov ecx, eax + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + add eax, patemp // pcv = pav + pbv + // pc = abs(pcv) + test eax, 0x80000000 + jz dpthpca + neg eax // reverse sign of neg values +dpthpca: + mov pctemp, eax // save pc for later use + // pb = abs(pbv) + test ecx, 0x80000000 + jz dpthpba + neg ecx // reverse sign of neg values +dpthpba: + mov pbtemp, ecx // save pb for later use + // pa = abs(pav) + mov eax, patemp + test eax, 0x80000000 + jz dpthpaa + neg eax // reverse sign of neg values +dpthpaa: + mov patemp, eax // save pa for later use + // test if pa <= pb + cmp eax, ecx + jna dpthabb + // pa > pb; now test if pb <= pc + cmp ecx, pctemp + jna dpthbbc + // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + mov cl, [esi + edx] // load Prior(x-bpp) into cl + jmp dpthpaeth +dpthbbc: + // pb <= pc; Raw(x) = Paeth(x) + Prior(x) + mov cl, [esi + ebx] // load Prior(x) into cl + jmp dpthpaeth +dpthabb: + // pa <= pb; now test if pa <= pc + cmp eax, pctemp + jna dpthabc + // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + mov cl, [esi + edx] // load Prior(x-bpp) into cl + jmp dpthpaeth +dpthabc: + // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp) + mov cl, [edi + edx] // load Raw(x-bpp) into cl +dpthpaeth: + inc ebx + inc edx + // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256 + add [edi + ebx - 1], cl + cmp ebx, diff + jb dpthlp1 +dpthgo: + mov ecx, FullLength + mov eax, ecx + sub eax, ebx // subtract alignment fix + and eax, 0x00000007 // calc bytes over mult of 8 + sub ecx, eax // drop over bytes from original length + mov MMXLength, ecx + } // end _asm block + // Now do the math for the rest of the row + switch ( bpp ) + { + case 3: + { + ActiveMask.use = 0x0000000000ffffff; + ActiveMaskEnd.use = 0xffff000000000000; + ShiftBpp.use = 24; // == bpp(3) * 8 + ShiftRem.use = 40; // == 64 - 24 + _asm + { + mov ebx, diff + mov edi, row + mov esi, prev_row + pxor mm0, mm0 + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] +dpth3lp: + psrlq mm1, ShiftRem // shift last 3 bytes to 1st 3 bytes + movq mm2, [esi + ebx] // load b=Prior(x) + punpcklbw mm1, mm0 // Unpack High bytes of a + movq mm3, [esi+ebx-8] // Prep c=Prior(x-bpp) bytes + punpcklbw mm2, mm0 // Unpack High bytes of b + psrlq mm3, ShiftRem // shift last 3 bytes to 1st 3 bytes + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + punpcklbw mm3, mm0 // Unpack High bytes of c + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + paddw mm7, mm3 + pxor mm0, mm0 + packuswb mm7, mm1 + movq mm3, [esi + ebx] // load c=Prior(x-bpp) + pand mm7, ActiveMask + movq mm2, mm3 // load b=Prior(x) step 1 + paddb mm7, [edi + ebx] // add Paeth predictor with Raw(x) + punpcklbw mm3, mm0 // Unpack High bytes of c + movq [edi + ebx], mm7 // write back updated value + movq mm1, mm7 // Now mm1 will be used as Raw(x-bpp) + // Now do Paeth for 2nd set of bytes (3-5) + psrlq mm2, ShiftBpp // load b=Prior(x) step 2 + punpcklbw mm1, mm0 // Unpack High bytes of a + pxor mm7, mm7 + punpcklbw mm2, mm0 // Unpack High bytes of b + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + psubw mm5, mm3 + psubw mm4, mm3 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = + // pav + pbv = pbv + pav + movq mm6, mm5 + paddw mm6, mm4 + + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm5 // Create mask pbv bytes < 0 + pcmpgtw mm7, mm4 // Create mask pav bytes < 0 + pand mm0, mm5 // Only pbv bytes < 0 in mm0 + pand mm7, mm4 // Only pav bytes < 0 in mm7 + psubw mm5, mm0 + psubw mm4, mm7 + psubw mm5, mm0 + psubw mm4, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + movq mm2, [esi + ebx] // load b=Prior(x) + pand mm3, mm7 + pandn mm7, mm0 + pxor mm1, mm1 + paddw mm7, mm3 + pxor mm0, mm0 + packuswb mm7, mm1 + movq mm3, mm2 // load c=Prior(x-bpp) step 1 + pand mm7, ActiveMask + punpckhbw mm2, mm0 // Unpack High bytes of b + psllq mm7, ShiftBpp // Shift bytes to 2nd group of 3 bytes + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + paddb mm7, [edi + ebx] // add Paeth predictor with Raw(x) + psllq mm3, ShiftBpp // load c=Prior(x-bpp) step 2 + movq [edi + ebx], mm7 // write back updated value + movq mm1, mm7 + punpckhbw mm3, mm0 // Unpack High bytes of c + psllq mm1, ShiftBpp // Shift bytes + // Now mm1 will be used as Raw(x-bpp) + // Now do Paeth for 3rd, and final, set of bytes (6-7) + pxor mm7, mm7 + punpckhbw mm1, mm0 // Unpack High bytes of a + psubw mm4, mm3 + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + pxor mm0, mm0 + paddw mm6, mm5 + + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + pandn mm0, mm1 + pandn mm7, mm4 + paddw mm0, mm2 + paddw mm7, mm5 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pand mm3, mm7 + pandn mm7, mm0 + paddw mm7, mm3 + pxor mm1, mm1 + packuswb mm1, mm7 + // Step ebx to next set of 8 bytes and repeat loop til done + add ebx, 8 + pand mm1, ActiveMaskEnd + paddb mm1, [edi + ebx - 8] // add Paeth predictor with Raw(x) + + cmp ebx, MMXLength + pxor mm0, mm0 // pxor does not affect flags + movq [edi + ebx - 8], mm1 // write back updated value + // mm1 will be used as Raw(x-bpp) next loop + // mm3 ready to be used as Prior(x-bpp) next loop + jb dpth3lp + } // end _asm block + } + break; + + case 6: + case 7: + case 5: + { + ActiveMask.use = 0x00000000ffffffff; + ActiveMask2.use = 0xffffffff00000000; + ShiftBpp.use = bpp << 3; // == bpp * 8 + ShiftRem.use = 64 - ShiftBpp.use; + _asm + { + mov ebx, diff + mov edi, row + mov esi, prev_row + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] + pxor mm0, mm0 +dpth6lp: + // Must shift to position Raw(x-bpp) data + psrlq mm1, ShiftRem + // Do first set of 4 bytes + movq mm3, [esi+ebx-8] // read c=Prior(x-bpp) bytes + punpcklbw mm1, mm0 // Unpack Low bytes of a + movq mm2, [esi + ebx] // load b=Prior(x) + punpcklbw mm2, mm0 // Unpack Low bytes of b + // Must shift to position Prior(x-bpp) data + psrlq mm3, ShiftRem + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + punpcklbw mm3, mm0 // Unpack Low bytes of c + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + paddw mm7, mm3 + pxor mm0, mm0 + packuswb mm7, mm1 + movq mm3, [esi + ebx - 8] // load c=Prior(x-bpp) + pand mm7, ActiveMask + psrlq mm3, ShiftRem + movq mm2, [esi + ebx] // load b=Prior(x) step 1 + paddb mm7, [edi + ebx] // add Paeth predictor with Raw(x) + movq mm6, mm2 + movq [edi + ebx], mm7 // write back updated value + movq mm1, [edi+ebx-8] + psllq mm6, ShiftBpp + movq mm5, mm7 + psrlq mm1, ShiftRem + por mm3, mm6 + psllq mm5, ShiftBpp + punpckhbw mm3, mm0 // Unpack High bytes of c + por mm1, mm5 + // Do second set of 4 bytes + punpckhbw mm2, mm0 // Unpack High bytes of b + punpckhbw mm1, mm0 // Unpack High bytes of a + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + pxor mm1, mm1 + paddw mm7, mm3 + pxor mm0, mm0 + // Step ex to next set of 8 bytes and repeat loop til done + add ebx, 8 + packuswb mm1, mm7 + paddb mm1, [edi + ebx - 8] // add Paeth predictor with Raw(x) + cmp ebx, MMXLength + movq [edi + ebx - 8], mm1 // write back updated value + // mm1 will be used as Raw(x-bpp) next loop + jb dpth6lp + } // end _asm block + } + break; + + case 4: + { + ActiveMask.use = 0x00000000ffffffff; + _asm { + mov ebx, diff + mov edi, row + mov esi, prev_row + pxor mm0, mm0 + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] // Only time should need to read + // a=Raw(x-bpp) bytes +dpth4lp: + // Do first set of 4 bytes + movq mm3, [esi+ebx-8] // read c=Prior(x-bpp) bytes + punpckhbw mm1, mm0 // Unpack Low bytes of a + movq mm2, [esi + ebx] // load b=Prior(x) + punpcklbw mm2, mm0 // Unpack High bytes of b + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + punpckhbw mm3, mm0 // Unpack High bytes of c + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + paddw mm7, mm3 + pxor mm0, mm0 + packuswb mm7, mm1 + movq mm3, [esi + ebx] // load c=Prior(x-bpp) + pand mm7, ActiveMask + movq mm2, mm3 // load b=Prior(x) step 1 + paddb mm7, [edi + ebx] // add Paeth predictor with Raw(x) + punpcklbw mm3, mm0 // Unpack High bytes of c + movq [edi + ebx], mm7 // write back updated value + movq mm1, mm7 // Now mm1 will be used as Raw(x-bpp) + // Do second set of 4 bytes + punpckhbw mm2, mm0 // Unpack Low bytes of b + punpcklbw mm1, mm0 // Unpack Low bytes of a + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + pxor mm1, mm1 + paddw mm7, mm3 + pxor mm0, mm0 + // Step ex to next set of 8 bytes and repeat loop til done + add ebx, 8 + packuswb mm1, mm7 + paddb mm1, [edi + ebx - 8] // add Paeth predictor with Raw(x) + cmp ebx, MMXLength + movq [edi + ebx - 8], mm1 // write back updated value + // mm1 will be used as Raw(x-bpp) next loop + jb dpth4lp + } // end _asm block + } + break; + case 8: // bpp == 8 + { + ActiveMask.use = 0x00000000ffffffff; + _asm { + mov ebx, diff + mov edi, row + mov esi, prev_row + pxor mm0, mm0 + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] // Only time should need to read + // a=Raw(x-bpp) bytes +dpth8lp: + // Do first set of 4 bytes + movq mm3, [esi+ebx-8] // read c=Prior(x-bpp) bytes + punpcklbw mm1, mm0 // Unpack Low bytes of a + movq mm2, [esi + ebx] // load b=Prior(x) + punpcklbw mm2, mm0 // Unpack Low bytes of b + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + punpcklbw mm3, mm0 // Unpack Low bytes of c + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + paddw mm7, mm3 + pxor mm0, mm0 + packuswb mm7, mm1 + movq mm3, [esi+ebx-8] // read c=Prior(x-bpp) bytes + pand mm7, ActiveMask + movq mm2, [esi + ebx] // load b=Prior(x) + paddb mm7, [edi + ebx] // add Paeth predictor with Raw(x) + punpckhbw mm3, mm0 // Unpack High bytes of c + movq [edi + ebx], mm7 // write back updated value + movq mm1, [edi+ebx-8] // read a=Raw(x-bpp) bytes + + // Do second set of 4 bytes + punpckhbw mm2, mm0 // Unpack High bytes of b + punpckhbw mm1, mm0 // Unpack High bytes of a + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + pxor mm1, mm1 + paddw mm7, mm3 + pxor mm0, mm0 + // Step ex to next set of 8 bytes and repeat loop til done + add ebx, 8 + packuswb mm1, mm7 + paddb mm1, [edi + ebx - 8] // add Paeth predictor with Raw(x) + cmp ebx, MMXLength + movq [edi + ebx - 8], mm1 // write back updated value + // mm1 will be used as Raw(x-bpp) next loop + jb dpth8lp + } // end _asm block + } + break; + + case 1: // bpp = 1 + case 2: // bpp = 2 + default: // bpp > 8 + { + _asm { + mov ebx, diff + cmp ebx, FullLength + jnb dpthdend + mov edi, row + mov esi, prev_row + // Do Paeth decode for remaining bytes + mov edx, ebx + xor ecx, ecx // zero ecx before using cl & cx in loop below + sub edx, bpp // Set edx = ebx - bpp +dpthdlp: + xor eax, eax + // pav = p - a = (a + b - c) - a = b - c + mov al, [esi + ebx] // load Prior(x) into al + mov cl, [esi + edx] // load Prior(x-bpp) into cl + sub eax, ecx // subtract Prior(x-bpp) + mov patemp, eax // Save pav for later use + xor eax, eax + // pbv = p - b = (a + b - c) - b = a - c + mov al, [edi + edx] // load Raw(x-bpp) into al + sub eax, ecx // subtract Prior(x-bpp) + mov ecx, eax + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + add eax, patemp // pcv = pav + pbv + // pc = abs(pcv) + test eax, 0x80000000 + jz dpthdpca + neg eax // reverse sign of neg values +dpthdpca: + mov pctemp, eax // save pc for later use + // pb = abs(pbv) + test ecx, 0x80000000 + jz dpthdpba + neg ecx // reverse sign of neg values +dpthdpba: + mov pbtemp, ecx // save pb for later use + // pa = abs(pav) + mov eax, patemp + test eax, 0x80000000 + jz dpthdpaa + neg eax // reverse sign of neg values +dpthdpaa: + mov patemp, eax // save pa for later use + // test if pa <= pb + cmp eax, ecx + jna dpthdabb + // pa > pb; now test if pb <= pc + cmp ecx, pctemp + jna dpthdbbc + // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + mov cl, [esi + edx] // load Prior(x-bpp) into cl + jmp dpthdpaeth +dpthdbbc: + // pb <= pc; Raw(x) = Paeth(x) + Prior(x) + mov cl, [esi + ebx] // load Prior(x) into cl + jmp dpthdpaeth +dpthdabb: + // pa <= pb; now test if pa <= pc + cmp eax, pctemp + jna dpthdabc + // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + mov cl, [esi + edx] // load Prior(x-bpp) into cl + jmp dpthdpaeth +dpthdabc: + // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp) + mov cl, [edi + edx] // load Raw(x-bpp) into cl +dpthdpaeth: + inc ebx + inc edx + // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256 + add [edi + ebx - 1], cl + cmp ebx, FullLength + jb dpthdlp +dpthdend: + } // end _asm block + } + return; // No need to go further with this one + } // end switch ( bpp ) + _asm + { + // MMX acceleration complete now do clean-up + // Check if any remaining bytes left to decode + mov ebx, MMXLength + cmp ebx, FullLength + jnb dpthend + mov edi, row + mov esi, prev_row + // Do Paeth decode for remaining bytes + mov edx, ebx + xor ecx, ecx // zero ecx before using cl & cx in loop below + sub edx, bpp // Set edx = ebx - bpp +dpthlp2: + xor eax, eax + // pav = p - a = (a + b - c) - a = b - c + mov al, [esi + ebx] // load Prior(x) into al + mov cl, [esi + edx] // load Prior(x-bpp) into cl + sub eax, ecx // subtract Prior(x-bpp) + mov patemp, eax // Save pav for later use + xor eax, eax + // pbv = p - b = (a + b - c) - b = a - c + mov al, [edi + edx] // load Raw(x-bpp) into al + sub eax, ecx // subtract Prior(x-bpp) + mov ecx, eax + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + add eax, patemp // pcv = pav + pbv + // pc = abs(pcv) + test eax, 0x80000000 + jz dpthpca2 + neg eax // reverse sign of neg values +dpthpca2: + mov pctemp, eax // save pc for later use + // pb = abs(pbv) + test ecx, 0x80000000 + jz dpthpba2 + neg ecx // reverse sign of neg values +dpthpba2: + mov pbtemp, ecx // save pb for later use + // pa = abs(pav) + mov eax, patemp + test eax, 0x80000000 + jz dpthpaa2 + neg eax // reverse sign of neg values +dpthpaa2: + mov patemp, eax // save pa for later use + // test if pa <= pb + cmp eax, ecx + jna dpthabb2 + // pa > pb; now test if pb <= pc + cmp ecx, pctemp + jna dpthbbc2 + // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + mov cl, [esi + edx] // load Prior(x-bpp) into cl + jmp dpthpaeth2 +dpthbbc2: + // pb <= pc; Raw(x) = Paeth(x) + Prior(x) + mov cl, [esi + ebx] // load Prior(x) into cl + jmp dpthpaeth2 +dpthabb2: + // pa <= pb; now test if pa <= pc + cmp eax, pctemp + jna dpthabc2 + // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + mov cl, [esi + edx] // load Prior(x-bpp) into cl + jmp dpthpaeth2 +dpthabc2: + // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp) + mov cl, [edi + edx] // load Raw(x-bpp) into cl +dpthpaeth2: + inc ebx + inc edx + // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256 + add [edi + ebx - 1], cl + cmp ebx, FullLength + jb dpthlp2 +dpthend: + emms // End MMX instructions; prep for possible FP instrs. + } // end _asm block +} + +// Optimized code for PNG Sub filter decoder +void /* PRIVATE */ +png_read_filter_row_mmx_sub(png_row_infop row_info, png_bytep row) +{ + //int test; + int bpp; + png_uint_32 FullLength; + png_uint_32 MMXLength; + int diff; + + bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel + FullLength = row_info->rowbytes - bpp; // # of bytes to filter + _asm { + mov edi, row + mov esi, edi // lp = row + add edi, bpp // rp = row + bpp + xor eax, eax + // get # of bytes to alignment + mov diff, edi // take start of row + add diff, 0xf // add 7 + 8 to incr past + // alignment boundary + xor ebx, ebx + and diff, 0xfffffff8 // mask to alignment boundary + sub diff, edi // subtract from start ==> value + // ebx at alignment + jz dsubgo + // fix alignment +dsublp1: + mov al, [esi+ebx] + add [edi+ebx], al + inc ebx + cmp ebx, diff + jb dsublp1 +dsubgo: + mov ecx, FullLength + mov edx, ecx + sub edx, ebx // subtract alignment fix + and edx, 0x00000007 // calc bytes over mult of 8 + sub ecx, edx // drop over bytes from length + mov MMXLength, ecx + } // end _asm block + + // Now do the math for the rest of the row + switch ( bpp ) + { + case 3: + { + ActiveMask.use = 0x0000ffffff000000; + ShiftBpp.use = 24; // == 3 * 8 + ShiftRem.use = 40; // == 64 - 24 + _asm { + mov edi, row + movq mm7, ActiveMask // Load ActiveMask for 2nd active byte group + mov esi, edi // lp = row + add edi, bpp // rp = row + bpp + movq mm6, mm7 + mov ebx, diff + psllq mm6, ShiftBpp // Move mask in mm6 to cover 3rd active + // byte group + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] +dsub3lp: + psrlq mm1, ShiftRem // Shift data for adding 1st bpp bytes + // no need for mask; shift clears inactive bytes + // Add 1st active group + movq mm0, [edi+ebx] + paddb mm0, mm1 + // Add 2nd active group + movq mm1, mm0 // mov updated Raws to mm1 + psllq mm1, ShiftBpp // shift data to position correctly + pand mm1, mm7 // mask to use only 2nd active group + paddb mm0, mm1 + // Add 3rd active group + movq mm1, mm0 // mov updated Raws to mm1 + psllq mm1, ShiftBpp // shift data to position correctly + pand mm1, mm6 // mask to use only 3rd active group + add ebx, 8 + paddb mm0, mm1 + cmp ebx, MMXLength + movq [edi+ebx-8], mm0 // Write updated Raws back to array + // Prep for doing 1st add at top of loop + movq mm1, mm0 + jb dsub3lp + } // end _asm block + } + break; + + case 1: + { + // Placed here just in case this is a duplicate of the + // non-MMX code for the SUB filter in png_read_filter_row below + // + // png_bytep rp; + // png_bytep lp; + // png_uint_32 i; + // bpp = (row_info->pixel_depth + 7) >> 3; + // for (i = (png_uint_32)bpp, rp = row + bpp, lp = row; + // i < row_info->rowbytes; i++, rp++, lp++) + // { + // *rp = (png_byte)(((int)(*rp) + (int)(*lp)) & 0xff); + // } + _asm { + mov ebx, diff + mov edi, row + cmp ebx, FullLength + jnb dsub1end + mov esi, edi // lp = row + xor eax, eax + add edi, bpp // rp = row + bpp +dsub1lp: + mov al, [esi+ebx] + add [edi+ebx], al + inc ebx + cmp ebx, FullLength + jb dsub1lp +dsub1end: + } // end _asm block + } + return; + + case 6: + case 7: + case 4: + case 5: + { + ShiftBpp.use = bpp << 3; + ShiftRem.use = 64 - ShiftBpp.use; + _asm { + mov edi, row + mov ebx, diff + mov esi, edi // lp = row + add edi, bpp // rp = row + bpp + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] +dsub4lp: + psrlq mm1, ShiftRem // Shift data for adding 1st bpp bytes + // no need for mask; shift clears inactive bytes + movq mm0, [edi+ebx] + paddb mm0, mm1 + // Add 2nd active group + movq mm1, mm0 // mov updated Raws to mm1 + psllq mm1, ShiftBpp // shift data to position correctly + // there is no need for any mask + // since shift clears inactive bits/bytes + add ebx, 8 + paddb mm0, mm1 + cmp ebx, MMXLength + movq [edi+ebx-8], mm0 + movq mm1, mm0 // Prep for doing 1st add at top of loop + jb dsub4lp + } // end _asm block + } + break; + + case 2: + { + ActiveMask.use = 0x00000000ffff0000; + ShiftBpp.use = 16; // == 2 * 8 + ShiftRem.use = 48; // == 64 - 16 + _asm { + movq mm7, ActiveMask // Load ActiveMask for 2nd active byte group + mov ebx, diff + movq mm6, mm7 + mov edi, row + psllq mm6, ShiftBpp // Move mask in mm6 to cover 3rd active + // byte group + mov esi, edi // lp = row + movq mm5, mm6 + add edi, bpp // rp = row + bpp + psllq mm5, ShiftBpp // Move mask in mm5 to cover 4th active + // byte group + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] +dsub2lp: + // Add 1st active group + psrlq mm1, ShiftRem // Shift data for adding 1st bpp bytes + // no need for mask; shift clears inactive + // bytes + movq mm0, [edi+ebx] + paddb mm0, mm1 + // Add 2nd active group + movq mm1, mm0 // mov updated Raws to mm1 + psllq mm1, ShiftBpp // shift data to position correctly + pand mm1, mm7 // mask to use only 2nd active group + paddb mm0, mm1 + // Add 3rd active group + movq mm1, mm0 // mov updated Raws to mm1 + psllq mm1, ShiftBpp // shift data to position correctly + pand mm1, mm6 // mask to use only 3rd active group + paddb mm0, mm1 + // Add 4th active group + movq mm1, mm0 // mov updated Raws to mm1 + psllq mm1, ShiftBpp // shift data to position correctly + pand mm1, mm5 // mask to use only 4th active group + add ebx, 8 + paddb mm0, mm1 + cmp ebx, MMXLength + movq [edi+ebx-8], mm0 // Write updated Raws back to array + movq mm1, mm0 // Prep for doing 1st add at top of loop + jb dsub2lp + } // end _asm block + } + break; + case 8: + { + _asm { + mov edi, row + mov ebx, diff + mov esi, edi // lp = row + add edi, bpp // rp = row + bpp + mov ecx, MMXLength + movq mm7, [edi+ebx-8] // PRIME the pump (load the first + // Raw(x-bpp) data set + and ecx, 0x0000003f // calc bytes over mult of 64 +dsub8lp: + movq mm0, [edi+ebx] // Load Sub(x) for 1st 8 bytes + paddb mm0, mm7 + movq mm1, [edi+ebx+8] // Load Sub(x) for 2nd 8 bytes + movq [edi+ebx], mm0 // Write Raw(x) for 1st 8 bytes + // Now mm0 will be used as Raw(x-bpp) for + // the 2nd group of 8 bytes. This will be + // repeated for each group of 8 bytes with + // the 8th group being used as the Raw(x-bpp) + // for the 1st group of the next loop. + paddb mm1, mm0 + movq mm2, [edi+ebx+16] // Load Sub(x) for 3rd 8 bytes + movq [edi+ebx+8], mm1 // Write Raw(x) for 2nd 8 bytes + paddb mm2, mm1 + movq mm3, [edi+ebx+24] // Load Sub(x) for 4th 8 bytes + movq [edi+ebx+16], mm2 // Write Raw(x) for 3rd 8 bytes + paddb mm3, mm2 + movq mm4, [edi+ebx+32] // Load Sub(x) for 5th 8 bytes + movq [edi+ebx+24], mm3 // Write Raw(x) for 4th 8 bytes + paddb mm4, mm3 + movq mm5, [edi+ebx+40] // Load Sub(x) for 6th 8 bytes + movq [edi+ebx+32], mm4 // Write Raw(x) for 5th 8 bytes + paddb mm5, mm4 + movq mm6, [edi+ebx+48] // Load Sub(x) for 7th 8 bytes + movq [edi+ebx+40], mm5 // Write Raw(x) for 6th 8 bytes + paddb mm6, mm5 + movq mm7, [edi+ebx+56] // Load Sub(x) for 8th 8 bytes + movq [edi+ebx+48], mm6 // Write Raw(x) for 7th 8 bytes + add ebx, 64 + paddb mm7, mm6 + cmp ebx, ecx + movq [edi+ebx-8], mm7 // Write Raw(x) for 8th 8 bytes + jb dsub8lp + cmp ebx, MMXLength + jnb dsub8lt8 +dsub8lpA: + movq mm0, [edi+ebx] + add ebx, 8 + paddb mm0, mm7 + cmp ebx, MMXLength + movq [edi+ebx-8], mm0 // use -8 to offset early add to ebx + movq mm7, mm0 // Move calculated Raw(x) data to mm1 to + // be the new Raw(x-bpp) for the next loop + jb dsub8lpA +dsub8lt8: + } // end _asm block + } + break; + + default: // bpp greater than 8 bytes + { + _asm { + mov ebx, diff + mov edi, row + mov esi, edi // lp = row + add edi, bpp // rp = row + bpp +dsubAlp: + movq mm0, [edi+ebx] + movq mm1, [esi+ebx] + add ebx, 8 + paddb mm0, mm1 + cmp ebx, MMXLength + movq [edi+ebx-8], mm0 // mov does not affect flags; -8 to offset + // add ebx + jb dsubAlp + } // end _asm block + } + break; + + } // end switch ( bpp ) + + _asm { + mov ebx, MMXLength + mov edi, row + cmp ebx, FullLength + jnb dsubend + mov esi, edi // lp = row + xor eax, eax + add edi, bpp // rp = row + bpp +dsublp2: + mov al, [esi+ebx] + add [edi+ebx], al + inc ebx + cmp ebx, FullLength + jb dsublp2 +dsubend: + emms // End MMX instructions; prep for possible FP instrs. + } // end _asm block +} + +// Optimized code for PNG Up filter decoder +void /* PRIVATE */ +png_read_filter_row_mmx_up(png_row_infop row_info, png_bytep row, + png_bytep prev_row) +{ + png_uint_32 len; + len = row_info->rowbytes; // # of bytes to filter + _asm { + mov edi, row + // get # of bytes to alignment + mov ecx, edi + xor ebx, ebx + add ecx, 0x7 + xor eax, eax + and ecx, 0xfffffff8 + mov esi, prev_row + sub ecx, edi + jz dupgo + // fix alignment +duplp1: + mov al, [edi+ebx] + add al, [esi+ebx] + inc ebx + cmp ebx, ecx + mov [edi + ebx-1], al // mov does not affect flags; -1 to offset inc ebx + jb duplp1 +dupgo: + mov ecx, len + mov edx, ecx + sub edx, ebx // subtract alignment fix + and edx, 0x0000003f // calc bytes over mult of 64 + sub ecx, edx // drop over bytes from length + // Unrolled loop - use all MMX registers and interleave to reduce + // number of branch instructions (loops) and reduce partial stalls +duploop: + movq mm1, [esi+ebx] + movq mm0, [edi+ebx] + movq mm3, [esi+ebx+8] + paddb mm0, mm1 + movq mm2, [edi+ebx+8] + movq [edi+ebx], mm0 + paddb mm2, mm3 + movq mm5, [esi+ebx+16] + movq [edi+ebx+8], mm2 + movq mm4, [edi+ebx+16] + movq mm7, [esi+ebx+24] + paddb mm4, mm5 + movq mm6, [edi+ebx+24] + movq [edi+ebx+16], mm4 + paddb mm6, mm7 + movq mm1, [esi+ebx+32] + movq [edi+ebx+24], mm6 + movq mm0, [edi+ebx+32] + movq mm3, [esi+ebx+40] + paddb mm0, mm1 + movq mm2, [edi+ebx+40] + movq [edi+ebx+32], mm0 + paddb mm2, mm3 + movq mm5, [esi+ebx+48] + movq [edi+ebx+40], mm2 + movq mm4, [edi+ebx+48] + movq mm7, [esi+ebx+56] + paddb mm4, mm5 + movq mm6, [edi+ebx+56] + movq [edi+ebx+48], mm4 + add ebx, 64 + paddb mm6, mm7 + cmp ebx, ecx + movq [edi+ebx-8], mm6 // (+56)movq does not affect flags; + // -8 to offset add ebx + jb duploop + + cmp edx, 0 // Test for bytes over mult of 64 + jz dupend + + + // 2 lines added by lcreeve at netins.net + // (mail 11 Jul 98 in png-implement list) + cmp edx, 8 //test for less than 8 bytes + jb duplt8 + + + add ecx, edx + and edx, 0x00000007 // calc bytes over mult of 8 + sub ecx, edx // drop over bytes from length + jz duplt8 + // Loop using MMX registers mm0 & mm1 to update 8 bytes simultaneously +duplpA: + movq mm1, [esi+ebx] + movq mm0, [edi+ebx] + add ebx, 8 + paddb mm0, mm1 + cmp ebx, ecx + movq [edi+ebx-8], mm0 // movq does not affect flags; -8 to offset add ebx + jb duplpA + cmp edx, 0 // Test for bytes over mult of 8 + jz dupend +duplt8: + xor eax, eax + add ecx, edx // move over byte count into counter + // Loop using x86 registers to update remaining bytes +duplp2: + mov al, [edi + ebx] + add al, [esi + ebx] + inc ebx + cmp ebx, ecx + mov [edi + ebx-1], al // mov does not affect flags; -1 to offset inc ebx + jb duplp2 +dupend: + // Conversion of filtered row completed + emms // End MMX instructions; prep for possible FP instrs. + } // end _asm block +} + + +// Optimized png_read_filter_row routines +void /* PRIVATE */ +png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep + row, png_bytep prev_row, int filter) +{ +#ifdef PNG_DEBUG + char filnm[10]; +#endif + + if (mmx_supported == 2) { +#if !defined(PNG_1_0_X) + /* this should have happened in png_init_mmx_flags() already */ + png_warning(png_ptr, "asm_flags may not have been initialized"); +#endif + png_mmx_support(); + } + +#ifdef PNG_DEBUG + png_debug(1, "in png_read_filter_row\n"); + switch (filter) + { + case 0: sprintf(filnm, "none"); + break; +#if !defined(PNG_1_0_X) + case 1: sprintf(filnm, "sub-%s", + (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)? "MMX" : "x86"); + break; + case 2: sprintf(filnm, "up-%s", + (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP)? "MMX" : "x86"); + break; + case 3: sprintf(filnm, "avg-%s", + (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)? "MMX" : "x86"); + break; + case 4: sprintf(filnm, "Paeth-%s", + (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)? "MMX":"x86"); + break; +#else + case 1: sprintf(filnm, "sub"); + break; + case 2: sprintf(filnm, "up"); + break; + case 3: sprintf(filnm, "avg"); + break; + case 4: sprintf(filnm, "Paeth"); + break; +#endif + default: sprintf(filnm, "unknw"); + break; + } + png_debug2(0,"row=%5d, %s, ", png_ptr->row_number, filnm); + png_debug2(0, "pd=%2d, b=%d, ", (int)row_info->pixel_depth, + (int)((row_info->pixel_depth + 7) >> 3)); + png_debug1(0,"len=%8d, ", row_info->rowbytes); +#endif /* PNG_DEBUG */ + + switch (filter) + { + case PNG_FILTER_VALUE_NONE: + break; + + case PNG_FILTER_VALUE_SUB: + { +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB) && + (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) && + (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold)) +#else + if (mmx_supported) +#endif + { + png_read_filter_row_mmx_sub(row_info, row); + } + else + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp = row + bpp; + png_bytep lp = row; + + for (i = bpp; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff); + rp++; + } + } + break; + } + + case PNG_FILTER_VALUE_UP: + { +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP) && + (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) && + (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold)) +#else + if (mmx_supported) +#endif + { + png_read_filter_row_mmx_up(row_info, row, prev_row); + } + else + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_bytep rp = row; + png_bytep pp = prev_row; + + for (i = 0; i < istop; ++i) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + } + break; + } + + case PNG_FILTER_VALUE_AVG: + { +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG) && + (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) && + (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold)) +#else + if (mmx_supported) +#endif + { + png_read_filter_row_mmx_avg(row_info, row, prev_row); + } + else + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop = row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) >> 1)) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++ + *lp++) >> 1)) & 0xff); + rp++; + } + } + break; + } + + case PNG_FILTER_VALUE_PAETH: + { +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH) && + (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) && + (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold)) +#else + if (mmx_supported) +#endif + { + png_read_filter_row_mmx_paeth(row_info, row, prev_row); + } + else + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_bytep cp = prev_row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop=row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) // use leftover rp,pp + { + int a, b, c, pa, pb, pc, p; + + a = *lp++; + b = *pp++; + c = *cp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + /* + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; + */ + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *rp = (png_byte)(((int)(*rp) + p) & 0xff); + rp++; + } + } + break; + } + + default: + png_warning(png_ptr, "Ignoring bad row filter type"); + *row=0; + break; + } +} + +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED && PNG_USE_PNGVCRD */ diff --git a/.svn/pristine/05/05307223de728f7306ffe553ec00689c462798f9.svn-base b/.svn/pristine/05/05307223de728f7306ffe553ec00689c462798f9.svn-base new file mode 100644 index 0000000..55e015c --- /dev/null +++ b/.svn/pristine/05/05307223de728f7306ffe553ec00689c462798f9.svn-base @@ -0,0 +1,1676 @@ +/* +Copyright 2001-2018 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 +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// Support for FLAMP compatible Mulitcast + +#include "bpqmail.h" + +void decodeblock( unsigned char in[4], unsigned char out[3]); // Base64 Decode + +time_t MulticastMaxAge = 48 * 60 * 60; // 48 Hours in secs + +struct MSESSION * MSessions = NULL; + +#ifndef LINBPQ + +#include "AFXRES.h" + +HWND hMCMonitor = NULL; +HWND MCList; + +static HMENU hMCMenu; // handle of menu + +static char MCClassName[]="BPQMCWINDOW"; + +RECT MCMonitorRect; + +static int Height, Width, LastY; + +#define BGCOLOUR RGB(236,233,216) + +void MCMoveWindows() +{ + RECT rcClient; + int ClientWidth, ClientHeight; + + GetClientRect(hMCMonitor, &rcClient); + + if (rcClient.bottom == 0) // Minimised + return; + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + MoveWindow(MCList, 0, 0, rcClient.right, rcClient.bottom, TRUE); +} + +void CopyMCToClipboard(HWND hWnd); + +LRESULT CALLBACK MCWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + struct MSESSION * Sess = MSessions; + struct MSESSION * Temp; + + switch (message) + { + + case WM_ACTIVATE: + + break; + + case WM_CLOSE: + if (wParam) // Used by Close All Programs. + return 0; + + return (DefWindowProc(hWnd, message, wParam, lParam)); + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + + case ID_EDIT_COPY: + + CopyMCToClipboard(hMCMonitor); + return 0;; + + case ID_EDIT_CLEAR: + + while (Sess) + { + ListView_DeleteItem(MCList, Sess->Index); + + if (Sess->FileName) + free(Sess->FileName); + + if (Sess->OrigTimeStamp) + free(Sess->OrigTimeStamp); + + if (Sess->Message) + free(Sess->Message); + + if (Sess->BlockList) + free(Sess->BlockList); + + if (Sess->ID) + free(Sess->ID); + + Temp = Sess; + Sess = Sess->Next; + } + + MSessions = NULL; + return 0; + + default: + return 0; + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case SC_MINIMIZE: + + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Height = lprc->bottom-lprc->top; + Width = lprc->right-lprc->left; + + MCMoveWindows(); + + return TRUE; + + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &MonitorRect); // For save soutine + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + + hMCMonitor = NULL; + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + +static void MoveMCWindows() +{ + RECT rcMain, rcClient; + int ClientHeight, ClientWidth; + + GetWindowRect(hMCMonitor, &rcMain); + GetClientRect(hMCMonitor, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + +// MoveWindow(hwndMon,2, 0, ClientWidth-4, SplitPos, TRUE); +// MoveWindow(hwndOutput,2, 2, ClientWidth-4, ClientHeight-4, TRUE); +// MoveWindow(hwndSplit,0, SplitPos, ClientWidth, SplitBarHeight, TRUE); +} + + + + +HWND CreateMCListView (HWND hwndParent) +{ + INITCOMMONCONTROLSEX icex; // Structure for control initialization. + HWND hList; + LV_COLUMN Column; + LOGFONT lf; + HFONT hFont; + int n = 0; + + memset(&lf, 0, sizeof(LOGFONT)); + + lf.lfHeight = 12; + lf.lfWidth = 8; + lf.lfPitchAndFamily = FIXED_PITCH; + strcpy (lf.lfFaceName, "FIXEDSYS"); + + hFont = CreateFontIndirect(&lf); + + icex.dwICC = ICC_LISTVIEW_CLASSES; + InitCommonControlsEx(&icex); + + // Create the list-view window in report view with label editing enabled. + + hList = CreateWindow(WC_LISTVIEW, + "Messages", + WS_CHILD | LVS_REPORT | LVS_EDITLABELS, + 0, 0, 100, 100, + hwndParent, + (HMENU)NULL, + hInst, + NULL); + + SendMessage(hList, WM_SETFONT,(WPARAM) hFont, 0); + + + ListView_SetExtendedListViewStyle(hList,LVS_EX_FULLROWSELECT); + + Column.cx=45; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="ID"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + Column.cx=95; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="From"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=140; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="FileName"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=50; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="Size"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=40; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="%"; + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=55; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="Time"; + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=55; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="Age"; + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=20; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="C"; + + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM)&Column); + + Column.cx=430; + Column.mask=LVCF_WIDTH | LVCF_TEXT; + Column.pszText="BlockList"; + SendMessage(hList, LVM_INSERTCOLUMN, n++, (LPARAM) &Column); + + ShowWindow(hList, SW_SHOWNORMAL); + UpdateWindow(hList); + + return (hList); +} + +#define OurSetItemText(hwndLV, i, iSubItem_, pszText_) \ +{ LV_ITEM _ms_lvi;\ + _ms_lvi.iSubItem = iSubItem_;\ + _ms_lvi.pszText = pszText_;\ + SNDMSG((hwndLV), LVM_SETITEMTEXT, (WPARAM)i, (LPARAM)(LV_ITEM FAR *)&_ms_lvi);\ +} + +void RefreshMCLine(struct MSESSION * MSession) +{ + LV_ITEM Item; + LVFINDINFO Finfo; + int ret, n, pcent; + char Time[80]; + char Agestring[80]; + char Size[16] = "??"; + char Percent[16] = "??"; + char Key[16]; + + char BlockList[101] = ""; + struct tm * TM; + time_t Age; + + if (MCList == 0) + return; + + sprintf(Key, "%04X", MSession->Key); + + Age = time(NULL) - MSession->LastUpdated; + +// if (LocalTime) +// TM = localtime(&MSession->LastUpdated); +// else + TM = gmtime(&Age); + + sprintf(Agestring, "%.2d:%.2d", + TM->tm_hour, TM->tm_min); + + TM = gmtime(&MSession->Created); + + sprintf(Time, "%.2d:%.2d", + TM->tm_hour, TM->tm_min); + + + Finfo.flags = LVFI_STRING; + Finfo.psz = Key; + Finfo.vkDirection = VK_DOWN; + ret = SendMessage(MCList, LVM_FINDITEM, (WPARAM)-1, (LPARAM) &Finfo); + + if (ret == -1) + { + n = ListView_GetItemCount(MCList); + MSession->Index = n; + } + else + MSession->Index = ret; + + Item.mask=LVIF_TEXT; + Item.iItem = MSession->Index; + Item.iSubItem = 0; + Item.pszText = Key; + + ret = SendMessage(MCList, LVM_SETITEMTEXT, (WPARAM)MSession->Index, (LPARAM) &Item); + + if (ret == 0) + MSession->Index = ListView_InsertItem(MCList, &Item); + + sprintf(Size, "%d", MSession->MessageLen); + + if (MSession->MessageLen) + { + int i; + + pcent = (MSession->BlocksReceived * 100) / MSession->BlockCount; + sprintf(Percent, "%d", pcent); + + // Flag received blocks. Normalise to 50 wide + + memset(BlockList, '.', 50); + + for (i = 0; i < 50; i++) + { + int posn = (i * MSession->BlockCount) / 50; + if (MSession->BlockList[posn] == 1) + BlockList[i] = 'Y'; + } + } + + n = 0; + + OurSetItemText(MCList, MSession->Index, n++, Key); + if (MSession->ID) + OurSetItemText(MCList, MSession->Index, n++, MSession->ID) + else + OurSetItemText(MCList, MSession->Index, n++, " "); + + OurSetItemText(MCList, MSession->Index, n++, MSession->FileName); + OurSetItemText(MCList, MSession->Index, n++, Size); + OurSetItemText(MCList, MSession->Index, n++, Percent); + OurSetItemText(MCList, MSession->Index, n++, Time); + OurSetItemText(MCList, MSession->Index, n++, Agestring); + + if (MSession->Completed) + OurSetItemText(MCList, MSession->Index, n++, "Y") + else + OurSetItemText(MCList, MSession->Index, n++, " "); + + OurSetItemText(MCList, MSession->Index, n++, BlockList); +} + + +BOOL CreateMulticastConsole() +{ + WNDCLASS wc; + HBRUSH bgBrush; + RECT rcClient; + + if (hMCMonitor) + { + ShowWindow(hMCMonitor, SW_SHOWNORMAL); + SetForegroundWindow(hMCMonitor); + return FALSE; // Already open + } + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = MCWndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = MCClassName; + + RegisterClass(&wc); + + hMCMonitor=CreateDialog(hInst, MCClassName, 0, NULL); + + if (!hMCMonitor) + return (FALSE); + + hMCMenu=GetMenu(hMCMonitor); + +// CheckMenuItem(hMenu,MONBBS, MonBBS ? MF_CHECKED : MF_UNCHECKED); +// CheckMenuItem(hMenu,MONCHAT, MonCHAT ? MF_CHECKED : MF_UNCHECKED); +// CheckMenuItem(hMenu,MONTCP, MonTCP ? MF_CHECKED : MF_UNCHECKED); + + DrawMenuBar(hMCMonitor); + + // Create List View + + GetClientRect (hMCMonitor, &rcClient); + + MCList = CreateMCListView(hMCMonitor); + + MoveWindow(MCList, 0, 0, rcClient.right, rcClient.bottom, TRUE); + + if (cfgMinToTray) + AddTrayMenuItem(hMCMonitor, "Mail Multicast Monitor"); + + ShowWindow(hMCMonitor, SW_SHOWNORMAL); + + if (MCMonitorRect.right < 100 || MCMonitorRect.bottom < 100) + { + GetWindowRect(hMCMonitor, &MCMonitorRect); + } + + MoveWindow(hMCMonitor, MCMonitorRect.left, MCMonitorRect.top, + MCMonitorRect.right-MCMonitorRect.left, MCMonitorRect.bottom-MCMonitorRect.top, TRUE); + + MoveMCWindows(); + + return TRUE; + +} +void CopyMCToClipboard(HWND hWnd) +{ + int i,n, len=0; + char * Buffer; + HGLOBAL hMem; + char * ptr; + + char Time[80]; + char Agestring[80]; + char From[16]; + char Size[16]; + char Percent[16]; + char FileName[128]; + char Key[16]; + char Complete[2]; + + char BlockList[128]; + + n = ListView_GetItemCount(MCList); + + Buffer = malloc((n + 1) * 200); + + len = sprintf(Buffer, "ID From FileName Size %% Time Age Blocklist\r\n"); + + for (i=0; i> 1) ^ 0xA001; + else + crcval = (crcval >> 1); + } +} + +static unsigned int CalcCRC(UCHAR * ptr, int Len) +{ + int i; + + crcval = 0xFFFF; + for (i = 0; i < Len; i++) + { + update(*ptr++); + } + return crcval; +} + +struct MSESSION * FindMSession(unsigned int Key) +{ + struct MSESSION * Sess = MSessions; + struct MSESSION * LastSess = NULL; + + while (Sess) + { + if (Sess->Key == Key) + return Sess; + + LastSess = Sess; + Sess = Sess->Next; + } + + // Not found + + Sess = zalloc(sizeof(struct MSESSION)); + + if (Sess == NULL) + return NULL; + + Sess->Key = Key; + + Sess->Created = time(NULL); + + if (LastSess) + LastSess->Next = Sess; + else + MSessions = Sess; + + return Sess; +} + +#include "LzmaLib.h" + +#define LZMA_STR "\1LZMA" + +UCHAR * LZUncompress(UCHAR * Decoded, size_t Len, size_t * NewLen) +{ + unsigned char * buf; + unsigned char inprops[LZMA_PROPS_SIZE]; + size_t inlen; + int r; + + size_t rlen = 0; + size_t outlen; + + memcpy(&rlen, &Decoded[5], 4); + + outlen = ntohl(rlen); + *NewLen = outlen; + + buf = malloc(outlen); + + if (outlen > 1 << 25) + { + Debugprintf("Refusing to decompress data (> 32 MiB)"); + return NULL; + } + + + memcpy(inprops, Decoded + strlen(LZMA_STR) + sizeof(int), LZMA_PROPS_SIZE); + + inlen = Len - strlen(LZMA_STR) - sizeof(int) - LZMA_PROPS_SIZE; + + if ((r = LzmaUncompress(buf, &outlen, (const unsigned char*)Decoded + Len - inlen, &inlen, + inprops, LZMA_PROPS_SIZE)) != SZ_OK) + { + Debugprintf("Lzma Uncompress failed: %s", LZMA_ERRORS[r]); + return NULL; + } + else + { + return buf; + } +} + +void decodeblock128(unsigned char in[8], unsigned char out[7] ) +{ + out[0] = (unsigned char) (in[0] << 1 | in[1] >> 6); + out[1] = (unsigned char) (in[1] << 2 | in[2] >> 5); + out[2] = (unsigned char) (in[2] << 3 | in[3] >> 4); + out[3] = (unsigned char) (in[3] << 4 | in[4] >> 3); + out[4] = (unsigned char) in[4] << 5 | in[5] >> 2; + out[5] = (unsigned char) in[5] << 6 | in[6] >> 1; + out[6] = (unsigned char) in[6] << 7 | in[7]; +} + + +void SaveMulticastMessage(struct MSESSION * MSession) +{ + UCHAR * Decoded = NULL; // Output from Basexxx decode + UCHAR * Uncompressed = NULL; + size_t DecodedLen; // Length of decoded message + size_t UncompressedLen; // Length of decompressed message + int ExpectedLen; // From front of Base128 or Base256 message + int HddrLen; // Length of Expected Len Header + + if (MSession->FileName == NULL) + return; // Need Name + + MSession->Completed = TRUE; // So we don't get it again + + // If compresses and encoded, decode and decompress + + if (memcmp(MSession->Message, "[b64:start]", 11) == 0) + { + UCHAR * ptr1 = &MSession->Message[11]; + UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller + + int Len = MSession->MessageLen - 21; // Header and Trailer + + Decoded = ptr2; + + // Decode Base64 encoding + + while (Len > 0) + { + decodeblock(ptr1, ptr2); + ptr1 += 4; + ptr2 += 3; + Len -= 4; + } + + DecodedLen = (int)(ptr2 - Decoded); + Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); + } + else if (memcmp(MSession->Message, "[b128:start]", 12) == 0) + { + UCHAR * ptr1 = &MSession->Message[12]; + UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller + UCHAR ch; + UCHAR * Intermed; + + int Len = MSession->MessageLen - 23; // Header and Trailer + + Intermed = ptr2; + + // Decode Base128 encoding + + // First remove transparency (as in base256) + + + // Extract decoded msg len + + ExpectedLen = atoi(ptr1); + + ptr1 = strchr(ptr1, 10); + ptr1++; + + HddrLen = (int)(ptr1 - &MSession->Message[12]); + + if (ExpectedLen == 0 || ExpectedLen > Len || ptr1 == (UCHAR *)1) + { + Debugprintf("MCAST Missing Length Field"); + return; + } + + Len -= HddrLen;; + + while (Len > 0) + { + ch = *(ptr1++); + Len --; + + if (ch == ':') + { + ch = *(ptr1++); + Len--; + + switch (ch) + { + case ':' : *(ptr2++) = ':'; break; + case '0' : *(ptr2++) = 0x00; break; + case '1' : *(ptr2++) = 0x01; break; + case '2' : *(ptr2++) = 0x02; break; + case '3' : *(ptr2++) = 0x03; break; + case '4' : *(ptr2++) = 0x04; break; + case '5' : *(ptr2++) = 0x05; break; + case '6' : *(ptr2++) = 0x06; break; + case '7' : *(ptr2++) = 0x07; break; + case '8' : *(ptr2++) = 0x08; break; + case '9' : *(ptr2++) = 0x09; break; + case 'A' : *(ptr2++) = '\n'; break; + case 'B' : *(ptr2++) = '\r'; break; + case 'C' : *(ptr2++) = '^'; break; + case 'D' : *(ptr2++) = 0x7F; break; + case 'E' : *(ptr2++) = 0xFF; break; + } + } + else + *(ptr2++) = ch; + } + + + Len = ptr2 - Intermed; + + ptr1 = Intermed; + ptr2 = malloc(MSession->MessageLen); // Must get smaller + Decoded = ptr2; + + while (Len > 0) + { + decodeblock128(ptr1, ptr2); + ptr1 += 8; + ptr2 += 7; + Len -= 8; + } + + DecodedLen = ptr2 - Decoded; + Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); + } + else if (memcmp(MSession->Message, "[b256:start]", 12) == 0) + { + UCHAR * ptr1 = &MSession->Message[12]; + UCHAR * ptr2 = malloc(MSession->MessageLen); // Must get smaller + UCHAR ch; + + int Len = MSession->MessageLen - 23; // Header and Trailer + + Decoded = ptr2; + + // Decode Base256 encoding + + // Extract decoded msg len + + ExpectedLen = atoi(ptr1); + + ptr1 = strchr(ptr1, 10); + ptr1++; + + HddrLen = ptr1 - &MSession->Message[12]; + + if (ExpectedLen == 0 || ExpectedLen > Len || ptr1 == (UCHAR *)1) + { + Debugprintf("MCAST Missing Length Field"); + return; + } + + Len -= HddrLen;; + + while (Len > 0) + { + ch = *(ptr1++); + Len --; + + if (ch == ':') + { + ch = *(ptr1++); + Len--; + + switch (ch) + { + case ':' : *(ptr2++) = ':'; break; + case '0' : *(ptr2++) = 0x00; break; + case '1' : *(ptr2++) = 0x01; break; + case '2' : *(ptr2++) = 0x02; break; + case '3' : *(ptr2++) = 0x03; break; + case '4' : *(ptr2++) = 0x04; break; + case '5' : *(ptr2++) = 0x05; break; + case '6' : *(ptr2++) = 0x06; break; + case '7' : *(ptr2++) = 0x07; break; + case '8' : *(ptr2++) = 0x08; break; + case '9' : *(ptr2++) = 0x09; break; + case 'A' : *(ptr2++) = '\n'; break; + case 'B' : *(ptr2++) = '\r'; break; + case 'C' : *(ptr2++) = '^'; break; + case 'D' : *(ptr2++) = 0x7F; break; + case 'E' : *(ptr2++) = 0xFF; break; + } + } + else + *(ptr2++) = ch; + } + + DecodedLen = ptr2 - Decoded; + Uncompressed = LZUncompress(Decoded, DecodedLen, &UncompressedLen); + } + else + { + // Plain Text + + UncompressedLen = MSession->MessageLen; + Uncompressed = MSession->Message; + + MSession->Message = NULL; // So we dont try to free again + } + + if (Decoded) + free(Decoded); + + if (Uncompressed) + { + // Write it away and free it + + char MsgFile[MAX_PATH]; + FILE * hFile; + int WriteLen=0; + UCHAR * ptr1 = Uncompressed; + + // Make Sure MCAST directory exists + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/MCAST", MailDir); + +#ifdef WIN32 + CreateDirectory(MsgFile, NULL); // Just in case +#else + mkdir(MsgFile, S_IRWXU | S_IRWXG | S_IRWXO); + chmod(MsgFile, S_IRWXU | S_IRWXG | S_IRWXO); +#endif + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/MCAST/%s", MailDir, MSession->FileName); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = (int)fwrite(Uncompressed, 1, UncompressedLen, hFile); + fclose(hFile); + } + + + // if it looks like an export file (Starts SP SB or ST) and ends /ex + // import and delete it. + + if (*(ptr1) == 'S' && ptr1[2] == ' ') + if (_memicmp(&ptr1[UncompressedLen - 5], "/EX", 3) == 0) + ImportMessages(NULL, MsgFile, TRUE); + + free (Uncompressed); + } +} + +VOID ProcessMCASTLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen) +{ + char Opcode[80]; + unsigned int checksum, len = 0; + char * data; + int headerlen = 0; + unsigned int crcval; + int n; + unsigned int Key; + struct MSESSION * MSession; + + if (MsgLen == 1 && Buffer[0] == 13) + return; + + MsgLen --; // Remove the CR we added + + Buffer[MsgLen] = 0; + + if (MsgLen == 1 && Buffer[0] == 13) + return; + +// return; + + n = sscanf(&Buffer[1], "%s %04d %04X", Opcode, &len, &checksum); + + if (n != 3) + return; + + data = strchr(Buffer, '>'); + + if (data) + headerlen = (int)(++data - Buffer); + + if (headerlen + len != MsgLen) + return; + + crcval = CalcCRC(data, len); + + if (checksum != crcval) + return; + + // Extract Session Key + + sscanf(&data[1], "%04X", &Key); + + MSession = FindMSession(Key); + + if (MSession == 0) + return; // ?? couldn't allocate + + MSession->LastUpdated = time(NULL); + + if (MSession->Completed) + return; // We already have it all + + if (strcmp(Opcode, "ID") == 0) + { + strlop(&data[6], ' '); + MSession->ID = _strdup(&data[6]); + + return; + } + + if (strcmp(Opcode, "PROG") == 0) + { + // Ignore for now + return; + } + + if (strcmp(Opcode, "FILE") == 0) + { + // {80BC}20141108142542:debug_log.txt + + char * FN = strchr(&data[6], ':'); + + if (FN) + { + *(FN++) = 0; + + MSession->FileName = _strdup(FN); + MSession->OrigTimeStamp = _strdup(&data[6]); + } + + // We could get whole message without getting the Name, + // so check + + if (MSession->BlockCount && MSession->BlocksReceived == MSession->BlockCount) + { + // We have the whole message. Decode and Save + + if (MSession->MessageLen) // Also need length + SaveMulticastMessage(MSession); + } + + RefreshMCLine(MSession); + return; + } + + if (strcmp(Opcode, "SIZE") == 0) + { + // SIZE 14 2995>{80BC}465 8 64 + + int a, b, c, n = sscanf(&data[6], "%d %d %d", &a, &b, &c); + + if (n == 3) + { + // We may already have some (or even all) the message if we + // missed the SIZE block first time round + + if (MSession->Message) + { + // Already have at least part of it + + if (MSession->BlockSize != c) + { + // We based blocksize on last packet, so need to sort out mess + + // Find where we put the block, and move it + + UCHAR * OldLoc; + + MSession->Message = realloc(MSession->Message, a); + MSession->BlockList = realloc(MSession->BlockList, b); + + OldLoc = &MSession->Message[(MSession->BlockCount - 1) * MSession->BlockSize]; + + memmove(&MSession->Message[(MSession->BlockCount - 1) * c], OldLoc, MSession->BlockSize); + + MSession->BlockSize = c; + } + + if (MSession->BlockCount < b) + { + // Dont have it all, so need to extend ; + + MSession->Message = realloc(MSession->Message, a); + MSession->BlockList = realloc(MSession->BlockList, b); + } + } + + MSession->MessageLen = a; + MSession->BlockCount = b; + MSession->BlockSize = c; + + if (MSession->Message == NULL) + { + MSession->Message = zalloc(b * c); + MSession->BlockList = zalloc(b); + } + + // We might have it all now + + if (MSession->BlocksReceived == MSession->BlockCount) + { + // We have the whole message. Decode and Save + + SaveMulticastMessage(MSession); + } + } + + RefreshMCLine(MSession); + return; + } + + if (strcmp(Opcode, "DATA") == 0) + { + // {80BC:1}[b256:start]401 + + int Blockno = atoi(&data[6]); + char * dataptr = strchr(&data[6], '}'); + + if (dataptr == 0) + return; + + dataptr++; + + // What should we do if we don't have Filename or Size?? + + // If we assume this isn't the last block, then we can get + // the block size from this message. This is pretty save, but + // I guess as we will only get one last block, if we subsequently + // get an earlier one that is bigger, we can recalculate the position + // of this block and move it. + + if (MSession->MessageLen == 0) + { + // Haven't received SIZE Message yet. Guess the blocksize + + int blocksize = MsgLen - (int)(dataptr - Buffer); + + if (MSession->BlockSize == 0) + { + MSession->BlockSize = blocksize; + } + else + { + if (MSession->BlockSize < blocksize) + { + // We based blocksize on last packet, so need to sort out mess + + // Find where we put the block, and move it + + UCHAR * OldLoc = &MSession->Message[(MSession->BlockCount - 1) * MSession->BlockSize]; + memmove(&MSession->Message[(MSession->BlockCount - 1) * blocksize], OldLoc, MSession->BlockSize); + + MSession->BlockSize = blocksize; + } + } + + // We need to realloc Message and Blocklist if this is a later block + + if (MSession->BlockCount < Blockno) + { + MSession->Message = realloc(MSession->Message, Blockno * MSession->BlockSize); + MSession->BlockList = realloc(MSession->BlockList, Blockno); + + memset(&MSession->BlockList[MSession->BlockCount], 0, Blockno - MSession->BlockCount); + MSession->BlockCount = Blockno; + + } + + } + if (Blockno == 0 || Blockno > MSession->BlockCount) + return; + + Blockno--; + + if (MSession->BlockList[Blockno] == 1) + { + // Already have this block + + return; + } + + + memcpy(&MSession->Message[Blockno * MSession->BlockSize], dataptr, MSession->BlockSize); + MSession->BlockList[Blockno] = 1; + + MSession->BlocksReceived++; + + if (MSession->BlocksReceived == MSession->BlockCount && MSession->MessageLen) + { + // We have the whole message. Decode and Save + + SaveMulticastMessage(MSession); + } + + RefreshMCLine(MSession); + + return; + } + + MsgLen++; + +/* + +QST DE GM8BPQ + +{80BC}FLAMP 2.2.03 +{80BC}20141108142542:debug_log.txt +{80BC}GM8BPQ Skigersta +{80BC}465 8 64 +{80BC:1}[b256:start]401 +:1LZMA:0:0:6-]:0:0:0:4:0$--:7m?8-v\-E-Y-[--- +{80BC:2}rS-)N{j--o--ZMPX-,-l-yD------E--;-o:6-|;--f---q----0<---%-- +{80BC:3}*N-?N--*:Cf{:9--z-J:9-HMd:8-------Q--D---_-a----:C$;A-j---(: +{80BC:4}DWb--K---Qq-uj-_--;i------T-\>-{:6---~-ij~-,-(-O--2--+ +-:8 +{80BC:5}p---:7Gf:E-5o->x---4--K--:3-\:E---gouuH-3'----:A!.:7 +--N:0S +{80BC:6}.---/-~#.-:D:7zg~--m--:8-'---Y%-?--ze\-ho:5-}-:C:A:1u-1-O- +- +{80BC:7}9p-42-w--G:2G:3--g--O---n----c-#----DF-!~--:D-A--|-e------- +{80BC:8}B{--:0 +[b256:end] +{80BC:EOF} +{80BC:EOT} + +DE GM8BPQ K + +*/ + + return; +/* + if (strcmp(Buffer, "ARQ::ETX\r") == 0) + { + // Decode it. + + UCHAR * ptr1, * ptr2, * ptr3; + int len, linelen; + struct MsgInfo * Msg = conn->TempMsg; + time_t Date; + char FullTo[100]; + char FullFrom[100]; + char ** RecpTo = NULL; // May be several Recipients + char ** HddrTo = NULL; // May be several Recipients + char ** Via = NULL; // May be several Recipients + int LocalMsg[1000] ; // Set if Recipient is a local wl2k address + + int B2To; // Offset to To: fields in B2 header + int Recipients = 0; + int RMSMsgs = 0, BBSMsgs = 0; + +// Msg->B2Flags |= B2Msg; + + + ptr1 = conn->MailBuffer; + len = Msg->length; + ptr1[len] = 0; + + if (strstr(ptr1, "ARQ:ENCODING::")) + { + // a file, not a message. If is called "BBSPOLL" do a reverse forward else Ignore for now + + _strupr(conn->MailBuffer); + if (strstr(conn->MailBuffer, "BBSPOLL")) + { + SendARQMail(conn); + } + + free(conn->MailBuffer); + conn->MailBuffer = NULL; + conn->MailBufferSize = 0; + + return; + } + Loop: + ptr2 = strchr(ptr1, '\r'); + + linelen = ptr2 - ptr1; + + if (_memicmp(ptr1, "From:", 5) == 0 && linelen > 6) // Can have empty From: + { + char SaveFrom[100]; + char * FromHA; + + memcpy(FullFrom, ptr1, linelen); + FullFrom[linelen] = 0; + + // B2 From may now contain an @BBS + + strcpy(SaveFrom, FullFrom); + + FromHA = strlop(SaveFrom, '@'); + + if (strlen(SaveFrom) > 12) SaveFrom[12] = 0; + + strcpy(Msg->from, &SaveFrom[6]); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + + // Remove any SSID + + ptr3 = strchr(Msg->from, '-'); + if (ptr3) *ptr3 = 0; + + } + else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0) + { + HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *)); + HddrTo[Recipients] = zalloc(100); + + memset(FullTo, 0, 99); + memcpy(FullTo, &ptr1[4], linelen-4); + memcpy(HddrTo[Recipients], ptr1, linelen+2); + LocalMsg[Recipients] = FALSE; + + _strupr(FullTo); + + B2To = ptr1 - conn->MailBuffer; + + if (_memicmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + strcpy(FullTo, "RMS"); + strcpy(Msg->via, &FullTo[4]); + } + else + { + ptr3 = strchr(FullTo, '@'); + + if (ptr3) + { + *ptr3++ = 0; + strcpy(Msg->via, ptr3); + } + else + Msg->via[0] = 0; + } + + if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) + { + // Airmail Sends MARS messages as SMTP + + if (CheckifPacket(Msg->via)) + { + // Packet Message + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + _strupr(Msg->via); + + // Update the saved to: line (remove the smtp:) + + strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); + BBSMsgs++; + goto BBSMsg; + } + + // If a winlink.org address we need to convert to call + + if (_stricmp(Msg->via, "winlink.org") == 0) + { + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + { + memcpy(Msg->via, &ptr1[9], linelen); + Msg->via[linelen - 9] = 0; + strcpy(FullTo,"RMS"); + } +// FullTo[0] = 0; + + BBSMsg: + _strupr(FullTo); + _strupr(Msg->via); + } + + if (memcmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + } + + if (strcmp(Msg->via, "RMS") == 0) + { + // replace RMS with @winlink.org + + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo); + } + + if (strlen(FullTo) > 6) + FullTo[6] = 0; + + strlop(FullTo, '-'); + + strcpy(Msg->to, FullTo); + + if (SendBBStoSYSOPCall) + if (_stricmp(FullTo, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0)) + { + // No routing - check @BBS and WP + + struct UserInfo * ToUser = LookupCall(FullTo); + + Msg->via[0] = 0; // In case BPQ and not found + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + } + } + else + { + WPRecP WP = LookupWP(FullTo); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + + } + } + + // Fix To: address in B2 Header + + if (Msg->via[0]) + sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via); + else + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + + } + + RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *)); + RecpTo[Recipients] = zalloc(10); + + Via=realloc(Via, (Recipients+1) * sizeof(void *)); + Via[Recipients] = zalloc(50); + + strcpy(Via[Recipients], Msg->via); + strcpy(RecpTo[Recipients++], FullTo); + + // Remove the To: Line from the buffer + + } + else if (_memicmp(ptr1, "Type:", 4) == 0) + { + if (ptr1[6] == 'N') + Msg->type = 'T'; // NTS + else + Msg->type = ptr1[6]; + } + else if (_memicmp(ptr1, "Subject:", 8) == 0) + { + int Subjlen = ptr2 - &ptr1[9]; + if (Subjlen > 60) Subjlen = 60; + memcpy(Msg->title, &ptr1[9], Subjlen); + + goto ProcessBody; + } +// else if (_memicmp(ptr1, "Body:", 4) == 0) +// { +// MsgLen = atoi(&ptr1[5]); +// StartofMsg = ptr1; +// } + else if (_memicmp(ptr1, "File:", 5) == 0) + { + Msg->B2Flags |= Attachments; + } + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: 2009/07/25 10:08 + + sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d:%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + sscanf(&ptr1[5], "%02d/%02d/%04d %02d:%02d:%02d", + &rtime.tm_mday, &rtime.tm_mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + Msg->datecreated = Date; + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip cr + goto Loop; + } + + + // Processed all headers +ProcessBody: + + ptr2 +=2; // skip crlf + + Msg->length = &conn->MailBuffer[Msg->length] - ptr2; + + memmove(conn->MailBuffer, ptr2, Msg->length); + + CreateMessageFromBuffer(conn); + + conn->BBSFlags = 0; // Clear ARQ Mode + return; + } + + // File away the data + + Buffer[MsgLen++] = 0x0a; // BBS Msgs stored with crlf + + if ((conn->TempMsg->length + MsgLen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, MsgLen); + + conn->TempMsg->length += MsgLen; + + return; + + // Not sure what to do yet with files, but will process emails (using text style forwarding +*/ +/* +ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::96 +ARQ::STX +//FLARQ COMPOSER +Date: 16/01/2014 22:26:06 +To: g8bpq +From: +Subject: test message + +Hello +Hello + +ARQ::ETX +*/ + + return; +} + +VOID MCastConTimer(ConnectionInfo * conn) +{ + conn->MCastListenTime--; + + if (conn->MCastListenTime == 0) + Disconnect(conn->BPQStream); +} + +VOID MCastTimer() +{ + struct MSESSION * Sess = MSessions; + struct MSESSION * Prev = NULL; + + time_t Now = time(NULL); + + while (Sess) + { + if (Sess->Completed == FALSE) + RefreshMCLine(Sess); + + if (Now - Sess->LastUpdated > MulticastMaxAge) + { + // remove from list + +#ifndef LINBPQ + ListView_DeleteItem(MCList, Sess->Index); +#endif + if (Prev) + Prev->Next = Sess->Next; + else + MSessions = Sess->Next; + + if (Sess->FileName) + free(Sess->FileName); + + if (Sess->OrigTimeStamp) + free(Sess->OrigTimeStamp); + + if (Sess->Message) + free(Sess->Message); + + if (Sess->BlockList) + free(Sess->BlockList); + + if (Sess->ID) + free(Sess->ID); + + free(Sess); + + return; // Saves messing with chain + + } + Prev = Sess; + Sess = Sess->Next; + } +} + +int MulticastStatusHTML(char * Reply) +{ + char StatusPage [] = + "
ID    From      FileName        Size  %%  Time   Age   Blocklist"
+		"                                                   "
+		"\r\n

"; + int Len = 0; + char Unknown[] = "???"; + + struct MSESSION * Sess = MSessions; + + if (Sess ==NULL) + return 0; + + Len = sprintf(Reply, "%s", StatusPage); + + while (Sess) + { + char Percent[16] = "???"; + char BlockList[51] = ""; + int i; + char Time[80]; + char Agestring[80]; + struct tm * TM; + time_t Age; + char * ID = Unknown; + char * FileName = Unknown; + + Age = time(NULL) - Sess->LastUpdated; + + TM = gmtime(&Age); + + sprintf(Agestring, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + TM = gmtime(&Sess->Created); + + sprintf(Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + if (Sess->MessageLen && Sess->BlockCount) + { + int pcent; + + pcent = (Sess->BlocksReceived * 100) / Sess->BlockCount; + sprintf(Percent, "%d", pcent); + } + + // Flag received blocks. Normalise to 50 wide + + memset(BlockList, '.', 50); + + if (Sess->BlockList) + { + for (i = 0; i < 50; i++) + { + int posn = (i * Sess->BlockCount) / 50; + if (Sess->BlockList[posn] == 1) + BlockList[i] = 'Y'; + } + } + if (Sess->FileName) + FileName = Sess->FileName; + + if (Sess->ID) + ID = Sess->ID; + + Len += sprintf(&Reply[Len], "%04X %-10s%-15s%5d %-3s %s %s %s\r\n", + Sess->Key, ID, FileName, + Sess->MessageLen, Percent, Time, Agestring, BlockList); + + Sess = Sess->Next; + } + + Len += sprintf(&Reply[Len], "%s", StatusTail); + + return Len; +} diff --git a/.svn/pristine/05/05be6263d55be8cd16e65e2edc8dcf1aab6277a1.svn-base b/.svn/pristine/05/05be6263d55be8cd16e65e2edc8dcf1aab6277a1.svn-base new file mode 100644 index 0000000..2ce8aa6 --- /dev/null +++ b/.svn/pristine/05/05be6263d55be8cd16e65e2edc8dcf1aab6277a1.svn-base @@ -0,0 +1,3143 @@ +/* +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 +*/ + + +// July 2010 + +// BPQ32 now reads bpqcfg.txt. This module converts it to the original binary format. + +// Based on the standalonw bpqcfg.c + +/************************************************************************/ +/* CONFIG.C Jonathan Naylor G4KLX, 19th November 1988 */ +/* */ +/* Program to produce configuration file for G8BPQ Network Switch */ +/* based on the original written in BASIC by G8BPQ. */ +/* */ +/* Subsequently extended by G8BPQ */ +/* */ +/************************************************************************/ +// +// 22/11/95 - Add second port alias for digipeating (for APRS) +// - Add PORTMAXDIGIS param + +// 1999 - Win32 Version (but should also compile in 16 bit + +// 5/12/99 - Add DLLNAME Param for ext driver + +// 26/11/02 - Added AUTOSAVE + +// Jan 2006 + +// Add params for input and output names +// Wait before exiting if error detected + +// March 2006 + +// Accept # as comment delimiter +// Display input and output filenames +// Wait before exit, even if ok + +// March 2006 + +// Add L4APPL param + +// Jan 2007 + +// Remove UNPROTO +// Add BTEXT +// Add BCALL + +// Nov 2007 + +// Convert calls and APPLICATIONS string to upper case + +// Jan 2008 + +// Remove trailing space from UNPROTO +// Don't warn BBSCALL etc missing if APPL1CALL etc present + +// August 2008 + +// Add IPGATEWAY Parameter +// Add Port DIGIMASK Parameter + +// December 2008 + +// Add C_IS_CHAT Parameter + +// March 2009 + +// Add C style COmments (/* */ at start of line) + +// August 2009 + +// Add INP3 flag to locked routes + +// November 2009 + +// Add PROTOCOL=PACTOR or WINMOR option + +// December 2009 + +// Add INP3 MAXRTT and MAXHOPS Commands + +// March 2010 + +// Add SIMPLE mode + +// March 2010 + +// Change SIMPLE mode default of Full_CTEXT to 1 + +// April 2010 + +// Add NoKeepAlive ROUTE option + +// Converted to intenal bpq32 process + +// Spetember 2010 + +// Add option of embedded port configuration + + + +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" + +#include +#include +#include +#include +#include + +#include "configstructs.h" + +// KISS Options Equates + +#define CHECKSUM 1 +#define POLLINGKISS 2 // KISSFLAGS BITS +#define ACKMODE 4 // CAN USE ACK REQURED FRAMES +#define POLLEDKISS 8 // OTHER END IS POLLING US +#define D700 16 // D700 Mode (Escape "C" chars +#define TNCX 32 // TNC-X Mode (Checksum of ACKMODE frames includes ACK bytes +#define PITNC 64 // PITNC Mode - can reset TNC with FEND 15 2 +#define NOPARAMS 128 // Don't send SETPARAMS frame +#define FLDIGI 256 // Support FLDIGI COmmand Frames +#define TRACKER 512 // SCS Tracker. Need to set KISS Mode +#define FASTI2C 1024 // Use BLocked I2C Reads (like ARDOP) +#define DRATS 2048 + + + +struct WL2KInfo * DecodeWL2KReportLine(char * buf); + +// Dummy file routines - write to buffer instead + +char * PortConfig[70]; +char * RadioConfigMsg[70]; +char * WL2KReportLine[70]; + +int nextRadioPort = 0; +int nextDummyInterlock = 233; + +BOOL PortDefined[70]; + +extern BOOL IncludesMail; +extern BOOL IncludesChat; +extern int needAIS; +extern int needADSB; + +extern int AGWPort; +extern int AGWSessions; +extern int AGWMask; + +extern BOOL LoopMonFlag; +extern BOOL Loopflag; + +extern char NodeMapServer[80]; +extern char ChatMapServer[80]; + +double LatFromLOC; +double LonFromLOC; + + + +VOID * zalloc(int len); + +int WritetoConsoleLocal(char * buff); +char * stristr (char *ch1, char *ch2); +int FromLOC(char * Locator, double * pLat, double * pLon); + +VOID Consoleprintf(const char * format, ...) +{ + char Mess[512]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + strcat(Mess, "\n"); + WritetoConsoleLocal(Mess); + + return; +} + + +#pragma pack() + +int tnctypes(int i, char value[],char rec[]); +int do_kiss (char value[],char rec[]); + +struct TNCDATA * TNCCONFIGTABLE = NULL; // malloc'ed +int NUMBEROFTNCPORTS = 0; + +struct UPNP * UPNPConfig = NULL; + +struct TNCDATA * TNC2ENTRY; + +extern char PWTEXT[]; +extern char HFCTEXT[]; +extern int HFCTEXTLEN; +extern char LOCATOR[]; +extern char MAPCOMMENT[]; +extern char LOC[]; +extern int RFOnly; + +int decode_rec(char *rec); +int applcallsign(int i,char *value,char *rec); +int appl_qual(int i,char *value,char *rec); +int callsign(char * val, char *value, char *rec); +int int_value(short * val, char *value, char *rec); +int hex_value(int * val, char *value, char *rec); +int bin_switch(char * val, char *value, char *rec); +int dec_switch(char * val, char *value, char *rec); +int applstrings(int i,char *value, char *rec); +int dotext(char * val, char *key_word, int max); +int dolinked(int i, char * value, char * rec); +int routes(int i); +int ports(int i); +int tncports(int i); +int dedports(int i); +int xindex(char *s,char *t); +int verify(char *s,char c); +int GetNextLine(char * rec); +int call_check(char *callsign, char * val); +int call_check_internal(char * callsign); +int callstring(int i,char *value,char *rec); +int decode_port_rec(char *rec); +int doid(int i,char *value,char *rec); +int dodll(int i,char *value,char *rec); +int doDriver(int i,char *value,char *rec); +int hwtypes(int i,char *value,char *rec); +int protocols(int i,char *value,char *rec); +int bbsflag(int i,char *value,char *rec); +int channel(int i,char *value,char *rec); +int validcalls(int i,char *value,char *rec); +int kissoptions(int i,char *value,char *rec); +int decode_tnc_rec(char *rec); +int tnctypes(int i,char *value,char *rec); +int do_kiss(char *value,char *rec); +int decode_ded_rec(char *rec); +int simple(int i); +int64_t int64_value(int64_t * val, char value[], char rec[]); + + +int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF); +int doSerialPortName(int i, char * value, char * rec); +int doPermittedAppls(int i, char * value, char * rec); +int doKissCommand(int i, char * value, char * rec); + +BOOL ProcessAPPLDef(char * rec); +BOOL ToLOC(double Lat, double Lon , char * Locator); + +//int i; +//char value[]; +//char rec[]; + +int FIRSTAPPL=1; +BOOL Comment = FALSE; +int CommentLine = 0; + +#define MAXLINE 512 +#define FILEVERSION 22 + +extern UCHAR BPQDirectory[]; + +struct CONFIGTABLE xxcfg; +struct PORTCONFIG xxp; + +char inputname[250]="bpqcfg.txt"; +char option[250]; + +/************************************************************************/ +/* STATIC VARIABLES */ +/************************************************************************/ + +static char *keywords[] = +{ +"OBSINIT", "OBSMIN", "NODESINTERVAL", "L3TIMETOLIVE", "L4RETRIES", "L4TIMEOUT", +"BUFFERS", "PACLEN", "TRANSDELAY", "T3", "IDLETIME", "BBS", +"NODE", "NODEALIAS", "BBSALIAS", "NODECALL", "BBSCALL", +"TNCPORT", "IDMSG:", "INFOMSG:", "ROUTES:", "PORT", "MAXLINKS", +"MAXNODES", "MAXROUTES", "MAXCIRCUITS", "IDINTERVAL", "MINQUAL", +"HIDENODES", "L4DELAY", "L4WINDOW", "BTINTERVAL", "UNPROTO", "BBSQUAL", +"APPLICATIONS", "EMS", "CTEXT:", "DESQVIEW", "HOSTINTERRUPT", "ENABLE_LINKED", +"XXDEDHOST", "FULL_CTEXT", "SIMPLE", "AUTOSAVE" , "L4APPL", +"APPL1CALL", "APPL2CALL", "APPL3CALL", "APPL4CALL", +"APPL5CALL", "APPL6CALL", "APPL7CALL", "APPL8CALL", +"APPL1ALIAS", "APPL2ALIAS", "APPL3ALIAS", "APPL4ALIAS", +"APPL5ALIAS", "APPL6ALIAS", "APPL7ALIAS", "APPL8ALIAS", +"APPL1QUAL", "APPL2QUAL", "APPL3QUAL", "APPL4QUAL", +"APPL5QUAL", "APPL6QUAL", "APPL7QUAL", "APPL8QUAL", +"BTEXT:", "NETROMCALL", "C_IS_CHAT", "MAXRTT", "MAXHOPS", // IPGATEWAY= no longer allowed +"LogL4Connects", "LogAllConnects", "SAVEMH", "ENABLEADIFLOG", "ENABLEEVENTS", "SAVEAPRSMSGS", +"EnableM0LTEMap", "MQTT", "MQTT_HOST", "MQTT_PORT", "MQTT_USER", "MQTT_PASS", +"L4Compress", "L4CompMaxframe", "L4CompPaclen", "L2Compress", "L2CompMaxframe", "L2CompPaclen" +}; /* parameter keywords */ + +static void * offset[] = +{ +&xxcfg.C_OBSINIT, &xxcfg.C_OBSMIN, &xxcfg.C_NODESINTERVAL, &xxcfg.C_L3TIMETOLIVE, &xxcfg.C_L4RETRIES, &xxcfg.C_L4TIMEOUT, +&xxcfg.C_BUFFERS, &xxcfg.C_PACLEN, &xxcfg.C_TRANSDELAY, &xxcfg.C_T3, &xxcfg.C_IDLETIME, &xxcfg.C_BBS, +&xxcfg.C_NODE, &xxcfg.C_NODEALIAS, &xxcfg.C_BBSALIAS, &xxcfg.C_NODECALL, &xxcfg.C_BBSCALL, +0, &xxcfg.C_IDMSG, &xxcfg.C_INFOMSG, &xxcfg.C_ROUTE, &xxcfg.C_PORT, &xxcfg.C_MAXLINKS, +&xxcfg.C_MAXDESTS, &xxcfg.C_MAXNEIGHBOURS, &xxcfg.C_MAXCIRCUITS, &xxcfg.C_IDINTERVAL, &xxcfg.C_MINQUAL, +&xxcfg.C_HIDENODES, &xxcfg.C_L4DELAY, &xxcfg.C_L4WINDOW, &xxcfg.C_BTINTERVAL, &xxcfg.C_WASUNPROTO, &xxcfg.C_BBSQUAL, +&xxcfg.C_APPL, &xxcfg.C_EMSFLAG, &xxcfg.C_CTEXT , &xxcfg.C_DESQVIEW, &xxcfg.C_HOSTINTERRUPT, &xxcfg.C_LINKEDFLAG, +0, &xxcfg.C_FULLCTEXT, 0, &xxcfg.C_AUTOSAVE, &xxcfg.C_L4APPL, +&xxcfg.C_APPL[0].ApplCall, &xxcfg.C_APPL[1].ApplCall, &xxcfg.C_APPL[2].ApplCall, &xxcfg.C_APPL[3].ApplCall, +&xxcfg.C_APPL[4].ApplCall, &xxcfg.C_APPL[5].ApplCall, &xxcfg.C_APPL[6].ApplCall, &xxcfg.C_APPL[7].ApplCall, +&xxcfg.C_APPL[0].ApplAlias, &xxcfg.C_APPL[1].ApplAlias, &xxcfg.C_APPL[2].ApplAlias, &xxcfg.C_APPL[3].ApplAlias, +&xxcfg.C_APPL[4].ApplAlias, &xxcfg.C_APPL[5].ApplAlias, &xxcfg.C_APPL[6].ApplAlias, &xxcfg.C_APPL[7].ApplAlias, +&xxcfg.C_APPL[0].ApplQual, &xxcfg.C_APPL[1].ApplQual, &xxcfg.C_APPL[2].ApplQual, &xxcfg.C_APPL[3].ApplQual, +&xxcfg.C_APPL[4].ApplQual, &xxcfg.C_APPL[5].ApplQual, &xxcfg.C_APPL[6].ApplQual, &xxcfg.C_APPL[7].ApplQual, +&xxcfg.C_BTEXT, &xxcfg.C_NETROMCALL, &xxcfg.C_C, &xxcfg.C_MAXRTT, &xxcfg.C_MAXHOPS, // IPGATEWAY= no longer allowed +&xxcfg.C_LogL4Connects, &xxcfg.C_LogAllConnects, &xxcfg.C_SaveMH, &xxcfg.C_ADIF, &xxcfg.C_EVENTS, &xxcfg.C_SaveAPRSMsgs, +&xxcfg.C_M0LTEMap, &xxcfg.C_MQTT, &xxcfg.C_MQTT_HOST, &xxcfg.C_MQTT_PORT, &xxcfg.C_MQTT_USER, &xxcfg.C_MQTT_PASS, +&xxcfg.C_L4Compress, &xxcfg.C_L4CompMaxframe, &xxcfg.C_L4CompPaclen, &xxcfg.C_L2Compress, &xxcfg.C_L2CompMaxframe, &xxcfg.C_L2CompPaclen}; /* offset for corresponding data in config file */ + +static int routine[] = +{ +1, 1, 1, 1, 1, 1, +1, 1, 1, 1, 1, 2, +2, 0, 0, 0, 0, +3, 4, 20, 5, 6, 1, +1, 1, 1, 1, 1, +2, 1, 1, 1, 7, 1, +8, 2, 4, 2, 9, 10, +11, 1, 12, 2 , 1, +13, 13, 13, 13, +13, 13 ,13, 13, +13, 13, 13, 13, +13, 13 ,13, 13, +14, 14, 14, 14, +14, 14 ,14, 14, +15, 0, 2, 9, 9, +2, 2, 1, 2, 2, 2, +2, 2, 0, 1, 20, 20, +1, 1, 1, 1, 1, 1} ; // Routine to process param + +int PARAMLIM = sizeof(routine)/sizeof(int); +//int NUMBEROFKEYWORDS = sizeof(routine)/sizeof(int); + +//#define PARAMLIM 74 + + +static char eof_message[] = "Unexpected end of file on input\n"; + +static char *pkeywords[] = +{ +"ID", "TYPE", "PROTOCOL", "IOADDR", "INTLEVEL", "SPEED", "CHANNEL", +"BBSFLAG", "QUALITY", "MAXFRAME", "TXDELAY", "SLOTTIME", "PERSIST", +"FULLDUP", "SOFTDCD", "FRACK", "RESPTIME", "RETRIES", +"PACLEN", "CWID", "PORTCALL", "PORTALIAS", "ENDPORT", "VALIDCALLS", +"QUALADJUST", "DIGIFLAG", "DIGIPORT", "USERS" ,"UNPROTO", "PORTNUM", +"TXTAIL", "ALIAS_IS_BBS", "L3ONLY", "KISSOPTIONS", "INTERLOCK", "NODESPACLEN", +"TXPORT", "MHEARD", "CWIDTYPE", "MINQUAL", "MAXDIGIS", "PORTALIAS2", "DLLNAME", +"BCALL", "DIGIMASK", "NOKEEPALIVES", "COMPORT", "DRIVER", "WL2KREPORT", "UIONLY", +"UDPPORT", "IPADDR", "I2CBUS", "I2CDEVICE", "UDPTXPORT", "UDPRXPORT", "NONORMALIZE", +"IGNOREUNLOCKEDROUTES", "INP3ONLY", "TCPPORT", "RIGPORT", "PERMITTEDAPPLS", "HIDE", +"SMARTID", "KISSCOMMAND", "SendtoM0LTEMap", "PortFreq", "M0LTEMapInfo", "QTSMPort"}; /* parameter keywords */ + +static void * poffset[] = +{ +&xxp.ID, &xxp.TYPE, &xxp.PROTOCOL, &xxp.IOADDR, &xxp.INTLEVEL, &xxp.SPEED, &xxp.CHANNEL, +&xxp.BBSFLAG, &xxp.QUALITY, &xxp.MAXFRAME, &xxp.TXDELAY, &xxp.SLOTTIME, &xxp.PERSIST, +&xxp.FULLDUP, &xxp.SOFTDCD, &xxp.FRACK, &xxp.RESPTIME, &xxp.RETRIES, +&xxp.PACLEN, &xxp.CWID, &xxp.PORTCALL, &xxp.PORTALIAS, 0, &xxp.VALIDCALLS, +&xxp.QUALADJUST, &xxp.DIGIFLAG, &xxp.DIGIPORT, &xxp.USERS, &xxp.UNPROTO, &xxp.PORTNUM, +&xxp.TXTAIL, &xxp.ALIAS_IS_BBS, &xxp.L3ONLY, &xxp.KISSOPTIONS, &xxp.INTERLOCK, &xxp.NODESPACLEN, +&xxp.TXPORT, &xxp.MHEARD, &xxp.CWIDTYPE, &xxp.MINQUAL, &xxp.MAXDIGIS, &xxp.PORTALIAS2, &xxp.DLLNAME, +&xxp.BCALL, &xxp.DIGIMASK, &xxp.DefaultNoKeepAlives, &xxp.IOADDR, &xxp.DLLNAME, &xxp.WL2K, &xxp.UIONLY, +&xxp.IOADDR, &xxp.IPADDR, &xxp.INTLEVEL, &xxp.IOADDR, &xxp.IOADDR, &xxp.ListenPort, &xxp.NoNormalize, +&xxp.IGNOREUNLOCKED, &xxp.INP3ONLY, &xxp.TCPPORT, &xxp.RIGPORT, &xxp.PERMITTEDAPPLS, &xxp.Hide, +&xxp.SmartID, &xxp.KissParams, &xxp.SendtoM0LTEMap, &xxp.PortFreq, &xxp.M0LTEMapInfo, &xxp.QtSMPort}; /* offset for corresponding data in config file */ + +static int proutine[] = +{ +4, 5, 8, 3, 1, 1, 7, +6, 1, 1, 1, 1, 1, +1, 1, 1, 1, 1, +1, 0, 0, 0, 9, 10, +1, 13, 13, 1, 11, 1, +1, 2, 2, 12, 1, 1, +1, 7, 7, 13, 13, 0, 14, +0, 1, 2, 18, 15, 16, 2, +1, 17, 1, 1, 1, 1, 2, +2, 2, 1, 1, 19, 2, +1, 20, 1, 21, 22, 1}; /* routine to process parameter */ + +int PPARAMLIM = sizeof(proutine)/sizeof(int); + +static int endport = 0; +static int portnum = 1; +static int portindex = 0; +static int porterror = 0; +static int tncporterror = 0; +static int dedporterror = 0; +static int kissflags = 0; +static int NextAppl = 0; +static int routeindex = 0; + + +/************************************************************************/ +/* Global variables */ +/************************************************************************/ + +int paramok[100] = {0}; /* PARAMETER OK FLAG */ + +FILE *fp1; /* TEXT INPUT FILE */ + +static char s1[80]; +static char s2[80]; +static char s3[80]; +static char s4[80]; +static char s5[80]; +static char s6[80]; +static char s7[80]; +static char s8[80]; + +char commas[]=",,,,,,,,,,,,,,,,"; + +char bbscall[11]; +char bbsalias[11]; +int bbsqual; + + +extern UCHAR ConfigDirectory[260]; + +BOOL LocSpecified = FALSE; + +/************************************************************************/ +/* MAIN PROGRAM */ +/************************************************************************/ + +VOID WarnThread(); + +int LineNo = 0; + +int heading = 0; + + +BOOL ProcessConfig() +{ + int i; + char rec[MAXLINE]; + int Cfglen = sizeof(xxcfg); + struct APPLCONFIG * App; + + memset(&xxcfg, 0, sizeof(xxcfg)); + memset(&xxp, 0, sizeof(xxp)); + + heading = 0; + portnum = 1; + NextAppl = 0; + LOCATOR[0] = 0; + MAPCOMMENT[0] = 0; + routeindex = 0; + portindex = 0; + + for (i = 0; i < 70; i++) + { + if (PortConfig[i]) + { + free(PortConfig[i]); + PortConfig[i] = NULL; + } + PortDefined[i] = FALSE; + + if (RadioConfigMsg[i]) + { + free(RadioConfigMsg[i]); + RadioConfigMsg[i] = NULL; + } + } + + nextRadioPort = 0; + + TNCCONFIGTABLE = NULL; + NUMBEROFTNCPORTS = 0; + + AGWMask = 0; + + UPNPConfig = NULL; + + Consoleprintf("Configuration file Preprocessor."); + + if (ConfigDirectory[0] == 0) + { + strcpy(inputname, "bpq32.cfg"); + } + else + { + strcpy(inputname,ConfigDirectory); + strcat(inputname,"/"); + strcat(inputname, "bpq32.cfg"); + } + + if ((fp1 = fopen(inputname,"r")) == NULL) + { + Consoleprintf("Could not open file %s Error code %d", inputname, errno); + return FALSE; + } + + Consoleprintf("Using Configuration file %s",inputname); + + memset(&xxcfg, 0, sizeof(xxcfg)); + + App = (struct APPLCONFIG *)&xxcfg.C_APPL[0]; + + for (i=0; i < NumberofAppls; i++) + { + memset(App->Command, ' ', 12); + memset(App->CommandAlias, ' ', 48); + memset(App->ApplCall, ' ', 10); + memset(App->ApplAlias, ' ', 10); + + App++; + } + +// xxcfg.SaveMH = TRUE; // Default to save + + GetNextLine(rec); + + while (rec[0]) + { + decode_rec(rec); + GetNextLine(rec); + } + + if (xxcfg.C_NODECALL[0] == ' ') + { + Consoleprintf("Missing NODECALL"); + heading = 1; + } + + + paramok[6]=1; /* dont need BUFFERS */ + paramok[8]=1; /* dont need TRANSDELAY */ + paramok[13]=1; // NodeAlias + paramok[17]=1; /* dont need TNCPORTS */ + paramok[20]=1; // Or ROUTES + + paramok[32]=1; /* dont need UNPROTO */ + + paramok[35]=1; /* dont need EMS */ + paramok[37]=1; /* dont need DESQVIEW */ + paramok[38]=1; /* dont need HOSTINTERRUPT */ + + paramok[40]=1; /* or DEDHOST */ + + paramok[42]=1; /* or SIMPLE */ + + paramok[43]=1; /* or AUTOSAVE */ + + paramok[44]=1; /* or L4APPL */ + + + paramok[16]=1; // BBSCALL + paramok[14]=1; // BBSALIAS + paramok[33]=1; // BBSQUAL + paramok[34]=1; // APPLICATIONS + + if (paramok[45]==1) + { + paramok[16]=1; // APPL1CALL overrides BBSCALL + memcpy(xxcfg.C_BBSCALL, xxcfg.C_APPL[0].ApplCall, 10); + } + + if (paramok[53]==1) + { + paramok[14]=1; // APPL1ALIAS overrides BBSALIAS + memcpy(xxcfg.C_BBSALIAS, xxcfg.C_APPL[0].ApplAlias, 10); + } + + if (paramok[61]==1) + { + paramok[33]=1; // APPL1QUAL overrides BBSQUAL + xxcfg.C_BBSQUAL = xxcfg.C_APPL[0].ApplQual; + } + + + for (i=0;i<24;i++) + + paramok[45+i]=1; /* or APPLCALLS, APPLALIASS APPLQUAL */ + + paramok[69]=1; // BText optional + paramok[70]=1; // IPGateway optional + paramok[71]=1; // C_IS_CHAT optional + + paramok[72]=1; // MAXRTT optional + paramok[73]=1; // MAXHOPS optional + paramok[74]=1; // LogL4Connects optional + paramok[75]=1; // LogAllConnects optional + paramok[76]=1; // SAVEMH optional + paramok[77]=1; // ENABLEADIFLOG optional + paramok[78]=1; // EnableEvents optional + paramok[79]=1; // SaveAPRSMsgs optional + paramok[79]=1; // SaveAPRSMsgs optional + paramok[80]=1; // EnableM0LTEMap optional + paramok[81]=1; // MQTT Params + paramok[82]=1; // MQTT Params + paramok[83]=1; // MQTT Params + paramok[84]=1; // MQTT Params + paramok[85]=1; // MQTT Params + paramok[86]=1; // L4Compress + paramok[87]=1; // L4Compress Maxframe + paramok[88]=1; // L4Compress Paclen + paramok[89]=1; // L2Compress + paramok[90]=1; // L2Compress Maxframe + paramok[91]=1; // L2Compress Paclen + + + for (i=0; i < PARAMLIM; i++) + { + if (paramok[i] == 0) + { + if (heading == 0) + { + Consoleprintf(" "); + Consoleprintf("The following parameters were not correctly specified"); + heading = 1; + } + Consoleprintf(keywords[i]); + } + } + + if (portnum == 1) + { + Consoleprintf("No valid radio ports defined"); + heading = 1; + } + + if (Comment) + { + Consoleprintf("Unterminated Comment (Missing */) at line %d", CommentLine); + heading = 1; + } + + fclose(fp1); + + if (LOCATOR[0] == 0 && LocSpecified == 0 && RFOnly == 0) + { + Consoleprintf(""); + Consoleprintf("Please enter a LOCATOR statement in your BPQ32.cfg"); + Consoleprintf("If you really don't want to be on the Node Map you can enter LOCATOR=NONE"); + Consoleprintf(""); + +// _beginthread(WarnThread, 0, 0); + } + + if (heading == 0) + { + Consoleprintf("Conversion (probably) successful"); + Consoleprintf(""); + } + else + { + Consoleprintf("Conversion failed"); + return FALSE; + } + +/* + // Dump to file for debugging + + sprintf_s(inputname, sizeof(inputname), "CFG%d", time(NULL)); + + fp1 = fopen(inputname, "wb"); + + if (fp1) + { + fwrite(ConfigBuffer, 1, 100000, fp1); + fclose(fp1); + } +*/ + return TRUE; +} + +/************************************************************************/ +/* Decode PARAM= */ +/************************************************************************/ + +int decode_rec(char * rec) +{ + int i; + int cn = 1; /* RETURN CODE FROM ROUTINES */ + + char key_word[300] = ""; + char value[300] = ""; + + if (_memicmp(rec, "NODEMAPSERVER=", 14) == 0) + { + // Map reporting override + + strcpy(NodeMapServer, &rec[14]); + strlop(NodeMapServer, ' '); + + return 1; + } + + if (_memicmp(rec, "CloseOnError=", 13) == 0) + { + // Close BPQ on trapped program error + + CloseOnError = atoi(&rec[13]); + return 1; + } + + if (_memicmp(rec, "CHATMAPSERVER=", 14) == 0) + { + // Map reporting override + + strcpy(ChatMapServer, &rec[14]); + strlop(ChatMapServer, ' '); + + return 1; + } + + if (_memicmp(rec, "IPGATEWAY", 9) == 0 && rec[9] != '=') // IPGATEWAY, not IPGATEWAY= + { + // Create Embedded IPGateway Config + + // Copy all subsequent lines up to **** to a memory buffer + + char * ptr; + + PortConfig[IPConfigSlot] = ptr = malloc(50000); + + *ptr = 0; + + GetNextLine(rec); + + while (!feof(fp1)) + { + if (_memicmp(rec, "****", 3) == 0) + { + PortConfig[IPConfigSlot] = realloc(PortConfig[IPConfigSlot], (strlen(ptr) + 1)); + xxcfg.C_IP = 1; + return 0; + } + + strcat(ptr, rec); + strcat(ptr, "\r\n"); + GetNextLine(rec); + } + + Consoleprintf("Missing **** for IPGateway Config"); + heading = 1; + + return 0; + } + + if (_memicmp(rec, "PORTMAPPER", 10) == 0) + { + // Create Embedded portmapper Config + + // Copy all subsequent lines up to **** to a memory buffer + + char * ptr; + + PortConfig[PortMapConfigSlot] = ptr = malloc(50000); + + *ptr = 0; + + GetNextLine(rec); + + while (!feof(fp1)) + { + if (_memicmp(rec, "****", 3) == 0) + { + PortConfig[PortMapConfigSlot] = realloc(PortConfig[PortMapConfigSlot], (strlen(ptr) + 1)); + xxcfg.C_PM = 1; + return 0; + } + + strcat(ptr, rec); + strcat(ptr, "\r\n"); + GetNextLine(rec); + } + + Consoleprintf("Missing **** for Portmapper Config"); + heading = 1; + + return 0; + } + + if (_memicmp(rec, "APRSDIGI", 8) == 0) + { + // Create Embedded APRS Config + + // Copy all subsequent lines up to **** to a memory buffer + + char * ptr; + + PortConfig[APRSConfigSlot] = ptr = malloc(50000); + + *ptr = 0; + + // Don't use GetNextLine - we need to keep ; in messages + + fgets(rec,MAXLINE,fp1); + LineNo++; + + while (!feof(fp1)) + { + if (_memicmp(rec, "****", 3) == 0) + { + PortConfig[APRSConfigSlot] = realloc(PortConfig[APRSConfigSlot], (strlen(ptr) + 1)); + return 0; + } + if (strlen(rec) > 1) + { + if (memcmp(rec, "/*", 2) == 0) + { + Comment = TRUE; + CommentLine = LineNo; + goto NextAPRS; + } + else if (memcmp(rec, "*/", 2) == 0) + { + Comment = FALSE; + goto NextAPRS; + } + } + + if (Comment) + goto NextAPRS; + + strcat(ptr, rec); + strcat(ptr, "\r\n"); +NextAPRS: + fgets(rec,MAXLINE,fp1); + LineNo++; + } + + if (_memicmp(rec, "****", 3) == 0) + return 0; // No Newline after *** + + Consoleprintf("Missing **** for APRS Config"); + heading = 1; + + return 0; + } + + if (_memicmp(rec, "PASSWORD", 8) == 0) + { + // SYSOP Password + + if (strlen(rec) > 88) rec[88] = 0; + + _strupr(rec); + + strcpy(PWTEXT, &rec[9]); + return 0; + } + +#ifdef LINBPQ + + if (_memicmp(rec, "LINMAIL", 7) == 0) + { + // Enable Mail on Linux Verdion + + IncludesMail = TRUE; + + return 0; + } + + if (_memicmp(rec, "LINCHAT", 7) == 0) + { + // Enable Chat on Linux Verdion + + IncludesChat = TRUE; + + return 0; + } +#endif + + if (_memicmp(rec, "ENABLEAIS", 9) == 0) + { + needAIS = TRUE; + return 0; + } + + if (_memicmp(rec, "ENABLEADSB", 9) == 0) + { + needADSB = TRUE; + return 0; + } + + if (_memicmp(rec, "HFCTEXT", 7) == 0) + { + // HF only CTEXT (normlly short to reduce traffic) + + if (strlen(rec) > 87) rec[87] = 0; + strcpy(HFCTEXT, &rec[8]); + HFCTEXTLEN = (int)(strlen(HFCTEXT)); + HFCTEXT[HFCTEXTLEN - 1] = '\r'; + return 0; + } + + if (_memicmp(rec, "LOCATOR", 7) == 0) + { + // Station Maidenhead Locator or Lat/Long + + char * Context; + char * ptr1 = strtok_s(&rec[7], " ,=\t\n\r:", &Context); + char * ptr2 = strtok_s(NULL, " ,=\t\n\r:", &Context); + + LocSpecified = TRUE; + + if (_memicmp(&rec[8], "NONE", 4) == 0) + return 0; + + if (_memicmp(&rec[8], "XXnnXX", 6) == 0) + return 0; + + if (ptr1) + { + strcpy(LOCATOR, ptr1); + if (ptr2) + { + strcat(LOCATOR, ":"); + strcat(LOCATOR, ptr2); + ToLOC(atof(ptr1), atof(ptr2), LOC); + LatFromLOC = atof(ptr1); + LonFromLOC = atof(ptr2); + + } + else + { + if (strlen(ptr1) == 6) + { + strcpy(LOC, ptr1); + FromLOC(LOC, &LatFromLOC, &LonFromLOC); + // Randomise in square + LatFromLOC += ((rand() / 24.0) / RAND_MAX); + LonFromLOC += ((rand() / 12.0) / RAND_MAX); + + } + } + } + return 0; + } + + if (_memicmp(rec, "MAPCOMMENT", 10) == 0) + { + // Additional Info for Node Map + + char * ptr1 = &rec[11]; + char * ptr2 = MAPCOMMENT; + + while (*ptr1) + { + if (*ptr1 == ',') + { + *ptr2++ = '&'; + *ptr2++ = '#'; + *ptr2++ = '4'; + *ptr2++ = '4'; + *ptr2++ = ';'; + } + else + *(ptr2++) = *ptr1; + + ptr1++; + + if ((ptr2 - MAPCOMMENT) > 248) + break; + } + + *ptr2 = 0; + + return 0; + } + + // Process BRIDGE statement + + if (_memicmp(rec, "BRIDGE", 6) == 0) + { + int DigiTo; + int Port; + char * Context; + char * p_value; + char * ptr; + + p_value = strtok_s(&rec[7], ",=\t\n\r", &Context); + + Port = atoi(p_value); + + if (Port > MaxBPQPortNo) + return FALSE; + + if (Context == NULL) + return FALSE; + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + DigiTo = atoi(ptr); + + if (DigiTo > MaxBPQPortNo) + return 0; + + if (Port != DigiTo) // Not to our port! + xxcfg.CfgBridgeMap[Port][DigiTo] = TRUE; + + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + return 0; + } + + + // AGW Emulator Params + + if (_memicmp(rec, "AGWPORT", 7) == 0) + { + AGWPort = atoi(&rec[8]); + return 0; + } + + if (_memicmp(rec, "AGWSESSIONS", 11) == 0) + { + AGWSessions = atoi(&rec[12]); + return 0; + } + + if (_memicmp(rec, "AGWMASK", 7) == 0) + { + AGWMask = strtol(&rec[8], 0, 0); + return 0; + } + + if (_memicmp(rec, "AGWAPPL", 7) == 0) + { + AGWMask |= 1 << (strtol(&rec[8], 0, 0) - 1); + return 0; + } + + if (_memicmp(rec, "AGWLOOPMON", 10) == 0) + { + LoopMonFlag = strtol(&rec[11], 0, 0); + return 0; + } + if (_memicmp(rec, "AGWLOOPTX", 9) == 0) + { + Loopflag = strtol(&rec[10], 0, 0); + return 0; + } + + if (_memicmp(rec, "APPLICATION ", 12) == 0 || _memicmp(rec, "APPLICATION=", 12) == 0) + { + // New Style APPLICATION Definition + + char save[300]; + + strcpy(save, rec); // Save in case error + + if (!ProcessAPPLDef(&rec[12])) + { + Consoleprintf("Invalid Record %s", save); + heading = 1; + } + else + paramok[34]=1; // Got APPLICATIONS + + return 0; + } + + if (_memicmp(rec, "EXCLUDE=", 8) == 0) + { + char * ptr2 = &rec[8]; + UCHAR * ptr3 = xxcfg.C_EXCLUDE; + + _strupr(ptr2); + while (*(ptr2) > 32) + { + ConvToAX25(ptr2, ptr3); + ptr3 += 7; + + if (strchr(ptr2, ',')) + { + ptr2 = strchr(ptr2, ','); + ptr2++; + } + else + break; + + if (ptr3 > &xxcfg.C_EXCLUDE[63]) + { + Consoleprintf("Too Many Excluded Calls"); + heading = 1; + break; + } + } + + return 0; + } + if (_memicmp(rec, "RADIO", 5) == 0) + { + if (strlen(rec) > 11) + { + RadioConfigMsg[nextRadioPort++] = _strdup(rec); + return 0; + } + else + { + // Multiline config, ending in **** + + char * rptr; + + RadioConfigMsg[nextRadioPort] = rptr = zalloc(50000); + + strcpy(rptr, rec); + + GetNextLine(rec); + + while(!feof(fp1)) + { + if (memcmp(rec, "***", 3) == 0) + { + RadioConfigMsg[nextRadioPort] = realloc(RadioConfigMsg[nextRadioPort], (strlen(rptr) + 1)); + nextRadioPort++; + return 0; + } + strcat(rptr, rec); + GetNextLine(rec); + } + } + } + + if (_memicmp(rec, "UPNP ", 5) == 0) + { + struct UPNP * Entry = (struct UPNP *)zalloc(sizeof(struct UPNP)); + char * ptr, * context; + char copy[256]; + + strcpy(copy, rec); + + ptr = strtok_s(&rec[5], ", ", &context); + + if (ptr) + Entry->Protocol = _strdup(ptr); + + ptr = strtok_s(NULL, ", ", &context); + + if (ptr) + Entry->LANport = Entry->WANPort = _strdup(ptr);; + + ptr = strtok_s(NULL, ", ", &context); + + if (ptr) + Entry->WANPort = _strdup(ptr);; + + if (Entry->LANport) + { + Entry->Next = UPNPConfig; + UPNPConfig = Entry; + return 1; + } + + Consoleprintf("Bad UPNP Line %s", copy); + heading = 1; + + return 0; + } + + + if (_memicmp("MQTT_HOST=", rec, 10) == 0) + { + strcpy(xxcfg.C_MQTT_HOST, &rec[10]); + xxcfg.C_MQTT_HOST[strlen(xxcfg.C_MQTT_HOST)-1] = '\0'; + return 0; + } + if (_memicmp("MQTT_USER=", rec, 10) == 0) + { + strcpy(xxcfg.C_MQTT_USER, &rec[10]); + xxcfg.C_MQTT_USER[strlen(xxcfg.C_MQTT_USER)-1] = '\0'; + return 0; + } + if (_memicmp("MQTT_PASS=", rec, 10) == 0) + { + strcpy(xxcfg.C_MQTT_PASS, &rec[10]); + xxcfg.C_MQTT_PASS[strlen(xxcfg.C_MQTT_PASS)-1] = '\0'; + return 0; +} + + + if (xindex(rec,"=") >= 0) + sscanf(rec,"%[^=]=%s",key_word,value); + else + sscanf(rec,"%s",key_word); + +/************************************************************************/ +/* SEARCH FOR KEYWORD IN TABLE */ +/************************************************************************/ + + for (i=0; i < PARAMLIM && _stricmp(keywords[i],key_word) != 0 ; i++) + ; + + if (i == PARAMLIM) + Consoleprintf("bpq32.cfg line no %d not recognised - Ignored: %s" ,LineNo, rec); + else + { + + switch (routine[i]) + { + case 0: + cn = callsign((char *)offset[i], value, rec); /* CALLSIGNS */ + break; + + case 1: + cn = int_value((short *)offset[i], value, rec); /* INTEGER VALUES */ + break; + + case 2: + cn = bin_switch((char *)offset[i], value, rec); /* 0/1 SWITCHES */ + break; + + case 3: + cn = tncports(i); /* VIRTUAL COMBIOS PORTS */ + break; + + case 4: + cn = dotext((char *)offset[i], key_word, 510); /* TEXT PARMS */ + break; + + case 20: + cn = dotext((char *)offset[i], key_word, 2000); /* INFO TEXT PARM */ + break; + + case 5: + cn = routes(i); /* ROUTES TO LOCK IN */ + break; + + case 6: + cn = ports(i); /* PORTS DEFINITION */ + break; + + case 7: + Consoleprintf("Obsolete Record %s ignored",rec); + Consoleprintf("UNPROTO address should now be specified in PORT definition"); + + break; + + case 8: + cn = applstrings(i,value,rec); /* APPLICATIONS LIST */ + break; + + case 9: + cn = dec_switch((char *)offset[i],value,rec); /* 0/9 SWITCHES */ + break; + + case 10: + cn = dolinked(i,value,rec); /* SINGLE CHAR */ + break; + + case 11: + Consoleprintf("Obsolete Record %s ignored", rec); + break; + + case 12: + cn = simple(i); /* Set up basic L2 system*/ + break; + + case 13: + cn = applcallsign(i,value,rec); /* CALLSIGNS */ + break; + + case 14: + cn = appl_qual(i,value,rec); /* INTEGER VALUES */ + break; + + case 15: + cn = dotext((char *)offset[i], key_word, 120); /* BTEXT */ + break; + } + + paramok[i] = cn; + } + + return 0; +} + +/************************************************************************/ +/* CALLSIGNS */ +/************************************************************************/ +int applcallsign(int i, char * value, char * rec) +{ + char * val = (char *)offset[i]; + + if (call_check_internal(value)) + { + // Invalid + + return 0; + } + + memcpy(val, value, 10); + + if (i==45) + strcpy(bbscall,value); + if (i==53) + strcpy(bbsalias,value); + + return 1; +} + +int appl_qual(int i, char * value, char * rec) +{ + int j, k; + int * val = (int *)offset[i]; + + k = sscanf(value," %d",&j); + + if (k != 1) + { + Consoleprintf("Invalid numerical value "); + Consoleprintf("%s\r\n",rec); + return(0); + } + + if (i==61) bbsqual=j; + + *val = j; + return(1); +} + + +int callsign(char * ptr, char * value, char * rec) +{ + if (call_check(value, ptr) == 1) + { + Consoleprintf("%s",rec); + return(0); + } + + return(1); +} + + +/************************************************************************/ +/* VALIDATE INT VALUES */ +/************************************************************************/ + +int int_value(short * val, char value[], char rec[]) +{ + int j,k; + + k = sscanf(value," %d",&j); + + if (k != 1) + { + Consoleprintf("Invalid numerical value "); + Consoleprintf("%s\r\n",rec); + return(0); + } + + val[0] = j; + return(1); +} + +int64_t int64_value(int64_t * val, char value[], char rec[]) +{ + *val = strtoll(value, NULL, 10); + return(1); +} + +/************************************************************************/ +/* VALIDATE HEX INT VALUES */ +/************************************************************************/ + +int hex_value(int * val, char value[], char rec[]) +{ + int j = -1, k = 0; + + k = sscanf(value, " %xH", &j); + + if (j < 0) + { + Consoleprintf("Bad Hex Value"); + Consoleprintf("%s\r\n", rec); + return(0); + } + + val[0] = j; + return(1); +} +; + +/************************************************************************/ +/* VALIDATE BINARY SWITCH DATA AND WRITE TO FILE */ +/************************************************************************/ + +int bin_switch(char * val, char * value, char * rec) +{ + int value_int; + + value_int = atoi(value); + + if (value_int == 0 || value_int == 1) + { + val[0] = value_int; + return 1; + } + else + { + Consoleprintf("Invalid switch value, must be either 0 or 1"); + Consoleprintf("%s\r\n",rec); + return(0); + } +} +/* +; single byte decimal +*/ +int dec_switch (char * val, char * value, char * rec) +{ + int value_int; + + value_int = atoi(value); + + if (value_int < 256) + { + val[0] = value_int; + return 1; + } + else + { + Consoleprintf("Invalid value, must be between 0 and 255"); + Consoleprintf("%s\r\n",rec); + return(0); + } +} + + +int applstrings(int i, char * value, char * rec) +{ + char appl[250]; // In case trailing spaces + char * ptr1; + char * ptr2; + struct APPLCONFIG * App; + int j; + + // strcat(rec,commas); // Ensure 16 commas + + ptr1 = &rec[13]; // skip APPLICATIONS= + + App = &xxcfg.C_APPL[0]; + + while (NextAppl++ < NumberofAppls) + { + memset(appl, ' ', 249); + appl[249] = 0; + + ptr2=appl; + + j = *ptr1++; + + while (j != ',' && j) + { + *(ptr2++) = toupper(j); + j = *ptr1++; + } + + ptr2 = strchr(appl, '/'); + + if (ptr2) + { + // Command has an Alias + + *ptr2++ = 0; + memcpy(App->CommandAlias, ptr2, 48); + strcat(appl, " "); + } + + memcpy(App->Command, appl, 12); + xxcfg.C_BBS = 1; + + if (*(ptr1 - 1) == 0) + return 1; + + App++; + } + return(1); +} + + +/************************************************************************/ +/* USE FOR FREE FORM TEXT IN MESSAGES */ +/************************************************************************/ + +int dotext(char * val, char * key_word, int max) +{ + int len = 0; + char * ptr; + + char rec[MAXLINE]; + + GetNextLine(rec); + + if (xindex(rec,"***") == 0) + *val = '\r'; + + while (xindex(rec,"***") != 0 && !feof(fp1)) + { + ptr = strchr(rec, 10); + if (ptr) *ptr = 0; + ptr = strchr(rec, 13); + if (ptr) *ptr = 0; + + strcat(rec, "\r"); + + len += (int)strlen(rec); + + if (len <= max) + { + strcpy(val, rec); + val += (int)strlen(rec); + } + + fgets(rec,MAXLINE,fp1); + LineNo++; + } + + if (len > max) + { + Consoleprintf("Text too long: %s (max %d\r\n",key_word, max); + return(0); + } + + if (feof(fp1)) + return(0); + else + return(1); +} + + +/************************************************************************/ +/* CONVERT PRE-SET ROUTES PARAMETERS TO BINARY */ +/************************************************************************/ + +int routes(int i) +{ + struct ROUTECONFIG * Route; + + int err_flag = 0; + int main_err = 0; + + char rec[MAXLINE]; + + + GetNextLine(rec); + + while (xindex(rec,"***") != 0 && !feof(fp1)) + { + char Param[8][256]; + char * ptr1, * ptr2; + int n = 0, inp3 = 0; + + Route = &xxcfg.C_ROUTE[routeindex++]; + + // strtok and sscanf can't handle successive commas, so split up usig strchr + + memset(Param, 0, 2048); + strlop(rec, 13); + strlop(rec, ';'); + + ptr1 = rec; + + while (ptr1 && *ptr1 && n < 8) + { + ptr2 = strchr(ptr1, ','); + if (ptr2) *ptr2++ = 0; + + strcpy(&Param[n++][0], ptr1); + ptr1 = ptr2; + while(ptr1 && *ptr1 && *ptr1 == ' ') + ptr1++; + } + + strcpy(Route->call, &Param[0][0]); + + Route->quality = atoi(Param[1]); + Route->port = atoi(Param[2]); + Route->pwind = atoi(Param[3]); + Route->pfrack = atoi(Param[4]); + Route->ppacl = atoi(Param[5]); + inp3 = atoi(Param[6]); + Route->farQual = atoi(Param[7]); + + if (Route->farQual < 0 || Route->farQual > 255) + { + Consoleprintf("Remote Quality must be between 0 and 255"); + Consoleprintf("%s\r\n",rec); + + err_flag = 1; + } + + if (Route->quality < 0 || Route->quality > 255) + { + Consoleprintf("Quality must be between 0 and 255"); + Consoleprintf("%s\r\n",rec); + + err_flag = 1; + } + + if (Route->port < 1 || Route->port > MaxBPQPortNo) + { + Consoleprintf("Port number must be between 1 and 64"); + Consoleprintf("%s\r\n",rec); + err_flag = 1; + } + + // Use top bit of window as INP3 Flag, next as NoKeepAlive + + if (inp3 & 1) + Route->pwind |= 0x80; + + if (inp3 & 2) + Route->pwind |= 0x40; + + if (err_flag == 1) + { + Consoleprintf("%s\r\n",rec); + main_err = 1; + err_flag = 0; + } + GetNextLine(rec); + } + + if (routeindex > MaxLockedRoutes) + { + routeindex--; + Consoleprintf("Route information too long "); + main_err = 1; + } + + if (feof(fp1)) + { + Consoleprintf(eof_message); + return(0); + } + + if (main_err == 1) + return(0); + else + return(1); +} + + +/************************************************************************/ +/* CONVERT PORT DEFINITIONS TO BINARY */ +/************************************************************************/ +int hw; // Hardware type +int LogicalPortNum; // As set by PORTNUM + +int ports(int i) +{ + char rec[MAXLINE]; + endport=0; + porterror=0; + kissflags=0; + + xxp.PORTNUM = portnum; + + LogicalPortNum = portnum; + + if (LogicalPortNum > MaxBPQPortNo) + { + Consoleprintf("Port Number must be between 1 and %d", MaxBPQPortNo); + heading = 1; + } + + xxp.SendtoM0LTEMap = 1; // Default to enabled + + while (endport == 0 && !feof(fp1)) + { + GetNextLine(rec); + decode_port_rec(rec); + } + if (porterror != 0) + { + Consoleprintf("Error in port definition"); + return(0); + } + + if (PortDefined[LogicalPortNum]) // Already defined? + { + Consoleprintf("Port %d already defined", LogicalPortNum); + heading = 1; + } + + PortDefined[LogicalPortNum] = TRUE; + + xxp.KISSOPTIONS = kissflags; + + // copy Port Config to main config + + memcpy(&xxcfg.C_PORT[portindex++], &xxp, sizeof(xxp)); + memset(&xxp, 0, sizeof(xxp)); + + portnum++; + + return(1); + +} + + +int tncports(int i) +{ + char rec[MAXLINE]; + endport=0; + tncporterror=0; + + TNC2ENTRY = zalloc(sizeof(struct TNCDATA)); + + TNC2ENTRY->APPLFLAGS = 6; + TNC2ENTRY->PollDelay = 1; + + while (endport == 0 && !feof(fp1)) + { + GetNextLine(rec); + decode_tnc_rec(rec); + } + if (tncporterror != 0) + { + Consoleprintf("Error in TNC PORT definition"); + free (TNC2ENTRY); + return(0); + } + + C_Q_ADD_NP(&TNCCONFIGTABLE, TNC2ENTRY); // Add to chain + + NUMBEROFTNCPORTS++; + + return(1); + + +} + + +/************************************************************************/ +/* MISC FUNCTIONS */ +/************************************************************************/ + +/************************************************************************/ +/* FIND OCCURENCE OF ONE STRING WITHIN ANOTHER */ +/************************************************************************/ + +int xindex(char s[], char t[]) +{ + int i, j ,k; + + for (i=0; s[i] != '\0'; i++) + { + for (j=i, k=0; t[k] != '\0' && s[i] == t[k]; j++, k++) + ; + if (t[k] == '\0') + return(i); + } + return(-1); +} + + +/************************************************************************/ +/* FIND FIRST OCCURENCE OF A CHARACTER THAT IS NOT c */ +/************************************************************************/ + +int verify(char s[], char c) +{ + int i; + + for (i = 0; s[i] != '\0'; i++) + if (s[i] != c) + return(i); + + return(-1); +} + +/************************************************************************/ +/* GET NEXT LINE THAT ISN'T BLANK OR IS A COMMENT LINE */ +/************************************************************************/ + +// Returns an empty string to indicate end of config + +// Modified Aril 2020 to allow #include of file fragments + +FILE * savefp = NULL; +int saveLineNo; +char includefilename[250]; + +int GetNextLine(char *rec) +{ + int i, j; + char * ret; + char * ptr, *context; + + while (TRUE) + { + ret = fgets(rec,MAXLINE,fp1); + LineNo++; + + if (ret == NULL) + { + if (savefp) + { + // we have reached eof on an include file - switch back + + fclose(fp1); + fp1 = savefp; + savefp = NULL; + LineNo = saveLineNo; + continue; + } + + rec[0] = 0; + return 0; // return end of config + } + + for (i=0; rec[i] != '\0'; i++) + if (rec[i] == '\t' || rec[i] == '\n' || rec[i] == '\r') + rec[i] = ' '; + + + + j = verify(rec,' '); + + if (j > 0) + { + // Remove Leading Spaces + + for (i=0; rec[j] != '\0'; i++, j++) + rec[i] = rec[j]; + + rec[i] = '\0'; + } + + if (stristr(rec,"WebTermCSS") == 0 && stristr(rec,"HybridCoLocatedRMS") == 0 && stristr(rec,"HybridFrequencies") == 0) // Needs ; in string + strlop(rec, ';'); + else + j = j; + + if (strlen(rec) > 1) + if (memcmp(rec, "/*",2) == 0) + { + Comment = TRUE; + CommentLine = LineNo; + } + else + if (memcmp(rec, "*/",2) == 0) + { + rec[0] = 32; + rec[1] = 0; + Comment = FALSE; + } + + if (Comment) + { + rec[0] = 32; + rec[1] = 0; + continue; + } + + // remove trailing spaces + + while(strlen(rec) && rec[strlen(rec) - 1] == ' ') + rec[strlen(rec) - 1] = 0; + + strcat(rec, " "); + + ptr = strtok_s(rec, " ", &context); + + // Put one back + + if (ptr) + { + if (context) + { + ptr[strlen(ptr)] = ' '; + } + rec = ptr; + + // look for #include + + if (_memicmp(rec, "#include ", 9) == 0) + { + savefp = fp1; + + if (BPQDirectory[0] == 0) + { + strcpy(includefilename, &rec[9]); + } + else + { + strcpy(includefilename,BPQDirectory); + strcat(includefilename,"/"); + strcat(includefilename, &rec[9]); + } + + if ((fp1 = fopen(includefilename,"r")) == NULL) + { + Consoleprintf("Could not open #include file %s Error code %d", includefilename, errno); + fp1 = savefp; + savefp = NULL; + } + else + { + saveLineNo = LineNo; + LineNo = 0; + } + continue; // get next line + } + return 0; + } + } + + // Should never reach this + + return 0; +} + + +/************************************************************************/ +/* TEST VALIDITY OF CALLSIGN */ +/************************************************************************/ + +int call_check_internal(char * callsign) +{ + char call[20]; + int ssid; + int err_flag = 0; + int i; + + if (xindex(callsign,"-") > 0) /* There is an SSID field */ + { + sscanf(callsign,"%[^-]-%d",call,&ssid); + if (strlen(call) > 6) + { + Consoleprintf("Callsign too long, 6 characters before SSID"); + Consoleprintf("%s\r\n",callsign); + err_flag = 1; + } + if (ssid < 0 || ssid > 15) + { + Consoleprintf("SSID out of range, must be between 0 and 15"); + Consoleprintf("%s\r\n",callsign); + err_flag = 1; + } + } + else /* No SSID field */ + { + if (strlen(callsign) > 6) + { + Consoleprintf("Callsign too long, 6 characters maximum"); + Consoleprintf("%s\r\n",callsign); + err_flag = 1; + } + } + + strcat(callsign," "); + callsign[10] = '\0'; + for (i=0; i< 10; i++) + callsign[i]=toupper(callsign[i]); + + return(err_flag); +} + +int call_check(char * callsign, char * loc) +{ + int err = call_check_internal(callsign); + memcpy(loc, callsign, 10); + return err; +} + + +/* Process UNPROTO string allowing VIA */ + +char workstring[80]; + +int callstring(int i, char * value, char * rec) +{ + char * val = (char *)poffset[i]; + size_t j = (int)strlen(value); + + memcpy(val, value, j); + return 1; +} + +/* + RADIO PORT PROCESSING +*/ + + +int decode_port_rec(char * rec) +{ + int i; + int cn = 1; /* RETURN CODE FROM ROUTINES */ + uint32_t IPADDR; +#ifdef WIN32 + WSADATA WsaData; // receives data from WSAStartupproblem +#endif + char key_word[30]=""; + char value[300]=""; + + if (_memicmp(rec, "CONFIG", 6) == 0) + { + // Create Embedded PORT Config + + // Copy all subseuent lines up to ENDPORT to a memory buffer + + char * ptr; + int i; + + if (LogicalPortNum > 64) + { + Consoleprintf("Portnum %d is invalid", LogicalPortNum); + LogicalPortNum = 0; + } + + PortConfig[LogicalPortNum] = ptr = malloc(50000); + *ptr = 0; + + GetNextLine(rec); + + while (!feof(fp1)) + { + if (_memicmp(rec, "ENDPORT", 7) == 0) + { + PortConfig[LogicalPortNum] = realloc(PortConfig[LogicalPortNum], (strlen(ptr) + 1)); + endport = 1; + return 0; + } + + i = (int)strlen(rec); + i--; + + while(i > 1) + { + if (rec[i] == ' ') + rec[i] = 0; // Remove trailing spaces + else + break; + + i--; + } + + // Pick out RIGCONFIG Records + + if (_memicmp(rec, "RIGCONTROL", 10) == 0) + { + // RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + + // Convert to new format (RADIO Interlockno); + + int Interlock = xxp.INTERLOCK; + char radio[16]; + + if (Interlock == 0) // Replace with dummy + { + Interlock = xxp.INTERLOCK = nextDummyInterlock; + nextDummyInterlock++; + } + + sprintf(radio, "RADIO %d ", Interlock); + memcpy(rec, radio, 10); + + if (strlen(rec) > 15) + { + RadioConfigMsg[nextRadioPort++] = _strdup(rec); + } + else + { + // Multiline config, ending in **** + + char * rptr; + + RadioConfigMsg[nextRadioPort] = rptr = zalloc(50000); + + strcpy(rptr, radio); + + GetNextLine(rec); + + while(!feof(fp1)) + { + if (memcmp(rec, "***", 3) == 0) + { + RadioConfigMsg[nextRadioPort] = realloc(RadioConfigMsg[nextRadioPort], (strlen(rptr) + 1)); + nextRadioPort++; + break; + } + strcat(rptr, rec); + GetNextLine(rec); + } + } + } + else + { + strcat(ptr, rec); + strcat(ptr, "\r\n"); + } + GetNextLine(rec); + } + + Consoleprintf("Missing ENDPORT for Port %d", portnum); + heading = 1; + + return 0; + } + + if (xindex(rec,"=") >= 0) + sscanf(rec,"%[^=]=%s",key_word,value); + else + sscanf(rec,"%s",key_word); + + if (_stricmp(key_word, "portnum") == 0) + { + // Save as LogicalPortNum + + LogicalPortNum = atoi(value); + } + + if (_stricmp(key_word, "XDIGI") == 0) + { + // Cross Port Digi definition + + // XDIGI=CALL,PORT,UI + + struct XDIGI * Digi = zalloc(sizeof(struct XDIGI)); // Chain + char * call, * pport, * Context; + + call = strtok_s(value, ",", &Context); + pport = strtok_s(NULL, ",", &Context); + + if (call && pport && ConvToAX25(call, Digi->Call)) + { + Digi->Port = atoi(pport); + if (Digi->Port) + { + if (Context) + { + _strupr(Context); + if (strstr(Context, "UI")) + Digi->UIOnly = TRUE; + } + + // Add to chain + + if (xxp.XDIGIS) + Digi->Next = xxp.XDIGIS; + + xxp.XDIGIS = Digi; + return 0; + } + } + Consoleprintf("Invalid XDIGI Statement %s", rec); + porterror = 1; + return 0; + } + + +/************************************************************************/ +/* SEARCH FOR KEYWORD IN TABLE */ +/************************************************************************/ + + for (i=0; i < PPARAMLIM && _stricmp(pkeywords[i],key_word) != 0 ; i++) + ; + + if (i == PPARAMLIM) + Consoleprintf("Source record not recognised - Ignored:%s\r\n",rec); + else + { + + switch (proutine[i]) + { + + case 0: + cn = callsign((char *)poffset[i], value, rec); /* CALLSIGNS */ + break; + + case 1: + cn = int_value((short *)poffset[i], value, rec); /* INTEGER VALUES */ + break; + + case 2: + cn = bin_switch((char *)poffset[i], value, rec); /* 0/1 SWITCHES */ + break; + + case 3: + cn = hex_value((int *)poffset[i], value, rec); /* HEX NUMBERS */ + break; + + case 4: + cn = doid(i,value,rec); /* ID PARMS */ + break; + + case 5: + cn = hwtypes(i,value,rec); /* HARDWARE TYPES */ + break; + + case 6: + cn = bbsflag(i,value,rec); + break; + + case 7: + cn = channel(i,value,rec); + break; + + case 8: + cn = protocols(i,value,rec); + break; + + case 10: + cn = validcalls(i,value,rec); + break; + + case 11: + cn = callstring(i,value,rec); + break; + + case 12: + cn = kissoptions(i,value,rec); + break; + + case 13: + cn = dec_switch((char *)poffset[i], value, rec); /* 0/9 SWITCHES */ + break; + + case 14: + cn = dodll(i,value,rec); /* DLL PARMS */ + break; + + case 15: + cn = doDriver(i,value,rec); /* DLL PARMS */ + break; + + case 16: + + xxp.WL2K = DecodeWL2KReportLine(rec); + break; + + case 17: + + // IP Address for KISS over UDP + +#ifdef WIN32 + WSAStartup(MAKEWORD(2, 0), &WsaData); +#endif + IPADDR = inet_addr(&rec[7]); + memcpy(&xxp.IPADDR, &IPADDR, 4); +#ifdef WIN32 + WSACleanup(); +#endif + break; + + case 18: + cn = doSerialPortName(i,value,rec); // COMPORT + break; + + case 19: + cn = doPermittedAppls(i,value,rec); // Permitted Apps + break; + + case 20: + cn = doKissCommand(i, value, rec); // Permitted Apps + break; + + case 21: + cn = int64_value(poffset[i], value, rec); /* INTEGER VALUES */ + break; + + case 22: + xxp.M0LTEMapInfo = _strdup(value); + cn = 1; + break; + + case 9: + + cn = 1; + endport=1; + + break; + } + } + if (cn == 0) porterror=1; + + return 0; +} + + +int doid(int i, char value[], char rec[]) +{ + unsigned int j; + for (j = 3;( j < (unsigned int)strlen(rec)+1); j++) + + workstring[j-3] = rec[j]; + + // Remove trailing spaces before checking length + + i = (int)strlen(workstring); + i--; + + while(i > 1) + { + if (workstring[i] == ' ') + workstring[i] = 0; // Remove trailing spaces + else + break; + + i--; + } + + if (i > 29) + { + Consoleprintf("Port description too long - Truncated"); + Consoleprintf("%s\r\n",rec); + } + strcat(workstring," "); + workstring[30] = '\0'; + + memcpy(xxp.ID, workstring, 30); + return(1); +} + +int dodll(int i, char value[], char rec[]) +{ + unsigned int j; + + strlop(rec, ' '); + for (j = 8;( j < (unsigned int)strlen(rec)+1); j++) + workstring[j-8] = rec[j]; + + if (j > 24) + { + Consoleprintf("DLL name too long - Truncated"); + Consoleprintf("%s\r\n",rec); + + } + + _strupr(workstring); + strcat(workstring," "); + + memcpy(xxp.DLLNAME, workstring, 16); + xxp.TYPE = 16; // External + + if (strstr(xxp.DLLNAME, "TELNET") || strstr(xxp.DLLNAME, "AXIP")) + RFOnly = FALSE; + + return(1); +} + +int doDriver(int i, char * value, char * rec) +{ + unsigned int j; + for (j = 7;( j < (unsigned int)strlen(rec)+1); j++) + workstring[j-7] = rec[j]; + + if (j > 23) + { + Consoleprintf("Driver name too long - Truncated"); + Consoleprintf("%s\r\n",rec); + } + + _strupr(workstring); + strcat(workstring," "); + + memcpy(xxp.DLLNAME, workstring, 16); + xxp.TYPE = 16; // External + + // Set some defaults in case HFKISS + + xxp.CHANNEL = 'A'; + xxp.FRACK = 7000; + xxp.RESPTIME = 1000; + xxp.MAXFRAME = 4; + xxp.RETRIES = 6; + + if (strstr(xxp.DLLNAME, "TELNET") || strstr(xxp.DLLNAME, "AXIP")) + RFOnly = FALSE; + + return 1; +} +int IsNumeric(char *str) +{ + while(*str) + { + if(!isdigit(*str)) + return 0; + str++; + } + + return 1; +} + + +int doSerialPortName(int i, char * value, char * rec) +{ + rec += 8; + + if (strlen(rec) > 250) + { + Consoleprintf("Serial Port Name too long - Truncated"); + Consoleprintf("%s\r\n",rec); + rec[250] = 0; + } + + strlop(rec, ' '); + + if (IsNumeric(rec)) + xxp.IOADDR = atoi(rec); + else + xxp.SerialPortName = _strdup(rec); + + return 1; +} + +int doPermittedAppls(int i, char * value, char * rec) +{ + unsigned int Mask = 0; + char * Context; + char * ptr1 = strtok_s(value, " ,=\t\n\r", &Context); + + // Param is a comma separated list of Appl Numbers allowed to connect on this port + + while (ptr1 && ptr1[0]) + { + Mask |= 1 << (atoi(ptr1) - 1); + ptr1 = strtok_s(NULL, " ,=\t\n\r", &Context); + } + + xxp.HavePermittedAppls = 1; // indicate used + xxp.PERMITTEDAPPLS = Mask; + + return 1; +} +int doKissCommand(int i, char * value, char * rec) +{ + // Param is kiss command and any operands as decimal bytes + + xxp.KissParams = _strdup(strlop(rec, '=')); + return 1; +} + + +int hwtypes(int i, char value[], char rec[]) +{ + hw = 255; + if (_stricmp(value,"ASYNC") == 0) + { + // Set some defaults + + xxp.CHANNEL = 'A'; + xxp.FRACK = 7000; + xxp.RESPTIME = 1000; + xxp.MAXFRAME = 4; + xxp.RETRIES = 6; + hw = 0; + } + + if (_stricmp(value,"PC120") == 0) + hw = 2; + if (_stricmp(value,"DRSI") == 0) + hw = 4; + if (_stricmp(value,"DE56") == 0) + hw = 4; + if (_stricmp(value,"TOSH") == 0) + hw = 6; + if (_stricmp(value,"QUAD") == 0) + hw = 8; + if (_stricmp(value,"RLC100") == 0) + hw = 10; + if (_stricmp(value,"RLC400") == 0) + hw = 12; + if (_stricmp(value,"INTERNAL") == 0 || _stricmp(value,"LOOPBACK") == 0) + { + // Set Sensible defaults + + memset(xxp.ID, ' ', 30); + memcpy(xxp.ID, "Loopback", 8); + xxp.CHANNEL = 'A'; + xxp.FRACK = 5000; + xxp.RESPTIME = 1000; + xxp.MAXFRAME = 4; + xxp.RETRIES = 5; + xxp.DIGIFLAG = 1; + hw = 14; + } + if (_stricmp(value,"EXTERNAL") == 0) + { + hw = 16; + + // Set some defaults in case KISSHF + + xxp.CHANNEL = 'A'; + xxp.FRACK = 7000; + xxp.RESPTIME = 1000; + xxp.MAXFRAME = 4; + xxp.RETRIES = 6; + } + + if (_stricmp(value,"BAYCOM") == 0) + hw = 18; + if (_stricmp(value,"PA0HZP") == 0) + hw = 20; + if (_stricmp(value,"I2C") == 0) + { + // Set some defaults + + xxp.CHANNEL = 'A'; + xxp.FRACK = 7000; + xxp.RESPTIME = 1000; + xxp.MAXFRAME = 4; + xxp.RETRIES = 6; + hw = 22; + } + + if (hw == 255) + { + Consoleprintf("Invalid Hardware Type (not DRSI PC120 INTERNAL EXTERNAL BAYCOM PA0HZP ASYNC QUAD)"); + Consoleprintf("%s\r\n", rec); + return (0); + } + else + xxp.TYPE = hw; + + return(1); +} +int protocols(int i, char value[], char rec[]) +{ + int hw; + + hw = 255; + if (_stricmp(value,"KISS") == 0) + hw = 0; + if (_stricmp(value,"NETROM") == 0) + hw = 2; + if (_stricmp(value,"BPQKISS") == 0) + hw = 4; + if (_stricmp(value,"HDLC") == 0) + hw = 6; + if (_stricmp(value,"L2") == 0) + hw = 8; + if (_stricmp(value,"PACTOR") == 0) + hw = 10; + if (_stricmp(value,"WINMOR") == 0) + hw = 10; + if (_stricmp(value,"ARQ") == 0) + hw = 12; + + if (hw == 255) + { + Consoleprintf("Invalid Protocol (not KISS NETROM PACTOR WINMOR ARQ HDLC )"); + Consoleprintf("%s\r\n", rec); + return (0); + } + else + xxp.PROTOCOL = hw; + return(1); +} + + +int bbsflag(int i, char value[],char rec[]) +{ + int hw=255; + + if (_stricmp(value,"NOBBS") == 0) + hw = 1; + if (_stricmp(value,"BBSOK") == 0) + hw = 0; + if (_stricmp(value,"") == 0) + hw = 0; + + if (hw==255) + { + Consoleprintf("BBS Flag must be NOBBS, BBSOK, or null"); + Consoleprintf("%s\r\n",rec); + return(0); + } + + xxp.BBSFLAG = hw; + + return(1); +} + +int channel(int i, char * value, char * rec) +{ + char * val = (char *)poffset[i]; + val[0] = value[0]; + return 1; +} + +int dolinked(int i, char * value, char * rec) +{ + char * val = (char *)offset[i]; + val[0] = value[0]; + return 1; +} + +int validcalls(int i, char * value, char * rec) +{ + if ((strlen(value) + (int)strlen(xxp.VALIDCALLS)) > 255) + { + Consoleprintf("Too Many VALIDCALLS"); + Consoleprintf("%s\r\n", rec); + return(0); + } + + strcat(xxp.VALIDCALLS, value); + return(1); +} + + +int kissoptions(int i, char value[], char rec[]) +{ + int err=255; + + char opt1[12] = ""; + char opt2[12] = ""; + char opt3[12] = ""; + char opt4[12] = ""; + char opt5[12] = ""; + char opt6[12] = ""; + char opt7[12] = ""; + char opt8[12] = ""; + + + + sscanf(value,"%[^,+],%[^,+],%[^,+],%[^,+],%[^,+],%[^,+],%[^,+],%[^,+]", + opt1,opt2,opt3,opt4,opt5,opt6,opt6,opt8); + + if (opt1[0] != '\0') {do_kiss(opt1,rec);} + if (opt2[0] != '\0') {do_kiss(opt2,rec);} + if (opt3[0] != '\0') {do_kiss(opt3,rec);} + if (opt4[0] != '\0') {do_kiss(opt4,rec);} + if (opt5[0] != '\0') {do_kiss(opt5,rec);} + if (opt6[0] != '\0') {do_kiss(opt6,rec);} + if (opt7[0] != '\0') {do_kiss(opt7,rec);} + if (opt8[0] != '\0') {do_kiss(opt8,rec);} + + return(1); +} + + + +/* + TNC PORT PROCESSING +*/ +static char *tkeywords[] = +{ +"COM", "TYPE", "APPLMASK", "KISSMASK", "APPLFLAGS", "ENDPORT" +}; /* parameter keywords */ + +static int toffset[] = +{ +0, 1, 2, 3, 5, 8 +}; /* offset for corresponding data in config file */ + +static int troutine[] = +{ +1, 5, 1, 3, 1, 9 +}; /* routine to process parameter */ + +#define TPARAMLIM 6 + + +typedef struct _TCMDX +{ + char String[12]; // COMMAND STRING + UCHAR CMDLEN; // SIGNIFICANT LENGTH + VOID (* CMDPROC)(struct TNCDATA * TNC, char * Tail, struct _TCMDX * CMD);// COMMAND PROCESSOR + size_t CMDFLAG; // FLAG/VALUE Offset + +} TCMDX; + + + +extern TCMDX TNCCOMMANDLIST[]; +extern int NUMBEROFTNCCOMMANDS; + +int decode_tnc_rec(char * rec) +{ + char key_word[20]; + char value[300]; + + if (xindex(rec,"=") >= 0) + sscanf(rec,"%[^=]=%s",key_word,value); + else + sscanf(rec,"%s",key_word); + + if (_stricmp(key_word, "ENDPORT") == 0) + { + endport=1; + return 0; + } + else if (_stricmp(key_word, "TYPE") == 0) + { + if (_stricmp(value, "TNC2") == 0) + { + TNC2ENTRY->Mode = TNC2; + + // Set Defaults + + TNC2ENTRY->SENDPAC = 13; + TNC2ENTRY->CRFLAG = 1; + TNC2ENTRY->MTX = 1; + TNC2ENTRY->MCOM = 1; + TNC2ENTRY->MMASK = -1; // MONITOR MASK FOR PORTS + + TNC2ENTRY->COMCHAR = 3; + TNC2ENTRY->CMDTIME = 10; // SYSTEM TIMER = 100MS + TNC2ENTRY->PASSCHAR = 0x16; // CTRL-V + TNC2ENTRY->StreamSW = 0x7C; // | + TNC2ENTRY->LCStream = 1; + } + else if (_stricmp(value, "DED") == 0) + TNC2ENTRY->Mode = DED; + else if (_stricmp(value, "KANT") == 0) + TNC2ENTRY->Mode = KANTRONICS; + else if (_stricmp(value, "SCS") == 0) + TNC2ENTRY->Mode = SCS; + else + { + Consoleprintf("Invalid TNC Type"); + Consoleprintf("%s\r\n",rec); + } + } + else if (_stricmp(key_word, "COMPORT") == 0) + strcpy(TNC2ENTRY->PORTNAME, value); + else if (_stricmp(key_word, "APPLMASK") == 0) + TNC2ENTRY->APPLICATION = strtol(value, 0, 0); + else if (_stricmp(key_word, "APPLNUM") == 0) + TNC2ENTRY->APPLICATION = 1 << (strtol(value, 0, 0) - 1); + else if (_stricmp(key_word, "APPLFLAGS") == 0) + TNC2ENTRY->APPLFLAGS = strtol(value, 0, 0); + else if (_stricmp(key_word, "CHANNELS") == 0) + TNC2ENTRY->HOSTSTREAMS = strtol(value, 0, 0); + else if (_stricmp(key_word, "STREAMS") == 0) + TNC2ENTRY->HOSTSTREAMS = strtol(value, 0, 0); + else if (_stricmp(key_word, "POLLDELAY") == 0) + TNC2ENTRY->PollDelay = strtol(value, 0, 0); + else if (_stricmp(key_word, "CONOK") == 0) + TNC2ENTRY->CONOK = strtol(value, 0, 0); + else if (_stricmp(key_word, "AUTOLF") == 0) + TNC2ENTRY->AUTOLF = strtol(value, 0, 0); + else if (_stricmp(key_word, "ECHO") == 0) + TNC2ENTRY->ECHOFLAG = (char)strtol(value, 0, 0); + else + { + if (TNC2ENTRY->Mode == TNC2) + { + // Try process as TNC2 Command + + int n = 0; + TCMDX * CMD = &TNCCOMMANDLIST[0]; + char * ptr1 = key_word; + UCHAR * valueptr; + + strcat(key_word, " "); + + _strupr(key_word); + + for (n = 0; n < NUMBEROFTNCCOMMANDS; n++) + { + int CL = CMD->CMDLEN; + + // ptr1 is input command + + ptr1 = key_word; + + 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) == ' ') + { + valueptr = (UCHAR *)TNC2ENTRY + CMD->CMDFLAG; + *valueptr = (UCHAR)strtol(value, 0, 0); + return 0; + } + } + CMD++; + } + } + + Consoleprintf("Source record not recognised - Ignored:%s\r\n",rec); + } + return 0; +} + + +int do_kiss (char * value,char * rec) +{ + int err=255; + + if (_stricmp(value,"POLLED") == 0) + { + err=0; + kissflags=kissflags | POLLINGKISS; + } + else if (_stricmp(value,"CHECKSUM") == 0) + { + err=0; + kissflags=kissflags | CHECKSUM; + } + else if (_stricmp(value,"D700") == 0) + { + err=0; + kissflags=kissflags | D700; + } + else if (_stricmp(value,"TNCX") == 0) + { + err=0; + kissflags=kissflags | TNCX; + } + else if (_stricmp(value,"PITNC") == 0) + { + err=0; + kissflags=kissflags | PITNC; + } + else if (_stricmp(value,"TRACKER") == 0) + { + err=0; + kissflags |= TRACKER; + } + else if (_stricmp(value,"NOPARAMS") == 0) + { + err=0; + kissflags=kissflags | NOPARAMS; + } + else if (_stricmp(value,"ACKMODE") == 0) + { + err=0; + kissflags=kissflags | ACKMODE; + } + else if (_stricmp(value,"SLAVE") == 0) + { + err=0; + kissflags=kissflags | POLLEDKISS; + } + else if (_stricmp(value,"FLDIGI") == 0) + { + err=0; + kissflags |= FLDIGI; + } + else if (_stricmp(value,"FASTI2C") == 0) + { + err=0; + kissflags |= FASTI2C; + } + + else if (_stricmp(value,"DRATS") == 0) + { + err=0; + kissflags |= DRATS; + } + + if (err == 255) + { + Consoleprintf("Invalid KISS Options (not POLLED ACKMODE CHECKSUM D700 SLAVE TNCX PITNC NOPARAMS FASTI2C DRATS)"); + Consoleprintf("%s\r\n",rec); + } + return (err); +} + + +int simple(int i) +{ + // Set up the basic config header + + xxcfg.C_AUTOSAVE = 1; + xxcfg.C_SaveMH = 1; + xxcfg.C_BBS = 1; + xxcfg.C_BTINTERVAL = 60; + xxcfg.C_BUFFERS = 999; + xxcfg.C_C = 1; + xxcfg.C_DESQVIEW = 0; + xxcfg.C_EMSFLAG = 0; + xxcfg.C_FULLCTEXT = 1; + xxcfg.C_HIDENODES = 0; + xxcfg.C_HOSTINTERRUPT = 127; + xxcfg.C_IDINTERVAL = 10; + xxcfg.C_IDLETIME = 900; + xxcfg.C_IP = 0; + xxcfg.C_PM = 0; + xxcfg.C_L3TIMETOLIVE = 25; + xxcfg.C_L4DELAY = 10; + xxcfg.C_L4RETRIES = 3; + xxcfg.C_L4TIMEOUT = 60; + xxcfg.C_L4WINDOW = 4; + xxcfg.C_LINKEDFLAG = 'A'; + xxcfg.C_MAXCIRCUITS = 128; + xxcfg.C_MAXDESTS = 250; + xxcfg.C_MAXHOPS = 4; + xxcfg.C_MAXLINKS = 64; + xxcfg.C_MAXNEIGHBOURS = 64; + xxcfg.C_MAXRTT = 90; + xxcfg.C_MINQUAL = 150; + xxcfg.C_NODE = 1; + xxcfg.C_NODESINTERVAL = 30; + xxcfg.C_OBSINIT = 6; + xxcfg.C_OBSMIN = 5; + xxcfg.C_PACLEN = 236; + xxcfg.C_T3 = 180; + xxcfg.C_TRANSDELAY = 1; + + /* Set PARAMOK flags on all values that are defaulted */ + + for (i=0; i < PARAMLIM; i++) + paramok[i]=1; + + paramok[15] = 0; // Must have callsign + paramok[45] = 0; // Dont Have Appl1Call + paramok[53] = 0; // or APPL1ALIAS + + return(1); +} + +VOID FreeConfig() +{ +} + +BOOL ProcessAPPLDef(char * buf) +{ + // New Style APPL definition + + // APPL n,COMMAND,CMDALIAS,APPLCALL,APPLALIAS,APPLQUAL,L2ALIAS + + char * ptr1, * ptr2; + int Appl, n = 0; + char Param[8][256]; + struct APPLCONFIG * App; + + memset(Param, 0, 2048); + + ptr1 = buf; + + while (ptr1 && *ptr1 && n < 8) + { + ptr2 = strchr(ptr1, ','); + if (ptr2) *ptr2++ = 0; + + strcpy(&Param[n++][0], ptr1); + ptr1 = ptr2; + } + + if (_stricmp(Param[1], Param[2]) == 0) + { + // Alias = Application - will loop. + + return FALSE; + } + + _strupr(Param[0]); + _strupr(Param[1]); + + // Leave Alias in original case + + _strupr(Param[3]); + _strupr(Param[4]); + _strupr(Param[5]); + _strupr(Param[6]); + _strupr(Param[7]); + + + Appl = atoi(Param[0]); + + if (Appl < 1 || Appl > NumberofAppls) return FALSE; + + App = &xxcfg.C_APPL[Appl - 1]; // Recs from zero + + if (Param[1][0] == 0) // No Application + return FALSE; + + if (strlen(Param[1]) > 12) return FALSE; + + memcpy(App->Command, Param[1], (int)strlen(Param[1])); + + xxcfg.C_BBS = 1; + + if (strlen(Param[2]) > 48) return FALSE; + + memcpy(App->CommandAlias, Param[2], (int)strlen(Param[2])); + + if (strlen(Param[3]) > 10) return FALSE; + + memcpy(App->ApplCall, Param[3], (int)strlen(Param[3])); + + if (strlen(Param[4]) > 10) return FALSE; + + memcpy(App->ApplAlias, Param[4], (int)strlen(Param[4])); + + App->ApplQual = atoi(Param[5]); + + if (strlen(Param[6]) > 10) return FALSE; + + memcpy(App->L2Alias, Param[6], (int)strlen(Param[6])); + + return TRUE; +} + +double xfmod(double p1, double p2) +{ + int temp; + + temp = (int)(p1/p2); + p1 = p1 -(p2 * temp); + return p1; +} + + BOOL ToLOC(double Lat, double Lon , char * Locator) + { + int i; + double S1, S2; + + Lon = Lon + 180; + Lat = Lat + 90; + + S1 = xfmod(Lon, 20); + + #pragma warning(push) + #pragma warning(disable : 4244) + + i = Lon / 20; + Locator[0] = 65 + i; + + S2 = xfmod(S1, 2); + + i = S1 / 2; + Locator[2] = 48 + i; + + i = S2 * 12; + Locator[4] = 65 + i; + + S1 = xfmod(Lat,10); + + i = Lat / 10; + Locator[1] = 65 + i; + + S2 = xfmod(S1,1); + + i = S1; + Locator[3] = 48 + i; + + i = S2 * 24; + Locator[5] = 65 + i; + + #pragma warning(pop) + + return TRUE; +} + +int FromLOC(char * Locator, double * pLat, double * pLon) +{ + double i; + double Lat, Lon; + + _strupr(Locator); + + *pLon = 0; + *pLat = 0; // in case invalid + + + // Basic validation for APRS positions + + // The first pair (a field) encodes with base 18 and the letters "A" to "R". + // The second pair (square) encodes with base 10 and the digits "0" to "9". + // The third pair (subsquare) encodes with base 24 and the letters "a" to "x". + + i = Locator[0]; + + if (i < 'A' || i > 'R') + return 0; + + Lon = (i - 65) * 20; + + i = Locator[2]; + if (i < '0' || i > '9') + return 0; + + Lon = Lon + (i - 48) * 2; + + i = Locator[4]; + if (i < 'A' || i > 'X') + return 0; + + Lon = Lon + (i - 65) / 12; + + i = Locator[1]; + if (i < 'A' || i > 'R') + return 0; + + Lat = (i - 65) * 10; + + i = Locator[3]; + if (i < '0' || i > '9') + return 0; + + Lat = Lat + (i - 48); + + i = Locator[5]; + if (i < 'A' || i > 'X') + return 0; + + Lat = Lat + (i - 65) / 24; + + if (Lon < 0 || Lon > 360) + Lon = 180; + if (Lat < 0 || Lat > 180) + Lat = 90; + + *pLon = Lon - 180; + *pLat = Lat - 90; + + return 1; +} diff --git a/.svn/pristine/05/05cb3c09ff4898bc77bda06f9cff0f0a53df0c80.svn-base b/.svn/pristine/05/05cb3c09ff4898bc77bda06f9cff0f0a53df0c80.svn-base new file mode 100644 index 0000000..7859400 --- /dev/null +++ b/.svn/pristine/05/05cb3c09ff4898bc77bda06f9cff0f0a53df0c80.svn-base @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.svn/pristine/06/06e4dc036e31dfb016e7d2147ca5564a44fa2e59.svn-base b/.svn/pristine/06/06e4dc036e31dfb016e7d2147ca5564a44fa2e59.svn-base new file mode 100644 index 0000000..da78480 --- /dev/null +++ b/.svn/pristine/06/06e4dc036e31dfb016e7d2147ca5564a44fa2e59.svn-base @@ -0,0 +1,223 @@ +/* LzmaDec.h -- LZMA Decoder + 2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZMADEC_H +#define __LZMADEC_H + +#include "types.h" + +/* #define _LZMA_PROB32 */ +/* _LZMA_PROB32 can increase the speed on some CPUs, + but memory usage for CLzmaDec::probs will be doubled in that case */ + +#ifdef _LZMA_PROB32 +#define CLzmaProb LZ_UInt32 +#else +#define CLzmaProb UInt16 +#endif + + +/* ---------- LZMA Properties ---------- */ + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaProps +{ + unsigned lc, lp, pb; + LZ_UInt32 dicSize; +} CLzmaProps; + +/* LzmaProps_Decode - decodes properties + Returns: + SZ_OK + SZ_ERROR_UNSUPPORTED - Unsupported properties + */ + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); + + +/* ---------- LZMA Decoder state ---------- */ + +/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. + Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ + +#define LZMA_REQUIRED_INPUT_MAX 20 + +typedef struct +{ + CLzmaProps prop; + CLzmaProb *probs; + Byte *dic; + const Byte *buf; + LZ_UInt32 range, code; + SizeT dicPos; + SizeT dicBufSize; + LZ_UInt32 processedPos; + LZ_UInt32 checkDicSize; + unsigned state; + LZ_UInt32 reps[4]; + unsigned remainLen; + int needFlush; + int needInitState; + LZ_UInt32 numProbs; + unsigned tempBufSize; + Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; +} CLzmaDec; + +#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } + +void LzmaDec_Init(CLzmaDec *p); + +/* There are two types of LZMA streams: + 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. + 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ + +typedef enum +{ + LZMA_FINISH_ANY, /* finish at any point */ + LZMA_FINISH_END /* block must be finished at the end */ +} ELzmaFinishMode; + +/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! + + You must use LZMA_FINISH_END, when you know that current output buffer + covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. + + If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, + and output value of destLen will be less than output buffer size limit. + You can check status result also. + + You can use multiple checks to test data integrity after full decompression: + 1) Check Result and "status" variable. + 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. + 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. + You must use correct finish mode in that case. */ + +typedef enum +{ + LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ + LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ + LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ + LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ +} ELzmaStatus; + +/* ELzmaStatus is used only as output value for function call */ + + +/* ---------- Interfaces ---------- */ + +/* There are 3 levels of interfaces: + 1) Dictionary Interface + 2) Buffer Interface + 3) One Call Interface + You can select any of these interfaces, but don't mix functions from different + groups for same object. */ + + +/* There are two variants to allocate state for Dictionary Interface: + 1) LzmaDec_Allocate / LzmaDec_Free + 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs + You can use variant 2, if you set dictionary buffer manually. + For Buffer Interface you must always use variant 1. + + LzmaDec_Allocate* can return: + SZ_OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + */ + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); + +SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); + +/* ---------- Dictionary Interface ---------- */ + +/* You can use it, if you want to eliminate the overhead for data copying from + dictionary to some other external buffer. + You must work with CLzmaDec variables directly in this interface. + + STEPS: + LzmaDec_Constr() + LzmaDec_Allocate() + for (each new stream) + { + LzmaDec_Init() + while (it needs more decompression) + { + LzmaDec_DecodeToDic() + use data from CLzmaDec::dic and update CLzmaDec::dicPos + } + } + LzmaDec_Free() + */ + +/* LzmaDec_DecodeToDic + + The decoding to internal dictionary buffer (CLzmaDec::dic). + You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! + + finishMode: + It has meaning only if the decoding reaches output limit (dicLimit). + LZMA_FINISH_ANY - Decode just dicLimit bytes. + LZMA_FINISH_END - Stream must be finished after dicLimit. + + Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + */ + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- Buffer Interface ---------- */ + +/* It's zlib-like interface. + See LzmaDec_DecodeToDic description for information about STEPS and return results, + but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need + to work with CLzmaDec variables manually. + + finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + */ + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- One Call Interface ---------- */ + +/* LzmaDecode + + finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + + Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). + */ + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, + const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status, ISzAlloc *alloc); + +#endif diff --git a/.svn/pristine/08/0811f3c6b3e6a6eafccc305825e33cd8c8621f75.svn-base b/.svn/pristine/08/0811f3c6b3e6a6eafccc305825e33cd8c8621f75.svn-base new file mode 100644 index 0000000..eb68488 --- /dev/null +++ b/.svn/pristine/08/0811f3c6b3e6a6eafccc305825e33cd8c8621f75.svn-base @@ -0,0 +1,4583 @@ +/* +Copyright 2001-2015 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 +*/ + +// Version 0.0.3.1 July 2016 +// Switch to Thunderforest tile server + +// Version 0.0.4.1 January 2019 +// Add option to set IS filter to map view automatically + +// Version 1.1.14.5 March 2020 +// Add option to run two instances of Linbpq and APRS + +// Version 1.1.14.6 Sept 2021 +// Use my Tile Servers + + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#define LINBPQ + +#include "compatbits.h" + +#include "BPQAPRS.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#define XK_MISCELLANY +#include + +#include +#include + +#include +#include + +#define LIBCONFIG_STATIC +#include "libconfig.h" + +#include +#include +#include + +#define PNG_SKIP_SETJMP_CHECK + +#include + +#define VOID void +#define UCHAR unsigned char +#define BOOL int +#define BYTE unsigned char +#define UINT unsigned int +#define TRUE 1 +#define FALSE 0 + + +#undef PNG_NO_STDIO + +int multiple = 0; + + +GtkWidget *dialog; +GtkWidget *window; +GtkWidget *dialog; +GtkWidget *window; +GtkWidget *box1; +GtkWidget *box2; +GtkWidget *box3; +GtkWidget *hbox; +GtkWidget *button; +GtkWidget *button2; +GtkWidget *checklabel; +GtkWidget *check1; +GtkWidget *check2; +GtkWidget *check3; +GtkWidget *check4; +GtkWidget *checkhbox; +GtkWidget *separator; +GtkWidget *table; +GtkWidget *vscrollbar; +GtkWidget *vscrollbar2; +GtkTextBuffer *text; +GtkTextBuffer *text2; +GtkWidget *entry; +GtkWidget *vpaned; +GtkWidget *frame1; +GtkWidget *frame2; +GtkWidget *view; +GtkWidget* scrolledwin; +GtkWidget *view2; +GtkWidget* scrolledwin2; +GtkWidget *box10; +GtkWidget *menubar; +GtkWidget *combo; +GtkWidget *label1, *label2; +GtkListStore *receiveditems; +GtkListStore *sentitems; + +GtkTreeModel *model; + +char MyFont[50] = "Monospace 10"; + +gchar *fontname; + +char RX_SOCK_PATH[] = "BPQAPRSrxsock"; +char TX_SOCK_PATH[] = "BPQAPRStxsock"; + +int sfd; +struct sockaddr_un my_addr, peer_addr; +socklen_t peer_addr_size; +int maxfd; + +struct SharedMem * SMEM; + +UCHAR * Shared; // Start of Shared Mememy +UCHAR * StnRecordBase; // Start of Station Records + +int AutoFilterTimer = 0; + +#define AUTOFILTERDELAY 20 // 20 secs + +VOID SecTimer(); +void plotLine(int x0, int y0, int x1, int y1, COLORREF rgb); +void SelectTXMsg (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data); +int LoadImageFile (void * hwnd, char * pstrPathName, + png_byte **ppbImage, int *pxImgSize, int *pyImgSize, + int *piChannels, png_color *pBkgColor); +BOOL PngLoadImage (char * pstrFileName, png_byte **ppbImageData, + png_uint_32 *piWidth, png_uint_32 *piHeight, int *piChannels, png_color *pBkgColor); + +BOOL RGBToJpegFile(char * fileName, BYTE *dataBuf, UINT widthPix, UINT height, BOOL color, int quality); +int XDestroyImage(XImage *ximage); + +int XLookupString(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, void *status_in_out); + +void RefreshTXList(); + +static png_color bkgColor = {127, 127, 127}; + +struct SEM +{ + UINT Flag; + int Clashes; + int Gets; + int Rels; +}; + + +struct SEM Semaphore = {0, 0, 0, 0}; + +void GetSemaphore(struct SEM * Semaphore) +{ + // + // Wait for it to be free + // + + if (Semaphore->Flag != 0) + { + Semaphore->Clashes++; + } + +loop1: + + while (Semaphore->Flag != 0) + { + Sleep(10); + } + + // try to get semaphore + + if (__sync_lock_test_and_set(&Semaphore->Flag, 1) != 0) + + // Failed to get it + goto loop1; // try again; + + //Ok. got it + + Semaphore->Gets++; + + return; +} + +void FreeSemaphore(struct SEM * Semaphore) +{ + if (Semaphore->Flag == 0) + printf("Free Semaphore Called when Sem not held\n"); + + Semaphore->Rels++; + Semaphore->Flag = 0; + + return; +} + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} + +unsigned long _beginthread(void(*start_address)(), unsigned stack_size, VOID * arglist) +{ + pthread_t thread; + + if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*) arglist) != 0) + perror("New Thread"); + else + pthread_detach(thread); + + return thread; +} + +int Sleep(int ms) +{ + usleep(ms * 1000); + return 0; +} + +struct OSMQUEUE OSMQueue = {NULL,0,0,0}; + +int OSMQueueCount = 0; + +static int cxWinSize = 788, cyWinSize = 788; +static int cxImgSize = 768, cyImgSize = 768; +static int topBorder = 30, bottomBorder = 0; +static int leftBorder = 2, rightBorder = 2; + +static int cImgChannels = 3; +static int ImgChannels; + +int Bytesperpixel = 4; + +int ExpireTime = 120; +int TrackExpireTime = 1440; +BOOL SuppressNullPosn = FALSE; +BOOL DefaultNoTracks = FALSE; +BOOL LocalTime = TRUE; +BOOL KM = FALSE; +BOOL AddViewToFilter = FALSE; + +char ISFilter[1000] = "m/50 u/APBPQ*"; + +int SlowTimer = 0; + +BOOL CreateJPEG = TRUE; +int JPEGInterval = 300; +int JPEGCounter = 0; +char JPEGFileName[MAX_PATH] = "BPQAPRS/HTML/APRSImage.jpg"; + +char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + +Display * display; +Window root, win; +GC gc; +XImage * image, * popupimage; + +int SetBaseX = 0; // Lowest Tiles in currently loaded set +int SetBaseY = 0; + +int TileX = 0; +int TileY = 0; + +int Zoom = 2; + +int MaxZoom = 16; + +int MapCentreX = 256; +int MapCentreY = 256; + +int MouseX, MouseY; +int PopupX, PopupY; + +double MouseLat, MouseLon; + +BOOL NeedRefresh = FALSE; +int NeedRedraw = 0; + +int ScrollX = 128; +int ScrollY = 128; + +int WindowX = 100, WindowY = 100; // Position of window on screen +int WindowWidth = 788; +int WindowHeight = 788; + +BOOL popupActive = FALSE; +BOOL selActive = FALSE; + +char OSMDir[256] = "BPQAPRS/OSMTiles"; + +struct STATIONRECORD ** StationRecords = NULL; +struct STATIONRECORD * ControlRecord; + +int StationCount; + + +UCHAR NextSeq = 1; + +char APRSCall[10]; +char LoppedAPRSCall[10]; +char BaseCall[10]; + + +// Image chunks are 256 rows of 3 * 256 bytes + +// Read 8 * 8 files, and copy to a 2048 * 3 * 2048 array. The display scrolls over this area, and +// it is refreshed when window approaches the edge of the array. + +int WIDTH; +int HEIGHT; + +int WIDTHTILES = 4; +int HEIGHTTILES = 4; + +UCHAR * Image = NULL; +UCHAR * iconImage = NULL; +UCHAR * PopupImage = NULL; + +BOOL ImageChanged = 0; + +int RetryCount = 7; +int RetryIntervals[] = {0, 512, 256, 128, 64, 32, 16, 8}; + +// Station Name Font + +const unsigned char ASCII[][5] = { +//const u08 ASCII[][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00} // 20 + ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 ! + ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 " + ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 # + ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $ + ,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 % + ,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 & + ,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 ' + ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 ( + ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 ) + ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a * + ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b + + ,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c , + ,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d - + ,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e . + ,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f / + ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0 + ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1 + ,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2 + ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3 + ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4 + ,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5 + ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6 + ,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7 + ,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8 + ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9 + ,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a : + ,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ; + ,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c < + ,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d = + ,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e > + ,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ? + ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @ + ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A + ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B + ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C + ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D + ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E + ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F + ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G + ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H + ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I + ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J + ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K + ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L + ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M + ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N + ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O + ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P + ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q + ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R + ,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S + ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T + ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U + ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V + ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W + ,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X + ,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y + ,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z + ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [ + ,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c + ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ] + ,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^ + ,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _ + ,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 ` + ,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a + ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b + ,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c + ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d + ,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e + ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f + ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g + ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h + ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i + ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j + ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k + ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l + ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m + ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n + ,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o + ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p + ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q + ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r + ,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s + ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t + ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u + ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v + ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w + ,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x + ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y + ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z + ,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b { + ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c | + ,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d } + ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ~ + ,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f DEL +}; + +COLORREF Colours[256] = {0, RGB(0,0,255), RGB(0,128,0), RGB(0,128,192), + RGB(0,192,0), RGB(0,192,255), RGB(0,255,0), RGB(128,0,128), + RGB(128,64,0), RGB(128,128,128), RGB(192,0,0), RGB(192,0,255), + RGB(192,64,128), RGB(192,128,255), RGB(255,0,0), RGB(255,0,255), // 81 + RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,128,0)}; + + + + +struct my_error_mgr { + struct jpeg_error_mgr pub; /* "public" fields */ + + jmp_buf setjmp_buffer; /* for return to caller */ +}; + +typedef struct my_error_mgr * my_error_ptr; + +void my_error_exit (j_common_ptr cinfo) +{ + /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ + my_error_ptr myerr = (my_error_ptr) cinfo->err; + + char buffer[JMSG_LENGTH_MAX]; + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + + /* Always display the message. */ + printf("JPEG Fatal Error"); + + + /* Return control to the setjmp point */ + longjmp(myerr->setjmp_buffer, 1); +} + +int memicmp(unsigned char *a, unsigned char *b, int n) +{ + if (n) + { + while (n && toupper(*a) == toupper(*b)) + n--, a++, b++; + + if (n) + return toupper(*a) - toupper(*b); + } + return 0; +} +int stricmp(const unsigned char * pStr1, const unsigned char *pStr2) +{ + unsigned char c1, c2; + int v; + + if (pStr1 == NULL) + { + return 1; + } + + + do { + c1 = *pStr1++; + c2 = *pStr2++; + /* The casts are necessary when pStr1 is shorter & char is signed */ + v = tolower(c1) - tolower(c2); + } while ((v == 0) && (c1 != '\0') && (c2 != '\0') ); + + return v; +} + +char * strupr(char* s) +{ + char* p = s; + + if (s == 0) + return 0; + + while (*p = toupper( *p )) p++; + return s; +} + +// Return coorinates in tiles. + +double long2x(double lon, int z) +{ + return (lon + 180.0) / 360.0 * pow(2.0, z); +} + +double lat2y(double lat, int z) +{ + return (1.0 - log( tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, z); +} + +double tilex2long(double x, int z) +{ + return x / pow(2.0, z) * 360.0 - 180; +} + +double tiley2lat(double y, int z) +{ + double n = M_PI - 2.0 * M_PI * y / pow(2.0, z); + return 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); +} + +void GetCornerLatLon(double * TLLat, double * TLLon, double * BRLat, double * BRLon) +{ + int X = ScrollX; + int Y = ScrollY; + + *TLLat = tiley2lat(SetBaseY + (Y / 256.0), Zoom); + *TLLon = tilex2long(SetBaseX + (X / 256.0), Zoom); + + X = ScrollX + cxWinSize; + Y = ScrollY + cyWinSize; + + *BRLat = tiley2lat(SetBaseY + (Y / 256.0), Zoom); + *BRLon = tilex2long(SetBaseX + (X / 256.0), Zoom); +} + + +void GetMouseLatLon(double * Lat, double * Lon) +{ + int X = ScrollX + MouseX; + int Y = ScrollY + MouseY; + + *Lat = tiley2lat(SetBaseY + (Y / 256.0), Zoom); + *Lon = tilex2long(SetBaseX + (X / 256.0), Zoom); +} + +BOOL GetLocPixels(double Lat, double Lon, int * X, int * Y) +{ + // Get the pixel offet of supplied location in current image. + + // If location is outside current image, return FAlSE + + int TileX; + int TileY; + int OffsetX, OffsetY; + double FX; + double FY; + + // if TileX or TileY are outside the window, return null + + FX = long2x(Lon, Zoom); + TileX = (int)floor(FX); + OffsetX = TileX - SetBaseX; + + if (OffsetX < 0 || OffsetX > 7) + return FALSE; + + FY = lat2y(Lat, Zoom); + TileY = (int)floor(FY); + OffsetY = TileY - SetBaseY; + + if (OffsetY < 0 || OffsetY > 7) + return FALSE; + + FX -= TileX; + FX = FX * 256.0; + + *X = (int)FX + 256 * OffsetX; + + FY -= TileY; + FY = FY * 256.0; + + *Y = (int)FY + 256 * OffsetY; + + return TRUE; +} + +int long2tilex(double lon, int z) +{ + return (int)(floor((lon + 180.0) / 360.0 * pow(2.0, z))); +} + +int lat2tiley(double lat, int z) +{ + return (int)(floor((1.0 - log( tan(lat * M_PI/180.0) + 1.0 / cos(lat * M_PI/180.0)) / M_PI) / 2.0 * pow(2.0, z))); +} + + +BOOL CentrePositionToMouse(double Lat, double Lon) +{ + // Positions specified location at the mouse + + int X, Y; + + SetBaseX = long2tilex(Lon, Zoom) - 2; + SetBaseY = lat2tiley(Lat, Zoom) - 2; // Set Location at middle + + if (GetLocPixels(Lat, Lon, &X, &Y) == FALSE) + return FALSE; // Off map + + ScrollX = X - cxWinSize/2; + ScrollY = Y - cyWinSize/2; + + +// Map is now centered at loc cursor was at + +// Need to move by distance mouse is from centre + + // if ScrollX, Y are zero, the centre of the map corresponds to 1024, 1024 + +// ScrollX -= 1024 - X; // Posn to centre +// ScrollY -= 1024 - Y; + + ScrollX += cxWinSize/2 - MouseX; + ScrollY += cyWinSize/2 - MouseY; + + // May Need to move image + + while(ScrollX < 0) + { + SetBaseX--; + ScrollX += 256; + } + + while(ScrollY < 0) + { + SetBaseY--; + ScrollY += 256; + } + + while(ScrollX > 255) + { + SetBaseX++; + ScrollX -= 256; + } + + while(ScrollY > 255) + { + SetBaseY++; + ScrollY -= 256; + } + + AutoFilterTimer = AUTOFILTERDELAY; // Update filter if no change for 30 secs + + return TRUE; +} + +SOCKADDR_IN destaddr1 = {0}; +SOCKADDR_IN destaddr2 = {0}; + +unsigned int ipaddr = 0; + +//char Host[] = "tile.openstreetmap.org"; + +//char Host[] = "oatile1.mqcdn.com"; //SAT +//char Host[] = "otile1.mqcdn.com"; + +//char Host[] = "tile.thunderforest.com"; + +char Host[] = "server.g8bpq.net"; +char Host1[] = "server1.g8bpq.net"; +char Host2[] = "server2.g8bpq.net"; + +int Host1Down = 0; +int Host2Down = 0; + +char mapStyle[64] = "outdoors"; //"neighbourhood mobile-atlas + + +char HeaderTemplate[] = "Accept: */*\r\nHost: %s\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n"; + + +VOID ResolveThread() +{ + struct hostent * HostEnt; + int err; + +// while (TRUE) + { + // Resolve Name if needed + + HostEnt = gethostbyname(Host1); + + if (!HostEnt) + { + err = WSAGetLastError(); + printf("Resolve Failed for %s %d %x\n", Host1, err); + } + else + { + memcpy(&destaddr1.sin_addr.s_addr,HostEnt->h_addr,4); + } + + HostEnt = gethostbyname(Host2); + + if (!HostEnt) + { + err = WSAGetLastError(); + printf("Resolve Failed for %s %d %x\n", Host2, err); + } + else + { + memcpy(&destaddr2.sin_addr.s_addr,HostEnt->h_addr,4); + } +/// Sleep(60 * 15 * 1000); + } +} + + + +VOID OSMGet(int x, int y, int zoom) +{ + struct OSMQUEUE * OSMRec = malloc(sizeof(struct OSMQUEUE)); + + GetSemaphore(&Semaphore); + + OSMQueueCount++; + + OSMRec->Next = OSMQueue.Next; + OSMQueue.Next = OSMRec; + OSMRec->x = x; + OSMRec->y = y; + OSMRec->Zoom = zoom; + + FreeSemaphore(&Semaphore); +} + +VOID RefreshTile(char * FN, int TileZoom, int Tilex, int Tiley); + +VOID OSMThread() +{ + // Request a page from OSM + + char FN[256]; + char Tile[80]; + struct OSMQUEUE * OSMRec; + int Zoom, x, y; + + SOCKET sock; + SOCKADDR_IN sinx; + int addrlen=sizeof(sinx); + int err; + u_long param=1; + BOOL bcopt=TRUE; + char Request[100]; + char Header[256]; + UCHAR Buffer[200000]; + int Len, InputLen = 0; + UCHAR * ptr; + int inptr = 0; + struct stat STAT; + FILE * Handle; + + destaddr1.sin_family = AF_INET; + destaddr1.sin_port = htons(7381); + destaddr2.sin_family = AF_INET; + destaddr2.sin_port = htons(7381); + + while (TRUE) + { + while (OSMQueue.Next) + { + GetSemaphore(&Semaphore); + + OSMRec = OSMQueue.Next; + OSMQueue.Next = OSMRec->Next; + + OSMQueueCount--; + + FreeSemaphore(&Semaphore); + + x = OSMRec->x; + y = OSMRec->y; + Zoom = OSMRec->Zoom; + + free(OSMRec); + +// wsprintf(Tile, "/%02d/%d/%d.png", Zoom, x, y); +// wsprintf(Tile, "/tiles/1.0.0/sat/%02d/%d/%d.jpg", Zoom, x, y); +// sprintf(Tile, "/tiles/1.0.0/osm/%02d/%d/%d.jpg", Zoom, x, y); + +// sprintf(Tile, "/%s/%d/%d/%d.png?apikey=41ab899ed1fd4d09b11da7caf3a48e1f", mapStyle, Zoom, x, y); + + sprintf(Tile, "/styles/klokantech-basic/%d/%d/%d.png", Zoom, x, y); + + sprintf(FN, "%s/%02d/%d/%d.png", OSMDir, Zoom, x, y); + + if (stat(FN, &STAT) == 0) + { + printf(" File %s Exists - skipping\n", FN); + continue; + } + + printf("Getting %s\n", FN); + + Len = sprintf(Request, "GET %s HTTP/1.0\r\n", Tile); + + // Allocate a Socket entry + + sock=socket(AF_INET,SOCK_STREAM,0); + + if (sock == INVALID_SOCKET) + return; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + if (Host1Down == 0) + { + if (connect(sock,(LPSOCKADDR) &destaddr1, sizeof(destaddr1)) != 0) + { + printf("OSM GET Connect to %s Failed %d\n", Host1, errno); + Host1Down = 600; // Don't try again for 10 mins + } + else + goto ConnectOK; + } + if (Host2Down == 0) + { + if (connect(sock,(LPSOCKADDR) &destaddr2, sizeof(destaddr2)) != 0) + { + printf("OSM GET Connect to %s Failed %d\n", Host2, errno); + Host1Down = 600; // Don't try again for 10 mins + } + else + goto ConnectOK; + } + + // + // Neither available or connect failed to both + // + + // Reduce retry timers + + if (Host1Down > 60 && Host2Down > 60) + { + Host1Down = 60; + Host2Down = 60; + } + + break; + +ConnectOK: + +//GET /15/15810/9778.png HTTP/1.0 +//Accept: */* +//Host: tile.openstreetmap.org +//Connection: close +//Content-Length: 0 +//User-Agent: APRSIS32(G8BPQ) + + InputLen = 0; + inptr = 0; + + send(sock, Request, Len, 0); + sprintf(Header, HeaderTemplate, Host); + send(sock, Header, strlen(Header), 0); + + while (InputLen != -1) + { + InputLen = recv(sock, &Buffer[inptr], 200000 - inptr, 0); + + if (InputLen > 0) + inptr += InputLen; + else + { + // File Complete?? + + if (strstr(Buffer, " 200 OK")) + { + ptr = strstr(Buffer, "Content-Length:"); + + if (ptr) + { + int FileLen = atoi(ptr + 15); + ptr = strstr(Buffer, "\r\n\r\n"); + + if (ptr) + { + ptr += 4; + char Dir[256]; + + if (FileLen == inptr - (ptr - Buffer)) + { + // File is OK + + int cnt; + + Handle = fopen(FN, "wb"); + + if (Handle) + { + fwrite(ptr, 1, FileLen, Handle); + fclose(Handle); + printf("Tile %s Loaded\n", FN); + RefreshTile(FN, Zoom, x, y); + break; + } + + if (errno != 2) // Bad Path + { + printf("Create %s failed %d\n", FN, errno); + perror("fopen"); + break; + } + + sprintf(Dir, "%s/%02d/%d", OSMDir, Zoom, x); + + if (mkdir(Dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) + { + printf("Error Creating %s\n", FN); + perror("mkdir"); + break; + } + + // Retry Create + + Handle = fopen(FN, "wb"); + + if (Handle) + { + fwrite(ptr, 1, FileLen, Handle); + fclose(Handle); + + printf("Tile %s Loaded\n", FN); + RefreshTile(FN, Zoom, x, y); + break; + } + + printf("Create %s falled\n", FN); + perror("fopen"); + break; + } + } + } + } + printf("OSM GET Bad Response %s ", Buffer); + sprintf(FN, "%s/DummyTile.jpg", OSMDir); + RefreshTile(FN, Zoom, x, y); + + break; + } + } + close(sock); + } + + // Queue is empty + + sleep(1); +} +} + +double radians(double Degrees) +{ + return M_PI * Degrees / 180; +} +double degrees(double Radians) +{ + return Radians * 180 / M_PI; +} + + + +double Distance(double laa, double loa) +{ + double lah = ControlRecord->Lat; + double loh = ControlRecord->Lon; + double dist; +/* + +'Great Circle Calculations. + +'dif = longitute home - longitute away + + +' (this should be within -180 to +180 degrees) +' (Hint: This number should be non-zero, programs should check for +' this and make dif=0.0001 as a minimum) +'lah = latitude of home +'laa = latitude of away + +'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif)) +'distance = dis / 180 * pi * ERAD +'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis))) + +'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians +*/ + + loh = radians(loh); lah = radians(lah); + loa = radians(loa); laa = radians(laa); + + dist = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945; + + if (KM) + dist *= 1.60934; + + return dist; +} + +double Bearing(double lat2, double lon2) +{ + double lat1 = ControlRecord->Lat; + double lon1 = ControlRecord->Lon; + double dlat, dlon, TC1; + + lat1 = radians(lat1); + lat2 = radians(lat2); + lon1 = radians(lon1); + lon2 = radians(lon2); + + dlat = lat2 - lat1; + dlon = lon2 - lon1; + + if (dlat == 0 || dlon == 0) return 0; + + TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2))); + TC1 = degrees(TC1); + + if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270; + + if (dlat > 0) + { + if (dlon > 0) return -TC1; + if (dlon < 0) return 360 - TC1; + return 0; + } + + if (dlat < 0) + { + if (dlon > 0) return TC1 = 180 - TC1; + if (dlon < 0) return TC1 = 180 - TC1; // 'ok? + return 180; + } + + return 0; + +} + + + +VOID DecodeWXReport(struct APRSConnectionInfo * sockptr, char * WX) +{ + UCHAR * ptr = strchr(WX, '_'); + char Type; + int Val; + + if (ptr == 0) + return; + + sockptr->WindDirn = atoi(++ptr); + ptr += 4; + sockptr->WindSpeed = atoi(ptr); + ptr += 3; +WXLoop: + + Type = *(ptr++); + + if (*ptr =='.') // Missing Value + { + while (*ptr == '.') + ptr++; + + goto WXLoop; + } + + Val = atoi(ptr); + + switch (Type) + { + case 'c': // = wind direction (in degrees). + + sockptr->WindDirn = Val; + break; + + case 's': // = sustained one-minute wind speed (in mph). + + sockptr->WindSpeed = Val; + break; + + case 'g': // = gust (peak wind speed in mph in the last 5 minutes). + + sockptr->WindGust = Val; + break; + + case 't': // = temperature (in degrees Fahrenheit). Temperatures below zero are expressed as -01 to -99. + + sockptr->Temp = Val; + break; + + case 'r': // = rainfall (in hundredths of an inch) in the last hour. + + sockptr->RainLastHour = Val; + break; + + case 'p': // = rainfall (in hundredths of an inch) in the last 24 hours. + + sockptr->RainLastDay = Val; + break; + + case 'P': // = rainfall (in hundredths of an inch) since midnight. + + sockptr->RainToday = Val; + break; + + case 'h': // = humidity (in %. 00 = 100%). + + sockptr->Humidity = Val; + break; + + case 'b': // = barometric pressure (in tenths of millibars/tenths of hPascal). + + sockptr->Pressure = Val; + break; + + default: + + return; + } + while(isdigit(*ptr)) + { + ptr++; + } + + if (*ptr != ' ') + goto WXLoop; +} + +struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFount) +{ + int i = 0; + struct STATIONRECORD * find; + struct STATIONRECORD * ptr; + struct STATIONRECORD * last = NULL; + int sum = 0; + + if (StationRecords == 0) + return FALSE; + + if (strlen(Call) > 9) + Call[9] = 0; + + find = *StationRecords; + while(find) + { + if (strlen(find->Callsign) > 9) + find->Callsign[9] = 0; + + if (strcmp(find->Callsign, Call) == 0) + return find; + + last = find; + find = find->Next; + i++; + } + + // Not found - add on end + +/* + if (AddIfNotFount) + { + // Get first from station record pool + + ptr = StationRecordPool; + + if (ptr) + { + StationRecordPool = ptr->Next; // Unchain + StationCount++; + } + else + { + // Get First from Stations + + ptr = *StationRecords; + if (ptr) + *StationRecords = ptr->Next; + } + + if (ptr == NULL) return NULL; + + memset(ptr, 0, sizeof(struct STATIONRECORD)); + +// EnterCriticalSection(&Crit); + + if (*StationRecords == NULL) + *StationRecords = ptr; + else + last->Next = ptr; + +// LeaveCriticalSection(&Crit); + + // Debugprintf("APRS Add Stn %s Station Count = %d", Call, StationCount); + + strcpy(ptr->Callsign, Call); + ptr->TimeAdded = time(NULL); + ptr->Index = i; + ptr->NoTracks = DefaultNoTracks; + + for (i = 0; i < 9; i++) + sum += Call[i]; + + sum %= 20; + + ptr->TrackColour = sum; + ptr->Moved = TRUE; + + return ptr; + } + else + */ + return NULL; +} + +int PopupHeight; +int PopupWidth; +int PopupLeft; +int PopupTop; +struct STATIONRECORD * popupStn; +struct STATIONRECORD * List[1000] = {0}; + + +VOID CreateStationPopup(struct STATIONRECORD * ptr, int RelX, int RelY) +{ + char Msg[80]; + int Len = 130; + int Line = 12; + struct tm * TM; + int x, y; + + PopupLeft = RelX - 10; + PopupTop = RelY - 30; + + if (PopupLeft + 400 > cxWinSize) + PopupLeft = cxWinSize - 405; + + if (PopupTop + 150> cyWinSize) + PopupTop= cyWinSize - 165; + + popupActive = TRUE; + popupStn = ptr; + PopupHeight = 200; + PopupWidth = 350; + + XClearArea(display, win, PopupLeft, PopupTop, 350, 200, FALSE); + XDrawRectangle(display, win, gc, PopupLeft, PopupTop, 350, 200); + + x = PopupLeft; + y = PopupTop; + + if (LocalTime) + TM = localtime(&ptr->TimeLastUpdated); + else + TM = gmtime(&ptr->TimeLastUpdated); + + Len = sprintf(Msg, "Last Heard: %.2d:%.2d:%.2d on Port %d", + TM->tm_hour, TM->tm_min, TM->tm_sec, ptr->LastPort); + + XDrawImageString(display, win, gc, x + 2, y + Line, ptr->Callsign, strlen(ptr->Callsign)); + Line += 12; + + XDrawImageString(display, win, gc, x + 2, y + Line, ptr->Path, strlen(ptr->Path)); + Line += 12; + +// XDrawImageString(display, win, gc, x + 2, y + Line, ptr->Status, 40); +// Line += 12; + + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + +// Item.pszText = ptr->LastPacket; + + Len = sprintf(Msg, "Distance %6.1f Bearing %3.0f Course %1.0f Speed %3.1f", + Distance(ptr->Lat, ptr->Lon), + Bearing(ptr->Lat, ptr->Lon), ptr->Course, ptr->Speed); + + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + + if (ptr->LastWXPacket[0]) + { + //display wx info + + struct APRSConnectionInfo temp; + + memset(&temp, 0, sizeof(temp)); + + DecodeWXReport(&temp, ptr->LastWXPacket); + + Len = sprintf(Msg, "Wind Speed %d MPH", temp.WindSpeed); + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + Len = sprintf(Msg, "Wind Gust %d MPH", temp.WindGust); + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + Len = sprintf(Msg, "Wind Direction %d°", temp.WindDirn); + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + Len = sprintf(Msg, "Temperature %d°F", temp.Temp); + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + Len = sprintf(Msg, "Pressure %05.1f", temp.Pressure /10.0); + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + + Len = sprintf(Msg, "Humidity %d%%", temp.Humidity); + XDrawImageString(display, win, gc, x + 2, y + Line, Msg, Len); + Line += 12; + } + +/* +Rain last hour##RAIN_HOUR_IN##" +Rain today##RAIN_TODAY_IN##" +Rain last 24 hours##RAIN_24_IN##" + +*/ +} + +VOID GetStationFromList(int MouseX, int MouseY) +{ + int RelX = MouseX + leftBorder; + int RelY = MouseY + topBorder; + + int index = (RelY - PopupTop) /12; + + if (List[index]) + { + selActive = FALSE; + CreateStationPopup(List[index], RelX, RelY); + } +} + +VOID FindStationsByPixel(int MouseX, int MouseY) +{ + int j=0; + struct STATIONRECORD * ptr = *StationRecords; + int RelX = MouseX - ScrollX + leftBorder; + int RelY = MouseY - ScrollY + topBorder; + + if (popupActive || selActive) + { + // if mouse within popup, leave alone + + if (RelX > PopupLeft && RelX < (PopupLeft + PopupWidth) && + RelY > PopupTop && RelY < (PopupTop + PopupHeight)) + return; + } + + while(ptr && j < 999) + { + if (abs((ptr->DispX - MouseX)) < 4 && abs((ptr->DispY - MouseY)) < 4) + List[j++] = ptr; + + ptr = ptr->Next; + } + + if (j == 0) + { + if (popupActive) + { + XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); + popupActive = 0; + } + + if (selActive) + { + XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); + selActive = 0; + } + return; + } + + // If only one, display info popup, else display selection popup + + if (popupActive || selActive) + return; // Already on display + + if (j == 1) + { + CreateStationPopup(List[0], RelX, RelY); + } + else + { + char Msg[80]; + int Line = 12; + int i; + + PopupLeft = RelX - 10; + PopupTop = RelY - 30; + + if (j > 20) + j = 20; + + PopupHeight = j * 12 + 4; + PopupWidth = 80; + + if (PopupLeft + 80 > cxWinSize) + PopupLeft = cxWinSize - 85; + + if (PopupTop + PopupHeight > cyWinSize) + PopupTop = cyWinSize - PopupHeight; + + selActive = TRUE; + + XClearArea(display, win, PopupLeft, PopupTop, 80, PopupHeight, FALSE); + XDrawRectangle(display, win, gc, PopupLeft, PopupTop, 80, PopupHeight); + + for (i = 0; i < j; i++) + { + memset(Msg, ' ', 12); + memcpy(Msg, List[i]->Callsign, strlen(List[i]->Callsign)); + XDrawImageString(display, win, gc, PopupLeft + 2, PopupTop + Line, Msg, 11); + Line += 12; + } + } +} + +VOID DrawCharacter(int X, int Y, int j, unsigned char chr) +{ + // Font is 5 bits wide x 8 high. Each byte of font contains one column, so 5 bytes per char + + int Pointer, i, c, index, bit, mask; + + Pointer = ((Y - 5) * WIDTH * Bytesperpixel) + ((X + 11) * Bytesperpixel) + (j * 6 * Bytesperpixel); + + mask = 1; + + for (i = 0; i < 2; i++) + { + for (index = 0 ; index < 6 ; index++) + { + Image[Pointer++] = 255; // Blank lines above chars + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + } + + Pointer += (WIDTH - 6) * Bytesperpixel; + } + + // Pointer = ((Y - 3) * 2048 * 3) + (X * 3) + 36 + (j * 18); + + for (i = 0; i < 7; i++) + { + Image[Pointer++] = 255; // Blank col between chars + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + for (index = 0 ; index < 5 ; index++) + { + c = ASCII[chr - 0x20][index]; // Font data + bit = c & mask; + + if (bit) + { + Image[Pointer++] = 0; + Image[Pointer++] = 0; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 0; + Pointer++; + } + } + else + { + Image[Pointer++] = 255; + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + } + } + mask <<= 1; + Pointer += (WIDTH - 6) * Bytesperpixel; + } + + // Pointer = ((Y - 3) * 2048 * 3) + (X * 3) + 36 + (j * 18); + + mask = 1; + + for (i = 0; i < 2; i++) + { + for (index = 0 ; index < 6 ; index++) + { + Image[Pointer++] = 255; // Blank lines below chars between chars + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + } + Pointer += (WIDTH - 6) * Bytesperpixel; + } +} + +int DrawStation(struct STATIONRECORD * ptr, BOOL AllStations) +{ + int X, Y, Pointer, i, c, index, bit, mask, calllen, calllenpixels; + UINT j; + char Overlay; + char * nptr; + time_t AgeLimit = time(NULL ) - (TrackExpireTime * 60); + int SavePointer; + + if (ptr->Moved == 0 && AllStations == 0) + return 0; // No need to repaint + + if (SuppressNullPosn && ptr->Lat == 0.0) + return 0; + + if (ptr->ObjState == '_') // Killed Object + return 0; + + if (GetLocPixels(ptr->Lat, ptr->Lon, &X, &Y)) + { + if (X < 12 || Y < 12 || X > (WIDTH - 36) || Y > (HEIGHT - 36)) + return 0; // Too close to edges + + if (ptr->LatTrack[0] && ptr->NoTracks == FALSE) + { + // Draw Track + + int Index = ptr->Trackptr; + int i, n; + int X, Y; + int LastX = 0, LastY = 0; + + for (n = 0; n < TRACKPOINTS; n++) + { + if (ptr->LatTrack[Index] && ptr->TrackTime[Index] > AgeLimit) + { + if (GetLocPixels(ptr->LatTrack[Index], ptr->LonTrack[Index], &X, &Y)) + { + if (LastX) + { + if (abs(X - LastX) < 600 && abs(Y - LastY) < 600) + if (X > 0 && Y > 0 && X < (WIDTH - 5) && Y < (HEIGHT - 5)) + plotLine(LastX, LastY, X, Y, Colours[ptr->TrackColour]); + + } + + LastX = X; + LastY = Y; + } + } + Index++; + if (Index == TRACKPOINTS) + Index = 0; + + } + } + + ptr->Moved = 0; + + ptr->DispX = X; + ptr->DispY = Y; // Save for mouse over checks + + // X and Y are offsets into the pixel data in array Image. Actual Bytes are at Y * 2048 * 3 + (X * 3) + + // Draw Icon + + if (Y < 8) Y = 8; + if (X < 8) X = 8; + + nptr = &Image[(((Y - 8) * WIDTH) + X - 8) * Bytesperpixel]; // Center icon on station + + j = (ptr->iconRow * 21 * 337 * Bytesperpixel) + + (ptr->iconCol * 21 * Bytesperpixel) + + 3 * Bytesperpixel + (337 * 3 * Bytesperpixel); + + for (i = 0; i < 16; i++) + { + memcpy(nptr, &iconImage[j], 16 * Bytesperpixel); + nptr += WIDTH * Bytesperpixel; + j += 337 * Bytesperpixel; + } + + // If an overlay is specified, add it + + Overlay = ptr->IconOverlay; + + if (Overlay) + { + Pointer = (((Y - 4) * WIDTH) + (X - 3)) * Bytesperpixel; + mask = 1; + + for (index = 0 ; index < 7 ; index++) + { + Image[Pointer++] = 255; // Blank line above chars + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + } + Pointer += (WIDTH - 7) * Bytesperpixel; + + for (i = 0; i < 7; i++) + { + Image[Pointer++] = 255; // Blank col + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + + for (index = 0 ; index < 5 ; index++) + { + c = ASCII[Overlay - 0x20][index]; // Font data + bit = c & mask; + + + if (bit) + { + Image[Pointer++] = 0; + Image[Pointer++] = 0; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 0; + Pointer++; + } + } + else + { + Image[Pointer++] = 255; + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + } + } + + Image[Pointer++] = 255; // Blank col + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + + mask <<= 1; + Pointer += (WIDTH - 7) * Bytesperpixel; + } + for (index = 0 ; index < 7 ; index++) + { + Image[Pointer++] = 255; // Blank line below chars + Image[Pointer++] = 255; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 255; + Pointer++; + } + } + Pointer += (WIDTH - 6) * Bytesperpixel; + } + + calllen = strlen(ptr->Callsign); + + while (calllen && ptr->Callsign[calllen - 1] == ' ') // Remove trailing spaces + calllen--; + + calllenpixels = (calllen + 1) * 6; + + // Draw Callsign Box + + Pointer = ((Y - 7) * WIDTH * Bytesperpixel) + ((X + 9) * Bytesperpixel); + + // Draw | at each end + + for (j = 0; j < 13; j++) + { + Image[Pointer] = 0; + Image[Pointer++ + calllenpixels * Bytesperpixel] = 0; + Image[Pointer] = 0; + Image[Pointer++ + calllenpixels * Bytesperpixel] = 0; + if (Bytesperpixel == 4) + { + Image[Pointer] = 0; + Image[Pointer++ + calllenpixels * Bytesperpixel] = 0; + Pointer++; + } + Pointer += (WIDTH - 1) * Bytesperpixel; + } + + // Draw Top Line + + for (i = 0; i < calllenpixels; i++) + { + Image[Pointer++] = 0; + Image[Pointer++] = 0; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 0; + Pointer++; + } + } + + // Draw Bottom Line + + Pointer = ((Y - 7) * WIDTH * Bytesperpixel) + ((X + 9) * Bytesperpixel); + + for (i = 0; i < calllenpixels; i++) + { + Image[Pointer++] = 0; + Image[Pointer++] = 0; + if (Bytesperpixel == 4) + { + Image[Pointer++] = 0; + Pointer++; + } + } + + // Draw Callsign. + + for (j = 0; j < calllen; j++) + { + DrawCharacter(X, Y,j, ptr->Callsign[j]); + } + ImageChanged = TRUE; + return 1; + } + else + { + ptr->DispX = 0; + ptr->DispY = 0; // Off Screen + } + return 0; +} + + +int RefreshStationMap(BOOL AllStations) +{ + struct STATIONRECORD * ptr = *StationRecords; + int blackColor = BlackPixel(display, DefaultScreen(display)); + int whiteColor = WhitePixel(display, DefaultScreen(display)); + int Changed = 0; + char msg[80]; + int i = 0, len; + + while (ptr) + { + Changed += DrawStation(ptr, AllStations); + i++; + ptr = ptr->Next; + } + +// NeedRefresh = FALSE; +// LastRefresh = time(NULL); + +// if (RecsDeleted) +// RefreshStationList();] + + len = sprintf(msg, "%d Stations Zoom = %d", i, Zoom); + XDrawImageString(display, win, gc, 20, 20, msg, len); + + StationCount = i; + return Changed; +} + + + +void j_putRGBScanline(BYTE *jpegline, + int widthPix, + unsigned char *outBuf, + int row, int XOffset, int YOffset) +{ + // Offsets are in tiles, not pixels + + int offset = row * WIDTH * Bytesperpixel; //widthPix + int count; + unsigned int val; + + offset += XOffset * 256 * Bytesperpixel; + offset += YOffset * 256 * WIDTH * Bytesperpixel; + + for (count = 0; count < 256; count++) + { + if (Bytesperpixel == 2) + { + val = (*(jpegline + count * 3 + 2) >> 3); + val |= ((*(jpegline + count * 3 + 1) >> 2) << 5); + val |= ((*(jpegline + count * 3 + 0) >> 3) << 11); + *(outBuf + offset++) = (val & 0xff); + *(outBuf + offset++) = (unsigned char)(val >> 8); + } + else + { + *(outBuf + offset++) = *(jpegline + count * 3 + 2); // Blue + *(outBuf + offset++) = *(jpegline + count * 3 + 1); // Green + *(outBuf + offset++) = *(jpegline + count * 3 + 0); // Red + offset++; + } + } +} + +// +// stash a gray scanline +// + +void j_putGrayScanlineToRGB(BYTE *jpegline, + int widthPix, + BYTE *outBuf, + int row) +{ + int offset = row * widthPix * 3; + int count; + for (count=0;countalloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + + /* Step 6: while (scan lines remain to be read) */ + /* jpeg_read_scanlines(...); */ + + /* Here we use the library's state variable cinfo.output_scanline as the + * loop counter, so that we don't have to keep track ourselves. + */ + while (cinfo.output_scanline < cinfo.output_height) { + /* jpeg_read_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could ask for + * more than one scanline at a time if that's more convenient. + */ + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + /* Assume put_scanline_someplace wants a pointer and sample count. */ + + // asuumer all 3-components are RGBs + if (cinfo.out_color_components==3) { + + j_putRGBScanline(buffer[0], + *width, + Image, + cinfo.output_scanline-1, XOffset, YOffset); + + } else if (cinfo.out_color_components==1) { + + // assume all single component images are grayscale + j_putGrayScanlineToRGB(buffer[0], + *width, + Image, + cinfo.output_scanline-1); + + } + + } + + /* Step 7: Finish decompression */ + + (void) jpeg_finish_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* Step 8: Release JPEG decompression object */ + + /* This is an important step since it will release a good deal of memory. */ + jpeg_destroy_decompress(&cinfo); + + /* After finish_decompress, we can close the input file. + * Here we postpone it until after no more JPEG errors are possible, + * so as to simplify the setjmp error logic above. (Actually, I don't + * think that jpeg_destroy can do an error exit, but why assume anything...) + */ + fclose(infile); + + /* At this point you may want to check to see whether any corrupt-data + * warnings occurred (test whether jerr.pub.num_warnings is nonzero). + */ + + return 0; +} +// store a scanline to our data buffer + +void j_putRGBScanline(BYTE *jpegline, + int widthPix, + BYTE *outBuf, + int row, int X, int Y); + +void j_putGrayScanlineToRGB(BYTE *jpegline, + int widthPix, + BYTE *outBuf, + int row); + +VOID LoadImageTile(int Zoom, int startx, int starty, int x, int y); + +VOID RefreshTile(char * FN, int TileZoom, int Tilex, int Tiley) +{ + // Called when a new tile has been diwnloaded from OSM + + int StartRow, StartCol; + UCHAR * pbImage = NULL; + int x, y, i, j; + int ImgChannels; + + if (TileZoom != Zoom) + return; // Zoom level has changed + + x = Tilex - SetBaseX; + y = Tiley - SetBaseY; + + if (x < 0 || x > WIDTHTILES -1 || y < 0 || y > HEIGHTTILES - 1) + return; // Tile isn't part of current image; + + LoadImageTile (Zoom, Tilex, Tiley, x, y); + NeedRedraw = 1; + +// XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); +} + + +VOID LoadImageTile(int Zoom, int startx, int starty, int x, int y) +{ + char FN[100]; + int i, j; + int StartRow; + int StartCol; + char Tile[100]; + UCHAR * pbImage = NULL; + int ImgChannels; + BOOL JPG=FALSE; + struct stat STAT; + int cx, cy; + + int Limit = (int)pow(2, Zoom); +/* + printf("LoadImage %d %d %d\n", Limit, startx, startx); + + if (startx < 0) + startx = startx + WIDTHTILES; + else + if (startx > WIDTHTILES - 1) + startx = WIDTHTILES - startx; + + if (starty < 0) + starty = starty + HEIGHTTILES; + else + if (starty > HEIGHTTILES -1 ) + starty = HEIGHTTILES - starty; + + if (startx < 0 || startx > WIDTHTILES) + x = WIDTHTILES / 2; + + if (starty < 0 || y > HEIGHTTILES) + starty = HEIGHTTILES /2; + + printf("LoadImage %d %d %d\n", Limit, startx, starty); +*/ + if ((startx) >= Limit || (starty) >= Limit || startx< 0 || starty < 0) + { +// printf("Not Loading %d %d %d\n",Limit, startx, startx ); + return; //goto NoFile; + } + + // May be PNG or JPG + + sprintf(Tile, "/%02d/%d/%d.png", Zoom, startx, starty); + sprintf(FN, "%s%s", OSMDir, Tile); + + if (stat(FN, &STAT) == 0) + goto gotfile; + + sprintf(Tile, "/%02d/%d/%d.jpg", Zoom, startx, starty); + sprintf(FN, "%s%s", OSMDir, Tile); + + JPG = TRUE; + + if (stat(FN, &STAT) == 0) + goto gotfile; + + + OSMGet(startx, starty, Zoom); + return; + +gotfile: + + if (JPG) + { + JpegFileToRGB(FN, &cx, &cy, x, y); + ImgChannels = 3; + } + else + { + int offset; + int cxImgSize, cyImgSize; + UCHAR * ImageSave; + + LoadImageFile (NULL, FN, &pbImage, &cxImgSize, &cyImgSize, &ImgChannels, &bkgColor); + +// printf("%d %d %d\n", cxImgSize, cyImgSize, ImgChannels); +// ImgChannels = 4; + StartCol = x * Bytesperpixel * 256; + StartRow = y * 256; + +// printf("WIDTH %d Height %d Bytesperpixel = %d x = %d y = %d\n", WIDTH, HEIGHT, Bytesperpixel, x, y); + if (pbImage == NULL) + { + pbImage = malloc(256 * ImgChannels * 256); + memset(pbImage, 0x40, 256 * ImgChannels * 256); + } + + ImageSave = pbImage; + + offset = ((StartRow) * WIDTH * ImgChannels) + StartCol; + +// printf ("x %d y %d offset %d \n", x, y, offset); + + + for (i = 0; i < 256; i++) + { + int count, val; + + offset = ((StartRow + i) * WIDTH * Bytesperpixel) + StartCol; + + // this does one scan line + + for (count = 0; count < 256; count++) + { + if (Bytesperpixel == 2) + { + val = (*(pbImage + count * ImgChannels + 2) >> 3); + val |= ((*(pbImage + count * ImgChannels + 1) >> 2) << 5); + val |= ((*(pbImage + count * ImgChannels + 0) >> 3) << 11); + Image[offset++] = (val & 0xff); + Image[offset++] = (unsigned char)(val >> 8); + } + else + { + Image[offset++] = *(pbImage + count * ImgChannels + 2); // Blue + Image[offset++] = *(pbImage + count * ImgChannels + 1); // Green + Image[offset++] = *(pbImage + count * ImgChannels + 0); // Red + offset++; + } + } + pbImage += ImgChannels * 256; + } + + free(ImageSave); + } +} + + +VOID LoadImageSet(int Zoom, int TileX, int TileY) +{ + int x, y; + + if (SetBaseX != TileX || SetBaseY != TileY) + { + // Only Load if changed + + SetBaseX = TileX; // Lowest Tiles in currently loaded set + SetBaseY = TileY; + + memset(Image, 0, WIDTH * Bytesperpixel * HEIGHT); + XClearWindow(display, win); + + for (y = 0; y < HEIGHTTILES; y++) + { + for (x = 0; x < WIDTHTILES; x++) + { + LoadImageTile(Zoom, TileX + x, TileY + y, x, y); + } + } + RefreshStationMap(TRUE); + } + XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); +} + +BYTE * ReadIcons(char * fileName, UINT *width, UINT *height) +{ + struct jpeg_decompress_struct cinfo; + struct my_error_mgr jmerr; + struct jpeg_error_mgr jerr; + FILE * infile=NULL; /* source file */ + + JSAMPARRAY buffer; /* Output row buffer */ + int row_stride; /* physical row width in output buffer */ + char buf[250]; + BYTE *dataBuf; + + *width=0; + *height=0; + + if ((infile = fopen(fileName, "rb")) == NULL) { + return NULL; + } + + cinfo.err = jpeg_std_error(&jerr); + + if (setjmp(jmerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + + jpeg_destroy_decompress(&cinfo); + + if (infile!=NULL) + fclose(infile); + return NULL; + } + + + jpeg_create_decompress(&cinfo); + + jpeg_stdio_src(&cinfo, infile); + + (void) jpeg_read_header(&cinfo, TRUE); + + (void) jpeg_start_decompress(&cinfo); + + dataBuf = malloc(cinfo.output_width * 4 * cinfo.output_height); + memset(dataBuf, 0, cinfo.output_width * 4 * cinfo.output_height); + + if (dataBuf==NULL) + { + jpeg_destroy_decompress(&cinfo); + fclose(infile); + return NULL; + } + + // how big is this thing gonna be? + *width = cinfo.output_width; + *height = cinfo.output_height; + + /* JSAMPLEs per row in output buffer */ + row_stride = cinfo.output_width * cinfo.output_components; + + /* Make a one-row-high sample array that will go away when done with image */ + buffer = (*cinfo.mem->alloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + + /* Step 6: while (scan lines remain to be read) */ + /* jpeg_read_scanlines(...); */ + + /* Here we use the library's state variable cinfo.output_scanline as the + * loop counter, so that we don't have to keep track ourselves. + */ + while (cinfo.output_scanline < cinfo.output_height) + { + int offset; + int count; + unsigned int val; + + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + + offset = (cinfo.output_scanline-1) * cinfo.output_width * Bytesperpixel; + + for (count = 0; count < cinfo.output_width; count++) + { + if (Bytesperpixel == 2) + { + val = (*(buffer[0] + count * 3 + 2) >> 3); + val |= ((*(buffer[0] + count * 3 + 1) >> 2) << 5); + val |= ((*(buffer[0] + count * 3 + 0) >> 3) << 11); + *(dataBuf + offset++) = (val & 0xff); + *(dataBuf + offset++) = (unsigned char)(val >> 8); + } + else + { + *(dataBuf + offset++) = *(buffer[0] + count * 3 + 2); // Blue + *(dataBuf + offset++) = *(buffer[0] + count * 3 + 1); // Green + *(dataBuf + offset++) = *(buffer[0] + count * 3 + 0); // Red + offset++; + } + } + } + + (void) jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + fclose(infile); + return dataBuf; +} +// store a scanline to our data buffer + +void ZoomIn() +{ + if (Zoom < 16) + { + Zoom ++; + CentrePositionToMouse(MouseLat, MouseLon); + TileX = SetBaseX; + TileY = SetBaseY; + NeedRefresh = TRUE; + } +} +void ZoomOut() +{ + if (Zoom > 1) + { + Zoom --; + CentrePositionToMouse(MouseLat, MouseLon); + TileX = SetBaseX; + TileY = SetBaseY; + if (Zoom == 1) + ScrollX = ScrollY = 0; + + NeedRefresh = TRUE; + } +} + +config_t cfg; +config_setting_t *croot, *group; + +int GetIntValue(config_setting_t * group, char * name, int defaultval) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return defaultval; +} + +VOID SaveIntValue(config_setting_t * group, char * name, int value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT); + if(setting) + config_setting_set_int(setting, value); +} + +BOOL GetStringValue(config_setting_t * group, char * name, char * value) +{ + const char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + + if (setting) + { + str = config_setting_get_string (setting); + strcpy(value, str); + return TRUE; + } + return FALSE; +} + +VOID SaveStringValue(config_setting_t * group, char * name, char * value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, value); + +} + + + +enum +{ + COL_FROM = 0, + COL_TO, + COL_SEQ, + COL_TIME, + COL_RECEIVED, + NUM_COLS +} ; + + +void CancelMessageSend (GtkWidget *menuitem, struct APRSMESSAGE * userdata) +{ +/* + userdata->Retries = 0; + userdata->RetryTimer = 0; + userdata->Cancelled = TRUE; + UpdateTXMessageLine(userdata); +*/ +} + + +void view_popup_menu_onDoNothing (GtkWidget *menuitem, gpointer userdata) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(userdata); +} + +void view_popup_menu (GtkWidget *treeview, GdkEventButton *event, struct APRSMESSAGE * userdata) +{ + GtkWidget *menu, *menuitem1,*menuitem2 ; + char Msg[80]; + + sprintf(Msg,"Cancel Message Seq %s to %s?", userdata->Seq, userdata->ToCall); + + menu = gtk_menu_new(); + + menuitem1 = gtk_menu_item_new_with_label(Msg); + menuitem2 = gtk_menu_item_new_with_label("Return"); + + g_signal_connect(menuitem1, "activate", + (GCallback) CancelMessageSend, (gpointer)userdata); + g_signal_connect(menuitem2, "activate", + (GCallback) view_popup_menu_onDoNothing, treeview); + + if (userdata->Retries) // Not active so cant cancel + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem1); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem2); + + gtk_widget_show_all(menu); + + /* Note: event can be NULL here when called from view_onPopupMenu; + * gdk_event_get_time() accepts a NULL argument */ + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, + (event != NULL) ? event->button : 0, + gdk_event_get_time((GdkEvent*)event)); + } + + +gboolean view_onButtonPressed (GtkWidget *treeview, GdkEventButton *event, gpointer userdata) +{ + // Right click on TX Message window. If a message is selected, + // Pop up a Cancel Message Window + + if (event->type == GDK_BUTTON_PRESS && event->button == 3) + { + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path; + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + + if (ptr == 0) + return TRUE; + + // Make sure the entry that was clicked is selected + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + + if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), + (gint) event->x, (gint) event->y, &path, NULL, NULL, NULL)) + { + gtk_tree_selection_unselect_all(selection); + gtk_tree_selection_select_path(selection, path); + gtk_tree_path_free(path); + } + + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + + if (gtk_tree_selection_get_selected(selection, &model, &iter)) + { + gchar *Seq; + + gtk_tree_model_get (model, &iter, 1, &Seq, -1); + + // Find the message + + while (ptr) + { + if (strcmp(ptr->Seq, Seq) == 0) + { + view_popup_menu(treeview, event, ptr); + g_free(Seq); + return TRUE; + } + ptr = ptr->Next; + } + + g_free(Seq); + g_print ("Msg not found.\n"); + } + g_print ("no row selected.\n"); + } + + return FALSE; /* we did not handle this */ +} + +gboolean view_onPopupMenu (GtkWidget *treeview, gpointer userdata) +{ + view_popup_menu(treeview, NULL, userdata); + + return TRUE; /* we handled this */ +} + + +static GtkWidget *create_sent_window( void ) +{ + GtkCellRenderer *renderer; + GtkTreeModel *model; + GtkTreeIter iter; + + view = gtk_tree_view_new(); + + gtk_signal_connect (GTK_OBJECT (view), "row_activated", + GTK_SIGNAL_FUNC (SelectTXMsg), NULL); + + + g_signal_connect(view, "button-press-event", (GCallback) view_onButtonPressed, NULL); + g_signal_connect(view, "popup-menu", (GCallback) view_onPopupMenu, NULL); + + + + + renderer = gtk_cell_renderer_text_new(); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "To", renderer, "text", 0, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "Seq", renderer, "text", 1, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "State", renderer, "text", 2, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "Time", renderer, "text", 3, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "Sent", renderer, "text", 4, NULL); + + sentitems = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + + model = GTK_TREE_MODEL(sentitems); + + gtk_tree_view_set_model((GtkTreeView *)view, model); + + /* The tree view has acquired its own reference to the + * model, so we can drop ours. That way the model will + * be freed automatically when the tree view is destroyed */ + + g_object_unref (model); + +// gtk_container_add (GTK_CONTAINER (window), view2); + + scrolledwin = gtk_scrolled_window_new(NULL,NULL); + gtk_container_set_border_width(GTK_CONTAINER(scrolledwin), 1); +// gtk_widget_set_size_request(scrolledwin, 300, 80); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), GTK_SHADOW_IN); +// gtk_container_add(GTK_CONTAINER(scrolledwin), view); + //tree_view = gtk_tree_view_new(); + gtk_container_add(GTK_CONTAINER (scrolledwin), view); + //gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (view)); + //gtk_widget_show(tree_view); +/* + gtk_table_attach (GTK_TABLE (table), scrolledwin,0, 1, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0); +*/ + gtk_widget_show(scrolledwin); + return scrolledwin; + +} + +GdkPixbuf *create_pixbuf(const gchar * filename) +{ + GdkPixbuf *pixbuf; + GError *error = NULL; + pixbuf = gdk_pixbuf_new_from_file(filename, &error); + if(!pixbuf) { + fprintf(stderr, "%s\n", error->message); + g_error_free(error); + } + return pixbuf; +} + + +static GtkWidget *create_received_window(void) +{ + GtkCellRenderer *renderer; + + view2 = gtk_tree_view_new(); + +// gtk_tree_view_set_fixed_height_mode(view2, TRUE); + + renderer = gtk_cell_renderer_text_new(); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "From", renderer, "text", COL_FROM, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "To", renderer, "text", COL_TO, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "Seq", renderer, "text", COL_SEQ, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "Time", renderer, "text", COL_TIME, NULL); + + renderer = gtk_cell_renderer_text_new (); + renderer->ypad = 0; + gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view2), -1, "Received", renderer, "text", COL_RECEIVED, NULL); + + receiveditems = gtk_list_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + + gtk_tree_view_set_model((GtkTreeView *)view2, (GtkTreeModel *)receiveditems); + + /* The tree view has acquired its own reference to the + * model, so we can drop ours. That way the model will + * be freed automatically when the tree view is destroyed */ + + g_object_unref (receiveditems); + + scrolledwin2 = gtk_scrolled_window_new(NULL,NULL); + gtk_container_set_border_width(GTK_CONTAINER(scrolledwin2), 2); +// gtk_widget_set_size_request(scrolledwin2, 300, 80); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin2),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin2), GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(scrolledwin2), view2); + + gtk_widget_show(scrolledwin2); + + return scrolledwin2; + +} + +char ToCalls[1024] = ""; + +VOID SendAPRSMessage(const char * Text, char * ToCall); + +void enter_callback( GtkWidget *widget, + GtkWidget *entry ) +{ + const gchar *entry_text; + entry_text = gtk_entry_get_text (GTK_ENTRY (entry)); + gchar * tocall = strupr(gtk_combo_box_text_get_active_text((GtkComboBoxText *)combo)); + char Key[32]; + + if (strlen(tocall) > 9) + tocall[9] = 0; + + sprintf(Key, "|%s|", tocall); + + if (tocall) + { + SendAPRSMessage(entry_text, tocall); + + // if new call add to combo box + + if (strstr(ToCalls, Key) == 0) + { + if (strlen(ToCalls) < 1000) + strcat(ToCalls, Key); + + gtk_combo_box_text_prepend_text ((GtkComboBoxText *)combo, tocall); + } + + g_free(tocall); + gtk_entry_set_text (GTK_ENTRY (entry), ""); + } +} + + +void SelectTXMsg (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) +{ + GtkTreeIter iter; + GtkTreeModel *model; + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + + if (ptr == 0) + return; + + model = gtk_tree_view_get_model(tree_view); + + if (gtk_tree_model_get_iter(model, &iter, path)) + { + gchar *seq; + + gtk_tree_model_get(model, &iter, 1, &seq, -1); + + g_print ("Double-clicked row contains seq %s\n", seq); + g_free(seq); + } + + return; +} + + +VOID GTKThread() +{ + gtk_main(); +} + +int msgWinWidth = 300; +int msgWinHeight = 300; +int msgWinX = 100; +int msgWinY = 100; +int Split = 100; // Rx/Tx Window split + + +void frame_callback(GtkWindow *window, GdkEvent *event, gpointer data) +{ + int x, y; + char buf[10]; + + msgWinX = event->configure.x; + msgWinY = event->configure.y; + msgWinWidth = event->configure.width; + msgWinHeight = event->configure.height; + + // gtk_widget_set_size_request(entry, msgWinWidth - 210 , 20); //gtk_entry_new_with_buffer(text); + + // gtk_window_set_title(window, buf); + // gtk_window_set_title (GTK_WINDOW (window), "BPQAPRS Messaging"); +} + +BOOL OnlyMine = FALSE; +BOOL OnlySeq = FALSE; +BOOL ShowBulls = FALSE; +BOOL AllSSID = FALSE; + + +void check_callback(GtkButton *button, gpointer user_data) +{ + GtkTreeIter iter; + struct APRSMESSAGE * ptr = SMEM->Messages; + int n = 0; + + char BaseFrom[10]; + + OnlyMine = gtk_toggle_button_get_active((GtkToggleButton *)check1); + OnlySeq = gtk_toggle_button_get_active((GtkToggleButton *)check2); + ShowBulls = gtk_toggle_button_get_active((GtkToggleButton *)check3); + AllSSID = gtk_toggle_button_get_active((GtkToggleButton *)check4); + + // rewite the Message display with new filter + + if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(receiveditems), &iter, NULL, 0)) + { + while (gtk_list_store_remove(receiveditems, &iter)) + {} + } + while (ptr) + { + if (memcmp(ptr->ToCall, "BLN", 3)== 0) + if (ShowBulls == TRUE) + goto wantit; + + if (strcmp(ptr->ToCall, APRSCall) == 0) // to me? + goto wantit; + + if (AllSSID) + { + memcpy(BaseFrom, ptr->ToCall, 10); + strlop(BaseFrom, ' '); + strlop(BaseFrom, '-'); + + if (strcmp(BaseFrom, BaseCall) == 0) + goto wantit; + } + + if (OnlyMine == FALSE) // Want All + if (OnlySeq == FALSE || ptr->Seq[0] != 0) + goto wantit; + + // ignore + + ptr = ptr->Next; + continue; + + wantit: + + gtk_list_store_insert_with_values( + receiveditems, &iter, -1, + COL_FROM, ptr->FromCall, + COL_TO, ptr->ToCall, + COL_SEQ, ptr->Seq, + COL_TIME, ptr->Time, + COL_RECEIVED, ptr->Text, -1); + + ptr = ptr->Next; + n++; + } + + + if (n) + gtk_tree_view_scroll_to_cell((GtkTreeView *)view2, + gtk_tree_model_get_path (GTK_TREE_MODEL(receiveditems), &iter), NULL, FALSE, 0, 0); + + +} + + +void button_callback(GtkButton *button, gpointer user_data) +{ + SMEM->ClearRX = 1; +} + + +void button2_callback(GtkButton *button, gpointer user_data) +{ + // Clear Sent Messages + + SMEM->ClearTX = 1; +} + + +static gboolean delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) +{ + // Don't allow window to be closed + + return TRUE; +} + +void SaveConfig() +{ + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + croot = config_root_setting(&cfg); + + gtk_window_get_size(GTK_WINDOW(window), &msgWinWidth, &msgWinHeight); + gtk_window_get_position(GTK_WINDOW(window), &msgWinX, &msgWinY); + Split = gtk_paned_get_position((GtkPaned *)vpaned); + + group = config_setting_add(croot, "APRS", CONFIG_TYPE_GROUP); + + SaveIntValue(group, "Zoom", Zoom); + SaveIntValue(group, "SetBaseX", SetBaseX); + SaveIntValue(group, "SetBaseY", SetBaseY); + SaveIntValue(group, "ScrollX", ScrollX); + SaveIntValue(group, "ScrollY", ScrollY); + SaveIntValue(group, "WindowX", WindowX); + SaveIntValue(group, "WindowY", WindowY); + SaveIntValue(group, "WindowWidth", WindowWidth); + SaveIntValue(group, "WindowHeight", WindowHeight); + SaveIntValue(group, "WIDTHTILES", WIDTHTILES); + SaveIntValue(group, "HEIGHTTILES", HEIGHTTILES); + SaveIntValue(group, "msgWinWidth", msgWinWidth); + SaveIntValue(group, "msgWinHeight", msgWinHeight); + SaveIntValue(group, "msgWinX", msgWinX); + SaveIntValue(group, "msgWinY", msgWinY); + SaveIntValue(group, "Split", Split); + + SaveIntValue(group, "OnlyMine", OnlyMine); + SaveIntValue(group, "OnlySeq", OnlySeq); + SaveIntValue(group, "ShowBulls", ShowBulls); + + SaveIntValue(group, "LocalTime", LocalTime); + SaveIntValue(group, "KM", KM); + SaveIntValue(group, "AddViewToFilter", AddViewToFilter); + SaveStringValue(group, "ISFilter", ISFilter); + + + SaveIntValue(group, "CreateJPEG", CreateJPEG); + SaveIntValue(group, "JPEGInterval", JPEGInterval); + SaveStringValue(group, "JPEGFileName", JPEGFileName); + + if(!config_write_file(&cfg, "BPQAPRS.cfg")) + printf("Error while writing config file.\n"); + else + printf("Config Saved\n"); + + config_destroy(&cfg); + + printf("%s\n", ToCalls); +} + +// Linux Signal Handlers + +BOOL Running = TRUE; + +static void sigterm_handler(int sig) +{ + printf("sigterm\n"); + Running = FALSE; +} + +static void sigint_handler(int sig) +{ + SaveConfig(); + Running = FALSE; + exit(0); +} + +int main(int argc, char *argv[]) +{ + UCHAR * pbImage = NULL; + char FN[256]; + int fd; + int x, y; + time_t TimeLoaded = time(NULL); + struct stat STAT; + char * Env; + char BPQDirectory[256]; + char SharedName[256]; + char * ptr1; + + int SharedSize; + + double vals[10]; + int screen_number, depth, bitmap_pad, status; + unsigned long white; + unsigned long black; + Visual * visual; + unsigned int i, j; + Pixmap pixmap, popuppixmap; + + int x11_fd; + fd_set in_fds; + + struct timeval tv; + XEvent event; + XConfigureEvent xce; + XKeyEvent xkeyev; + Time lastupevent; + + char text[256]; + long unsigned int key; + + int LastX, LastY; // Saved mouse position when button down + int MovedX, MovedY; + + double sx, sy; + UCHAR * APRSStationMemory; + Atom wmDeleteMessage; + PangoFontDescription *font_desc; + GtkTreeIter iter; + +#ifndef WIN32 + signal(SIGHUP, SIG_IGN); + signal(SIGINT, sigint_handler); + signal(SIGTERM, sigterm_handler); + signal(SIGPIPE, SIG_IGN); +#endif + + printf("G8BPQ APRS Client for Linux Version 1.1.14.7 \n"); + printf("Copyright(c) 2004-2021 John Wiseman G8BPQ\n"); + printf("APRS is a registered trademark of Bob Bruninga.\n"); + printf("This software is based in part on the work of the Independent JPEG Group.\n"); + printf("Mapping from OpenStreetMap (http://openstreetmap.org)\n"); + printf("Imagery (c) OpenMapTiles (https://openmaptiles.org)\n\n"); + + if (argc > 1 && argv[1] && stricmp(argv[1], "-v") == 0) + return 0; + + config_init(&cfg); + + if (argc > 1 && argv[1] && stricmp(argv[1], "multiple") == 0) + { + multiple = 1; + printf("Running in multiple instance mode\n\n"); + } + + /* Read the file. If there is an error, report it and exit. */ + + if(!config_read_file(&cfg, "BPQAPRS.cfg")) + { + fprintf(stderr, "%d - %s\n", + config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + } + else + { + group = config_lookup (&cfg, "APRS"); + + if (group) + { + Zoom = GetIntValue(group, "Zoom", 2); + TileX = GetIntValue(group, "SetBaseX", 0); + TileY = GetIntValue(group, "SetBaseY", 0); + ScrollX = GetIntValue(group, "ScrollX", 0); + ScrollY = GetIntValue(group, "ScrollY", 0); + WindowX = GetIntValue(group, "WindowX", 100); + WindowY = GetIntValue(group, "WindowY", 100); + WindowWidth = GetIntValue(group, "WindowWidth", 788); + WindowHeight = GetIntValue(group, "WindowHeight", 788); + HEIGHTTILES = GetIntValue(group, "HEIGHTTILES", 4); + WIDTHTILES = GetIntValue(group, "WIDTHTILES", 4); + + msgWinWidth = GetIntValue(group, "msgWinWidth", 300); + msgWinHeight = GetIntValue(group, "msgWinHeight", 300); + msgWinX = GetIntValue(group, "msgWinX", 100); + msgWinY = GetIntValue(group, "msgWinY", 100); + Split = GetIntValue(group, "Split", 100); + + OnlyMine = GetIntValue(group, "OnlyMine", 0); + OnlySeq = GetIntValue(group, "OnlySeq", 1); + ShowBulls = GetIntValue(group, "ShowBulls", 0); + + LocalTime = GetIntValue(group, "LocalTime", 0); + KM = GetIntValue(group, "KM", 0); + + AddViewToFilter = GetIntValue(group, "AddViewToFilter", 0); + + CreateJPEG = GetIntValue(group, "CreateJPEG", 1); + JPEGInterval = GetIntValue(group, "JPEGInterval", 300); + GetStringValue(group, "JPEGFileName", JPEGFileName); + GetStringValue(group, "ISFilter", ISFilter); + } + } + + if (Zoom == 0) + Zoom = 2; + + HEIGHT = HEIGHTTILES * 256; + WIDTH = WIDTHTILES * 256; + + Env = getenv("DISPLAY"); + + if (Env == NULL) + { + printf("DISPLAY is not set - can't run without X\n", Env); + return 0; + } + + printf("DISPLAY is set to %s\n", Env); + + if (strstr(Env, "localhost:1")) + printf("!!! WARNING !!! X session seems to be tunneled over an SSH session\nThis program will run much faster if you set DISPLAY to the Host running your X Server\n"); + + // Get shared memory + + // Append last bit of current directory to shared name + + getcwd(BPQDirectory, 256); + ptr1 = BPQDirectory; + + while (strchr(ptr1, '/')) + { + ptr1 = strchr(ptr1, '/'); + ptr1++; + } + + if (multiple) + sprintf(SharedName, "/BPQAPRSSharedMem%s", ptr1); + else + strcpy(SharedName, "/BPQAPRSSharedMem"); + + printf("Using Shared Memory %s\n", SharedName); + + + fd = shm_open(SharedName, O_RDWR, S_IRUSR | S_IWUSR); + if (fd == -1) + { + printf("Open APRS Shared Memory %s Failed\n", SharedName); + return 0; + } + else + { + // Map shared memory object + + Shared = mmap((void *)APRSSHAREDMEMORYBASE, 8192 * 4096, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); + + if (Shared == MAP_FAILED) + { + printf("Map APRS Shared Memory Failed\n"); + APRSStationMemory = NULL; + return 0; + } + } + + if (Shared != (void *)APRSSHAREDMEMORYBASE) + { + printf("Map APRS Shared Memory Allocated at wrong address %x Should be 0x43000000\n", Shared); + return 0; + } + + printf("Map APRS Shared Memory Allocated at %x\n", Shared); + + + SMEM = (struct SharedMem *)Shared; + SharedSize = SMEM->SharedMemLen; + + printf("Shared Memory Size %d Max %d\n", SharedSize, 8192 * 4096); + + if (SharedSize > 8192 * 4096) + { + printf("MAXSTATIONS too high\n"); + return 0; + } + + StnRecordBase = Shared + 32; + StationRecords = (struct STATIONRECORD**)StnRecordBase; + + ControlRecord = (struct STATIONRECORD*)StnRecordBase; + + memset(APRSCall, 0x20, 9); + memcpy(APRSCall, ControlRecord->Callsign, strlen(ControlRecord->Callsign)); + + printf("LinBPQ Configured with MaxStations %d APRSCall %s\n", + ControlRecord->LastPort, APRSCall); + + memcpy(BaseCall, APRSCall, 10); // Get call less SSID + strlop(BaseCall, ' '); + strlop(BaseCall, '-'); + + // Remap with Server's view of MaxStations + +// munmap(APRSStationMemory, 4096 * 4096); + +// perror("munmap"); + +// Shared = mmap((void *)APRSSHAREDMEMORYBASE, SharedSize, +// PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + +// printf("Map APRS Shared Memory Allocated at %x\n", Shared); + + if (Shared == MAP_FAILED) + { + printf("Extend APRS Shared Memory Failed\n"); + APRSStationMemory = NULL; + return 0; + } + + SMEM->NeedRefresh = 1; // Initial Load of messages + + maxfd = sfd = socket(AF_UNIX, SOCK_DGRAM, 0); + + if (sfd == -1) + { + perror("Socket"); + } + else + { + memset(&my_addr, 0, sizeof(struct sockaddr_un)); + my_addr.sun_family = AF_UNIX; + strncpy(my_addr.sun_path, RX_SOCK_PATH, sizeof(my_addr.sun_path) - 1); + + memset(&peer_addr, 0, sizeof(struct sockaddr_un)); + peer_addr.sun_family = AF_UNIX; + strncpy(peer_addr.sun_path, TX_SOCK_PATH, sizeof(peer_addr.sun_path) - 1); + + unlink(RX_SOCK_PATH); + + if (bind(sfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_un)) == -1) + perror("bind"); + } + + XInitThreads(); + + ResolveThread(); + _beginthread(OSMThread, 0, NULL); + + + display = XOpenDisplay(NULL); + + if (! display) + { + printf("Couldn't open X display\n"); + return 1; + } + + screen_number = DefaultScreen (display); + depth = DefaultDepth (display, screen_number); + visual = DefaultVisual (display, screen_number); + gc = DefaultGC (display, screen_number); + bitmap_pad = BitmapPad (display); + white = WhitePixel (display, screen_number); + black = BlackPixel (display, screen_number); + root = DefaultRootWindow (display); + + if (depth == 16) + Bytesperpixel = 2; + + Image = malloc(WIDTH * Bytesperpixel * HEIGHT + 100); // Seems past last byte gets corrupt + + if (mkdir(OSMDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) + { + if (errno != 17) // File exists + { + printf("Error Creating %s\n", OSMDir); + perror("mkdir"); + } + } + + for (i = 0; i < 20; i++) + { + sprintf(FN, "%s/%02d", OSMDir, i); + if (mkdir(FN, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) + { + if (errno != 17) // File exists + { + printf("Error Creating %s\n", FN); + perror("mkdir"); + } + } + } + + // Read Icons + + iconImage = ReadIcons("BPQAPRS/Symbols.jpg", &x, &y); + + if (x == 0) + printf("Couldn't load Icons\n"); + +// win = XCreateSimpleWindow (display, root, 50, 50, 800, 800, 0, black, white); + win = XCreateWindow (display, root, 50, 50, 788, 788, 0, depth, CopyFromParent, CopyFromParent, 0, 0); + XStoreName(display, win, "BPQAPRS Map"); + + wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(display, win, &wmDeleteMessage, 1); + +// XSelectInput(display, win, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask); + + XSelectInput(display, win, ExposureMask | KeyPressMask | PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | StructureNotifyMask); + + XSetWindowBackground(display, win, 0xffffff); + XClearWindow(display, win); + XMapWindow (display, win); + + XMoveResizeWindow(display, win, WindowX, WindowY, WindowWidth, WindowHeight); + + image = XCreateImage (display, visual, depth, ZPixmap, 0, NULL, WIDTH, HEIGHT, bitmap_pad, 0); + + printf("depth : %d\nbitmap_pad : %d\nimage bpp : %d\n", depth, bitmap_pad, image->bits_per_pixel); + + image->data = Image; + + pixmap = XCreatePixmap (display, root, WIDTH, HEIGHT, depth); + + XSetLineAttributes(display, gc, 2, LineSolid, CapNotLast, JoinMiter); + + SetBaseX = -1; // force reload + SetBaseY = -1; + + LoadImageSet(Zoom, TileX, TileY); // Loads 1024 * 1024 Block + + x11_fd = ConnectionNumber(display); + + if (x11_fd > maxfd) + maxfd = x11_fd; + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size(GTK_WINDOW (window), msgWinWidth, msgWinHeight); + gtk_widget_set_uposition(GTK_WIDGET(window),msgWinX, msgWinY); + + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (gtk_main_quit), NULL); + gtk_container_set_border_width (GTK_CONTAINER (window), 10); + + gtk_window_set_resizable(GTK_WINDOW (window), TRUE); +// g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (close_application), NULL); + gtk_window_set_title (GTK_WINDOW (window), "BPQAPRS Messaging"); + gtk_container_set_border_width(GTK_CONTAINER (window), 0); + + // Load bpqicon if present + + if (stat("bpqicon.png", &STAT) == 0) + gtk_window_set_icon(GTK_WINDOW(window), create_pixbuf("bpqicon.png")); + + //gtk_window_get_frame_dimensions(GTK_WINDOW(window),&left,&top,&right,&bottom); + + + gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(delete_event), NULL); + +// g_signal_connect(G_OBJECT(window), "configure-event", G_CALLBACK(frame_callback), NULL); + + // Create a box for the menu + + box1 = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), box1); + + checklabel = gtk_label_new(" "); + + check1 = gtk_check_button_new_with_label ("Show only My Msgs "); + check4 = gtk_check_button_new_with_label ("All SSIDs "); + check2 = gtk_check_button_new_with_label ("Show only Sequenced Msgs "); + check3 = gtk_check_button_new_with_label ("Show Bulls "); + + button = gtk_button_new_with_label("Clear RX"); + button2 = gtk_button_new_with_label("Clear TX"); + + gtk_toggle_button_set_active((GtkToggleButton *)check1, OnlyMine); + gtk_toggle_button_set_active((GtkToggleButton *)check2, OnlySeq); + gtk_toggle_button_set_active((GtkToggleButton *)check3, ShowBulls); + gtk_toggle_button_set_active((GtkToggleButton *)check4, AllSSID); + + g_signal_connect(G_OBJECT(check1), "clicked", G_CALLBACK(check_callback), "1"); + g_signal_connect(G_OBJECT(check2), "clicked", G_CALLBACK(check_callback), "2"); + g_signal_connect(G_OBJECT(check3), "clicked", G_CALLBACK(check_callback), "3"); + g_signal_connect(G_OBJECT(check4), "clicked", G_CALLBACK(check_callback), "4"); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_callback), "1"); + g_signal_connect(G_OBJECT(button2), "clicked", G_CALLBACK(button2_callback), "1"); + + // hBox for Check boxes + + checkhbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (checkhbox), checklabel, FALSE, FALSE, 0); + gtk_container_add (GTK_CONTAINER (checkhbox), check1); + gtk_container_add (GTK_CONTAINER (checkhbox), check4); + gtk_container_add (GTK_CONTAINER (checkhbox), check2); + gtk_container_add (GTK_CONTAINER (checkhbox), check3); + gtk_container_add (GTK_CONTAINER (checkhbox), button); + gtk_container_add (GTK_CONTAINER (checkhbox), button2); + checklabel = gtk_label_new(" "); + gtk_container_add (GTK_CONTAINER (checkhbox), checklabel); + + gtk_box_pack_start (GTK_BOX (box1), checkhbox, FALSE, FALSE, 0); + + box10 = gtk_vbox_new (FALSE, 0); + +// menubar = get_menubar_menu (window); + +// gtk_box_pack_start (GTK_BOX (box1), menubar, FALSE, TRUE, 1); + gtk_container_add (GTK_CONTAINER (box1), box10); + gtk_widget_show (window); + + vpaned = gtk_vpaned_new (); + gtk_container_add (GTK_CONTAINER (box10), vpaned); + gtk_paned_set_position(GTK_PANED(vpaned), Split); + gtk_widget_show (vpaned); + + /* Now create the contents of the two halves of the window */ + + frame1 = create_received_window(); + gtk_paned_add1 (GTK_PANED (vpaned), frame1); + gtk_widget_show (frame1); + + frame2 = create_sent_window(); + gtk_paned_add2 (GTK_PANED (vpaned), frame2); + gtk_widget_show (frame2); + +// separator = gtk_hseparator_new (); +// gtk_box_pack_start(GTK_BOX (box1), separator, FALSE, TRUE, 0); + + box2 = gtk_hbox_new(FALSE, 10); + gtk_container_set_border_width(GTK_CONTAINER (box2), 1); + gtk_box_pack_start(GTK_BOX (box10), box2, FALSE, FALSE, 0); + + // set up the text entry line + + label1 = gtk_label_new(" To"); + label2 = gtk_label_new("Message"); + combo = gtk_combo_box_text_new_with_entry(); + gtk_widget_set_size_request(combo, 100, 10); + + entry = gtk_entry_new(); +// gtk_widget_set_size_request(entry, 100, 20); //gtk_entry_new_with_buffer(text); + gtk_entry_set_max_length(GTK_ENTRY(entry), 100); + gtk_entry_set_activates_default(GTK_ENTRY (entry), TRUE); + g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK(enter_callback), (gpointer)entry); + gtk_box_pack_start(GTK_BOX(box2), label1, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(box2), combo, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(box2), label2, FALSE, FALSE, 0); + gtk_container_add(GTK_CONTAINER(box2), entry); + gtk_widget_grab_focus(entry); + + + + + font_desc=pango_font_description_from_string(MyFont); + gtk_widget_modify_font (entry, font_desc); + gtk_widget_modify_font (combo, font_desc); + gtk_widget_modify_font (view, font_desc); + gtk_widget_modify_font (view2, font_desc); + + gtk_widget_show_all (window); + gtk_widget_show (window); + + // Main loop + + _beginthread(GTKThread, 0, NULL); + _beginthread(SecTimer, 0, NULL); + + AutoFilterTimer = AUTOFILTERDELAY; // Update filter if no change for 30 secs + + while(Running) + { + unsigned char Msg[256]; + int numBytes; + struct STATIONRECORD * Station; + + FD_ZERO(&in_fds); + FD_SET(x11_fd, &in_fds); + FD_SET(sfd, &in_fds); + + tv.tv_usec = 0; + tv.tv_sec = 1; + + // Wait for X Event, Message from LinBPQ or a Timer + + if (select(maxfd+1, &in_fds, 0, 0, &tv)) + { + if (FD_ISSET(sfd, &in_fds)) + { + numBytes = recvfrom(sfd, Msg, 256, 0, NULL, NULL); + } + + // may be X event, but pick up later + } + else + { + // Handle timer here + + if (SMEM->NeedRefresh) + { + SMEM->NeedRefresh = FALSE; + + // Use Checkbox event to rewrite display + + check_callback((GtkButton *)check1, "1"); + RefreshTXList(); + } + + // NeedRedraw += RefreshStationMap(FALSE); // Draw new or moved stations + + // Do a full redraw at least evey 2 mins if anything has changed + +// SlowTimer++; +// if (SlowTimer > 40) // 2 Mins +// if (NeedRedraw) +// NeedRefresh = TRUE; + } + + // Handle XEvents and flush the input + + while(Running && XPending(display)) + { + XNextEvent(display, &event); + + MouseX = event.xbutton.x - leftBorder; + MouseY = event.xbutton.y - topBorder; + + GetMouseLatLon(&MouseLat, &MouseLon); + + switch (event.type) + { + + case ClientMessage: + + if (event.xclient.data.l[0] == wmDeleteMessage) + Running = FALSE; + break; + + case KeyPress: + + xkeyev = event.xkey; + + XLookupString(&event.xkey, text, 255, &key, 0); + +// printf("Key %c Hex %x Code %d %x\n", text[0], text[0], key, key); + + switch(key) + { + case XK_Left: + + WindowX -= 8; + XMoveWindow(display, win, WindowX, WindowY); + break; + + case XK_Up: + + WindowY -= 8; + XMoveWindow(display, win, WindowX, WindowY); + break; + + case XK_Right: + + WindowX += 8; + XMoveWindow(display, win, WindowX, WindowY); + break; + + case XK_Down: + + WindowY += 8; + XMoveWindow(display, win, WindowX, WindowY); + break; + + case '-': + + ZoomOut(); + break; + + case '=': + case '+': + + ZoomIn(); + break; + } + + break; + + case Expose: + + XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); + break; + + case ConfigureNotify: + + xce = event.xconfigure; + + // This event type is generated for a variety of + // happenings, so check whether the window has been + // resized. + + WindowX = xce.x; + WindowY = xce.y; + WindowWidth = xce.width; + WindowHeight = xce.height; + + if (xce.width != cxWinSize || xce.height != cyWinSize) + { + cxWinSize = xce.width; + cyWinSize = xce.height; + cxImgSize = cxWinSize - (leftBorder + rightBorder); + cyImgSize = cyWinSize - (topBorder + bottomBorder); + + XClearWindow(display, win); + XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); + } + + break; + + case MotionNotify: + + FindStationsByPixel(MouseX + ScrollX, MouseY + ScrollY); + break; + + + case ButtonPress: + + switch (event.xbutton.button) + { + case 1: // Left Button + + LastX = MouseX; + LastY = MouseY; + break; + + case 4: // Scrollup + + ZoomIn(); + break; + + case 5: // Scrolldown + + ZoomOut(); + break; + } + + break; + + case ButtonRelease: + + if (popupActive && (event.xbutton.time - lastupevent) < 300) + { + // Double Click on Station + + char Key[32]; + char LoppedCall[10]; + int n = 8; + + memcpy(LoppedCall, popupStn->Callsign, 9); + + while (n && LoppedCall[n] == ' ') + { + n--; + } + + if (n) + LoppedCall[n + 1] = 0; + + sprintf(Key, "|%s|", LoppedCall); + + // if new call add to combo box + + if (strstr(ToCalls, Key) == 0) + { + if (strlen(ToCalls) < 1000) + strcat(ToCalls, Key); + + gtk_combo_box_text_prepend_text ((GtkComboBoxText *)combo, LoppedCall); + } + } + + lastupevent = event.xbutton.time; + + switch (event.xbutton.button) + { + case 1: // Left Button + + // if a Popup is on display, then select station, else scroll map + + if (selActive) + { + GetStationFromList(MouseX, MouseY); + break; + } + + MovedX = MouseX - LastX; + MovedY = MouseY - LastY; + + if (MovedX == 0 && MovedY == 0) + break; + + ScrollX -= (MovedX); + ScrollY -= (MovedY); + + while (ScrollX < 0) + { + TileX--; + ScrollX += 256; + } + + while (ScrollX > 255) + { + TileX++; + ScrollX -= 256; + } + + if (TileX < 0) + { + TileX = 0; + ScrollX = 0; + } + + while (ScrollY < 0) + { + TileY--; + ScrollY += 256; + } + + while (ScrollY > 255) + { + TileY++; + ScrollY -= 256; + } + + if (TileY < 0) + { + TileY = 0; + ScrollY = 0; + } + + NeedRefresh = TRUE; + AutoFilterTimer = AUTOFILTERDELAY; // Update filter if no change for 30 secs + + break; + } + break; + } + } // end of while xpending + + if (popupActive || selActive) + { + } + else + { + if (NeedRefresh) + { + SetBaseX = -1; + LoadImageSet(Zoom, TileX, TileY); + NeedRefresh = FALSE; + SlowTimer = 0; + } + if (NeedRedraw) + { + NeedRedraw = 0; + XPutImage (display, win, gc, image, ScrollX, ScrollY, leftBorder, topBorder, cxImgSize, cyImgSize); + } + } + } + + SaveConfig(); + + status = XDestroyImage (image); + return 0; +} + +void PutAPRSMessage(char * Frame, int Len) +{ + if (sendto(sfd, Frame, Len, 0, (struct sockaddr *) &peer_addr, sizeof(struct sockaddr_un)) != Len) + perror("sendto"); +} + +VOID SendFilterCommand(char * Filter) +{ + char Msg[2000]; + int n; + + strcpy(Msg, "SERVER"); + strcpy(&Msg[10], Filter); + + PutAPRSMessage(Msg, strlen(&Msg[10]) + 11); + + strcpy(&Msg[10], "filter?"); + PutAPRSMessage(Msg, strlen(&Msg[10]) + 11); +} + +void RefreshTXList() +{ + struct APRSMESSAGE * Message = SMEM->OutstandingMsgs; + int n = 0; + + GtkTreeIter iter; + gchar *seq; + char status[10]; + + // Clear old list + + if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(sentitems), &iter, NULL, 0)) + { + while (gtk_list_store_remove(sentitems, &iter)) + {} + } + while (Message) + { + if (Message->Acked) + strcpy(status, "A"); + else if (Message->Cancelled) + strcpy(status, "C"); + else if (Message->Retries == 0) + strcpy(status, "F"); + else + sprintf(status, "%d", Message->Retries); + + gtk_list_store_insert_with_values( + sentitems, &iter, -1, + 0, Message->ToCall, + 1, Message->Seq, + 2, status, + 3, Message->Time, + 4, Message->Text, -1); + n++; + + Message = Message->Next; + } + + if (n) + gtk_tree_view_scroll_to_cell ((GtkTreeView *)view, + gtk_tree_model_get_path (GTK_TREE_MODEL(sentitems), &iter), NULL, FALSE, 0, 0); +} + +VOID SendAPRSMessage(const char * Text, char * ToCall) +{ + char Msg[255]; + + strcpy(Msg, ToCall); + strcpy(&Msg[10], Text); + + PutAPRSMessage(Msg, strlen(&Msg[10]) + 11); + + return; +} + +VOID SecTimer() +{ + while (TRUE) + { + struct STATIONRECORD * sptr = *StationRecords; + int n = 0; + char Msg[20]; + + if (Host1Down) + Host1Down --; + + if (Host2Down) + Host2Down --; + + + // See if changed flag set on any stations + + NeedRedraw += RefreshStationMap(FALSE); // Draw new or moved stations + + // Do a full redraw at least evey 2 mins if anything has changed + + SlowTimer++; + if (SlowTimer > 120) // 2 Mins + if (NeedRedraw) + NeedRefresh = TRUE; + +/* + while (sptr) + { + if (sptr->Moved) + DrawStation(sptr, FALSE); + + sptr = sptr->Next; + } +*/ + JPEGCounter++; + + if (CreateJPEG) + { + if (JPEGCounter > JPEGInterval) + { + if (RGBToJpegFile(JPEGFileName, Image, cxImgSize, cyImgSize, TRUE, 50)) + JPEGCounter = 0; + } + } + +/* + if (SendWX) + SendWeatherBeacon(); + + // If any changes to image redraw it + + if (ImageChanged) + { + // We have drawn a new Icon. As we only redraw if it has moved, + // we need to reload image every now and again to get rid of ghost images + + time_t NOW = time(NULL); + + if ((NOW - LastRefresh) > 10) + { + LastRefresh = NOW; + ReloadMaps = TRUE; + } + + ImageChanged = FALSE; + InvalidateRect(hMapWnd, NULL, FALSE); + } + + wsprintf(Msg, "%d", StationCount); + SendMessage(hStatus, SB_SETTEXT, (WPARAM)(INT) 0 | 1, (LPARAM)Msg); + + wsprintf(Msg, "%d", OSMQueueCount); + SendMessage(hStatus, SB_SETTEXT, (WPARAM)(INT) 0 | 2, (LPARAM)Msg); + + wsprintf(Msg, "%d", Zoom); + SendMessage(hStatus, SB_SETTEXT, (WPARAM)(INT) 0 | 3, (LPARAM)Msg); +*/ + + if (AutoFilterTimer) + { + AutoFilterTimer--; + + if (AutoFilterTimer == 0 && AddViewToFilter) + { + // send filter to IS + + double TLLat, TLLon, BRLat, BRLon; + char Filter[256]; + + GetCornerLatLon(&TLLat, &TLLon, &BRLat, &BRLon); + sprintf(Filter, "%s a/%.3f/%.3f/%.3f/%.3f", ISFilter, TLLat, TLLon, BRLat, BRLon); + + SendFilterCommand(Filter); + } + } + + Sleep(1000); + } +} + +BOOL RGBToJpegFile(char * fileName, BYTE *dataBuf, UINT widthPix, UINT height, BOOL color, int quality) +{ + struct jpeg_compress_struct cinfo; + struct my_error_mgr jerr; + FILE * outfile=NULL; + unsigned char * RGBBuff = malloc(widthPix * 3); // no idea why + unsigned char * ptr1, * ptr2; + int n; + unsigned int val, r, g, b; + char Message[32]; + int X, Y, j, Len; + struct tm * TM; + time_t NOW = time(NULL); + + if (dataBuf==NULL) + return FALSE; + if (widthPix==0) + return FALSE; + if (height==0) + return FALSE; + + // Write a Date/Time stamp to top left + + if (LocalTime) + TM = localtime(&NOW); + else + TM = gmtime(&NOW); + + Len = sprintf(Message, "%02d:%02d:%02d %02d %s %04d", + TM->tm_hour, TM->tm_min, TM->tm_sec, + TM->tm_mday, month[TM->tm_mon], TM->tm_year + 1900); + + X = ScrollX; + Y = ScrollY + 8; + + for (j = 0; j < Len; j++) + { + DrawCharacter(X, Y, j, Message[j]); + } + + + + /* More stuff */ + /* Step 1: allocate and initialize JPEG compression object */ + + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = my_error_exit; + + /* Establish the setjmp return context for my_error_exit to use. */ + + if (setjmp(jerr.setjmp_buffer)) + { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + + jpeg_destroy_compress(&cinfo); + + if (outfile!=NULL) + fclose(outfile); + + return FALSE; + } + + /* Now we can initialize the JPEG compression object. */ + + jpeg_create_compress(&cinfo); + + /* Step 2: specify data destination (eg, a file) */ + /* Note: steps 2 and 3 can be done in either order. */ + + if ((outfile = fopen(fileName, "wb")) == NULL) + { +// char buf[250]; +// sprintf(buf, "JpegFile :\nCan't open %s\n", fileName); +// MessageBox(NULL, buf, "", 0); + return FALSE; + } + + jpeg_stdio_dest(&cinfo, outfile); + + /* Step 3: set parameters for compression */ + + /* First we supply a description of the input image. + * Four fields of the cinfo struct must be filled in: + */ + cinfo.image_width = widthPix; /* image widthPix and height, in pixels */ + cinfo.image_height = height; + + cinfo.input_components = 3; /* # of color components per pixel */ + cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + +/* Now use the library's routine to set default compression parameters. + * (You must set at least cinfo.in_color_space before calling this, + * since the defaults depend on the source color space.) + */ + + jpeg_set_defaults(&cinfo); + /* Now you can set any non-default parameters you wish to. + * Here we just illustrate the use of quality (quantization table) scaling: + */ + jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); + + /* Step 4: Start compressor */ + + /* TRUE ensures that we will write a complete interchange-JPEG file. + * Pass TRUE unless you are very sure of what you're doing. + */ + jpeg_start_compress(&cinfo, TRUE); + + /* Step 5: while (scan lines remain to be written) */ + /* jpeg_write_scanlines(...); */ + + /* Here we use the library's state variable cinfo.next_scanline as the + * loop counter, so that we don't have to keep track ourselves. + * To keep things simple, we pass one scanline per call; you can pass + * more if you wish, though. + */ + + while (cinfo.next_scanline < cinfo.image_height) + { + /* jpeg_write_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could pass + * more than one scanline at a time if that's more convenient. + */ + + unsigned char * outRow; + + outRow = RGBBuff; + + // We have to convert from 2 or 4 bytes to pixel to 3 byte rgb + + ptr1 = dataBuf + ((ScrollY * WIDTH) + ScrollX + (cinfo.next_scanline * WIDTH)) * Bytesperpixel; + + ptr2 = RGBBuff; + + for (n = 0; n < widthPix; n++) + { + if (Bytesperpixel == 2) + { + val = (*(ptr1++)); + val |= (*(ptr1++)) << 8; + + b = val << 3; + g = (val >> 5) << 2; + r = (val >> 11) << 3; + + *(ptr2++) = r; + *(ptr2++) = g; + *(ptr2++) = b; + } + else + { + *(ptr2++) = *(ptr1+2); + *(ptr2++) = *(ptr1+1); + *(ptr2++) = *(ptr1); + ptr1 += 4; + } + } + (void) jpeg_write_scanlines(&cinfo, &outRow, 1); + } + + /* Step 6: Finish compression */ + + jpeg_finish_compress(&cinfo); + + /* After finish_compress, we can close the output file. */ + fclose(outfile); + + /* Step 7: release JPEG compression object */ + + /* This is an important step since it will release a good deal of memory. */ + jpeg_destroy_compress(&cinfo); + + /* And we're done! */ + + return TRUE; +} + +VOID plot(int X, int Y, COLORREF rgb) +{ + char * nptr; + int i, j; + unsigned int val; + + if ((X > (WIDTH - 3)) || (Y > (HEIGHT - 3))) + return; + + nptr = &Image[(Y * WIDTH * Bytesperpixel) + (X * Bytesperpixel)]; + + for (j = 0; j < 2; j++) + { + for (i = 0; i < 2; i++) + { + if (Bytesperpixel == 4) + { + *(nptr++) = (rgb >> 16) & 0xff; + *(nptr++) = (rgb >> 8) & 0xff; + *(nptr++) = GetRValue(rgb); + nptr++; + } + else + { + val = ((rgb & 0xff) >> 3) << 11; // Red + val |= (GetGValue(rgb) >> 2) << 5; + val |= (rgb >> 19); // Blue + *(nptr++) = (val & 0xff); + *(nptr++) = (unsigned char)(val >> 8); + } + + } + nptr += (WIDTH - 2) * Bytesperpixel; + } +} + +// Algorithm assumes y increases slower than x. If not, swap x and y in plotline and plotpoint + +void plotLineTB(int x0, int y0, int x1, int y1, COLORREF rgb); +void plotLineLR(int x0, int y0, int x1, int y1, COLORREF rgb); + +void plotLine(int x0, int y0, int x1, int y1, COLORREF rgb) +{ + if (abs(x1 - x0) > abs(y1 - y0)) + plotLineLR(x0, y0, x1, y1, rgb); + else + plotLineTB(y0, x0, y1, x1, rgb); +} + +void plotLineLR(int x0, int y0, int x1, int y1, COLORREF rgb) +{ + int dx; + int dy; + int D, x, y; + + // Must draw with increacing x and y, but can draw either way round, so if x is decreasing, + // just swap ends, so we always draw left to right + + if (x0 > x1) + { + x = x0; + x0 = x1; + x1 = x; + + y = y0; + y0 = y1; + y1 = y; + } + + // if y is now decreasing, we must reverse algorithm + + if (y1 > y0) + { + dx = x1 - x0; + dy = y1 - y0; + D = 2 * dy - dx; + + plot (x0, y0, 0); + + y = y0; + + for (x = x0+1; x < x1; x++) + { + if (D < 0) + { + D = D + (2*dy); + } + else + { + y = y+1; + D = D + (2*dy-2*dx); + + } + plot(x, y, rgb); + } + } + else + { + dx = x1 - x0; + dy = y0 - y1; + D = 2 * dy - dx; + + plot (x0, y0, rgb); + + y = y0; + + for (x = x0+1; x <= x1; x++) + { + if (D > 0) + { + y = y-1; + plot(x, y, rgb); + D = D + (2*dy-2*dx); + } + else + { + plot(x, y, rgb); + D = D + (2*dy); + } + } + } + +} + +void plotLineTB(int x0, int y0, int x1, int y1, COLORREF rgb) +{ + int dx; + int dy; + int D, x, y; + + // Must draw with increacing x and y, but can draw either way round, so if x is decreasing, + // just swap ends, so we always draw left to right + + if (x0 > x1) + { + x = x0; + x0 = x1; + x1 = x; + + y = y0; + y0 = y1; + y1 = y; + } + + // if y is now decreasing, we must reverse algorithm + + if (y1 > y0) + { + dx = x1 - x0; + dy = y1 - y0; + D = 2 * dy - dx; + + plot (y0, x0, 0); + + y = y0; + + for (x = x0+1; x < x1; x++) + { + if (D < 0) + { + D = D + (2*dy); + } + else + { + y = y+1; + D = D + (2*dy-2*dx); + + } + plot(y, x, rgb); + } + } + else + { + dx = x1 - x0; + dy = y0 - y1; + D = 2 * dy - dx; + + plot (y0, x0, 0); + + y = y0; + + for (x = x0+1; x <= x1; x++) + { + if (D > 0) + { + y = y-1; + D = D + (2*dy-2*dx); + } + else + { + D = D + (2*dy); + } + plot(y, x, rgb); + + } + } +} + + +png_const_charp msg; + + +static png_structp png_ptr = NULL; +static png_infop info_ptr = NULL; + + +// cexcept interface + +static void +png_cexcept_error(png_structp png_ptr, png_const_charp msg) +{ + if(png_ptr) + ; +#ifndef PNG_NO_CONSOLE_IO + fprintf(stderr, "libpng error: %s\n", msg); +#endif + { +// Throw msg; + } +} + + +int LoadImageFile (void * hwnd, char * pstrPathName, + png_byte **ppbImage, int *pxImgSize, int *pyImgSize, + int *piChannels, png_color *pBkgColor) +{ + + // if there's an existing PNG, free the memory + + if (*ppbImage) + { + free (*ppbImage); + *ppbImage = NULL; + } + + PngLoadImage (pstrPathName, ppbImage, pxImgSize, pyImgSize, piChannels, + pBkgColor); + + + + if (*ppbImage != NULL) + { + // sprintf (szTmp, "VisualPng - %s", strrchr(pstrPathName, '\\') + 1); + // SetWindowText (hwnd, szTmp); + } + else + { + return FALSE; + } + + return TRUE; +} + +//---------------- +// PNG image handler functions + +BOOL PngLoadImage (char * pstrFileName, png_byte **ppbImageData, + png_uint_32 *piWidth, png_uint_32 *piHeight, int *piChannels, png_color *pBkgColor) +{ + static FILE *pfFile; + png_byte pbSig[8]; + int iBitDepth; + int iColorType; + double dGamma; + png_color_16 *pBackground; + png_uint_32 ulChannels; + png_uint_32 ulRowBytes; + png_byte *pbImageData = *ppbImageData; + static png_byte **ppbRowPointers = NULL; + int i; + + // open the PNG input file + + if (!pstrFileName) + { + *ppbImageData = pbImageData = NULL; + printf("Load PNG Failed 1\n"); + return FALSE; + } + + if (!(pfFile = fopen(pstrFileName, "rb"))) + { + *ppbImageData = pbImageData = NULL; + printf("Load PNG Failed 2\n"); + return FALSE; + } + + // first check the eight byte PNG signature + + fread(pbSig, 1, 8, pfFile); + + if(png_sig_cmp(pbSig, 0, 8) != 0) + //if (!png_check_sig(pbSig, 8)) + { + *ppbImageData = pbImageData = NULL; + + if (pfFile) + fclose (pfFile); + + printf("Bad file %s", pstrFileName); + unlink(pstrFileName); + + return FALSE; + } + + // create the two png(-info) structures + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, + (png_error_ptr)png_cexcept_error, (png_error_ptr)NULL); + if (!png_ptr) + { + *ppbImageData = pbImageData = NULL; + printf("Load PNG Failed 4\n"); + return FALSE; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + *ppbImageData = pbImageData = NULL; + printf("Load PNG Failed 5\n"); + return FALSE; + } + +// Try + { + + // initialize the png structure + +#if !defined(PNG_NO_STDIO) + png_init_io(png_ptr, pfFile); +#else + png_set_read_fn(png_ptr, (png_voidp)pfFile, png_read_data); +#endif + + png_set_sig_bytes(png_ptr, 8); + + // read all PNG info up to image data + + png_read_info(png_ptr, info_ptr); + + // get width, height, bit-depth and color-type + + png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth, + &iColorType, NULL, NULL, NULL); + + // expand images of all color-type and bit-depth to 3x8 bit RGB images + // let the library process things like alpha, transparency, background + + if (iBitDepth == 16) + png_set_strip_16(png_ptr); + if (iColorType == PNG_COLOR_TYPE_PALETTE) + png_set_expand(png_ptr); + if (iBitDepth < 8) + png_set_expand(png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand(png_ptr); + if (iColorType == PNG_COLOR_TYPE_GRAY || + iColorType == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + // set the background color to draw transparent and alpha images over. + if (png_get_bKGD(png_ptr, info_ptr, &pBackground)) + { + png_set_background(png_ptr, pBackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); + pBkgColor->red = (byte) pBackground->red; + pBkgColor->green = (byte) pBackground->green; + pBkgColor->blue = (byte) pBackground->blue; + } + else + { + pBkgColor = NULL; + } + + // if required set gamma conversion + if (png_get_gAMA(png_ptr, info_ptr, &dGamma)) + png_set_gamma(png_ptr, (double) 2.2, dGamma); + + // after the transformations have been registered update info_ptr data + + png_read_update_info(png_ptr, info_ptr); + + // get again width, height and the new bit-depth and color-type + + png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth, + &iColorType, NULL, NULL, NULL); + + + // row_bytes is the width x number of channels + + ulRowBytes = png_get_rowbytes(png_ptr, info_ptr); + ulChannels = png_get_channels(png_ptr, info_ptr); + + *piChannels = ulChannels; + + // now we can allocate memory to store the image + + if (pbImageData) + { + free (pbImageData); + pbImageData = NULL; + } + if ((pbImageData = (png_byte *) malloc(ulRowBytes * (*piHeight) + * sizeof(png_byte))) == NULL) + { + png_error(png_ptr, "Visual PNG: out of memory"); + } + *ppbImageData = pbImageData; + + // and allocate memory for an array of row-pointers + + if ((ppbRowPointers = (png_bytepp) malloc((*piHeight) + * sizeof(png_bytep))) == NULL) + { + png_error(png_ptr, "Visual PNG: out of memory"); + } + + // set the individual row-pointers to point at the correct offsets + + for (i = 0; i < (*piHeight); i++) + ppbRowPointers[i] = pbImageData + i * ulRowBytes; + + // now we can go ahead and just read the whole image + + png_read_image(png_ptr, ppbRowPointers); + + // read the additional chunks in the PNG file (not really needed) + + png_read_end(png_ptr, NULL); + + // and we're done + + free (ppbRowPointers); + ppbRowPointers = NULL; + + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + + // yepp, done + } +/* + Catch (msg) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + *ppbImageData = pbImageData = NULL; + + if(ppbRowPointers) + free (ppbRowPointers); + + fclose(pfFile); + + return FALSE; + } +*/ + if (pfFile) + fclose (pfFile); + + return TRUE; +} + diff --git a/.svn/pristine/09/091a2424fc9a479a4b97e349b069851ef8cd3fc2.svn-base b/.svn/pristine/09/091a2424fc9a479a4b97e349b069851ef8cd3fc2.svn-base new file mode 100644 index 0000000..47a463d --- /dev/null +++ b/.svn/pristine/09/091a2424fc9a479a4b97e349b069851ef8cd3fc2.svn-base @@ -0,0 +1,135 @@ +/* LzmaLib.h -- LZMA library interface +2008-08-05 +Igor Pavlov +Public domain */ + +#ifndef __LZMALIB_H +#define __LZMALIB_H + +#include "types.h" + +#ifdef __cplusplus + #define MY_EXTERN_C extern "C" +#else + #define MY_EXTERN_C extern +#endif + +#define MY_STDAPI MY_EXTERN_C int MY_STD_CALL + +#define LZMA_PROPS_SIZE 5 + +/* +RAM requirements for LZMA: + for compression: (dictSize * 11.5 + 6 MB) + state_size + for decompression: dictSize + state_size + state_size = (4 + (1.5 << (lc + lp))) KB + by default (lc=3, lp=0), state_size = 16 KB. + +LZMA properties (5 bytes) format + Offset Size Description + 0 1 lc, lp and pb in encoded form. + 1 4 dictSize (little endian). +*/ + +/* +LzmaCompress +------------ + +outPropsSize - + In: the pointer to the size of outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. + Out: the pointer to the size of written properties in outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. + + LZMA Encoder will use defult values for any parameter, if it is + -1 for any from: level, loc, lp, pb, fb, numThreads + 0 for dictSize + +level - compression level: 0 <= level <= 9; + + level dictSize algo fb + 0: 16 KB 0 32 + 1: 64 KB 0 32 + 2: 256 KB 0 32 + 3: 1 MB 0 32 + 4: 4 MB 0 32 + 5: 16 MB 1 32 + 6: 32 MB 1 32 + 7+: 64 MB 1 64 + + The default value for "level" is 5. + + algo = 0 means fast method + algo = 1 means normal method + +dictSize - The dictionary size in bytes. The maximum value is + 128 MB = (1 << 27) bytes for 32-bit version + 1 GB = (1 << 30) bytes for 64-bit version + The default value is 16 MB = (1 << 24) bytes. + It's recommended to use the dictionary that is larger than 4 KB and + that can be calculated as (1 << N) or (3 << N) sizes. + +lc - The number of literal context bits (high bits of previous literal). + It can be in the range from 0 to 8. The default value is 3. + Sometimes lc=4 gives the gain for big files. + +lp - The number of literal pos bits (low bits of current position for literals). + It can be in the range from 0 to 4. The default value is 0. + The lp switch is intended for periodical data when the period is equal to 2^lp. + For example, for 32-bit (4 bytes) periodical data you can use lp=2. Often it's + better to set lc=0, if you change lp switch. + +pb - The number of pos bits (low bits of current position). + It can be in the range from 0 to 4. The default value is 2. + The pb switch is intended for periodical data when the period is equal 2^pb. + +fb - Word size (the number of fast bytes). + It can be in the range from 5 to 273. The default value is 32. + Usually, a big number gives a little bit better compression ratio and + slower compression process. + +numThreads - The number of thereads. 1 or 2. The default value is 2. + Fast mode (algo = 0) can use only 1 thread. + +Out: + destLen - processed output size +Returns: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, + unsigned char *outProps, size_t *outPropsSize, /* *outPropsSize must be = 5 */ + int level, /* 0 <= level <= 9, default = 5 */ + unsigned dictSize, /* default = (1 << 24) */ + int lc, /* 0 <= lc <= 8, default = 3 */ + int lp, /* 0 <= lp <= 4, default = 0 */ + int pb, /* 0 <= pb <= 4, default = 2 */ + int fb, /* 5 <= fb <= 273, default = 32 */ + int numThreads /* 1 or 2, default = 2 */ + ); + +/* +LzmaUncompress +-------------- +In: + dest - output data + destLen - output data size + src - input data + srcLen - input data size +Out: + destLen - processed output size + srcLen - processed input size +Returns: + SZ_OK - OK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation arror + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - it needs more bytes in input buffer (src) +*/ + +MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, SizeT *srcLen, + const unsigned char *props, size_t propsSize); + +#endif diff --git a/.svn/pristine/0a/0a715995817f6af3c897a4b559859d3abacad0d6.svn-base b/.svn/pristine/0a/0a715995817f6af3c897a4b559859d3abacad0d6.svn-base new file mode 100644 index 0000000..a57a91c Binary files /dev/null and b/.svn/pristine/0a/0a715995817f6af3c897a4b559859d3abacad0d6.svn-base differ diff --git a/.svn/pristine/0a/0ab80fa8538be287532e4ec6563efe6456432d68.svn-base b/.svn/pristine/0a/0ab80fa8538be287532e4ec6563efe6456432d68.svn-base new file mode 100644 index 0000000..17a7946 --- /dev/null +++ b/.svn/pristine/0a/0ab80fa8538be287532e4ec6563efe6456432d68.svn-base @@ -0,0 +1,30 @@ +/* See md5.c for explanation and copyright information. */ + +/* + * $FreeBSD: src/contrib/cvs/lib/md5.h,v 1.2 1999/12/11 15:10:02 peter Exp $ + */ + +#ifndef MD5_H +#define MD5_H + +/* Unlike previous versions of this code, uint32 need not be exactly + 32 bits, merely 32 bits or more. Choosing a data type which is 32 + bits instead of 64 is not important; speed is considerably more + important. ANSI guarantees that "unsigned long" will be big enough, + and always using it seems to have few disadvantages. */ +typedef unsigned long cvs_uint32; + +struct cvs_MD5Context { + cvs_uint32 buf[4]; + cvs_uint32 bits[2]; + unsigned char in[64]; +}; + +void cvs_MD5Init(struct cvs_MD5Context *context); +void cvs_MD5Update(struct cvs_MD5Context *context, + unsigned char const *buf, unsigned len); +void cvs_MD5Final(unsigned char digest[16], + struct cvs_MD5Context *context); +void cvs_MD5Transform(cvs_uint32 buf[4], const unsigned char in[64]); + +#endif /* !MD5_H */ diff --git a/.svn/pristine/0b/0b2525a7593ec9c79302aca324e8bd42b0ed4e56.svn-base b/.svn/pristine/0b/0b2525a7593ec9c79302aca324e8bd42b0ed4e56.svn-base new file mode 100644 index 0000000..18e0979 --- /dev/null +++ b/.svn/pristine/0b/0b2525a7593ec9c79302aca324e8bd42b0ed4e56.svn-base @@ -0,0 +1,6751 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modifyextern int HTTP +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 +*/ +// +// 409l Oct 2001 Fix l3timeout for KISS +// +// 409m Oct 2001 Fix Crossband Digi +// +// 409n May 2002 Change error handling on load ext DLL + +// 409p March 2005 Allow Multidigit COM Ports (kiss.c) + +// 409r August 2005 Treat NULL string in Registry as use current directory +// Allow shutdown to close BPQ Applications + +// 409s October 2005 Add DLL:Export entries to API for BPQTNC2 + +// 409t January 2006 +// +// Add API for Perl "GetPerlMsg" +// Add API for BPQ1632 "GETBPQAPI" - returns address of Assembler API routine +// Add Registry Entry "BPQ Directory". If present, overrides "Config File Location" +// Add New API "GetBPQDirectory" - Returns location of config file +// Add New API "ChangeSessionCallsign" - equivalent to "*** linked to" command +// Rename BPQNODES to BPQNODES.dat +// New API "GetAttachedProcesses" - returns number of processes connected. +// Warn if user trys to close Console Window. +// Add Debug entries to record Process Attach/Detach +// Fix recovery following closure of first process + +// 409t Beta 2 February 2006 +// +// Add API Entry "GetPortNumber" +// +// 409u February 2006 +// +// Fix crash if allocate/deallocate called with stream=0 +// Add API to ch +// Display config file path +// Fix saving of Locked Node flag +// Added SAVENODES SYSOP command +// +// 409u 2 March 2006 +// +// Fix SetupBPQDirectory +// Add CopyBPQDirectory (for Basic Programs) +// +// 409u 3 March 2006 +// +// Release streams on DLL unload + +// 409v October 2006 +// +// Support Minimize to Tray for all BPQ progams +// Implement L4 application callsigns + +// 410 November 2006 +// +// Modified to compile with C++ 2005 Express Edition +// Make MCOM MTX MMASK local variables +// +// 410a January 2007 +// +// Add program name to Attach-Detach messages +// Attempt to detect processes which have died +// Fix bug in NETROM and IFrame decode which would cause crash if frame was corrupt +// Add BCALL - origin call for Beacons +// Fix KISS ACKMODE ACK processing +// + +// 410b November 2007 +// +// Allow CTEXT of up to 510, and enforce PACLEN, fragmenting if necessary + +// 410c December 2007 + +// Fix problem with NT introduced in V410a +// Display location of DLL on Console + +// 410d January 2008 + +// Fix crash in DLL Init caused by long path to program +// Invoke Appl2 alias on C command (if enabled) +// Allow C command to be disabled +// Remove debug trap in GETRAWFRAME +// Validate Alias of directly connected node, mainly for KPC3 DISABL Problem +// Move Port statup code out of DLLInit (mainly for perl) +// Changes to allow Load/Unload of bpq32.dll by appl +// CloseBPQ32 API added +// Ext Driver Close routes called +// Changes to release Mutex + +// 410e May 2008 + +// Fix missing SSID on last call of UNPROTO string (CONVTOAX25 in main.asm) +// Fix VCOM Driver (RX Len was 1 byte too long) +// Fix possible crash on L4CODE if L4DACK received out of sequence +// Add basic IP decoding + +// 410f October 2008 + +// Add IP Gateway +// Add Multiport DIGI capability +// Add GetPortDescription API +// Fix potential hangs if RNR lost +// Fix problem if External driver failes to load +// Put pushad/popad round _INITIALISEPORTS (main.asm) +// Add APIs GetApplCallVB and GetPortDescription (mainly for RMS) +// Ensure Route Qual is updated if Port Qual changed +// Add Reload Option, plus menu items for DUMP and SAVENODES + +// 410g December 2008 + +// Restore API Exports BPQHOSTAPIPTR and MONDECODEPTR (accidentally deleted) +// Fix changed init of BPQDirectory (accidentally changed) +// Fix Checks for lost processes (accidentally deleted) +// Support HDLC Cards on W2K and above +// Delete Tray List entries for crashed processes +// Add Option to NODES command to sort by Callsign +// Add options to save or clear BPQNODES before Reconfig. +// Fix Reconfig in Win98 +// Monitor buffering tweaks +// Fix Init for large (>64k) tables +// Fix Nodes count in Stats + +// 410h January 2009 + +// Add Start Minimized Option +// Changes to KISS for WIn98 Virtual COM +// Open \\.\com instead of //./COM +// Extra Dignostics + +// 410i Febuary 2009 + +// Revert KISS Changes +// Save Window positions + +// 410j June 2009 + +// Fix tidying of window List when program crashed +// Add Max Nodes to Stats +// Don't update APPLnALIAS with received NODES info +// Fix MH display in other timezones +// Fix Possible crash when processing NETROM type Zero frames (eg NRR) +// Basic INP3 Stuff +// Add extra diagnostics to Lost Process detection +// Process Netrom Record Route frames. + +// 410k June 2009 + +// Fix calculation of %retries in extended ROUTES display +// Fix corruption of ROUTES table + +// 410l October 2009 + +// Add GetVersionString API call. +// Add GetPortTableEntry API call +// Keep links to neighbouring nodes open + +// Build 2 + +// Fix PE in NOROUTETODEST (missing POP EBX) + +// 410m November 2009 + +// Changes for PACTOR and WINMOR to support the ATTACH command +// Enable INP3 if configured on a route. +// Fix count of nodes in Stats Display +// Overwrite the worst quality unused route if a call is received from a node not in your +// table when the table is full + +// Build 5 + +// Rig Control Interface +// Limit KAM VHF attach and RADIO commands to authorised programs (MailChat and BPQTerminal) + +// Build 6 + +// Fix reading INP3 Flag from BPQNODES + +// Build 7 + +// Add MAXHOPS and MAXRTT config options + +// Build 8 + +// Fix INP3 deletion of Application Nodes. +// Fix GETCALLSIGN for Pactor Sessions +// Add N Call* to display all SSID's of a call +// Fix flow control on Pactor sessions. + +// Build 9 + +// HDLC Support for XP +// Add AUTH routines + +// Build 10 + +// Fix handling commands split over more that one packet. + +// Build 11 + +// Attach cmd changes for winmor disconnecting state +// Option Interlock Winmor/Pactor ports + +// Build 12 + +// Add APPLS export for winmor +// Handle commands ending CR LF + +// Build 13 + +// Incorporate Rig Control in Kernel + +// Build 14 + +// Fix config reload for Rig COntrol + +// 410n March 2010 + +// Implement C P via PACTOR/WINMOR (for Airmail) + +// Build 2 + +// Don't flip SSID bits on Downlink Connect if uplink is Pactor/WINMOR +// Fix resetting IDLE Timer on Pactor/WINMOR sessions +// Send L4 KEEPLI messages based on IDLETIME + +// 410o July 2010 + +// Read bpqcfg.txt instead of .bin +// Support 32 bit MMASK (Allowing 32 Ports) +// Support 32 bit _APPLMASK (Allowing 32 Applications) +// Allow more commands +// Allow longer command aliases +// Fix logic error in RIGControl Port Initialisation (wasn't always raising RTS and DTR +// Clear RIGControl RTS and DTR on close + +// 410o Build 2 August 2010 + +// Fix couple of errors in config (needed APPLICATIONS and BBSCALL/ALIAS/QUAL) +// Fix Kenwood Rig Control when more than one message received at once. +// Save minimzed state of Rigcontrol Window + +// 410o Build 3 August 2010 + +// Fix reporting of set errors in scan to a random session + +// 410o Build 4 August 2010 + +// Change All xxx Ports are in use to no xxxx Ports are available if there are no sessions with _APPLMASK +// Fix validation of TRANSDELAY + +// 410o Build 5 August 2010 + +// Add Repeater Shift and Set Data Mode options to Rigcontrol (for ICOM only) +// Add WINMOR and SCS Pactor mode control option to RigControl +// Extend INFOMSG to 2000 bytes +// Improve Scan freq change lock (check both SCS and WINMOR Ports) + +// 410o Build 6 September 2010 + +// Incorporate IPGateway in main code. +// Fix GetSessionInfo for Pactor/Winmor Ports +// Add Antenna Selection to RigControl +// Allow Bandwidth options on RADIO command line (as well as in Scan definitions) + +// 410o Build 7 September 2010 + +// Move rigconrtol display to driver windows +// Move rigcontrol config to driver config. +// Allow driver and IPGateway config info in bpq32.cfg +// Move IPGateway, AXIP, VKISS, AGW and WINMOR drivers into bpq32.dll +// Add option to reread IP Gateway config. +// Fix Reinit after process with timer closes (error in TellSessions). + +// 410p Build 2 October 2010 + +// Move KAM and SCS drivers to bpq32.dll + +// 410p Build 3 October 2010 + +// Support more than one axip port. + +// 410p Build 4 October 2010 + +// Dynamically load psapi.dll (for 98/ME) + +// 410p Build 5 October 2010 + +// Incorporate TelnetServer +// Fix AXIP ReRead Config +// Report AXIP accept() fails to syslog, not a popup. + +// 410p Build 6 October 2010 + +// Includes HAL support +// Changes to Pactor Drivers disconnect code +// AXIP now sends with source port = dest port, unless overridden by SOURCEPORT param +// Config now checks for duplicate port definitions +// Add Node Map reporting +// Fix WINMOR deferred disconnect. +// Report Pactor PORTCALL to WL2K instead of RMS Applcall + +// 410p Build 7 October 2010 + +// Add In/Out flag to Map reporting, and report centre, not dial +// Write Telnet log to BPQ Directory +// Add Port to AXIP resolver display +// Send Reports to update.g8bpq.net:81 +// Add support for FT100 to Rigcontrol +// Add timeout to Rigcontrol PTT +// Add Save Registry Command + +// 410p Build 8 November 2010 + +// Add NOKEEPALIVES Port Param +// Renumbered for release + +// 410p Build 9 November 2010 + +// Get Bandwith for map report from WL2K Report Command +// Fix freq display for FT100 (was KHz, not MHz) +// Don't try to change SCS mode whilst initialising +// Allow reporting of Lat/Lon as well as Locator +// Fix Telnet Log Name +// Fix starting with Minimized windows when Minimizetotray isn't set +// Extra Program Error trapping in SessionControl +// Fix reporting same freq with different bandwidths at different times. +// Code changes to support SCS Robust Packet Mode. +// Add FT2000 to Rigcontrol +// Only Send CTEXT to connects to Node (not to connects to an Application Call) + +// Released as Build 10 + +// 410p Build 11 January 2011 + +// Fix MH Update for SCS Outgoing Calls +// Add Direct CMS Access to TelnetServer +// Restructure DISCONNECT processing to run in Timer owning process + +// 410p Build 12 January 2011 + +// Add option for Hardware PTT to use a different com port from the scan port +// Add CAT PTT for Yaesu 897 (and maybe others) +// Fix RMS Packet ports busy after restart +// Fix CMS Telnet with MAXSESSIONS > 10 + +// 410p Build 13 January 2011 + +// Fix loss of buffers in TelnetServer +// Add CMS logging. +// Add non - Promiscuous mode option for BPQETHER + +// 410p Build 14 January 2011 + +// Add support for BPQTermTCP +// Allow more that one FBBPORT +// Allow Telnet FBB mode sessions to send CRLF as well as CR on user and pass msgs +// Add session length to CMS Telnet logging. +// Return Secure Session Flag from GetConnectionInfo +// Show Uptime as dd/hh/mm + +// 4.10.16.17 March 2011 + +// Add "Close all programs" command +// Add BPQ Program Directory registry key +// Use HKEY_CURRENT_USER on Vista and above (and move registry if necessary) +// Time out IP Gateway ARP entries, and only reload ax.25 ARP entries +// Add support for SCS Tracker HF Modes +// Fix WL2K Reporting +// Report Version to WL2K +// Add Driver to support Tracker with multiple sessions (but no scanning, wl2k report, etc) + + +// Above released as 5.0.0.1 + +// 5.2.0.1 + +// Add caching of CMS Server IP addresses +// Initialise TNC State on Pactor Dialogs +// Add Shortened (6 digit) AUTH mode. +// Update MH with all frames (not just I/UI) +// Add IPV6 Support for TelnetServer and AXIP +// Fix TNC OK Test for Tracker +// Fix crash in CMS mode if terminal disconnects while tcp commect in progress +// Add WL2K reporting for Robust Packet +// Add option to suppress WL2K reporting for specific frequencies +// Fix Timeband processing for Rig Control +// New Driver for SCS Tracker allowing multiple connects, so Tracker can be used for user access +// New Driver for V4 TNC + +// 5.2.1.3 October 2011 + +// Combine busy detector on Interlocked Ports (SCS PTC, WINMOR or KAM) +// Improved program error logging +// WL2K reporting changed to new format agreed with Lee Inman + +// 5.2.3.1 January 2012 + +// Connects from the console to an APPLCALL or APPLALIAS now invoke any Command Alias that has been defined. +// Fix reporting of Tracker freqs to WL2K. +// Fix Tracker monitoring setup (sending M UISC) +// Fix possible call/application routing error on RP +// Changes for P4Dragon +// Include APRS Digi/IGate +// Tracker monitoring now includes DIGIS +// Support sending UI frames using SCSTRACKER, SCTRKMULTI and UZ7HO drivers +// Include driver for UZ7HO soundcard modem. +// Accept DRIVER as well as DLLNAME, and COMPORT as well as IOADDR in bpq32.cfg. COMPORT is decimal +// No longer supports separate config files, or BPQTELNETSERVER.exe +// Improved flow control for Telnet CMS Sessions +// Fix handling Config file without a newline after last line +// Add non - Promiscuous mode option for BPQETHER +// Change Console Window to a Dialog Box. +// Fix possible corruption and loss of buffers in Tracker drivers +// Add Beacon After Session option to Tracker and UZ7HO Drivers +// Rewrite RigControl and add "Reread Config Command" +// Support User Mode VCOM Driver for VKISS ports + +// 5.2.4.1 January 2012 + +// Remove CR from Telnet User and Password Prompts +// Add Rigcontrol to UZ7HO driver +// Fix corruption of Free Buffer Count by Rigcontol +// Fix WINMOR and V4 PTT +// Add MultiPSK Driver +// Add SendBeacon export for BPQAPRS +// Add SendChatReport function +// Fix check on length of Port Config ID String with trailing spaces +// Fix interlock when Port Number <> Port Slot +// Add NETROMCALL for L3 Activity +// Add support for APRS Application +// Fix Telnet with FBBPORT and no TCPPORT +// Add Reread APRS Config +// Fix switching to Pactor after scanning in normal packet mode (PTC) + +// 5.2.5.1 February 2012 + +// Stop reading Password file. +// Add extra MPSK commands +// Fix MPSK Transparency +// Make LOCATOR command compulsory +// Add MobileBeaconInterval APRS param +// Send Course and Speed when APRS is using GPS +// Fix Robust Packet reporting in PTC driver +// Fix corruption of some MIC-E APRS packets + +// 5.2.6.1 February 2012 + +// Convert to MDI presentation of BPQ32.dll windows +// Send APRS Status packets +// Send QUIT not EXIT in PTC Init +// Implement new WL2K reporting format and include traffic reporting info in CMS signon +// New WL2KREPORT format +// Prevent loops when APPL alias refers to itself +// Add RigControl for Flex radios and ICOM IC-M710 Marine radio + +// 5.2.7.1 + +// Fix opening more thn one console window on Win98 +// Change method of configuring multiple timelots on WL2K reporting +// Add option to update WK2K Sysop Database +// Add Web server +// Add UIONLY port option + +// 5.2.7.2 + +// Fix handling TelnetServer packets over 500 bytes in normal mode + +// 5.2.7.3 + +// Fix Igate handling packets from UIView + +// 5.2.7.4 + +// Prototype Baycom driver. + +// 5.2.7.5 + +// Set WK2K group ref to MARS (3) if using a MARS service code + +// 5.2.7.7 + +// Check for programs calling CloseBPQ32 when holding semaphore +// Try/Except round Status Timer Processing + +// 5.2.7.8 + +// More Try/Except round Timer Processing + +// 5.2.7.9 + +// Enable RX in Baycom, and remove test loopback in tx + +// 5.2.7.10 + +// Try/Except round ProcessHTTPMessage + +// 5.2.7.11 + +// BAYCOM tweaks + +// 5.2.7.13 + +// Release semaphore after program error in Timer Processing +// Check fro valid dest in REFRESHROUTE + + +// Add TNC-X KISSOPTION (includes the ACKMODE bytes in the checksum( + +// Version 5.2.9.1 Sept 2012 + +// Fix using KISS ports with COMn > 16 +// Add "KISS over UDP" driver for PI as a TNC concentrator + +// Version 6.0.1.1 + +// Convert to C for linux portability +// Try to speed up kiss polling + +// Version 6.0.2.1 + +// Fix operation on Win98 +// Fix callsign error with AGWtoBPQ +// Fix PTT problem with WINMOR +// Fix Reread telnet config +// Add Secure CMS signon +// Fix error in cashing addresses of CMS servers +// Fix Port Number when using Send Raw. +// Fix PE in KISS driver if invalid subchannel received +// Fix Orignal address of beacons +// Speed up Telnet port monitoring. +// Add TNC Emulators +// Add CountFramesQueuedOnStream API +// Limit number of frames that can be queued on a session. +// Add XDIGI feature +// Add Winmor Robust Mode switching for compatibility with new Winmor TNC +// Move most APRS code from BPQAPRS to here +// Stop corruption caused by overlong KISS frames + +// Version 6.0.3.1 + +// Add starting/killing WINMOR TNC on remote host +// Fix Program Error when APRS Item or Object name is same as call of reporting station +// Dont digi a frame that we have already digi'ed +// Add ChangeSessionIdleTime API +// Add WK2KSYSOP Command +// Add IDLETIME Command +// Fix Errors in RELAYAPPL processing +// Fix PE cauaed by invalid Rigcontrol Line + +// Version 6.0.4.1 + +// Add frequency dependent autoconnect appls for SCS Pactor +// Fix DED Monitoring of I and UI with no data +// Include AGWPE Emulator (from AGWtoBPQ) +// accept DEL (Hex 7F) as backspace in Telnet +// Fix re-running resolver on re-read AXIP config +// Speed up processing, mainly for Telnet Sessions +// Fix APRS init on restart of bpq32.exe +// Change to 2 stop bits +// Fix scrolling of WINMOR trace window +// Fix Crash when ueing DED TNC Emulator +// Fix Disconnect when using BPQDED2 Driver with Telnet Sessions +// Allow HOST applications even when CMS option is disabled +// Fix processing of APRS DIGIMAP command with no targets (didn't suppress default settings) + +// Version 6.0.5.1 January 2014 + +// Add UTF8 conversion mode to Telnet (converts non-UTF-8 chars to UTF-8) +// Add "Clear" option to MH command +// Add "Connect to RMS Relay" Option +// Revert to one stop bit on serial ports, explictly set two on FT2000 rig control +// Fix routing of first call in Robust Packet +// Add Options to switch input source on rigs with build in soundcards (sor far only IC7100 and Kenwood 590) +// Add RTS>CAT PTT option for Sound Card rigs +// Add Clear Nodes Option (NODE DEL ALL) +// SCS Pactor can set differeant APPLCALLS when scanning. +// Fix possible Scan hangup after a manual requency change with SCS Pactor +// Accept Scan entry of W0 to disable WINMOR on that frequency +// Fix corruption of NETROMCALL by SIMPLE config command +// Enforce Pactor Levels +// Add Telnet outward connect +// Add Relay/Trimode Emulation +// Fix V4 Driver +// Add PTT Mux +// Add Locked ARP Entries (via bpq32.cfg) +// Fix IDLETIME node command +// Fix STAY param on connect +// Add STAY option to Attach and Application Commands +// Fix crash on copying a large AXIP MH Window +// Fix possible crash when bpq32.exe dies +// Fix DIGIPORT for UI frames + +// Version 6.0.6.1 April 2014 + +// FLDigi Interface +// Fix "All CMS Servers are inaccessible" message so Mail Forwarding ELSE works. +// Validate INP3 messages to try to prevent crash +// Fix possible crash if an overlarge KISS frame is received +// Fix error in AXR command +// Add LF to Telnet Outward Connect signin if NEEDLF added to connect line +// Add CBELL to TNC21 emulator +// Add sent objects and third party messages to APRS Dup List +// Incorporate UIUtil +// Use Memory Mapped file to pass APRS info to BPQAPRS, and process APRS HTTP in BPQ32 +// Improvements to FLDIGI interlocking +// Fix TNC State Display for Tracker +// Cache CMS Addresses on LinBPQ +// Fix count error on DED Driver when handling 256 byte packets +// Add basic SNMP interface for MRTG +// Fix memory loss from getaddrinfo +// Process "BUSY" response from Tracker +// Handle serial port writes that don't accept all the data +// Trap Error 10038 and try to reopen socket +// Fix crash if overlong command line received + +// Version 6.0.7.1 Aptil 2014 +// Fix RigContol with no frequencies for Kenwood and Yaesu +// Add busy check to FLDIGI connects + +// Version 6.0.8.1 August 2014 + +// Use HKEY_CURRENT_USER on all OS versions +// Fix crash when APRS symbol is a space. +// Fixes for FT847 CAT +// Fix display of 3rd byte of FRMR +// Add "DEFAULT ROBUST" and "FORCE ROBUST" commands to SCSPactor Driver +// Fix possible memory corruption in WINMOR driver +// Fix FT2000 Modes +// Use new WL2K reporting system (Web API Based) +// APRS Server now cycles through hosts if DNS returns more than one +// BPQ32 can now start and stop FLDIGI +// Fix loss of AXIP Resolver when running more than one AXIP port + +// Version 6.0.9.1 November 2014 + +// Fix setting NOKEEPALIVE flag on route created from incoming L3 message +// Ignore NODES from locked route with quality 0 +// Fix seting source port in AXIP +// Fix Dual Stack (IPV4/V6) on Linux. +// Fix RELAYSOCK if IPv6 is enabled. +// Add support for FT1000 +// Fix hang when APRS Messaging packet received on RF +// Attempt to normalize Node qualies when stations use widely differing Route qualities +// Add NODES VIA command to display nodes reachable via a specified neighbour +// Fix applying "DisconnectOnClose" setting on HOST API connects (Telnet Server) +// Fix buffering large messages in Telnet Host API +// Fix occasional crash in terminal part line processing +// Add "NoFallback" command to Telnet server to disable "fallback to Relay" +// Improved support for APPLCALL scanning with Pactor +// MAXBUFFS config statement is no longer needed. +// Fix USEAPPLCALLS with Tracker when connect to APPLCALL fails +// Implement LISTEN and CQ commands +// FLDIGI driver can now start FLDIGI on a remote system. +// Add IGNOREUNLOCKEDROUTES parameter +// Fix error if too many Telnet server connections + +// Version 6.0.10.1 Feb 2015 + +// Fix crash if corrupt HTML request received. +// Allow SSID's of 'R' and 'T' on non-ax.25 ports for WL2K Radio Only network. +// Make HTTP server HTTP Version 1.1 complient - use persistent conections and close after 2.5 mins +// Add INP3ONLY flag. +// Fix program error if enter UNPROTO without a destination path +// Show client IP address on HTTP sessions in Telnet Server +// Reduce frequency and number of attempts to connect to routes when Keepalives or INP3 is set +// Add FT990 RigControl support, fix FT1000MP support. +// Support ARMV5 processors +// Changes to support LinBPQ APRS Client +// Add IC7410 to supported Soundcard rigs +// Add CAT PTT to NMEA type (for ICOM Marine Radios_ +// Fix ACKMODE +// Add KISS over TCP +// Support ACKMode on VKISS +// Improved reporting of configuration file format errors +// Experimental driver to support ARQ sessions using UI frames + +// Version 6.0.11.1 September 2015 + +// Fixes for IPGateway configuration and Virtual Circuit Mode +// Separate Portmapper from IPGateway +// Add PING Command +// Add ARDOP Driver +// Add basic APPLCALL support for PTC-PRO/Dragon 7800 Packet (using MYALIAS) +// Add "VeryOldMode" for KAM Version 5.02 +// Add KISS over TCP Slave Mode. +// Support Pactor and Packet on P4Dragon on one port +// Add "Remote Staton Quality" to Web ROUTES display +// Add Virtual Host option for IPGateway NET44 Encap +// Add NAT for local hosts to IPGateway +// Fix setting filter from RADIO command for IC7410 +// Add Memory Channel Scanning for ICOM Radios +// Try to reopen Rig Control port if it fails (could be unplugged USB) +// Fix restoring position of Monitor Window +// Stop Codec on Winmor and ARDOP when an interlocked port is attached (instead of listen false) +// Support APRS beacons in RP mode on Dragon// +// Change Virtual MAC address on IPGateway to include last octet of IP Address +// Fix "NOS Fragmentation" in IP over ax.25 Virtual Circuit Mode +// Fix sending I frames before L2 session is up +// Fix Flow control on Telnet outbound sessions. +// Fix reporting of unterminatred comments in config +// Add option for RigControl to not change mode on FT100/FT990/FT1000 +// Add "Attach and Connect" for Telnet ports + +// Version 6.0.12.1 November 2015 + +// Fix logging of IP addresses for connects to FBBPORT +// Allow lower case user and passwords in Telnet "Attach and Connect" +// Fix possible hang in KISS over TCP Slave mode +// Fix duplicating LinBPQ process if running ARDOP fails +// Allow lower case command aliases and increase alias length to 48 +// Fix saving long IP frames pending ARP resolution +// Fix dropping last entry from a RIP44 message. +// Fix displaying Digis in MH list +// Add port name to Monitor config screen port list +// Fix APRS command display filter and add port filter +// Support port names in BPQTermTCP Monitor config +// Add FINDBUFFS command to dump lost buffers to Debugview/Syslog +// Buffer Web Mgmt Edit Config output +// Add WebMail Support +// Fix not closing APRS Send WX file. +// Add RUN option to APRS Config to start APRS Client +// LinBPQ run FindLostBuffers and exit if QCOUNT < 5 +// Close and reopen ARDOP connection if nothing received for 90 secs +// Add facility to bridge traffic between ports (similar to APRS Bridge but for all frame types) +// Add KISSOPTION TRACKER to set SCS Tracker into KISS Mode + +// 6.0.13.1 + +// Allow /ex to exit UNPROTO mode +// Support ARQBW commands. +// Support IC735 +// Fix sending ARDOP beacons after a busy holdoff +// Enable BPQDED driver to beacon via non-ax.25 ports. +// Fix channel number in UZ7HO monitoring +// Add SATGate mode to APRSIS Code. +// Fix crash caused by overlong user name in telnet logon +// Add option to log L4 connects +// Add AUTOADDQuiet mode to AXIP. +// Add EXCLUDE processing +// Support WinmorControl in UZ7HO driver and fix starting TNC on Linux +// Convert calls in MAP entries to upper case. +// Support Linux COM Port names for APRS GPS +// Fix using NETROM serial protocol on ASYNC Port +// Fix setting MYLEVEL by scanner after manual level change. +// Add DEBUGLOG config param to SCS Pactor Driver to log serial port traffic +// Uue #myl to set SCS Pactor MYLEVEL, and add checklevel command +// Add Multicast RX interface to FLDIGI Driver +// Fix processing application aliases to a connect command. +// Fix Buffer loss if radio connected to PTC rig port but BPQ not configured to use it +// Save backups of bpq32.cfg when editing with Web interface and report old and new length +// Add DD command to SCS Pactor, and use it for forced disconnect. +// Add ARDOP mode select to scan config +// ARDOP changes for ARDOP V 0.5+ +// Flip SSID bits on UZ7HO downlink connects + + +// Version 6.0.14.1 + +// Fix Socket leak in ARDOP and FLDIGI drivers. +// Add option to change CMS Server hostname +// ARDOP Changes for 0.8.0+ +// Discard Terminal Keepalive message (two nulls) in ARDOP command hander +// Allow parameters to be passed to ARDOP TNC when starting it +// Fix Web update of Beacon params +// Retry connects to KISS ports after failure +// Add support for ARDOP Serial Interface Native mode. +// Fix gating APRS-IS Messages to RF +// Fix Beacons when PORTNUM used +// Make sure old monitor flag is cleared for TermTCP sessions +// Add CI-V antenna control for IC746 +// Don't allow ARDOP beacons when connected +// Add support for ARDOP Serial over I2C +// Fix possble crash when using manual RADIO messages +// Save out of sequence L2 frames for possible reuse after retry +// Add KISS command to send KISS control frame to TNC +// Stop removing unused digis from packets sent to APRS-IS + +// Processing of ARDOP PING and PINGACK responses +// Handle changed encoding of WL2K update responses. +// Allow anonymous logon to telnet +// Don't use APPL= for RP Calls in Dragon Single mode. +// Add basic messaging page to APRS Web Server +// Add debug log option to SCSTracker and TrkMulti Driver +// Support REBOOT command on LinBPQ +// Allow LISTEN command on all ports that support ax.25 monitoring + +// Version 6.0.15.1 Feb 2018 + +// partial support for ax.25 V2.2 +// Add MHU and MHL commands and MH filter option +// Fix scan interlock with ARDOP +// Add Input source seiect for IC7300 +// Remove % transparency from web terminal signon message +// Fix L4 Connects In count on stats +// Fix crash caused by corrupt CMSInfo.txt +// Add Input peaks display to ARDOP status window +// Add options to show time in local and distances in KM on APRS Web pages +// Add VARA support +// Fix WINMOR Busy left set when port Suspended +// Add ARDOP-Packet Support +// Add Antenna Switching for TS 480 +// Fix possible crash in Web Terminal +// Support different Code Pages on Console sessions +// Use new Winlink API interface (api.winlink.org) +// Support USB/ACC switching on TS590SG +// Fix scanning when ARDOP or WINMOR is used without an Interlocked Pactor port. +// Set NODECALL to first Application Callsign if NODE=0 and BBSCALL not set. +// Add RIGCONTROL TUNE and POWER commands for some ICOM and Kenwwod rigs +// Fix timing out ARDOP PENDING Lock +// Support mixed case WINLINK Passwords +// Add TUNE and POWER Rigcontol Commands for some radios +// ADD LOCALTIME and DISPKM options to APRS Digi/Igate + +// 6.0.16.1 March 2018 + +// Fix Setting data mode and filter for IC7300 radios +// Add VARA to WL2KREPORT +// Add trace to SCS Tracker status window +// Fix possible hang in IPGATEWAY +// Add BeacontoIS parameter to APRSDIGI. Allows you to stop sending beacons to APRS-IS. +// Fix sending CTEXT on WINMOR sessions + +// 6.0.17.1 November 2018 + +// Change WINMOR Restart after connection to Restart after Failure and add same option to ARDOP and VARA +// Add Abort Connection to WINMOR and VARA Interfaces +// Reinstate accidentally removed CMS Access logging +// Fix MH CLEAR +// Fix corruption of NODE table if NODES received from station with null alias +// Fix loss of buffer if session closed with something in PARTCMDBUFFER +// Fix Spurious GUARD ZONE CORRUPT message in IP Code. +// Remove "reread bpq32.cfg and reconfigure" menu options +// Add support for PTT using CM108 based soundcard interfaces +// Datestamp Telnet log files and delete old Telnet and CMSAcces logs + +// 6.0.18.1 January 2019 + +// Fix validation of NODES broadcasts +// Fix HIDENODES +// Check for failure to reread config on axip reconfigure +// Fix crash if STOPPORT or STARTPORT used on KISS over TCP port +// Send Beacons from BCALL or PORTCALL if configured +// Fix possible corruption of last entry in MH display +// Ensure RTS/DTR is down when opening PTT Port +// Remove RECONFIG command +// Preparations for 64 bit version + +// 6.0.19 Sept 2019 +// Fix UZ7HO interlock +// Add commands to set Centre Frequency and Modem with UZ7HO Soundmodem (on Windows only) +// Add option to save and restore MH lists and SAVEMH command +// Add Frequency (if known) to UZ7HO MH lists +// Add Gateway option to Telnet for PAT +// Try to fix SCS Tracker recovery +// Ensure RTS/DTR is down on CAT port if using that line for PTT +// Experimental APRS Messaging in Kernel +// Add Rigcontrol on remote PC's using WinmorControl +// ADD VARAFM and VARAFM96 WL2KREPORT modes +// Fix WL2K sysop update for new Winlink API +// Fix APRS when using PORTNUM higher than the number of ports +// Add Serial Port Type +// Add option to linbpq to log APRS-IS messages. +// Send WL2K Session Reports +// Drop Tunneled Packets from 44.192 - 44.255 +// Log incoming Telnet Connects +// Add IPV4: and IPV6: overrides on AXIP Resolver. +// Add SessionTimeLimit to HF sessions (ARDOP, SCSPactor, WINMOR, VARA) +// Add RADIO FREQ command to display current frequency + +// 6.0.20 April 2020 + +// Trap and reject YAPP file transfer request. +// Fix possible overrun of TCP to Node Buffer +// Fix possible crash if APRS WX file doesn't have a terminating newline +// Change communication with BPQAPRS.exe to restore old message popup behaviour +// Preparation for 64 bit version +// Improve flow control on SCS Dragon +// Fragment messages from network links to L2 links with smaller paclen +// Change WL2K report rate to once every two hours +// Add PASS, CTEXT and CMSG commands and Stream Switch support to TNC2 Emulator +// Add SessionTimeLimit command to HF drivers (ARDOP, SCSPactor, WINMOR, VARA) +// Add links to Ports Web Manangement Page to open individual Driver windows +// Add STOPPORT/STARTPORT support to ARDOP, KAM and SCSPactor drivers +// Add CLOSE and OPEN RADIO command so Rigcontrol port can be freed fpr other use. +// Don't try to send WL2K Traffic report if Internet is down +// Move WL2K Traffic reporting to a separate thread so it doesn't block if it can't connect to server +// ADD AGWAPPL config command to set application number. AGWMASK is still supported +// Register Node Alias with UZ7HO Driver +// Register calls when UZ7HO TNC Restarts and at intervals afterwards +// Fix crash when no IOADDR or COMPORT in async port definition +// Fix Crash with Paclink-Unix when parsing ; VE7SPR-10 DE N7NIX QTC 1 +// Only apply BBSFLAG=NOBBS to APPPLICATION 1 +// Add RIGREONFIG command +// fix APRS RECONFIG on LinBPQ +// Fix Web Terminal scroll to end problem on some browsers +// Add PTT_SETS_INPUT option for IC7600 +// Add TELRECONFIG command to reread users or whole config +// Enforce PACLEN on UZ7HO ports +// Fix PACLEN on Command Output. +// Retry axip resolver if it fails at startup +// Fix AGWAPI connect via digis +// Fix Select() for Linux in MultiPSK, UZ7HO and V4 drivers +// Limit APRS OBJECT length to 80 chars +// UZ7HO disconnect incoming call if no free streams +// Improve response to REJ (no F) followed by RR (F). +// Try to prevent more than MAXFRAME frames outstanding when transmitting +// Allow more than one instance of APRS on Linux +// Stop APRS digi by originating station +// Send driver window trace to main monitor system +// Improve handling of IPOLL messages +// Fix setting end of address bit on dest call on connects to listening sessions +// Set default BBS and CHAT application number and number of streams on LinBPQ +// Support #include in bpq32.cfg processing + +// Version 6.0.21 14 December 2020 + +// Fix occasional missing newlines in some node command reponses +// More 64 bit fixes +// Add option to stop setting PDUPLEX param in SCSPACTOR +// Try to fix buffer loss +// Remove extra space from APRS position reports +// Suppress VARA IAMALIVE messages +// Add display and control of QtSoundModem modems +// Only send "No CMS connection available" message if fallbacktorelay is set. +// Add HAMLIB backend and emulator support to RIGCONTROL +// Ensure all beacons are sent even with very short beacon intervals +// Add VARA500 WL2K Reporting Mode +// Fix problem with prpcessing frame collector +// Temporarily disable L2 and L4 collectors till I can find problem +// Fix possible problem with interactive RADIO commands not giving a response, +// Incease maximum length of NODE command responses to handle maximum length INFO message, +// Allow WL2KREPORT in CONFIG section of UZ7HO port config. +// Fix program error in processing hamlib frame +// Save RestartAfterFailure option for VARA +// Check callsign has a winlink account before sending WL2KREPORT messages +// Add Bandwidth control to VARA scanning +// Renable L2 collector +// Fix TNCPORT reconnect on Linux +// Add SecureTelnet option to limit telnet outward connect to sysop mode sessions or Application Aliases +// Add option to suppress sending call to application in Telnet HOST API +// Add FT991A support to RigControl +// Use background.jpg for Edit Config page +// Send OK response to SCS Pactor commands starting with # +// Resend ICOM PTT OFF command after 30 seconds +// Add WXCall to APRS config +// Fixes for AEAPactor +// Allow PTTMUX to use real or com0com com ports +// Fix monitoring with AGW Emulator +// Derive approx position from packets on APRS ports with a valid 6 char location +// Fix corruption of APRS message lists if the station table fills up. +// Don't accept empty username or password on Relay sessions. +// Fix occasional empty Nodes broadcasts +// Add Digis to UZ7HO Port MH list +// Add PERMITTEDAPPLS port param +// Fix WK2K Session Record Reporting for Airmail and some Pactor Modes. +// Fix handling AX/IP (proto 93) frames +// Fix possible corruption sending APRS messages +// Allow Telnet connections to be made using Connect command as well as Attach then Connect +// Fix Cancel Sysop Signin +// Save axip resolver info and restore on restart +// Add Transparent mode to Telnet Server HOST API +// Fix Tracker driver if WL2KREPRRT is in main config section +// SNMP InOctets count corrected to include all frames and encoding of zero values fixed. +// Change IP Gateway to exclude handling bits of 44 Net sold to Amazon +// Fix crash in Web terminal when processing very long lines + +// Version 6.0.22.1 August 2021 + +// Fix bug in KAM TNCEMULATOR +// Add WinRPR Driver (DED over TCP) +// Fix handling of VARA config commands FM1200 and FM9600 +// Improve Web Termanal Line folding +// Add StartTNC to WinRPR driver +// Add support for VARA2750 Mode +// Add support for VARA connects via a VARA Digipeater +// Add digis to SCSTracker and WinRPR MHeard +// Separate RIGCONTROL config from PORT config and add RigControl window +// Fix crash when a Windows HID device doesn't have a product_string +// Changes to VARA TNC connection and restart process +// Trigger FALLBACKTORELAY if attempt to connect to all CMS servers fail. +// Fix saving part lines in adif log and Winlink Session reporting +// Add port specific CTEXT +// Add FRMR monitoring to UZ7HO driver +// Add audio input switching for IC7610 +// Include Rigcontrol Support for IC-F8101E +// Process any response to KISS command +// Fix NODE ADD command +// Add noUpdate flag to AXIP MAP +// Fix clearing NOFALLBACK flag in Telnet Server +// Allow connects to RMS Relay running on another host +// Allow use of Power setting in Rigcontol scan lines for Kenwood radios +// Prevent problems caused by using "CMS" as a Node Alias +// Include standard APRS Station pages in code +// Fix VALIDCALLS processing in HF drivers +// Send Netrom Link reports to Node Map +// Add REALTELNET mode to Telnet Outward Connect +// Fix using S (Stay) parameter on Telnet connects when using CMDPORT and C HOST +// Add Default frequency to rigcontrol to set a freq/mode to return to after a connection +// Fix long (> 60 seconds) scan intervals +// Improved debugging of stuck semaphores +// Fix potential securiby bug in BPQ Web server +// Send Chat Updates to chatupdate.g8bpq.net port 81 +// Add ReportRelayTraffic to Telnet config to send WL2K traffic reports for connections to RELAY +// Add experimental Mode reporting +// Add SendTandRtoRelay param to SCS Pactor, ARDOP and VARA drivers to divert calls to CMS for -T and -R to RELAY +// Add UPNP Support + +// Version 6.0.23.1 June 2022 + +// Add option to control which applcalls are enabled in VARA +// Add support for rtl_udp to Rig Control +// Fix Telnet Auto Conneect to Application when using TermTCP or Web Terminal +// Allow setting css styles for Web Terminal +// And Kill TNC and Kill and Restart TNC commands to Web Driver Windows +// More flexible RigControl for split frequency operation, eg for QO100 +// Increase stack size for ProcessHTMLMessage (.11) +// Fix HTML Content-Type on images (.12) +// Add AIS and ADSB Support (.13) +// Compress web pages (.14) +// Change minidump routine and close after program error (.15) +// Add RMS Relay SYNC Mode (.17) +// Changes for compatibility with Winlink Hybrid +// Add Rigcontrol CMD feature to Yaesu code (21) +// More diagnostic code +// Trap potential buffer overrun in ax/tcp code +// Fix possible hang in UZ7HO driver if connect takes a long time to succeed or fail +// Add FLRIG as backend for RigControl (.24) +// Fix bug in compressing some management web pages +// Fix bugs in AGW Emulator (.25) +// Add more PTT_Sets_Freq options for split frequency working (.26) +// Allow RIGCONTROL using Radio Number (Rnn) as well as Port (.26) +// Fix Telnet negotiation and backspace processing (.29) +// Fix VARA Mode change when scanning (.30) +// Add Web Mgmt Log Display (.33) +// Fix crash when connecting to RELAY when CMS=0 (.36) +// Send OK to user for manual freq changes with hamlib or flrig +// Fix Rigcontrol leaving port disabled when using an empty timeband +// Fix processing of backspace in Telnet character processing (.40) +// Increase max size of connect script +// Fix HAMLIB Slave Thread control +// Add processing of VARA mode responses and display of VARA Mode (41) +// Fix crash when VARA session aborted on LinBPQ (43) +// Fix handling port selector (2:call or p2 call) on SCS PTC packet ports (44) +// Include APRS Map web page +// Add Enable/Disable to KAMPACTOR scan control (use P0 or P1) (45) +// Add Basic DRATS interface (46) +// Fix MYCALLS on VARA (49) +// Add FreeData driver (51) +// Add additonal Rigcontrol options for QO100 (51) +// Set Content-Type: application/pdf for pdf files downloaded via web interface (51) +// Fix sending large compressed web messages (52) +// Fix freq display when using flrig or hamlib backends to rigcontrol +// Change VARA Driver to send ABORT when Session Time limit expires +// Add Chat Log to Web Logs display +// Fix possible buffer loss in RigControl +// Allow hosts on local lan to be treated as secure +// Improve validation of data sent to Winlink SessionAdd API call +// Add support for FreeDATA modem. +// Add GetLOC API Call +// Change Leaflet link in aprs map. +// Add Connect Log (64) +// Fix crash when Resolve CMS Servers returns ipv6 addresses +// Fix Reporting P4 sessions to Winlink (68) +// Add support for FreeBSD (68) +// Fix Rigcontrol PTCPORT (69) +// Set TNC Emulator sessions as secure (72) +// Fix not always detecting loss of FLRIG (73) +// Add ? and * wildcards to NODES command (74) +// Add Port RADIO config parameter (74) + +// Version 6.0.24.1 August 2023 + +// Apply NODES command wildcard to alias as well a call (2) +// Add STOPPORT/STARTPORT to VARA Driver (2) +// Add bandwidth setting to FLRIG interface. (2) +// Fix N VIA (3) +// Fix NODE ADD and NODE DEL (4) +// Improvements to FLRIG Rigcontrol backend (6, 7) +// Fix UZ7HO Window Title Update +// Reject L2 calls with a blank from call (8) +// Update WinRPR Window header with BPQ Port Description (8) +// Fix error in blank call code (9) +// Change web buttons to white on black when pressed (10) +// Fix Port CTEXT paclen on Tracker and WinRPR drivers (11) +// Add RADIO PTT command for testing PTT (11) +// Fix using APPLCALLs on SCSTracker RP call (12) +// Add Rigcntol Web Page (13) +// Fix scan bandwidth change with ARDOPOFDM (13) +// Fix setting Min Pactor Level in SCSPactor (13) +// Fix length of commands sent via CMD_TO_APPL flag (14) +// Add filter by quality option to N display (15) +// Fix VARA Mode reporting to WL2K (16) +// Add FLRIG POWER and TUNE commands (18) +// Fix crash when processing "C " without a call in UZ7HO, FLDIGI or MULTIPSK drivers (19) +// FLDIGI improvements (19) +// Fix hang at start if Telnet port Number > Number of Telnet Streams (20) +// Fix processing C command if first port driver is SCSPACTROR (20) +// Fix crash in UZ7HO driver if bad raw frame received (21) +// Fix using FLARQ chat mode with FLDIGI ddriover (22) +// Fix to KISSHF driver (23) +// Fix for application buffer loss (24) +// Add Web Sockets auto-refresh option for Webmail index page (25) +// Fix FREEDATA driver for compatibility with FreeData TNC version 0.6.4-alpha.3 (25) +// Add SmartID for bridged frames - Send ID only if packets sent recently (26) +// Add option to save and restore received APRS messages (27) +// Add mechanism to run a user program on certain events (27) +// If BeacontoIS is zero don't Gate any of our messages received locally to APRS-IS (28) +// Add Node Help command (28) +// Add APRS Igate RXOnly option (29) +// Fix RMC message handling with prefixes other than GP (29) +// Add GPSD support for APRS (30) +// Attempt to fix Tracker/WinRPR reconnect code (30) +// Changes to FreeDATA - Don't use deamon and add txlevel and send text commands (31) +// Fix interactive commands in tracker driver (33) +// Fix SESSIONTIMELIMIT processing +// Add STOPPORT/STARTPORT for UZ7HO driver +// Fix processing of extended QtSM 'g' frame (36) +// Allow setting just freq on Yaseu rigs (37) +// Enable KISSHF driver on Linux (40) +// Allow AISHOST and ADSBHOST to be a name as well as an address (41) +// Fix Interlock of incoming UZ7HO connections (41) +// Disable VARA Actions menu if not sysop (41) +// Fix Port CTEXT on UZ7HO B C or D channels (42) +// Fix repeated trigger of SessionTimeLimit (43) +// Fix posible memory corruption in UpateMH (44) +// Add PHG to APRS beacons (45) +// Dont send DM to stations in exclude list(45) +// Improvements to RMS Relay SYNC Mode (46) +// Check L4 connects against EXCLUDE list (47) +// Add vaidation of LOC in WL2K Session Reports (49) +// Change gpsd support for compatibility with Share Gps (50) +// Switch APRS Map to my Tiles (52) +// Fix using ; in UNPROTO Mode messages (52) +// Use sha1 code from https://www.packetizer.com/security/sha1/ instead of openssl (53) +// Fix TNC Emulator Monitoring (53) +// Fix attach and connect on Telnet port bug introduced in .55 (56) +// Fix stopping WinRPR TNC and Start/Stop UZ7HO TNCX on Linux (57) +// Fix stack size in beginthread for MAC (58) +// Add NETROM over VARA (60) +// Add Disconnect Script (64) +// Add node commands to set UZ7HO modem mode and freq (64) +// Trap empty NODECALL or NETROMCALL(65) +// Trap NODES messages with empty From Call (65) +// Add RigControl for SDRConsole (66) +// Fix FLRig crash (66) +// Fix VARA disconnect handling (67) +// Support 64 ports (69) +// Fix Node commands for setting UZ7HO Modem (70) +// Fix processing SABM on an existing session (71) +// Extend KISS Node command to send more than one parameter byte (72) +// Add G7TAJ's code to record activity of HF ports for stats display (72) +// Add option to send KISS command to TNC on startup (73) +// Fix Bug in DED Emulator Monitor code (74) +// Add Filters to DED Monitor code (75) +// Detect loss of DED application (76) +// Fix connects to Application Alias with UZ7HO Driver (76) +// Fix Interlock of ports on same UZ7HO modem. (76) +// Add extended Ports command (77) +// Fix crash in Linbpq when stdout is redirected to /dev/tty? and stdin ia redirected (78) +// Fix Web Terminal (80) +// Trap ENCRYPTION message from VARA (81) +// Fix processing of the Winlink API /account/exists response (82) +// Fix sending CTEXT to L4 connects to Node when FULL_CTEXT is not set + +// Version 6.0.25.? + +// Fix 64 bit compatibility problems in SCSTracker and UZ7HO drivers +// Add Chat PACLEN config (5) +// Fix NC to Application Call (6) +// Fix INP3 L3RTT messages on Linux and correct RTT calculation (9) +// Get Beacon config from config file on Windows (9) +// fix processing DED TNC Emulator M command with space between M and params (10) +// Fix sending UI frames on SCSPACTOR (11) +// Dont allow ports that can't set digi'ed bit in callsigns to digipeat. (11) +// Add SDRAngel rig control (11) +// Add option to specify config and data directories on linbpq (12) +// Allow zero resptime (send RR immediately) (13) +// Make sure CMD bit is set on UI frames +// Add setting Modem Flags in QtSM AGW mode +// If FT847 om PTC Port send a "Cat On" command (17) +// Fix some 63 port bugs in RigCOntrol (17) +// Fix 63 port bug in Bridging (18) +// Add FTDX10 Rigcontrol (19) +// Fix 64 bit bug in displaying INP3 Messages (20) +// Improve restart of WinRPR TNC on remote host (21) +// Fix some Rigcontrol issues with empty timebands (22) +// Fix 64 bit bug in processing INP3 Messages (22) +// First pass at api (24) +// Send OK in response to Rigcontrol CMD (24) +// Disable CTS check in WriteComBlock (26) +// Improvments to reporting to M0LTE Map (26) +// IPGateway fix from github user isavitsky (27) +// Fix possible crash in SCSPactor PTCPORT code (29) +// Add NodeAPI call sendLinks and remove get from other calls (32) +// Improve validation of Web Beacon Config (33) +// Support SNMP via host ip stack as well as IPGateway (34) +// Switch APRS Map to OSM tile servers (36) +// Fix potential buffer overflow in Telnet login (36) +// Allow longer serial device names (37) +// Fix ICF8101 Mode setting (37) +// Kill link if we are getting repeated RR(F) after timeout +// (Indicating other station is seeing our RR(P) but not the resent I frame) (40) +// Change default of SECURETELNET to 1 (41) +// Add optional ATTACH time limit for ARDOP (42) +// Fix buffer overflow risk in HTTP Terminal(42) +// Fix KISSHF Interlock (43) +// Support other than channel A on HFKISS (43) +// Support additional port info reporting for M0LTE Map (44) +// Allow interlocking of KISS and Session mode ports (eg ARDOP and VARA) (45) +// Add ARDOP UI Packets to MH (45) +// Add support for Qtsm Mgmt Interface (45) +// NodeAPI improvements (46) +// Add MQTT Interface (46) +// Fix buffer leak in ARDOP code(46) +// Fix possible crash if MQTT not in use (47) +// Add optional ATTACH time limit for VARA (48) +// API format fixes (48) +// AGWAPI Add protection against accidental connects from a non-agw application (50) +// Save MH and NODES every hour (51) +// Fix handling long unix device names (now max 250 bytes) (52) +// Fix error reporting in api update (53) +// Coding changes to remove some compiler warnings (53, 54) +// Add MQTT reporting of Mail Events (54) +// Fix beaconong on KISSHF ports (55) +// Fix MailAPI msgs endpoint +// Attempt to fix NC going to wrong application. (57) +// Improve ARDOP end of session code (58) +// Run M0LTE Map reporting in a separate thread (59/60) +// Add RHP support for WhatsPac (59) +// Add timestamps to LIS monitor (60) +// Fix problem with L4 frames being delivered out of sequence (60) +// Add Compression of Netrom connections (62) +// Improve handling of Locked Routes (62) +// Add L4 RESET (Paula G8PZT's extension to NETROM) +// Fix problem using SENDRAW from BPQMail (63) +// Fix compatibility with latest ardopcf (64) +// Fix bug in RHP socket timeout code (65) + + +#define CKernel + +#include "Versions.h" + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "compatbits.h" +#include "AsmStrucs.h" + +#include "SHELLAPI.H" +#include "kernelresource.h" + +#include +#include +#include "BPQTermMDI.h" + +#include "GetVersion.h" + +#define DllImport __declspec( dllimport ) + +#define CheckGuardZone() _CheckGuardZone(__FILE__, __LINE__) +void _CheckGuardZone(char * File, int Line); + +#define CHECKLOADED 0 +#define SETAPPLFLAGS 1 +#define SENDBPQFRAME 2 +#define GETBPQFRAME 3 +#define GETSTREAMSTATUS 4 +#define CLEARSTREAMSTATUS 5 +#define BPQCONDIS 6 +#define GETBUFFERSTATUS 7 +#define GETCONNECTIONINFO 8 +#define BPQRETURN 9 // GETCALLS +//#define RAWTX 10 //IE KISS TYPE DATA +#define GETRAWFRAME 11 +#define UPDATESWITCH 12 +#define BPQALLOC 13 +//#define SENDNETFRAME 14 +#define GETTIME 15 + +extern short NUMBEROFPORTS; +extern long PORTENTRYLEN; +extern long LINKTABLELEN; +extern struct PORTCONTROL * PORTTABLE; +extern void * FREE_Q; +extern UINT APPL_Q; // Queue of frames for APRS Appl + +extern TRANSPORTENTRY * L4TABLE; +extern UCHAR NEXTID; +extern DWORD MAXCIRCUITS; +extern DWORD L4DEFAULTWINDOW; +extern DWORD L4T1; +extern APPLCALLS APPLCALLTABLE[]; +extern char * APPLS; + +extern struct WL2KInfo * WL2KReports; + +extern int NUMBEROFTNCPORTS; + + +void * VCOMExtInit(struct PORTCONTROL * PortEntry); +void * AXIPExtInit(struct PORTCONTROL * PortEntry); +void * SCSExtInit(struct PORTCONTROL * PortEntry); +void * AEAExtInit(struct PORTCONTROL * PortEntry); +void * KAMExtInit(struct PORTCONTROL * PortEntry); +void * HALExtInit(struct PORTCONTROL * PortEntry); +void * ETHERExtInit(struct PORTCONTROL * PortEntry); +void * AGWExtInit(struct PORTCONTROL * PortEntry); +void * WinmorExtInit(EXTPORTDATA * PortEntry); +void * TelnetExtInit(EXTPORTDATA * PortEntry); +//void * SoundModemExtInit(EXTPORTDATA * PortEntry); +void * TrackerExtInit(EXTPORTDATA * PortEntry); +void * TrackerMExtInit(EXTPORTDATA * PortEntry); +void * V4ExtInit(EXTPORTDATA * PortEntry); +void * UZ7HOExtInit(EXTPORTDATA * PortEntry); +void * MPSKExtInit(EXTPORTDATA * PortEntry); +void * FLDigiExtInit(EXTPORTDATA * PortEntry); +void * UIARQExtInit(EXTPORTDATA * PortEntry); +void * SerialExtInit(EXTPORTDATA * PortEntry); +void * ARDOPExtInit(EXTPORTDATA * PortEntry); +void * VARAExtInit(EXTPORTDATA * PortEntry); +void * KISSHFExtInit(EXTPORTDATA * PortEntry); +void * WinRPRExtInit(EXTPORTDATA * PortEntry); +void * HSMODEMExtInit(EXTPORTDATA * PortEntry); +void * FreeDataExtInit(EXTPORTDATA * PortEntry); +void * SIXPACKExtInit(EXTPORTDATA * PortEntry); + +extern char * ConfigBuffer; // Config Area +VOID REMOVENODE(dest_list * DEST); +DllExport int ConvFromAX25(unsigned char * incall,unsigned char * outcall); +DllExport int ConvToAX25(unsigned char * incall,unsigned char * outcall); +VOID GetUIConfig(); +VOID ADIFWriteFreqList(); +void SaveAIS(); +void initAIS(); +void initADSB(); + +extern BOOL ADIFLogEnabled; + +int CloseOnError = 0; + +char UIClassName[]="UIMAINWINDOW"; // the main window class name + +HWND UIhWnd; + +extern char AUTOSAVE; +extern char AUTOSAVEMH; + +extern char MYNODECALL; // 10 chars,not null terminated + +extern QCOUNT; +extern BPQVECSTRUC BPQHOSTVECTOR[]; +#define BPQHOSTSTREAMS 64 +#define IPHOSTVECTOR BPQHOSTVECTOR[BPQHOSTSTREAMS + 3] + +extern char * CONFIGFILENAME; + +DllExport BPQVECSTRUC * BPQHOSTVECPTR; + +extern int DATABASESTART; + +extern struct ROUTE * NEIGHBOURS; +extern int ROUTE_LEN; +extern int MAXNEIGHBOURS; + +extern struct DEST_LIST * DESTS; // NODE LIST +extern int DEST_LIST_LEN; +extern int MAXDESTS; // MAX NODES IN SYSTEM + +extern struct _LINKTABLE * LINKS; +extern int LINK_TABLE_LEN; +extern int MAXLINKS; + +extern double LatFromLOC; +extern double LonFromLOC; + + +extern int BPQHOSTAPI(); +extern int INITIALISEPORTS(); +extern int TIMERINTERRUPT(); +extern int MONDECODE(); +extern int BPQMONOPTIONS(); +extern char PWTEXT[]; +extern char PWLen; + +extern int FINDFREEDESTINATION(); +extern int RAWTX(); +extern int RELBUFF(); +extern int SENDNETFRAME(); +extern char MYCALL[]; // 7 chars, ax.25 format + +extern HWND hIPResWnd; +extern BOOL IPMinimized; + +extern int NODESINPROGRESS; +extern VOID * CURRENTNODE; + + +BOOL Start(); + +VOID SaveWindowPos(int port); +VOID SaveAXIPWindowPos(int port); +VOID SetupRTFHddr(); +DllExport VOID APIENTRY CreateNewTrayIcon(); +int DoReceivedData(int Stream); +int DoStateChange(int Stream); +int DoMonData(int Stream); +struct ConsoleInfo * CreateChildWindow(int Stream, BOOL DuringInit); +CloseHostSessions(); +SaveHostSessions(); +VOID SaveBPQ32Windows(); +VOID CloseDriverWindow(int port); +VOID CheckWL2KReportTimer(); +VOID SetApplPorts(); +VOID WriteMiniDump(); +VOID FindLostBuffers(); +BOOL InitializeTNCEmulator(); +VOID TNCTimer(); +char * strlop(char * buf, char delim); + +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 BOOL APIENTRY GetAllocationState(int Stream); +DllExport int APIENTRY GetMsg(int stream, char * msg, int * len, int * count ); +DllExport int APIENTRY RXCount(int Stream); +DllExport int APIENTRY TXCount(int Stream); +DllExport int APIENTRY MONCount(int Stream); +DllExport int APIENTRY GetCallsign(int stream, char * callsign); +DllExport VOID APIENTRY RelBuff(VOID * Msg); +void SaveMH(); +void DRATSPoll(); + +#define C_Q_ADD(s, b) _C_Q_ADD(s, b, __FILE__, __LINE__); +int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line); + +VOID SetWindowTextSupport(); +int WritetoConsoleSupport(char * buff); +VOID PMClose(); +VOID MySetWindowText(HWND hWnd, char * Msg); +BOOL CreateMonitorWindow(char * MonSize); +VOID FormatTime3(char * Time, time_t cTime); + +char EXCEPTMSG[80] = ""; + +char SIGNONMSG[128] = ""; +char SESSIONHDDR[80] = ""; +int SESSHDDRLEN = 0; + +BOOL IncludesMail = FALSE; +BOOL IncludesChat = FALSE; // Set if pgram is running - used for Web Page Index + + +char WL2KCall[10]; +char WL2KLoc[7]; + +extern char LOCATOR[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char MAPCOMMENT[]; // Locator for Reporting - may be Maidenhead or LAT:LON +extern char LOC[7]; // Maidenhead Locator for Reporting +extern char ReportDest[7]; + +extern UCHAR ConfigDirectory[260]; + +extern uint64_t timeLoadedMS; + +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Consoleprintf(const char * format, ...); + +DllExport int APIENTRY CloseBPQ32(); +DllExport char * APIENTRY GetLOC(); +DllExport int APIENTRY SessionControl(int stream, int command, int param); + +int DoRefreshWebMailIndex(); + +BOOL APIENTRY Init_IP(); +BOOL APIENTRY Poll_IP(); + +BOOL APIENTRY Init_PM(); +BOOL APIENTRY Poll_PM(); + +BOOL APIENTRY Init_APRS(); +BOOL APIENTRY Poll_APRS(); +VOID HTTPTimer(); + +BOOL APIENTRY Rig_Init(); +BOOL APIENTRY Rig_Close(); +BOOL Rig_Poll(); + +VOID IPClose(); +VOID APRSClose(); +VOID CloseTNCEmulator(); + +VOID Poll_AGW(); +void RHPPoll(); +BOOL AGWAPIInit(); +int AGWAPITerminate(); + +int * Flag = (int *)&Flag; // for Dump Analysis +int MAJORVERSION=4; +int MINORVERSION=9; + +struct SEM Semaphore = {0, 0, 0, 0}; +struct SEM APISemaphore = {0, 0, 0, 0}; +int SemHeldByAPI = 0; +int LastSemGets = 0; +UINT Sem_eax = 0; +UINT Sem_ebx = 0; +UINT Sem_ecx = 0; +UINT Sem_edx = 0; +UINT Sem_esi = 0; +UINT Sem_edi = 0; + + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); +void FreeSemaphore(struct SEM * Semaphore); + +DllExport void * BPQHOSTAPIPTR = &BPQHOSTAPI; +//DllExport long MONDECODEPTR = (long)&MONDECODE; + +extern UCHAR BPQDirectory[]; +extern UCHAR LogDirectory[]; +extern UCHAR BPQProgramDirectory[]; + +static char BPQWinMsg[] = "BPQWindowMessage"; + +static char ClassName[] = "BPQMAINWINDOW"; + +HKEY REGTREE = HKEY_CURRENT_USER; +char REGTREETEXT[100] = "HKEY_CURRENT_USER"; + +UINT BPQMsg=0; + +#define MAXLINELEN 120 +#define MAXSCREENLEN 50 + +#define BGCOLOUR RGB(236,233,216) + +HBRUSH bgBrush = NULL; + +//int LINELEN=120; +//int SCREENLEN=50; + +//char Screen[MAXLINELEN*MAXSCREENLEN]={0}; + +//int lineno=0; +//int col=0; + +#define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer +int ReportTimer = 0; + +HANDLE OpenConfigFile(char * file); + +VOID SetupBPQDirectory(); +VOID SendLocation(); + +//uintptr_t _beginthread(void(*start_address)(), unsigned stack_size, int arglist); + +#define TRAY_ICON_ID 1 // ID number for the Notify Icon +#define MY_TRAY_ICON_MESSAGE WM_APP // the message ID sent to our window + +NOTIFYICONDATA niData; + +int SetupConsoleWindow(); + +BOOL StartMinimized=FALSE; +BOOL MinimizetoTray=TRUE; + +BOOL StatusMinimized = FALSE; +BOOL ConsoleMinimized = FALSE; + +HMENU trayMenu=0; + +HWND hConsWnd = NULL, hWndCons = NULL, hWndBG = NULL, ClientWnd = NULL, FrameWnd = NULL, StatusWnd = NULL; + +BOOL FrameMaximized = FALSE; + +BOOL IGateEnabled = TRUE; +extern int ISDelayTimer; // Time before trying to reopen APRS-IS link +extern int ISPort; + +UINT * WINMORTraceQ = NULL; +UINT * SetWindowTextQ = NULL; + +static RECT Rect = {100,100,400,400}; // Console Window Position +RECT FRect = {100,100,800,600}; // Frame +static RECT StatusRect = {100,100,850,500}; // Status Window + +DllExport int APIENTRY DumpSystem(); +DllExport int APIENTRY SaveNodes (); +DllExport int APIENTRY ClearNodes (); +DllExport int APIENTRY SetupTrayIcon(); + +#define Q_REM(s) _Q_REM(s, __FILE__, __LINE__) + +VOID * _Q_REM(VOID *Q, char * File, int Line); + +UINT ReleaseBuffer(UINT *BUFF); + + +VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime ); + +DllExport int APIENTRY DeallocateStream(int stream); + +int VECTORLENGTH = sizeof (struct _BPQVECSTRUC); + +int FirstEntry = 1; +BOOL CloseLast = TRUE; // If the user started BPQ32.exe, don't close it when other programs close +BOOL Closing = FALSE; // Set if Close All called - prevents respawning bpq32.exe + +BOOL BPQ32_EXE; // Set if Process is running BPQ32.exe. Not initialised. + // Used to Kill surplus BPQ32.exe processes + +DWORD Our_PID; // Our Process ID - local variable + +void * InitDone = 0; +int FirstInitDone = 0; +int PerlReinit = 0; +UINT_PTR TimerHandle = 0; +UINT_PTR SessHandle = 0; + +BOOL EventsEnabled = 0; + +unsigned int TimerInst = 0xffffffff; + +HANDLE hInstance = 0; + +int AttachedProcesses = 0; +int AttachingProcess = 0; +HINSTANCE hIPModule = 0; +HINSTANCE hRigModule = 0; + +BOOL ReconfigFlag = FALSE; +BOOL RigReconfigFlag = FALSE; +BOOL APRSReconfigFlag = FALSE; +BOOL CloseAllNeeded = FALSE; +BOOL NeedWebMailRefresh = FALSE; + +int AttachedPIDList[100] = {0}; + +HWND hWndArray[100] = {0}; +int PIDArray[100] = {0}; +char PopupText[30][100] = {""}; + +// Next 3 should be uninitialised so they are local to each process + +UCHAR MCOM; +UCHAR MTX; // Top bit indicates use local time +uint64_t MMASK; +UCHAR MUIONLY; + +UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list + +char pgm[256]; // Uninitialised so per process + +HANDLE Mutex; + +BOOL PartLine = FALSE; +int pindex = 0; +DWORD * WritetoConsoleQ; + + +LARGE_INTEGER lpFrequency = {0}; +LARGE_INTEGER lastRunTime; +LARGE_INTEGER currentTime; + +int ticksPerMillisec; +int interval; + + +VOID CALLBACK SetupTermSessions(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime); + + +TIMERPROC lpTimerFunc = (TIMERPROC) TimerProc; +TIMERPROC lpSetupTermSessions = (TIMERPROC) SetupTermSessions; + + +BOOL ProcessConfig(); +VOID FreeConfig(); + +DllExport int APIENTRY WritetoConsole(char * buff); + +BOOLEAN CheckifBPQ32isLoaded(); +BOOLEAN StartBPQ32(); +DllExport VOID APIENTRY Send_AX(VOID * Block, DWORD len, UCHAR Port); +BOOL LoadIPDriver(); +BOOL Send_IP(VOID * Block, DWORD len); +VOID CheckforLostProcesses(); +BOOL LoadRigDriver(); +VOID SaveConfig(); +VOID CreateRegBackup(); +VOID ResolveUpdateThread(); +VOID OpenReportingSockets(); +DllExport VOID APIENTRY CloseAllPrograms(); +DllExport BOOL APIENTRY SaveReg(char * KeyIn, HANDLE hFile); +int upnpClose(); + +BOOL IPActive = FALSE; +extern BOOL IPRequired; +BOOL PMActive = FALSE; +extern BOOL PMRequired; +BOOL RigRequired = TRUE; +BOOL RigActive = FALSE; +BOOL APRSActive = FALSE; +BOOL AGWActive = FALSE; +BOOL needAIS = FALSE; +int needADSB = 0; + +extern int AGWPort; + +Tell_Sessions(); + + +typedef int (WINAPI FAR *FARPROCX)(); + +FARPROCX CreateToolHelp32SnapShotPtr; +FARPROCX Process32Firstptr; +FARPROCX Process32Nextptr; + +void LoadToolHelperRoutines() +{ + HINSTANCE ExtDriver=0; + int err; + char msg[100]; + + ExtDriver=LoadLibrary("kernel32.dll"); + + if (ExtDriver == NULL) + { + err=GetLastError(); + sprintf(msg,"BPQ32 Error loading kernel32.dll - Error code %d\n", err); + OutputDebugString(msg); + return; + } + + CreateToolHelp32SnapShotPtr = (FARPROCX)GetProcAddress(ExtDriver,"CreateToolhelp32Snapshot"); + Process32Firstptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32First"); + Process32Nextptr = (FARPROCX)GetProcAddress(ExtDriver,"Process32Next"); + + if (CreateToolHelp32SnapShotPtr == 0) + { + err=GetLastError(); + sprintf(msg,"BPQ32 Error getting CreateToolhelp32Snapshot entry point - Error code %d\n", err); + OutputDebugString(msg); + return; + } +} + +BOOL GetProcess(int ProcessID, char * Program) +{ + HANDLE hProcessSnap; + PROCESSENTRY32 pe32; + int p; + + if (CreateToolHelp32SnapShotPtr==0) + { + return (TRUE); // Routine not available + } + // Take a snapshot of all processes in the system. + hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); + return( FALSE ); + } + + // Set the size of the structure before using it. + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + // Retrieve information about the first process, + // and exit if unsuccessful + if( !Process32Firstptr( hProcessSnap, &pe32 ) ) + { + OutputDebugString( "Process32First Failed\n" ); // Show cause of failure + CloseHandle( hProcessSnap ); // Must clean up the snapshot object! + return( FALSE ); + } + + // Now walk the snapshot of processes, and + // display information about each process in turn + do + { + if (ProcessID==pe32.th32ProcessID) + { + // if running on 98, program contains the full path - remove it + + for (p = (int)strlen(pe32.szExeFile); p >= 0; p--) + { + if (pe32.szExeFile[p]=='\\') + { + break; + } + } + p++; + + sprintf(Program,"%s", &pe32.szExeFile[p]); + CloseHandle( hProcessSnap ); + return( TRUE ); + } + + } while( Process32Nextptr( hProcessSnap, &pe32 ) ); + + + sprintf(Program,"PID %d Not Found", ProcessID); + CloseHandle( hProcessSnap ); + return(FALSE); +} + +BOOL IsProcess(int ProcessID) +{ + // Check that Process exists + + HANDLE hProcessSnap; + PROCESSENTRY32 pe32; + + if (CreateToolHelp32SnapShotPtr==0) return (TRUE); // Routine not available + + hProcessSnap = (HANDLE)CreateToolHelp32SnapShotPtr(TH32CS_SNAPPROCESS, 0); + + if( hProcessSnap == INVALID_HANDLE_VALUE ) + { + OutputDebugString( "CreateToolhelp32Snapshot (of processes) Failed\n" ); + return(TRUE); // Don't know, so assume ok + } + + pe32.dwSize = sizeof( PROCESSENTRY32 ); + + if( !Process32Firstptr( hProcessSnap, &pe32 ) ) + { + OutputDebugString( "Process32First Failed\n" ); // Show cause of failure + CloseHandle( hProcessSnap ); // Must clean up the snapshot object! + return(TRUE); // Don't know, so assume ok + } + + do + { + if (ProcessID==pe32.th32ProcessID) + { + CloseHandle( hProcessSnap ); + return( TRUE ); + } + + } while( Process32Nextptr( hProcessSnap, &pe32 ) ); + + CloseHandle( hProcessSnap ); + return(FALSE); +} + +#include "DbgHelp.h" + +VOID MonitorThread(int x) +{ + // Thread to detect killed processes. Runs in process owning timer. + + // Obviously can't detect loss of timer owning thread! + + do + { + if (Semaphore.Gets == LastSemGets && Semaphore.Flag) + { + // It is stuck - try to release + + Debugprintf ("Semaphore locked - Process ID = %d, Held By %d from %s Line %d", + Semaphore.SemProcessID, SemHeldByAPI, Semaphore.File, Semaphore.Line); + + // Write a minidump + + WriteMiniDump(); + + Semaphore.Flag = 0; + } + + LastSemGets = Semaphore.Gets; + + Sleep(30000); + CheckforLostProcesses(); + + } while (TRUE); +} + +VOID CheckforLostProcesses() +{ + UCHAR buff[100]; + char Log[80]; + int i, n, ProcessID; + + for (n=0; n < AttachedProcesses; n++) + { + ProcessID=AttachedPIDList[n]; + + if (!IsProcess(ProcessID)) + { + // Process has died - Treat as a detach + + sprintf(Log,"BPQ32 Process %d Died\n", ProcessID); + OutputDebugString(Log); + + // Remove Tray Icon Entry + + for( i = 0; i < 100; ++i ) + { + if (PIDArray[i] == ProcessID) + { + hWndArray[i] = 0; + sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); + OutputDebugString(Log); + DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); + } + } + + // If process had the semaphore, release it + + if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].STREAMOWNER == AttachedPIDList[n]) + { + DeallocateStream(i); + } + } + + if (TimerInst == ProcessID) + { + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; +// Tell_Sessions(); + OutputDebugString("BPQ32 Process was running timer \n"); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + + } + + // Remove this entry from PID List + + for (i=n; i< AttachedProcesses; i++) + { + AttachedPIDList[i]=AttachedPIDList[i+1]; + } + AttachedProcesses--; + + sprintf(buff,"BPQ32 Lost Process - %d Process(es) Attached\n", AttachedProcesses); + OutputDebugString(buff); + } + } +} +VOID MonitorTimerThread(int x) +{ + // Thread to detect killed timer process. Runs in all other BPQ32 processes. + + do { + + Sleep(60000); + + if (TimerInst != 0xffffffff && !IsProcess(TimerInst)) + { + // Timer owning Process has died - Force a new timer to be created + // New timer thread will detect lost process and tidy up + + Debugprintf("BPQ32 Process %d with Timer died", TimerInst); + + // If process was holding the semaphore, release it + + if (Semaphore.Flag == 1 && TimerInst == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process was holding Semaphore - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + +// KillTimer(NULL,TimerHandle); +// TimerHandle=0; +// TimerInst=0xffffffff; +// Tell_Sessions(); + + CheckforLostProcesses(); // Normally only done in timer thread, which is now dead + + // Timer can only run in BPQ32.exe + + TimerInst=0xffffffff; // So we dont keep doing it + TimerHandle = 0; // So new process attaches + + if (Closing == FALSE && AttachingProcess == FALSE) + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + +// if (MinimizetoTray) +// Shell_NotifyIcon(NIM_DELETE,&niData); + } + + } while (TRUE); +} + +VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len); + +VOID TimerProcX(); + +VOID CALLBACK TimerProc( + HWND hwnd, // handle of window for timer messages + UINT uMsg, // WM_TIMER message + UINT idEvent, // timer identifier + DWORD dwTime) // current system time +{ + KillTimer(NULL,TimerHandle); + TimerProcX(); + TimerHandle = SetTimer(NULL,0,100,lpTimerFunc); +} +VOID TimerProcX() +{ + struct _EXCEPTION_POINTERS exinfo; + + // + // Get semaphore before proceeeding + // + + GetSemaphore(&Semaphore, 2); + + // Get time since last run + + QueryPerformanceCounter(¤tTime); + + interval = (int)(currentTime.QuadPart - lastRunTime.QuadPart) / ticksPerMillisec; + lastRunTime.QuadPart = currentTime.QuadPart; + + //Debugprintf("%d", interval); + + // Process WINMORTraceQ + + while (WINMORTraceQ) + { + UINT * Buffer = Q_REM(&WINMORTraceQ); + struct TNCINFO * TNC = (struct TNCINFO * )Buffer[1]; + int Len = Buffer[2]; + char * Msg = (char *)&Buffer[3]; + + WritetoTraceSupport(TNC, Msg, Len); + RelBuff(Buffer); + } + + if (SetWindowTextQ) + SetWindowTextSupport(); + + while (WritetoConsoleQ) + { + UINT * Buffer = Q_REM(&WritetoConsoleQ); + WritetoConsoleSupport((char *)&Buffer[2]); + RelBuff(Buffer); + } + + strcpy(EXCEPTMSG, "Timer ReconfigProcessing"); + + __try + { + + if (trayMenu == NULL) + SetupTrayIcon(); + + // See if reconfigure requested + + if (CloseAllNeeded) + { + CloseAllNeeded = FALSE; + CloseAllPrograms(); + } + + if (ReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + int i; + BPQVECSTRUC * HOSTVEC; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + WSADATA WsaData; // receives data from WSAStartup + RECT cRect; + + ReconfigFlag = FALSE; + + SetupBPQDirectory(); + + WritetoConsole("Reconfiguring ...\n\n"); + OutputDebugString("BPQ32 Reconfiguring ...\n"); + + GetWindowRect(FrameWnd, &FRect); + + SaveWindowPos(70); // Rigcontrol + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + CloseDriverWindow(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + if (AGWActive) + AGWAPITerminate(); + + WSACleanup(); + + WL2KReports = NULL; + + Sleep(2000); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + Start(); + + INITIALISEPORTS(); // Restart Ports + + SetApplPorts(); + + FreeConfig(); + + for (i=1; i<68; i++) // Include Telnet, APRS and IP Vec + { + HOSTVEC=&BPQHOSTVECTOR[i-1]; + + HOSTVEC->HOSTTRACEQ=0; // Clear header (pool has been reinitialized + + if (HOSTVEC->HOSTSESSION !=0) + { + // Had a connection + + HOSTVEC->HOSTSESSION=0; + HOSTVEC->HOSTFLAGS |=3; // Disconnected + + PostMessage(HOSTVEC->HOSTHANDLE, BPQMsg, i, 4); + } + } + + // Free the APRS Appl Q + + APPL_Q = 0; + + OpenReportingSockets(); + + WritetoConsole("\n\nReconfiguration Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + RigActive = Rig_Init(); + + if (NUMBEROFTNCPORTS) + { + FreeSemaphore(&Semaphore); + InitializeTNCEmulator(); + GetSemaphore(&Semaphore, 0); + } + + FreeSemaphore(&Semaphore); + AGWActive = AGWAPIInit(); + GetSemaphore(&Semaphore, 0); + + OutputDebugString("BPQ32 Reconfiguration Complete\n"); + } + } + + + if (RigReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + RigReconfigFlag = FALSE; + CloseDriverWindow(70); + Rig_Close(); + Sleep(6000); // Allow any CATPTT, HAMLIB and FLRIG threads to close + RigActive = Rig_Init(); + + WritetoConsole("Rigcontrol Reconfiguration Complete\n"); + } + } + + if (APRSReconfigFlag) + { + // Only do it it timer owning process, or we could get in a real mess! + + if(TimerInst == GetCurrentProcessId()) + { + APRSReconfigFlag = FALSE; + APRSClose(); + APRSActive = Init_APRS(); + + WritetoConsole("APRS Reconfiguration Complete\n"); + } + } + + } + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + strcpy(EXCEPTMSG, "Timer Processing"); + + __try + { + if (IPActive) Poll_IP(); + if (PMActive) Poll_PM(); + if (RigActive) Rig_Poll(); + + if (NeedWebMailRefresh) + DoRefreshWebMailIndex(); + + CheckGuardZone(); + + if (APRSActive) + { + Poll_APRS(); + CheckGuardZone(); + } + + CheckWL2KReportTimer(); + + CheckGuardZone(); + + TIMERINTERRUPT(); + + CheckGuardZone(); + + FreeSemaphore(&Semaphore); // SendLocation needs to get the semaphore + + if (NUMBEROFTNCPORTS) + TNCTimer(); + + if (AGWActive) + Poll_AGW(); + + DRATSPoll(); + RHPPoll(); + + CheckGuardZone(); + + strcpy(EXCEPTMSG, "HTTP Timer Processing"); + + HTTPTimer(); + + CheckGuardZone(); + + strcpy(EXCEPTMSG, "WL2K Report Timer Processing"); + + if (ReportTimer) + { + ReportTimer--; + + if (ReportTimer == 0) + { + ReportTimer = REPORTINTERVAL; + SendLocation(); + } + } + } + + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + CheckGuardZone(); + + return; +} + +HANDLE NPHandle; + +int (WINAPI FAR *GetModuleFileNameExPtr)() = NULL; +int (WINAPI FAR *EnumProcessesPtr)() = NULL; + +FirstInit() +{ + WSADATA WsaData; // receives data from WSAStartup + HINSTANCE ExtDriver=0; + RECT cRect; + + + // First Time Ports and Timer init + + // Moved from DLLINIT to sort out perl problem, and meet MS Guidelines on minimising DLLMain + + // Call wsastartup - most systems need winsock, and duplicate statups could be a problem + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + // Load Psapi.dll if possible + + ExtDriver=LoadLibrary("Psapi.dll"); + + SetupTrayIcon(); + + if (ExtDriver) + { + GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); + EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); + } + + timeLoadedMS = GetTickCount(); + + INITIALISEPORTS(); + + OpenReportingSockets(); + + WritetoConsole("\n"); + WritetoConsole("Port Initialisation Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + APRSActive = Init_APRS(); + + if (APRSActive) + { + hWndBG = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 0,0,40,546, hConsWnd, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Enable IGate", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, + 8,0,90,24, hConsWnd, (HMENU)-1, hInstance, NULL); + + CreateWindowEx(0, "BUTTON", "", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP, + 95,1,18,24, hConsWnd, (HMENU)IDC_ENIGATE, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "IGate State - Disconnected", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 125, 0, 195, 24, hConsWnd, (HMENU)IGATESTATE, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "IGATE Stats - Msgs 0 Local Stns 0", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 320, 0, 240, 24, hConsWnd, (HMENU)IGATESTATS, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "GPS Off", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 560, 0, 80, 24, hConsWnd, (HMENU)IDC_GPS, hInstance, NULL); + } + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + RigActive = Rig_Init(); + + _beginthread(MonitorThread,0,0); + + TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); + TimerInst=GetCurrentProcessId(); + SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); + + // If ARIF reporting is enabled write a Trimode Like ini for RMS Analyser + + if (ADIFLogEnabled) + ADIFWriteFreqList(); + + OutputDebugString("BPQ32 Port Initialisation Complete\n"); + + if (needAIS) + initAIS(); + + if (needADSB) + initADSB(); + + return 0; +} + +Check_Timer() +{ + if (Closing) + return 0; + + if (Semaphore.Flag) + return 0; + + if (InitDone == (void *)-1) + { + GetSemaphore(&Semaphore, 3); + Sleep(15000); + FreeSemaphore(&Semaphore); + exit (0); + } + + if (FirstInitDone == 0) + { + GetSemaphore(&Semaphore, 3); + + if (_stricmp(pgm, "bpq32.exe") == 0) + { + FirstInit(); + FreeSemaphore(&Semaphore); + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + FirstInitDone=1; // Only init in BPQ32.exe + return 0; + } + else + { + FreeSemaphore(&Semaphore); + return 0; + } + } + + if (TimerHandle == 0 && FirstInitDone == 1) + { + WSADATA WsaData; // receives data from WSAStartup + HINSTANCE ExtDriver=0; + RECT cRect; + + // Only attach timer to bpq32.exe + + if (_stricmp(pgm, "bpq32.exe") != 0) + { + return 0; + } + + GetSemaphore(&Semaphore, 3); + OutputDebugString("BPQ32 Reinitialising External Ports and Attaching Timer\n"); + + if (!ProcessConfig()) + { + ShowWindow(hConsWnd, SW_RESTORE); + SendMessage(hConsWnd, WM_PAINT, 0, 0); + SetForegroundWindow(hConsWnd); + + InitDone = (void *)-1; + FreeSemaphore(&Semaphore); + + MessageBox(NULL,"Configuration File Error","BPQ32",MB_ICONSTOP); + + exit (0); + } + + GetVersionInfo("bpq32.dll"); + + SetupConsoleWindow(); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + Consoleprintf("Reinitialising..."); + + SetupBPQDirectory(); + + Sleep(1000); // Allow time for sockets to close + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + // Load Psapi.dll if possible + + ExtDriver = LoadLibrary("Psapi.dll"); + + SetupTrayIcon(); + + if (ExtDriver) + { + GetModuleFileNameExPtr = (FARPROCX)GetProcAddress(ExtDriver,"GetModuleFileNameExA"); + EnumProcessesPtr = (FARPROCX)GetProcAddress(ExtDriver,"EnumProcesses"); + } + + Start(); + + INITIALISEPORTS(); + + OpenReportingSockets(); + + NODESINPROGRESS = 0; + CURRENTNODE = 0; + + SetApplPorts(); + + WritetoConsole("\n\nPort Reinitialisation Complete\n"); + + BPQMsg = RegisterWindowMessage(BPQWinMsg); + + CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); + +// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", +// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); + + if (IPRequired) IPActive = Init_IP(); + if (PMRequired) PMActive = Init_PM(); + + RigActive = Rig_Init(); + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + CheckDlgButton(hConsWnd, IDC_ENIGATE, IGateEnabled); + + GetClientRect(hConsWnd, &cRect); + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + { + ShowWindow(GetDlgItem(hConsWnd, IDC_GPS), SW_HIDE); + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + } + InvalidateRect(hConsWnd, NULL, TRUE); + + FreeConfig(); + + _beginthread(MonitorThread,0,0); + + ReportTimer = 0; + + OpenReportingSockets(); + + FreeSemaphore(&Semaphore); + + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + + if (StartMinimized) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, SW_SHOWMINIMIZED); + else + ShowWindow(FrameWnd, SW_RESTORE); + + TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); + TimerInst=GetCurrentProcessId(); + SessHandle = SetTimer(NULL, 0, 5000, lpSetupTermSessions); + + return (1); + } + + return (0); +} + +DllExport INT APIENTRY CheckTimer() +{ + return Check_Timer(); +} + +Tell_Sessions() +{ + // + // Post a message to all listening sessions, so they call the + // API, and cause a new timer to be allocated + // + HWND hWnd; + int i; + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].HOSTFLAGS & 0x80) + { + hWnd = BPQHOSTVECTOR[i-1].HOSTHANDLE; + PostMessage(hWnd, BPQMsg,i, 1); + PostMessage(hWnd, BPQMsg,i, 2); + } + } + return (0); +} + +BOOL APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved) +{ + DWORD n; + char buf[350]; + + int i; + unsigned int ProcessID; + + OSVERSIONINFO osvi; + + memset(&osvi, 0, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + GetVersionEx(&osvi); + + + switch( ul_reason_being_called ) + { + case DLL_PROCESS_ATTACH: + + if (sizeof(HDLCDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much HDLC data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct KISSINFO) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much KISS data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct _EXTPORTDATA) > PORTENTRYLEN + 200) // 200 bytes of Hardwaredata + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"BPQ32 Too much _EXTPORTDATA data - Recompile","BPQ32", MB_OK); + return 0; + } + + if (sizeof(LINKTABLE) != LINK_TABLE_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"L2 LINK Table .c and .asm mismatch - fix and rebuild","BPQ32", MB_OK); + return 0; + } + if (sizeof(struct ROUTE) != ROUTE_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"ROUTE Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); + return 0; + } + + if (sizeof(struct DEST_LIST) != DEST_LIST_LEN) + { + // Catastrophic - Refuse to load + + MessageBox(NULL,"NODES Table .c and .asm mismatch - fix and rebuild", "BPQ32", MB_OK); + return 0; + } + + GetSemaphore(&Semaphore, 4); + + BPQHOSTVECPTR = &BPQHOSTVECTOR[0]; + + LoadToolHelperRoutines(); + + Our_PID = GetCurrentProcessId(); + + QueryPerformanceFrequency(&lpFrequency); + + ticksPerMillisec = (int)lpFrequency.QuadPart / 1000; + + lastRunTime.QuadPart = lpFrequency.QuadPart; + + GetProcess(Our_PID, pgm); + + if (_stricmp(pgm, "regsvr32.exe") == 0 || _stricmp(pgm, "bpqcontrol.exe") == 0) + { + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 1; + } + + if (_stricmp(pgm,"BPQ32.exe") == 0) + BPQ32_EXE = TRUE; + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + IncludesMail = TRUE; + + if (_stricmp(pgm,"BPQMail.exe") == 0) + IncludesMail = TRUE; + + if (_stricmp(pgm,"BPQChat.exe") == 0) + IncludesChat = TRUE; + + if (FirstEntry) // If loaded by BPQ32.exe, dont close it at end + { + FirstEntry = 0; + if (BPQ32_EXE) + CloseLast = FALSE; + } + else + { + if (BPQ32_EXE && AttachingProcess == 0) + { + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + MessageBox(NULL,"BPQ32.exe is already running\r\n\r\nIt should only be run once", "BPQ32", MB_OK); + return 0; + } + } + + if (_stricmp(pgm,"BPQTelnetServer.exe") == 0) + { + MessageBox(NULL,"BPQTelnetServer is no longer supported\r\n\r\nUse the TelnetServer in BPQ32.dll", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + + if (_stricmp(pgm,"BPQUIUtil.exe") == 0) + { + MessageBox(NULL,"BPQUIUtil is now part of BPQ32.dll\r\nBPQUIUtil.exe cannot be run\r\n", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + { + MessageBox(NULL,"BPQMailChat is obsolete. Run BPQMail.exe and/or BPQChat.exe instead", "BPQ32", MB_OK); + AttachedProcesses++; // We will get a detach + FreeSemaphore(&Semaphore); + return 0; + } + AuthorisedProgram = TRUE; + + if (InitDone == 0) + { +// #pragma warning(push) +// #pragma warning(disable : 4996) + +// if (_winver < 0x0600) +// #pragma warning(pop) +// { +// // Below Vista +// +// REGTREE = HKEY_LOCAL_MACHINE; +// strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); +// } + + hInstance=hInst; + + Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex != NULL) + { + OutputDebugString("Another BPQ32.dll is loaded\n"); + i=MessageBox(NULL,"BPQ32 DLL already loaded from another directory\nIf you REALLY want this, hit OK, else hit Cancel","BPQ32",MB_OKCANCEL); + FreeSemaphore(&Semaphore); + + if (i != IDOK) return (0); + + CloseHandle(Mutex); + } + + if (!BPQ32_EXE) + { + if (CheckifBPQ32isLoaded() == FALSE) // Start BPQ32.exe if needed + { + // Wasn't Loaded, so we have started it, and should let it init system + + goto SkipInit; + } + } + + GetVersionInfo("bpq32.dll"); + + sprintf (SIGNONMSG, "G8BPQ AX25 Packet Switch System Version %s %s\r\n%s\r\n", + TextVerstring, Datestring, VerCopyright); + + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for Win32 (", TextVerstring); + + SetupConsoleWindow(); + SetupBPQDirectory(); + + if (!ProcessConfig()) + { + StartMinimized = FALSE; + MinimizetoTray = FALSE; + ShowWindow(FrameWnd, SW_MAXIMIZE); + ShowWindow(hConsWnd, SW_MAXIMIZE); + ShowWindow(StatusWnd, SW_HIDE); + + SendMessage(hConsWnd, WM_PAINT, 0, 0); + SetForegroundWindow(hConsWnd); + + InitDone = (void *)-1; + FreeSemaphore(&Semaphore); + + MessageBox(NULL,"Configuration File Error\r\nProgram will close in 15 seconds","BPQ32",MB_ICONSTOP); + + return (0); + } + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + if (Start() !=0) + { + Sleep(3000); + FreeSemaphore(&Semaphore); + return (0); + } + else + { + SetApplPorts(); + + GetUIConfig(); + + InitDone = &InitDone; + BPQMsg = RegisterWindowMessage(BPQWinMsg); +// TimerHandle=SetTimer(NULL,0,100,lpTimerFunc); +// TimerInst=GetCurrentProcessId(); + +/* Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex != NULL) + { + OutputDebugString("Another BPQ32.dll is loaded\n"); + MessageBox(NULL,"BPQ32 DLL already loaded from another directory","BPQ32",MB_ICONSTOP); + FreeSemaphore(&Semaphore); + return (0); + } + +*/ + Mutex=CreateMutex(NULL,TRUE,"BPQLOCKMUTEX"); + +// CreatePipe(&H1,&H2,NULL,1000); + +// GetLastError(); + +// NPHandle=CreateNamedPipe("\\\\.\\pipe\\BPQ32pipe", +// PIPE_ACCESS_DUPLEX,0,64,4096,4096,1000,NULL); + +// GetLastError(); + +/* + // + // Read SYSOP password + // + + if (PWTEXT[0] == 0) + { + handle = OpenConfigFile("PASSWORD.BPQ"); + + if (handle == INVALID_HANDLE_VALUE) + { + WritetoConsole("Can't open PASSWORD.BPQ\n"); + PWLen=0; + PWTEXT[0]=0; + } + else + { + ReadFile(handle,PWTEXT,78,&n,NULL); + CloseHandle(handle); + } + } +*/ + for (i=0;PWTEXT[i] > 0x20;i++); //Scan for cr or null + PWLen=i; + + } + } + else + { + if (InitDone != &InitDone) + { + MessageBox(NULL,"BPQ32 DLL already loaded at another address","BPQ32",MB_ICONSTOP); + FreeSemaphore(&Semaphore); + return (0); + } + } + + // Run timer monitor thread in all processes - it is possible for the TImer thread not to be the first thread +SkipInit: + + _beginthread(MonitorTimerThread,0,0); + + FreeSemaphore(&Semaphore); + + AttachedPIDList[AttachedProcesses++] = GetCurrentProcessId(); + + if (_stricmp(pgm,"bpq32.exe") == 0 && AttachingProcess == 1) AttachingProcess = 0; + + GetProcess(GetCurrentProcessId(),pgm); + n=sprintf(buf,"BPQ32 DLL Attach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); + OutputDebugString(buf); + + // Set up local variables + + MCOM=1; + MTX=1; + MMASK=0xffffffffffffffff; + +// if (StartMinimized) +// if (MinimizetoTray) +// ShowWindow(FrameWnd, SW_HIDE); +// else +// ShowWindow(FrameWnd, SW_SHOWMINIMIZED); +// else +// ShowWindow(FrameWnd, SW_RESTORE); + + return 1; + + case DLL_THREAD_ATTACH: + + return 1; + + case DLL_THREAD_DETACH: + + return 1; + + case DLL_PROCESS_DETACH: + + if (_stricmp(pgm,"BPQMailChat.exe") == 0) + IncludesMail = FALSE; + + if (_stricmp(pgm,"BPQChat.exe") == 0) + IncludesChat = FALSE; + + ProcessID=GetCurrentProcessId(); + + Debugprintf("BPQ32 Process %d Detaching", ProcessID); + + // Release any streams that the app has failed to release + + for (i=1;i<65;i++) + { + if (BPQHOSTVECTOR[i-1].STREAMOWNER == ProcessID) + { + // If connected, disconnect + + SessionControl(i, 2, 0); + DeallocateStream(i); + } + } + + // Remove any Tray Icon Entries + + for( i = 0; i < 100; ++i ) + { + if (PIDArray[i] == ProcessID) + { + char Log[80]; + hWndArray[i] = 0; + sprintf(Log,"BPQ32 Removing Tray Item %s\n", PopupText[i]); + OutputDebugString(Log); + DeleteMenu(trayMenu,TRAYBASEID+i,MF_BYCOMMAND); + } + } + + if (Mutex) CloseHandle(Mutex); + + // Remove our entry from PID List + + for (i=0; i< AttachedProcesses; i++) + if (AttachedPIDList[i] == ProcessID) + break; + + for (; i< AttachedProcesses; i++) + { + AttachedPIDList[i]=AttachedPIDList[i+1]; + } + + AttachedProcesses--; + + if (TimerInst == ProcessID) + { + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + OutputDebugString("BPQ32 Process with Timer closing\n"); + + // Call Port Close Routines + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR && PORTVEC->DLLhandle == NULL) // Don't call if real .dll - it's not there! + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + if (AGWActive) + AGWAPITerminate(); + + upnpClose(); + + WSACleanup(); + WSAGetLastError(); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + if (hConsWnd) DestroyWindow(hConsWnd); + + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; + + if (AttachedProcesses && Closing == FALSE && AttachingProcess == 0) // Other processes + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + } + else + { + // Not Timer Process + + if (AttachedProcesses == 1 && CloseLast) // Only bpq32.exe left + { + Debugprintf("Only BPQ32.exe running - close it"); + CloseAllNeeded = TRUE; + } + } + + if (AttachedProcesses < 2) + { + if (AUTOSAVE) + SaveNodes(); + if (AUTOSAVEMH) + SaveMH(); + + if (needAIS) + SaveAIS(); + } + if (AttachedProcesses == 0) + { + Closing = TRUE; + KillTimer(NULL,TimerHandle); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + // Unload External Drivers + + { + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10 && PORTVEC->DLLhandle) + FreeLibrary(PORTVEC->DLLhandle); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + } + } + + GetProcess(GetCurrentProcessId(),pgm); + n=sprintf(buf,"BPQ32 DLL Detach complete - Program %s - %d Process(es) Attached\n",pgm,AttachedProcesses); + OutputDebugString(buf); + + return 1; + } + return 1; +} + +DllExport int APIENTRY CloseBPQ32() +{ + // Unload External Drivers + + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + int i; + int ProcessID = GetCurrentProcessId(); + + if (Semaphore.Flag == 1 && ProcessID == Semaphore.SemProcessID) + { + OutputDebugString("BPQ32 Process holding Semaphore called CloseBPQ32 - attempting recovery\r\n"); + Debugprintf("Last Sem Call %d %x %x %x %x %x %x", SemHeldByAPI, + Sem_eax, Sem_ebx, Sem_ecx, Sem_edx, Sem_esi, Sem_edi); + + Semaphore.Flag = 0; + SemHeldByAPI = 0; + } + + if (TimerInst == ProcessID) + { + OutputDebugString("BPQ32 Process with Timer called CloseBPQ32\n"); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + KillTimer(NULL,TimerHandle); + TimerHandle=0; + TimerInst=0xffffffff; + + IPClose(); + PMClose(); + APRSClose(); + Rig_Close(); + if (AGWActive) + AGWAPITerminate(); + + upnpClose(); + + CloseTNCEmulator(); + WSACleanup(); + + if (hConsWnd) DestroyWindow(hConsWnd); + + Debugprintf("AttachedProcesses %d ", AttachedProcesses); + + if (AttachedProcesses > 1 && Closing == FALSE && AttachingProcess == 0) // Other processes + { + OutputDebugString("BPQ32 Reloading BPQ32.exe\n"); + StartBPQ32(); + } + } + + return 0; +} + +BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut); + +VOID SetupBPQDirectory() +{ + HKEY hKey = 0; + HKEY hKeyIn = 0; + HKEY hKeyOut = 0; + int disp; + int retCode,Type,Vallen=MAX_PATH,i; + char msg[512]; + char ValfromReg[MAX_PATH] = ""; + char DLLName[256]="Not Known"; + char LogDir[256]; + char Time[64]; + +/* +•NT4 was/is '4' +•Win 95 is 4.00.950 +•Win 98 is 4.10.1998 +•Win 98 SE is 4.10.2222 +•Win ME is 4.90.3000 +•2000 is NT 5.0.2195 +•XP is actually 5.1 +•Vista is 6.0 +•Win7 is 6.1 + + i = _osver; / Build + i = _winmajor; + i = _winminor; +*/ +/* +#pragma warning(push) +#pragma warning(disable : 4996) + +if (_winver < 0x0600) +#pragma warning(pop) + { + // Below Vista + + REGTREE = HKEY_LOCAL_MACHINE; + strcpy(REGTREETEXT, "HKEY_LOCAL_MACHINE"); + ValfromReg[0] = 0; + } + else +*/ + { + if (_stricmp(pgm, "regsvr32.exe") == 0) + { + Debugprintf("BPQ32 loaded by regsvr32.exe - Registry not copied"); + } + else + { + // If necessary, move reg from HKEY_LOCAL_MACHINE to HKEY_CURRENT_USER + + retCode = RegOpenKeyEx (HKEY_LOCAL_MACHINE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_READ, + &hKeyIn); + + retCode = RegCreateKeyEx(HKEY_CURRENT_USER, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKeyOut, &disp); + + // See if Version Key exists in HKEY_CURRENT_USER - if it does, we have already done the copy + + Vallen = MAX_PATH; + retCode = RegQueryValueEx(hKeyOut, "Version" ,0 , &Type,(UCHAR *)&msg, &Vallen); + + if (retCode != ERROR_SUCCESS) + if (hKeyIn) + CopyReg(hKeyIn, hKeyOut); + + RegCloseKey(hKeyIn); + RegCloseKey(hKeyOut); + } + } + + GetModuleFileName(hInstance,DLLName,256); + + BPQDirectory[0]=0; + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + // Try "BPQ Directory" + + Vallen = MAX_PATH; + retCode = RegQueryValueEx(hKey,"BPQ Directory",0, + &Type,(UCHAR *)&ValfromReg,&Vallen); + + if (retCode == ERROR_SUCCESS) + { + if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') + ValfromReg[0]=0; + } + + if (ValfromReg[0] == 0) + { + // BPQ Directory absent or = "" - try "Config File Location" + + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey,"Config File Location",0, + &Type,(UCHAR *)&ValfromReg,&Vallen); + + if (retCode == ERROR_SUCCESS) + { + if (strlen(ValfromReg) == 2 && ValfromReg[0] == '"' && ValfromReg[1] == '"') + ValfromReg[0]=0; + } + } + + if (ValfromReg[0] == 0) GetCurrentDirectory(MAX_PATH, ValfromReg); + + // Get StartMinimized and MinimizetoTray flags + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); + + ExpandEnvironmentStrings(ValfromReg, BPQDirectory, MAX_PATH); + + // Also get "BPQ Program Directory" + + ValfromReg[0] = 0; + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey, "BPQ Program Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); + + if (retCode == ERROR_SUCCESS) + ExpandEnvironmentStrings(ValfromReg, BPQProgramDirectory, MAX_PATH); + + // And Log Directory + + ValfromReg[0] = 0; + Vallen = MAX_PATH; + + retCode = RegQueryValueEx(hKey, "Log Directory",0 , &Type, (UCHAR *)&ValfromReg, &Vallen); + + if (retCode == ERROR_SUCCESS) + ExpandEnvironmentStrings(ValfromReg, LogDirectory, MAX_PATH); + + RegCloseKey(hKey); + } + + strcpy(ConfigDirectory, BPQDirectory); + + if (LogDirectory[0] == 0) + strcpy(LogDirectory, BPQDirectory); + + if (BPQProgramDirectory[0] == 0) + strcpy(BPQProgramDirectory, BPQDirectory); + + sprintf(msg,"BPQ32 Ver %s Loaded from: %s by %s\n", VersionString, DLLName, pgm); + WritetoConsole(msg); + OutputDebugString(msg); + FormatTime3(Time, time(NULL)); + sprintf(msg,"Loaded %s\n", Time); + WritetoConsole(msg); + OutputDebugString(msg); + +#pragma warning(push) +#pragma warning(disable : 4996) + +#if _MSC_VER >= 1400 + +#define _winmajor 6 +#define _winminor 0 + +#endif + + i=sprintf(msg,"Windows Ver %d.%d, Using Registry Key %s\n" ,_winmajor, _winminor, REGTREETEXT); + +#pragma warning(pop) + + WritetoConsole(msg); + OutputDebugString(msg); + + i=sprintf(msg,"BPQ32 Using config from: %s\n\n",BPQDirectory); + WritetoConsole(&msg[6]); + msg[i-1]=0; + OutputDebugString(msg); + + // Don't write the Version Key if loaded by regsvr32.exe (Installer is running with Admin rights, + // so will write the wrong tree on ) + + if (_stricmp(pgm, "regsvr32.exe") == 0) + { + Debugprintf("BPQ32 loaded by regsvr32.exe - Version String not written"); + } + else + { + retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + sprintf(msg,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); + retCode = RegSetValueEx(hKey, "Version",0, REG_SZ,(BYTE *)msg, strlen(msg) + 1); + + RegCloseKey(hKey); + } + + // Make sure Logs Directory exists + + sprintf(LogDir, "%s/Logs", LogDirectory); + + CreateDirectory(LogDir, NULL); + + return; +} + +HANDLE OpenConfigFile(char *fn) +{ + HANDLE handle; + UCHAR Value[MAX_PATH]; + FILETIME LastWriteTime; + SYSTEMTIME Time; + char Msg[256]; + + + // If no directory, use current + if (BPQDirectory[0] == 0) + { + strcpy(Value,fn); + } + else + { + strcpy(Value,BPQDirectory); + strcat(Value,"\\"); + strcat(Value,fn); + } + + handle = CreateFile(Value, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + GetFileTime(handle, NULL, NULL, &LastWriteTime); + FileTimeToSystemTime(&LastWriteTime, &Time); + + sprintf(Msg,"BPQ32 Config File %s Created %.2d:%.2d %d/%.2d/%.2d\n", Value, + Time.wHour, Time.wMinute, Time.wYear, Time.wMonth, Time.wDay); + + OutputDebugString(Msg); + + return(handle); +} + +#ifdef _WIN64 +int BPQHOSTAPI() +{ + return 0; +} +#endif + + +DllExport int APIENTRY GETBPQAPI() +{ + return (int)BPQHOSTAPI; +} + +//DllExport UINT APIENTRY GETMONDECODE() +//{ +// return (UINT)MONDECODE; +//} + + +DllExport INT APIENTRY BPQAPI(int Fn, char * params) +{ + +/* +; +; BPQ HOST MODE SUPPORT CODE +; +; 22/11/95 +; +; MOVED FROM TNCODE.ASM COS CONITIONALS WERE GETTING TOO COMPLICATED +; (OS2 VERSION HAD UPSET KANT VERISON +; +; +*/ + + +/* + + BPQHOSTPORT: +; +; SPECIAL INTERFACE, MAINLY FOR EXTERNAL HOST MODE SUPPORT PROGS +; +; COMMANDS SUPPORTED ARE +; +; AH = 0 Get node/switch version number and description. On return +; AH='B',AL='P',BH='Q',BL=' ' +; DH = major version number and DL = minor version number. +; +; +; AH = 1 Set application mask to value in DL (or even DX if 16 +; applications are ever to be supported). +; +; Set application flag(s) to value in CL (or CX). +; whether user gets connected/disconnected messages issued +; by the node etc. +; +; +; AH = 2 Send frame in ES:SI (length CX) +; +; +; AH = 3 Receive frame into buffer at ES:DI, length of frame returned +; in CX. BX returns the number of outstanding frames still to +; be received (ie. after this one) or zero if no more frames +; (ie. this is last one). +; +; +; +; AH = 4 Get stream status. Returns: +; +; CX = 0 if stream disconnected or CX = 1 if stream connected +; DX = 0 if no change of state since last read, or DX = 1 if +; the connected/disconnected state has changed since +; last read (ie. delta-stream status). +; +; +; +; AH = 6 Session control. +; +; CX = 0 Conneect - _APPLMASK in DL +; CX = 1 connect +; CX = 2 disconnect +; CX = 3 return user to node +; +; +; AH = 7 Get buffer counts for stream. Returns: +; +; AX = number of status change messages to be received +; BX = number of frames queued for receive +; CX = number of un-acked frames to be sent +; DX = number of buffers left in node +; SI = number of trace frames queued for receive +; +;AH = 8 Port control/information. Called with a stream number +; in AL returns: +; +; AL = Radio port on which channel is connected (or zero) +; AH = SESSION TYPE BITS +; BX = L2 paclen for the radio port +; CX = L2 maxframe for the radio port +; DX = L4 window size (if L4 circuit, or zero) +; ES:DI = CALLSIGN + +;AH = 9 Fetch node/application callsign & alias. AL = application +; number: +; +; 0 = node +; 1 = BBS +; 2 = HOST +; 3 = SYSOP etc. etc. +; +; Returns string with alias & callsign or application name in +; user's buffer pointed to by ES:SI length CX. For example: +; +; "WORCS:G8TIC" or "TICPMS:G8TIC-10". +; +; +; AH = 10 Unproto transmit frame. Data pointed to by ES:SI, of +; length CX, is transmitted as a HDLC frame on the radio +; port (not stream) in AL. +; +; +; AH = 11 Get Trace (RAW Data) Frame into ES:DI, +; Length to CX, Timestamp to AX +; +; +; AH = 12 Update Switch. At the moment only Beacon Text may be updated +; DX = Function +; 1=update BT. ES:SI, Len CX = Text +; 2=kick off nodes broadcast +; +; AH = 13 Allocate/deallocate stream +; If AL=0, return first free stream +; If AL>0, CL=1, Allocate stream. If aleady allocated, +; return CX nonzero, else allocate, and return CX=0 +; If AL>0, CL=2, Release stream +; +; +; AH = 14 Internal Interface for IP Router +; +; Send frame - to NETROM L3 if DL=0 +; to L2 Session if DL<>0 +; +; +; AH = 15 Get interval timer + + +*/ + + + switch(Fn) + { + + case CHECKLOADED: + + params[0]=MAJORVERSION; + params[1]=MINORVERSION; + params[2]=QCOUNT; + + return (1); + } + return 0; +} + +DllExport int APIENTRY InitSwitch() +{ + return (0); +} + +/*DllExport int APIENTRY SwitchTimer() +{ + GetSemaphore((&Semaphore); + + TIMERINTERRUPT(); + + FreeSemaphore(&Semaphore); + + return (0); +} +*/ +DllExport int APIENTRY GetFreeBuffs() +{ +// Returns number of free buffers +// (BPQHOST function 7 (part)). + return (QCOUNT); +} + +DllExport UCHAR * APIENTRY GetNodeCall() +{ + return (&MYNODECALL); +} + + +DllExport UCHAR * APIENTRY GetNodeAlias() +{ + return (&MYALIASTEXT[0]); +} + +DllExport UCHAR * APIENTRY GetBBSCall() +{ + return (UCHAR *)(&APPLCALLTABLE[0].APPLCALL_TEXT); +} + + +DllExport UCHAR * APIENTRY GetBBSAlias() +{ + return (UCHAR *)(&APPLCALLTABLE[0].APPLALIAS_TEXT); +} + +DllExport VOID APIENTRY GetApplCallVB(int Appl, char * ApplCall) +{ + if (Appl < 1 || Appl > NumberofAppls ) return; + + strncpy(ApplCall,(char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT, 10); +} + +BOOL UpdateNodesForApp(int Appl); + +DllExport BOOL APIENTRY SetApplCall(int Appl, char * NewCall) +{ + char Call[10]=" "; + int i; + + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + i=strlen(NewCall); + + if (i > 10) i=10; + + strncpy(Call,NewCall,i); + + strncpy((char *)&APPLCALLTABLE[Appl-1].APPLCALL_TEXT,Call,10); + + if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLCALL)) return FALSE; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + +DllExport BOOL APIENTRY SetApplAlias(int Appl, char * NewCall) +{ + char Call[10]=" "; + int i; + + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + i=strlen(NewCall); + + if (i > 10) i=10; + + strncpy(Call,NewCall,i); + + strncpy((char *)&APPLCALLTABLE[Appl-1].APPLALIAS_TEXT,Call,10); + + if (!ConvToAX25(Call,APPLCALLTABLE[Appl-1].APPLALIAS)) return FALSE; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + + + +DllExport BOOL APIENTRY SetApplQual(int Appl, int NewQual) +{ + if (Appl < 1 || Appl > NumberofAppls ) return FALSE; + + APPLCALLTABLE[Appl-1].APPLQUAL=NewQual; + + UpdateNodesForApp(Appl); + + return TRUE; + +} + + +BOOL UpdateNodesForApp(int Appl) +{ + int App=Appl-1; + int DestLen = sizeof (struct DEST_LIST); + int n = MAXDESTS; + + struct DEST_LIST * DEST = APPLCALLTABLE[App].NODEPOINTER; + APPLCALLS * APPL=&APPLCALLTABLE[App]; + + if (DEST == NULL) + { + // No dest at the moment. If we have valid call and Qual, create an entry + + if (APPLCALLTABLE[App].APPLQUAL == 0) return FALSE; + + if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; + + + GetSemaphore(&Semaphore, 5); + + DEST = DESTS; + + while (n--) + { + if (DEST->DEST_CALL[0] == 0) // Spare + break; + } + + if (n == 0) + { + // no dests + + FreeSemaphore(&Semaphore); + return FALSE; + } + + NUMBEROFNODES++; + APPL->NODEPOINTER = DEST; + + memmove (DEST->DEST_CALL,APPL->APPLCALL,13); + + DEST->DEST_STATE=0x80; // SPECIAL ENTRY + + DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + + FreeSemaphore(&Semaphore); + + return TRUE; + } + + // We have a destination. If Quality is zero, remove it, else update it + + if (APPLCALLTABLE[App].APPLQUAL == 0) + { + GetSemaphore(&Semaphore, 6); + + REMOVENODE(DEST); // Clear buffers, Remove from Sorted Nodes chain, and zap entry + + APPL->NODEPOINTER=NULL; + + FreeSemaphore(&Semaphore); + return FALSE; + + } + + if (APPLCALLTABLE[App].APPLCALL[0] < 41) return FALSE; + + GetSemaphore(&Semaphore, 7); + + memmove (DEST->DEST_CALL,APPL->APPLCALL,13); + + DEST->DEST_STATE=0x80; // SPECIAL ENTRY + + DEST->NRROUTE[0].ROUT_QUALITY = (BYTE)APPL->APPLQUAL; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + + FreeSemaphore(&Semaphore); + return TRUE; + +} + + +DllExport UCHAR * APIENTRY GetSignOnMsg() +{ + return (&SIGNONMSG[0]); +} + + +DllExport HKEY APIENTRY GetRegistryKey() +{ + return REGTREE; +} + +DllExport char * APIENTRY GetRegistryKeyText() +{ + return REGTREETEXT;; +} + +DllExport UCHAR * APIENTRY GetBPQDirectory() +{ + while (BPQDirectory[0] == 0) + { + Debugprintf("BPQ Directory not set up - waiting"); + Sleep(1000); + } + return (&BPQDirectory[0]); +} + +DllExport UCHAR * APIENTRY GetProgramDirectory() +{ + return (&BPQProgramDirectory[0]); +} + +DllExport UCHAR * APIENTRY GetLogDirectory() +{ + return (&LogDirectory[0]); +} + +// Version for Visual Basic + +DllExport char * APIENTRY CopyBPQDirectory(char * dir) +{ + return (strcpy(dir,BPQDirectory)); +} + +DllExport int APIENTRY GetMsgPerl(int stream, char * msg) +{ + int len,count; + + GetMsg(stream, msg, &len, &count ); + + return len; +} + +int Rig_Command(int Session, char * Command); + +BOOL Rig_CommandInt(int Session, char * Command) +{ + return Rig_Command(Session, Command); +} + +DllExport int APIENTRY BPQSetHandle(int Stream, HWND hWnd) +{ + BPQHOSTVECTOR[Stream-1].HOSTHANDLE=hWnd; + return (0); +} + +#define L4USER 0 + +BPQVECSTRUC * PORTVEC ; + +VOID * InitializeExtDriver(PEXTPORTDATA PORTVEC) +{ + HINSTANCE ExtDriver=0; + char msg[128]; + int err=0; + HKEY hKey=0; + UCHAR Value[MAX_PATH]; + + // If no directory, use current + + if (BPQDirectory[0] == 0) + { + strcpy(Value,PORTVEC->PORT_DLL_NAME); + } + else + { + strcpy(Value,BPQDirectory); + strcat(Value,"\\"); + strcat(Value,PORTVEC->PORT_DLL_NAME); + } + + // Several Drivers are now built into bpq32.dll + + _strupr(Value); + + if (strstr(Value, "BPQVKISS")) + return VCOMExtInit; + + if (strstr(Value, "BPQAXIP")) + return AXIPExtInit; + + if (strstr(Value, "BPQETHER")) + return ETHERExtInit; + + if (strstr(Value, "BPQTOAGW")) + return AGWExtInit; + + if (strstr(Value, "AEAPACTOR")) + return AEAExtInit; + + if (strstr(Value, "HALDRIVER")) + return HALExtInit; + + if (strstr(Value, "KAMPACTOR")) + return KAMExtInit; + + if (strstr(Value, "SCSPACTOR")) + return SCSExtInit; + + if (strstr(Value, "WINMOR")) + return WinmorExtInit; + + if (strstr(Value, "V4")) + return V4ExtInit; + + if (strstr(Value, "TELNET")) + return TelnetExtInit; + +// if (strstr(Value, "SOUNDMODEM")) +// return SoundModemExtInit; + + if (strstr(Value, "SCSTRACKER")) + return TrackerExtInit; + + if (strstr(Value, "TRKMULTI")) + return TrackerMExtInit; + + if (strstr(Value, "UZ7HO")) + return UZ7HOExtInit; + + if (strstr(Value, "MULTIPSK")) + return MPSKExtInit; + + if (strstr(Value, "FLDIGI")) + return FLDigiExtInit; + + if (strstr(Value, "UIARQ")) + return UIARQExtInit; + +// if (strstr(Value, "BAYCOM")) +// return (UINT) BaycomExtInit; + + if (strstr(Value, "VARA")) + return VARAExtInit; + + if (strstr(Value, "ARDOP")) + return ARDOPExtInit; + + if (strstr(Value, "SERIAL")) + return SerialExtInit; + + if (strstr(Value, "KISSHF")) + return KISSHFExtInit; + + if (strstr(Value, "WINRPR")) + return WinRPRExtInit; + + if (strstr(Value, "HSMODEM")) + return HSMODEMExtInit; + + if (strstr(Value, "FREEDATA")) + return FreeDataExtInit; + + if (strstr(Value, "6PACK")) + return SIXPACKExtInit; + + ExtDriver = LoadLibrary(Value); + + if (ExtDriver == NULL) + { + err=GetLastError(); + + sprintf(msg,"Error loading Driver %s - Error code %d", + PORTVEC->PORT_DLL_NAME,err); + + MessageBox(NULL,msg,"BPQ32",MB_ICONSTOP); + + return(0); + } + + PORTVEC->DLLhandle=ExtDriver; + + return (GetProcAddress(ExtDriver,"_ExtInit@4")); + +} + +/* +_DATABASE LABEL BYTE + +FILLER DB 14 DUP (0) ; PROTECTION AGENST BUFFER PROBLEMS! + DB MAJORVERSION,MINORVERSION +_NEIGHBOURS DD 0 + DW TYPE ROUTE +_MAXNEIGHBOURS DW 20 ; MAX ADJACENT NODES + +_DESTS DD 0 ; NODE LIST + DW TYPE DEST_LIST +MAXDESTS DW 100 ; MAX NODES IN SYSTEM +*/ + + +DllExport int APIENTRY GetAttachedProcesses() +{ + return (AttachedProcesses); +} + +DllExport int * APIENTRY GetAttachedProcessList() +{ + return (&AttachedPIDList[0]); +} + +DllExport int * APIENTRY SaveNodesSupport() +{ + return (&DATABASESTART); +} + +// +// Internal BPQNODES support +// + +#define UCHAR unsigned char + +/* +ROUTE ADD G1HTL-1 2 200 0 0 0 +ROUTE ADD G4IRX-3 2 200 0 0 0 +NODE ADD MAPPLY:G1HTL-1 G1HTL-1 2 200 G4IRX-3 2 98 +NODE ADD NOT:GB7NOT G1HTL-1 2 199 G4IRX-3 2 98 + +*/ + +struct DEST_LIST * Dests; +struct ROUTE * Routes; + +int MaxNodes; +int MaxRoutes; +int NodeLen; +int RouteLen; + +int count; +int cursor; + +int len,i; + +ULONG cnt; +char Normcall[10]; +char Portcall[10]; +char Alias[7]; + +char line[100]; + +HANDLE handle; + +int APIENTRY Restart() +{ + int i, Count = AttachedProcesses; + HANDLE hProc; + DWORD PID; + + for (i = 0; i < Count; i++) + { + PID = AttachedPIDList[i]; + + // Kill Timer Owner last + + if (TimerInst != PID) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + } + } + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TimerInst); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + + return 0; +} + +int APIENTRY Reboot() +{ + // Run shutdown -r -f + + STARTUPINFO SInfo; + PROCESS_INFORMATION PInfo; + char Cmd[] = "shutdown -r -f"; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + return CreateProcess(NULL, Cmd, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo); +} +/* +int APIENTRY Reconfig() +{ + if (!ProcessConfig()) + { + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 1; +} +*/ +// Code to support minimizing all BPQ Apps to a single Tray ICON + +// As we can't minimize the console window to the tray, I'll use an ordinary +// window instead. This also gives me somewhere to post the messages to + + +char AppName[] = "BPQ32"; +char Title[80] = "BPQ32.dll Console"; + +int NewLine(); + +char FrameClassName[] = TEXT("MdiFrame"); + +HWND ClientWnd; //This stores the MDI client area window handle + +LOGFONT LFTTYFONT ; + +HFONT hFont ; + +HMENU hPopMenu, hWndMenu; +HMENU hMainFrameMenu = NULL; +HMENU hBaseMenu = NULL; +HMENU hConsMenu = NULL; +HMENU hTermMenu = NULL; +HMENU hMonMenu = NULL; +HMENU hTermActMenu, hTermCfgMenu, hTermEdtMenu, hTermHlpMenu; +HMENU hMonActMenu, hMonCfgMenu, hMonEdtMenu, hMonHlpMenu; + + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +DllExport int APIENTRY DeleteTrayMenuItem(HWND hWnd); + +#define BPQMonitorAvail 1 +#define BPQDataAvail 2 +#define BPQStateChange 4 + +VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value); +SOCKET OpenWL2KHTTPSock(); +SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); + +BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER); +BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL); + + +static INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + { + char _REPLYBUFFER[1000] = ""; + char Value[1000]; + + if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER)) + { +// if (strstr(_REPLYBUFFER, "\"ErrorMessage\":") == 0) + + GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Value); + SetDlgItemText(hDlg, NAME, Value); + + GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", Value); + SetDlgItemText(hDlg, IDC_Locator, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Value); + SetDlgItemText(hDlg, ADDR1, Value); + + GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Value); + SetDlgItemText(hDlg, ADDR2, Value); + + GetJSONValue(_REPLYBUFFER, "\"City\":", Value); + SetDlgItemText(hDlg, CITY, Value); + + GetJSONValue(_REPLYBUFFER, "\"State\":", Value); + SetDlgItemText(hDlg, STATE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Country\":", Value); + SetDlgItemText(hDlg, COUNTRY, Value); + + GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", Value); + SetDlgItemText(hDlg, POSTCODE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Email\":", Value); + SetDlgItemText(hDlg, EMAIL, Value); + + GetJSONValue(_REPLYBUFFER, "\"Website\":", Value); + SetDlgItemText(hDlg, WEBSITE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Phones\":", Value); + SetDlgItemText(hDlg, PHONE, Value); + + GetJSONValue(_REPLYBUFFER, "\"Comments\":", Value); + SetDlgItemText(hDlg, ADDITIONALDATA, Value); + + } + + return (INT_PTR)TRUE; + } + case WM_COMMAND: + + switch(LOWORD(wParam)) + { + + case ID_SAVE: + { + char Name[100]; + char PasswordText[100]; + char LocatorText[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]; + + SOCKET sock; + + int Len; + char Message[2048]; + char Reply[2048] = ""; + + + GetDlgItemText(hDlg, NAME, Name, 99); + GetDlgItemText(hDlg, IDC_Password, PasswordText, 99); + GetDlgItemText(hDlg, IDC_Locator, LocatorText, 99); + GetDlgItemText(hDlg, ADDR1, Addr1, 99); + GetDlgItemText(hDlg, ADDR2, Addr2, 99); + GetDlgItemText(hDlg, CITY, City, 99); + GetDlgItemText(hDlg, STATE, State, 99); + GetDlgItemText(hDlg, COUNTRY, Country, 99); + GetDlgItemText(hDlg, POSTCODE, PostCode, 99); + GetDlgItemText(hDlg, EMAIL, Email, 99); + GetDlgItemText(hDlg, WEBSITE, Website, 99); + GetDlgItemText(hDlg, PHONE, Phone, 99); + GetDlgItemText(hDlg, ADDITIONALDATA, Data, 99); + + +//{"Callsign":"String","GridSquare":"String","SysopName":"String", +//"StreetAddress1":"String","StreetAddress2":"String","City":"String", +//"State":"String","Country":"String","PostalCode":"String","Email":"String", +//"Phones":"String","Website":"String","Comments":"String"} + + Len = sprintf(Message, + "\"Callsign\":\"%s\"," + "\"Password\":\"%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, PasswordText, LocatorText, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + char * ptr; + + SendHTTPRequest(sock, + "/sysop/add", Message, Len, Reply); + + ptr = strstr(Reply, "\"ErrorCode\":"); + + if (ptr) + { + ptr = strstr(ptr, "Message"); + if (ptr) + { + ptr += 10; + strlop(ptr, '"'); + MessageBox(NULL ,ptr, "Error", MB_OK); + } + } + else + MessageBox(NULL, "Sysop Record Updated", "BPQ32", MB_OK); + + } + closesocket(sock); + } + + case ID_CANCEL: + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + } + return (INT_PTR)FALSE; +} + + + +LRESULT CALLBACK UIWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +VOID WINAPI OnTabbedDialogInit(HWND hDlg); + +LRESULT CALLBACK FrameWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + BOOL ret; + + CLIENTCREATESTRUCT MDIClientCreateStruct; // Structure to be used for MDI client area + //HWND m_hwndSystemInformation = 0; + + if (message == BPQMsg) + { + if (lParam & BPQDataAvail) + DoReceivedData(wParam); + + if (lParam & BPQMonitorAvail) + DoMonData(wParam); + + if (lParam & BPQStateChange) + DoStateChange(wParam); + + return (0); + } + + switch (message) + { + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + // SetForegroundWindow(FrameWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, FrameWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_SIZING: + case WM_SIZE: + + SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + break; + + case WM_NCCREATE: + + ret = DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + return TRUE; + + case WM_CREATE: + + // On creation of main frame, create the MDI client area + + MDIClientCreateStruct.hWindowMenu = NULL; + MDIClientCreateStruct.idFirstChild = IDM_FIRSTCHILD; + + ClientWnd = CreateWindow(TEXT("MDICLIENT"), // predefined value for MDI client area + NULL, // no caption required + WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE, + 0, // No need to give any x/y or height/width since this client + // will just be used to get client windows created, effectively + // in the main window we will be seeing the mainframe window client area itself. + 0, + 0, + 0, + hWnd, + NULL, + hInstance, + (void *) &MDIClientCreateStruct); + + + return 0; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd) + ShowWindow(handle, SW_NORMAL); + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + switch(wmId) + { + struct ConsoleInfo * Cinfo = NULL; + + case ID_NEWWINDOW: + Cinfo = CreateChildWindow(0, FALSE); + if (Cinfo) + SendMessage(ClientWnd, WM_MDIACTIVATE, (WPARAM)Cinfo->hConsole, 0); + break; + + case ID_WINDOWS_CASCADE: + SendMessage(ClientWnd, WM_MDICASCADE, 0, 0); + return 0; + + case ID_WINDOWS_TILE: + SendMessage(ClientWnd, WM_MDITILE , MDITILE_HORIZONTAL, 0); + return 0; + + case BPQCLOSEALL: + CloseAllPrograms(); + // SendMessage(ClientWnd, WM_MDIICONARRANGE, 0 ,0); + + return 0; + + case BPQUICONFIG: + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName, 0, NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Configuration"); + MySetWindowText(UIhWnd, Title); + ShowWindow(UIhWnd, SW_NORMAL); + + OnTabbedDialogInit(UIhWnd); // Set up pages + + // UpdateWindow(UIhWnd); + return 0; + } + + + case IDD_WL2KSYSOP: + + if (WL2KCall[0] == 0) + { + MessageBox(NULL,"WL2K Reporting is not configured","BPQ32", MB_OK); + break; + } + + DialogBox(hInstance, MAKEINTRESOURCE(IDD_WL2KSYSOP), hWnd, ConfigWndProc); + break; + + + // Handle MDI Window commands + + default: + { + if(wmId >= IDM_FIRSTCHILD) + { + DefFrameProc(hWnd, ClientWnd, message, wParam, lParam); + } + else + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_COMMAND, wParam, lParam); + } + } + } + + break; + + case WM_INITMENUPOPUP: + { + HWND hChild = (HWND)SendMessage(ClientWnd, WM_MDIGETACTIVE,0,0); + + if(hChild) + SendMessage(hChild, WM_INITMENUPOPUP, wParam, lParam); + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + FrameMaximized = TRUE; + break; + + case SC_RESTORE: + + FrameMaximized = FALSE; + break; + + case SC_MINIMIZE: + + if (MinimizetoTray) + { + ShowWindow(hWnd, SW_HIDE); + return TRUE; + } + } + + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + case WM_CLOSE: + + PostQuitMessage(0); + + if (MinimizetoTray) + DeleteTrayMenuItem(hWnd); + + break; + + default: + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); + + } + return (DefFrameProc(hWnd, ClientWnd, message, wParam, lParam)); +} + +int OffsetH, OffsetW; + +int SetupConsoleWindow() +{ + WNDCLASS wc; + int i; + int retCode, Type, Vallen; + HKEY hKey=0; + char Size[80]; + WNDCLASSEX wndclassMainFrame; + RECT CRect; + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"FrameWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d",&FRect.left,&FRect.right,&FRect.top,&FRect.bottom); + + if (FRect.top < - 500 || FRect.left < - 500) + { + FRect.left = 0; + FRect.top = 0; + FRect.right = 600; + FRect.bottom = 400; + } + + + Vallen=80; + retCode = RegQueryValueEx(hKey,"WindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &ConsoleMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + Vallen=80; + + retCode = RegQueryValueEx(hKey,"StatusWindowSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size, "%d,%d,%d,%d,%d", &StatusRect.left, &StatusRect.right, + &StatusRect.top, &StatusRect.bottom, &StatusMinimized); + + if (StatusRect.top < - 500 || StatusRect.left < - 500) + { + StatusRect.left = 0; + StatusRect.top = 0; + StatusRect.right = 850; + StatusRect.bottom = 500; + } + + + // Get StartMinimized and MinimizetoTray flags + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Start Minimized", 0, &Type, (UCHAR *)&StartMinimized, &Vallen); + + Vallen = 4; + retCode = RegQueryValueEx(hKey, "Minimize to Tray", 0, &Type, (UCHAR *)&MinimizetoTray, &Vallen); + } + + wndclassMainFrame.cbSize = sizeof(WNDCLASSEX); + wndclassMainFrame.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wndclassMainFrame.lpfnWndProc = FrameWndProc; + wndclassMainFrame.cbClsExtra = 0; + wndclassMainFrame.cbWndExtra = 0; + wndclassMainFrame.hInstance = hInstance; + wndclassMainFrame.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON)); + wndclassMainFrame.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclassMainFrame.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH); + wndclassMainFrame.lpszMenuName = NULL; + wndclassMainFrame.lpszClassName = FrameClassName; + wndclassMainFrame.hIconSm = NULL; + + if(!RegisterClassEx(&wndclassMainFrame)) + { + return 0; + } + + pindex = 0; + PartLine = FALSE; + + bgBrush = CreateSolidBrush(BGCOLOUR); + +// hMainFrameMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME_MENU)); + + hBaseMenu = LoadMenu(hInstance, MAKEINTRESOURCE(CONS_MENU)); + hConsMenu = GetSubMenu(hBaseMenu, 1); + hWndMenu = GetSubMenu(hBaseMenu, 0); + + hTermMenu = LoadMenu(hInstance, MAKEINTRESOURCE(TERM_MENU)); + hTermActMenu = GetSubMenu(hTermMenu, 1); + hTermCfgMenu = GetSubMenu(hTermMenu, 2); + hTermEdtMenu = GetSubMenu(hTermMenu, 3); + hTermHlpMenu = GetSubMenu(hTermMenu, 4); + + hMonMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MON_MENU)); + hMonCfgMenu = GetSubMenu(hMonMenu, 1); + hMonEdtMenu = GetSubMenu(hMonMenu, 2); + hMonHlpMenu = GetSubMenu(hMonMenu, 3); + + hMainFrameMenu = CreateMenu(); + AppendMenu(hMainFrameMenu, MF_STRING + MF_POPUP, (UINT)hWndMenu, "Window"); + + //Create the main MDI frame window + + ClientWnd = NULL; + + FrameWnd = CreateWindow(FrameClassName, + "BPQ32 Console", + WS_OVERLAPPEDWINDOW |WS_CLIPCHILDREN, + FRect.left, + FRect.top, + FRect.right - FRect.left, + FRect.bottom - FRect.top, + NULL, // handle to parent window + hMainFrameMenu, // handle to menu + hInstance, // handle to the instance of module + NULL); // Long pointer to a value to be passed to the window through the + // CREATESTRUCT structure passed in the lParam parameter the WM_CREATE message + + + // Get Client Params + + if (FrameWnd == 0) + { + Debugprintf("SetupConsoleWindow Create Frame failed %d", GetLastError()); + return 0; + } + + ShowWindow(FrameWnd, SW_RESTORE); + + + GetWindowRect(FrameWnd, &FRect); + OffsetH = FRect.bottom - FRect.top; + OffsetW = FRect.right - FRect.left; + GetClientRect(FrameWnd, &CRect); + OffsetH -= CRect.bottom; + OffsetW -= CRect.right; + OffsetH -= 4; + + // Create Console Window + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = ClassName; + + i=RegisterClass(&wc); + + sprintf (Title, "BPQ32.dll Console Version %s", VersionString); + + hConsWnd = CreateMDIWindow(ClassName, "Console", 0, + 0,0,0,0, ClientWnd, hInstance, 1234); + + i = GetLastError(); + + if (!hConsWnd) { + return (FALSE); + } + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)StatusWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = 0; + wc.lpszClassName = "Status"; + + i=RegisterClass(&wc); + + if (StatusRect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - StatusRect.top; + StatusRect.top += Error; + StatusRect.bottom += Error; + } + + StatusWnd = CreateMDIWindow("Status", "Stream Status", 0, + StatusRect.left, StatusRect.top, StatusRect.right - StatusRect.left, + StatusRect.bottom - StatusRect.top, ClientWnd, hInstance, 1234); + + SetTimer(StatusWnd, 1, 1000, NULL); + + hPopMenu = GetSubMenu(hBaseMenu, 1) ; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + DrawMenuBar(hConsWnd); + + // setup default font information + + LFTTYFONT.lfHeight = 12; + LFTTYFONT.lfWidth = 8 ; + LFTTYFONT.lfEscapement = 0 ; + LFTTYFONT.lfOrientation = 0 ; + LFTTYFONT.lfWeight = 0 ; + LFTTYFONT.lfItalic = 0 ; + LFTTYFONT.lfUnderline = 0 ; + LFTTYFONT.lfStrikeOut = 0 ; + LFTTYFONT.lfCharSet = 0; + LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; + LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; + LFTTYFONT.lfQuality = DEFAULT_QUALITY ; + LFTTYFONT.lfPitchAndFamily = FIXED_PITCH; + lstrcpy(LFTTYFONT.lfFaceName, "FIXEDSYS" ) ; + + hFont = CreateFontIndirect(&LFTTYFONT) ; + + SetWindowText(hConsWnd,Title); + + if (Rect.right < 100 || Rect.bottom < 100) + { + GetWindowRect(hConsWnd, &Rect); + } + + if (Rect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + + + MoveWindow(hConsWnd, Rect.left - (OffsetW /2), Rect.top - OffsetH, Rect.right-Rect.left, Rect.bottom-Rect.top, TRUE); + + MoveWindow(StatusWnd, StatusRect.left - (OffsetW /2), StatusRect.top - OffsetH, + StatusRect.right-StatusRect.left, StatusRect.bottom-StatusRect.top, TRUE); + + hWndCons = CreateWindowEx(WS_EX_CLIENTEDGE, "LISTBOX", "", + WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | LBS_NOSEL | WS_VSCROLL | WS_HSCROLL, + Rect.left, Rect.top, Rect.right - Rect.left, Rect.bottom - Rect.top, + hConsWnd, NULL, hInstance, NULL); + +// SendMessage(hWndCons, WM_SETFONT, hFont, 0); + + SendMessage(hWndCons, LB_SETHORIZONTALEXTENT , 1000, 0); + + if (ConsoleMinimized) + ShowWindow(hConsWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hConsWnd, SW_RESTORE); + + if (StatusMinimized) + ShowWindow(StatusWnd, SW_SHOWMINIMIZED); + else + ShowWindow(StatusWnd, SW_RESTORE); + + ShowWindow(FrameWnd, SW_RESTORE); + + + LoadLibrary("riched20.dll"); + + if (StartMinimized) + if (MinimizetoTray) + ShowWindow(FrameWnd, SW_HIDE); + else + ShowWindow(FrameWnd, SW_SHOWMINIMIZED); + else + ShowWindow(FrameWnd, SW_RESTORE); + + CreateMonitorWindow(Size); + + return 0; +} + +DllExport int APIENTRY SetupTrayIcon() +{ + if (MinimizetoTray == 0) + return 0; + + trayMenu = CreatePopupMenu(); + + for( i = 0; i < 100; ++i ) + { + if (strcmp(PopupText[i],"BPQ32 Console") == 0) + { + hWndArray[i] = FrameWnd; + goto doneit; + } + } + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] == 0) + { + hWndArray[i] = FrameWnd; + strcpy(PopupText[i],"BPQ32 Console"); + break; + } + } +doneit: + + for( i = 0; i < 100; ++i ) + { + if (hWndArray[i] != 0) + AppendMenu(trayMenu,MF_STRING,TRAYBASEID+i,PopupText[i]); + } + + // Set up Tray ICON + + ZeroMemory(&niData,sizeof(NOTIFYICONDATA)); + + niData.cbSize = sizeof(NOTIFYICONDATA); + + // the ID number can be any UINT you choose and will + // be used to identify your icon in later calls to + // Shell_NotifyIcon + + niData.uID = TRAY_ICON_ID; + + // state which structure members are valid + // here you can also choose the style of tooltip + // window if any - specifying a balloon window: + // NIF_INFO is a little more complicated + + strcpy(niData.szTip,"BPQ32 Windows"); + + niData.uFlags = NIF_ICON|NIF_MESSAGE|NIF_TIP; + + // load the icon note: you should destroy the icon + // after the call to Shell_NotifyIcon + + niData.hIcon = + + //LoadIcon(NULL, IDI_APPLICATION); + + (HICON)LoadImage( hInstance, + MAKEINTRESOURCE(BPQICON), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_DEFAULTCOLOR); + + + // set the window you want to receive event messages + + niData.hWnd = FrameWnd; + + // set the message to send + // note: the message value should be in the + // range of WM_APP through 0xBFFF + + niData.uCallbackMessage = MY_TRAY_ICON_MESSAGE; + + // Call Shell_NotifyIcon. NIM_ADD adds a new tray icon + + if (Shell_NotifyIcon(NIM_ADD,&niData)) + Debugprintf("BPQ32 Create Tray Icon Ok"); +// else +// Debugprintf("BPQ32 Create Tray Icon failed %d", GetLastError()); + + return 0; +} + +VOID SaveConfig() +{ + HKEY hKey=0; + int retCode, disp; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "Start Minimized", 0, REG_DWORD, (UCHAR *)&StartMinimized, 4); + retCode = RegSetValueEx(hKey, "Minimize to Tray", 0, REG_DWORD, (UCHAR *)&MinimizetoTray, 4); + } +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + POINT pos; + HWND handle; + RECT cRect; + + switch (message) + { + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + // GetSubMenu function should retrieve a handle to the drop-down menu or submenu. + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case MY_TRAY_ICON_MESSAGE: + + switch(lParam) + { + case WM_RBUTTONUP: + case WM_LBUTTONUP: + + GetCursorPos(&pos); + + SetForegroundWindow(hWnd); + + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); + return 0; + } + + break; + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId == IDC_ENIGATE) + { + int retCode, disp; + HKEY hKey=0; + + IGateEnabled = IsDlgButtonChecked(hWnd, IDC_ENIGATE); + + if (IGateEnabled) + ISDelayTimer = 60; + + retCode = RegCreateKeyEx(REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, // Reserved + 0, // Class + 0, // Options + KEY_ALL_ACCESS, + NULL, // Security Attrs + &hKey, + &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey,"IGateEnabled", 0 , REG_DWORD,(BYTE *)&IGateEnabled, 4); + RegCloseKey(hKey); + } + + return 0; + } + + if (wmId == BPQSAVENODES) + { + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + return 0; + } + if (wmId == BPQCLEARRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + ClearNodes(); + WritetoConsole("Nodes file Cleared\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + SaveNodes(); + WritetoConsole("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsole("Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == SCANRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + RigReconfigFlag = TRUE; + WritetoConsole("Rigcontrol Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + + if (wmId == APRSRECONFIG) + { + if (!ProcessConfig()) + { + MessageBox(NULL,"Configuration File check falled - will continue with old config","BPQ32",MB_OK); + return (0); + } + + APRSReconfigFlag=TRUE; + WritetoConsole("APRS Reconfig requested ... Waiting for Timer Poll\n"); + return 0; + } + if (wmId == BPQDUMP) + { + DumpSystem(); + return 0; + } + + if (wmId == BPQCLOSEALL) + { + CloseAllPrograms(); + return 0; + } + + if (wmId == BPQUICONFIG) + { + int err, i=0; + char Title[80]; + WNDCLASS wc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = UIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = UIClassName; + + RegisterClass(&wc); + + UIhWnd = CreateDialog(hInstance, UIClassName,0,NULL); + + if (!UIhWnd) + { + err=GetLastError(); + return FALSE; + } + + wsprintf(Title,"BPQ32 Beacon Utility Version"); + MySetWindowText(UIhWnd, Title); + return 0; + } + + if (wmId == BPQSAVEREG) + { + CreateRegBackup(); + return 0; + } + + if (wmId == BPQMINTOTRAY) + { + MinimizetoTray = !MinimizetoTray; + + if (MinimizetoTray) + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQMINTOTRAY, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId == BPQSTARTMIN) + { + StartMinimized = !StartMinimized; + + if (StartMinimized) + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_CHECKED); + else + CheckMenuItem(hPopMenu, BPQSTARTMIN, MF_UNCHECKED); + + SaveConfig(); + return 0; + } + + if (wmId >= TRAYBASEID && wmId < (TRAYBASEID + 100)) + { + handle=hWndArray[wmId-TRAYBASEID]; + + if (handle == FrameWnd && FrameMaximized == TRUE) + PostMessage(handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + else + PostMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + SetForegroundWindow(handle); + return 0; + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MINIMIZE: + + ConsoleMinimized = TRUE; + break; + + case SC_RESTORE: + + ConsoleMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SIZE: + + GetClientRect(hWnd, &cRect); + + MoveWindow(hWndBG, 0, 0, cRect.right, 26, TRUE); + + if (APRSActive) + MoveWindow(hWndCons, 2, 26, cRect.right-4, cRect.bottom - 32, TRUE); + else + MoveWindow(hWndCons, 2, 2, cRect.right-4, cRect.bottom - 4, TRUE); + +// InvalidateRect(hWnd, NULL, TRUE); + break; + +/* + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i 300) + len = 300; + + memcpy(&buffptr[2], buff, len + 1); + + C_Q_ADD(&WritetoConsoleQ, buffptr); + + return 0; +} + +int WritetoConsoleSupport(char * buff) +{ + + int len=strlen(buff); + char Temp[2000]= ""; + char * ptr; + + if (PartLine) + { + SendMessage(hWndCons, LB_GETTEXT, pindex, (LPARAM)(LPCTSTR) Temp); + SendMessage(hWndCons, LB_DELETESTRING, pindex, 0); + PartLine = FALSE; + } + + if ((strlen(Temp) + strlen(buff)) > 1990) + Temp[0] = 0; // Should never have anything this long + + strcat(Temp, buff); + + ptr = strchr(Temp, '\n'); + + if (ptr) + *ptr = 0; + else + PartLine = TRUE; + + pindex=SendMessage(hWndCons, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Temp); + return 0; + } + +DllExport VOID APIENTRY BPQOutputDebugString(char * String) +{ + OutputDebugString(String); + return; + } + +HANDLE handle; +char fn[]="BPQDUMP"; +ULONG cnt; +char * stack; +//char screen[1920]; +//COORD ReadCoord; + +#define DATABYTES 400000 + +extern UCHAR DATAAREA[]; + +DllExport int APIENTRY DumpSystem() +{ + char fn[200]; + char Msg[250]; + + sprintf(fn,"%s\\BPQDUMP",BPQDirectory); + + handle = CreateFile(fn, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + +#ifndef _WIN64 + + _asm { + + mov stack,esp + } + + WriteFile(handle,stack,128,&cnt,NULL); +#endif + +// WriteFile(handle,Screen,MAXLINELEN*MAXSCREENLEN,&cnt,NULL); + + WriteFile(handle,DATAAREA, DATABYTES,&cnt,NULL); + + CloseHandle(handle); + + sprintf(Msg, "Dump to %s Completed\n", fn); + WritetoConsole(Msg); + + FindLostBuffers(); + + return (0); +} + +BOOLEAN CheckifBPQ32isLoaded() +{ + HANDLE Mutex; + + // See if BPQ32 is running - if we create it in the NTVDM address space by + // loading bpq32.dll it will not work. + + Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"BPQLOCKMUTEX"); + + if (Mutex == NULL) + { + if (AttachingProcess == 0) // Already starting BPQ32 + { + OutputDebugString("BPQ32 No other bpq32 programs running - Loading BPQ32.exe\n"); + StartBPQ32(); + } + return FALSE; + } + + CloseHandle(Mutex); + + return TRUE; +} + +BOOLEAN StartBPQ32() +{ + UCHAR Value[100]; + + char bpq[]="BPQ32.exe"; + char *fn=(char *)&bpq; + HKEY hKey=0; + int ret,Type,Vallen=99; + + char Errbuff[100]; + char buff[20]; + + STARTUPINFO StartupInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION ProcessInformation; // pointer to PROCESS_INFORMATION + + AttachingProcess = 1; + +// Get address of BPQ Directory + + Value[0]=0; + + ret = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (ret == ERROR_SUCCESS) + { + ret = RegQueryValueEx(hKey, "BPQ Program Directory", 0, &Type,(UCHAR *)&Value, &Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + + if (Value[0] == 0) + { + + // BPQ Directory absent or = "" - "try Config File Location" + + ret = RegQueryValueEx(hKey,"BPQ Directory",0, + &Type,(UCHAR *)&Value,&Vallen); + + if (ret == ERROR_SUCCESS) + { + if (strlen(Value) == 2 && Value[0] == '"' && Value[1] == '"') + Value[0]=0; + } + + } + RegCloseKey(hKey); + } + + if (Value[0] == 0) + { + strcpy(Value,fn); + } + else + { + strcat(Value,"\\"); + strcat(Value,fn); + } + + StartupInfo.cb=sizeof(StartupInfo); + StartupInfo.lpReserved=NULL; + StartupInfo.lpDesktop=NULL; + StartupInfo.lpTitle=NULL; + StartupInfo.dwFlags=0; + StartupInfo.cbReserved2=0; + StartupInfo.lpReserved2=NULL; + + if (!CreateProcess(Value,NULL,NULL,NULL,FALSE, + CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, + NULL,NULL,&StartupInfo,&ProcessInformation)) + { + ret=GetLastError(); + + _itoa(ret,buff,10); + + strcpy(Errbuff, "BPQ32 Load "); + strcat(Errbuff,Value); + strcat(Errbuff," failed "); + strcat(Errbuff,buff); + OutputDebugString(Errbuff); + AttachingProcess = 0; + return FALSE; + } + + return TRUE; +} + + +DllExport BPQVECSTRUC * APIENTRY GetIPVectorAddr() +{ + return &IPHOSTVECTOR; +} + +DllExport UINT APIENTRY GETSENDNETFRAMEADDR() +{ + return (UINT)&SENDNETFRAME; +} + +DllExport VOID APIENTRY RelBuff(VOID * Msg) +{ + UINT * pointer, * BUFF = Msg; + + if (Semaphore.Flag == 0) + Debugprintf("ReleaseBuffer called without semaphore"); + + pointer = FREE_Q; + + *BUFF =(UINT)pointer; + + FREE_Q = BUFF; + + QCOUNT++; + + return; +} + +extern int MINBUFFCOUNT; + +DllExport VOID * APIENTRY GetBuff() +{ + UINT * Temp = Q_REM(&FREE_Q); + + if (Semaphore.Flag == 0) + Debugprintf("GetBuff called without semaphore"); + + if (Temp) + { + QCOUNT--; + + if (QCOUNT < MINBUFFCOUNT) + MINBUFFCOUNT = QCOUNT; + } + + return Temp; +} + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[10000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + + return; +} + +unsigned short int compute_crc(unsigned char *buf, int txlen); + +extern SOCKADDR_IN reportdest; + +extern SOCKET ReportSocket; + +extern SOCKADDR_IN Chatreportdest; + +DllExport VOID APIENTRY SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen) +{ + unsigned short int crc = compute_crc(buff, txlen); + + crc ^= 0xffff; + + buff[txlen++] = (crc&0xff); + buff[txlen++] = (crc>>8); + + sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest)); +} + +VOID CreateRegBackup() +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + char RegFileName[MAX_PATH]; + char Msg[80]; + HANDLE handle; + int len, written; + char RegLine[300]; + +// SHELLEXECUTEINFO sei; +// STARTUPINFO SInfo; +// PROCESS_INFORMATION PInfo; + + sprintf(RegFileName, "%s\\BPQ32.reg", BPQDirectory); + + // Keep 4 Generations + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, RegFileName); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + strcpy(Backup2, RegFileName); + strcat(Backup2, ".bak"); + + CopyFile(RegFileName, Backup2, FALSE); // Copy to .bak + + handle = CreateFile(RegFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle == INVALID_HANDLE_VALUE) + { + sprintf(Msg, "Failed to open Registry Save File\n"); + WritetoConsole(Msg); + return; + } + + len = sprintf(RegLine, "Windows Registry Editor Version 5.00\r\n\r\n"); + WriteFile(handle, RegLine, len, &written, NULL); + + if (SaveReg("Software\\G8BPQ\\BPQ32", handle)) + WritetoConsole("Registry Save complete\n"); + else + WritetoConsole("Registry Save failed\n"); + + CloseHandle(handle); + return ; +/* + + if (REGTREE == HKEY_LOCAL_MACHINE) // < Vista + { + sprintf(cmd, + "regedit /E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&SInfo, sizeof(SInfo)); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0 ,NULL, NULL, &SInfo, &PInfo) == 0) + { + sprintf(Msg, "Error: CreateProcess for regedit failed 0%d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + else + { + + sprintf(cmd, + "/E \"%s\\BPQ32.reg\" %s\\Software\\G8BPQ\\BPQ32", BPQDirectory, REGTREETEXT); + + ZeroMemory(&sei, sizeof(sei)); + + sei.cbSize = sizeof(SHELLEXECUTEINFOW); + sei.hwnd = hWnd; + sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI; + sei.lpVerb = "runas"; + sei.lpFile = "regedit.exe"; + sei.lpParameters = cmd; + sei.nShow = SW_SHOWNORMAL; + + if (!ShellExecuteEx(&sei)) + { + sprintf(Msg, "Error: ShellExecuteEx for regedit failed %d\n", GetLastError() ); + WritetoConsole(Msg); + return; + } + } + + sprintf(Msg, "Registry Save Initiated\n", fn); + WritetoConsole(Msg); + + return ; +*/ +} + +BOOL CALLBACK EnumForCloseProc(HWND hwnd, LPARAM lParam) +{ + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowThreadProcessId(hwnd, &ProcessId); + + for (i=0; i< AttachedProcesses; i++) + { + if (AttachedPIDList[i] == ProcessId) + { + Debugprintf("BPQ32 Close All Closing PID %d", ProcessId); + PostMessage(hwnd, WM_CLOSE, 1, 1); + // AttachedPIDList[i] = 0; // So we don't do it again + break; + } + } + + return (TRUE); +} +DllExport BOOL APIENTRY RestoreFrameWindow() +{ + return ShowWindow(FrameWnd, SW_RESTORE); +} + +DllExport VOID APIENTRY CreateNewTrayIcon() +{ + Shell_NotifyIcon(NIM_DELETE,&niData); + trayMenu = NULL; +} + +DllExport VOID APIENTRY CloseAllPrograms() +{ +// HANDLE hProc; + + // Close all attached BPQ32 programs + + Closing = TRUE; + + ShowWindow(FrameWnd, SW_RESTORE); + + GetWindowRect(FrameWnd, &FRect); + + SaveBPQ32Windows(); + CloseHostSessions(); + + if (AttachedProcesses == 1) + CloseBPQ32(); + + Debugprintf("BPQ32 Close All Processes %d PIDS %d %d %d %d", AttachedProcesses, AttachedPIDList[0], + AttachedPIDList[1], AttachedPIDList[2], AttachedPIDList[3]); + + if (MinimizetoTray) + Shell_NotifyIcon(NIM_DELETE,&niData); + + EnumWindows(EnumForCloseProc, (LPARAM)NULL); +} + +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_NAME 16383 +#define MAX_VALUE_DATA 65536 + +BOOL CopyReg(HKEY hKeyIn, HKEY hKeyOut) +{ + TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name + DWORD cbName; // size of name string + TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name + DWORD cchClassName = MAX_PATH; // size of class string + DWORD cSubKeys=0; // number of subkeys + DWORD cbMaxSubKey; // longest subkey size + DWORD cchMaxClass; // longest class string + DWORD cValues; // number of values for key + DWORD cchMaxValue; // longest value name + DWORD cbMaxValueData; // longest value data + DWORD cbSecurityDescriptor; // size of security descriptor + FILETIME ftLastWriteTime; // last write time + + DWORD i, retCode; + + TCHAR achValue[MAX_VALUE_NAME]; + DWORD cchValue = MAX_VALUE_NAME; + + // Get the class name and the value count. + retCode = RegQueryInfoKey( + hKeyIn, // key handle + achClass, // buffer for class name + &cchClassName, // size of class string + NULL, // reserved + &cSubKeys, // number of subkeys + &cbMaxSubKey, // longest subkey size + &cchMaxClass, // longest class string + &cValues, // number of values for this key + &cchMaxValue, // longest value name + &cbMaxValueData, // longest value data + &cbSecurityDescriptor, // security descriptor + &ftLastWriteTime); // last write time + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + Debugprintf( "\nNumber of subkeys: %d\n", cSubKeys); + + for (i=0; i 76) + { + len = sprintf(RegLine, "%s\\\r\n", RegLine); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + + len = sprintf(RegLine, "%s%02x,", RegLine, Value[k]); + } + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + + break; + + case REG_DWORD: //( 4 ) // 32-bit number +// case REG_DWORD_LITTLE_ENDIAN: //( 4 ) // 32-bit number (same as REG_DWORD) + + memcpy(&Intval, Value, 4); + len = sprintf(RegLine, "\"%s\"=dword:%08x\r\n", achValue, Intval); + break; + + case REG_DWORD_BIG_ENDIAN: //( 5 ) // 32-bit number + break; + case REG_LINK: //( 6 ) // Symbolic Link (unicode) + break; + case REG_MULTI_SZ: //( 7 ) // Multiple Unicode strings + + len = sprintf(RegLine, "\"%s\"=hex(7):%02x,00,", achValue, Value[0]); + for (k = 1; k < ValLen; k++) + { + if (len > 76) + { + len = sprintf(RegLine, "%s\\\r\n", RegLine); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + len = 2; + } + len = sprintf(RegLine, "%s%02x,", RegLine, Value[k]); + if (len > 76) + { + len = sprintf(RegLine, "%s\\\r\n", RegLine); + WriteFile(hFile, RegLine, len, &written, NULL); + strcpy(RegLine, " "); + } + len = sprintf(RegLine, "%s00,", RegLine); + } + + RegLine[--len] = 0x0d; + RegLine[++len] = 0x0a; + len++; + break; + + case REG_RESOURCE_LIST: //( 8 ) // Resource list in the resource map + break; + case REG_FULL_RESOURCE_DESCRIPTOR: //( 9 ) // Resource list in the hardware description + break; + case REG_RESOURCE_REQUIREMENTS_LIST://( 10 ) + break; + case REG_QWORD: //( 11 ) // 64-bit number +// case REG_QWORD_LITTLE_ENDIAN: //( 11 ) // 64-bit number (same as REG_QWORD) + break; + + } + + WriteFile(hFile, RegLine, len, &written, NULL); + } + } + } + + WriteFile(hFile, "\r\n", 2, &written, NULL); + + // Enumerate the subkeys, until RegEnumKeyEx fails. + + if (cSubKeys) + { + for (i=0; i> 1; + } + + Flags=GetApplFlags(i); + + if (OneBits > 1) + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %03x %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + else + sprintf(&NewScreen[(i+1)*54],"%2d%s%3d %3d %3d %3d %3x %10s%-20s", + i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, + BPQHOSTVECTOR[i-1].PgmName); + + } + } + + #include "StdExcept.c" + + if (Semaphore.Flag && Semaphore.SemProcessID == GetCurrentProcessId()) + FreeSemaphore(&Semaphore); + + } + + if (memcmp(Screen, NewScreen, 33 * 108) == 0) // No Change + return 0; + + memcpy(Screen, NewScreen, 33 * 108); + InvalidateRect(StatusWnd,NULL,FALSE); + + return(0); +} + +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + HGLOBAL hMem; + MINMAXINFO * mmi; + int i; + + switch (message) + { + case WM_TIMER: + + if (Semaphore.Flag == 0) + DoStatus(); + break; + + case WM_MDIACTIVATE: + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hConsMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + } + else + { + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 850; + mmi->ptMaxSize.y = 500; + mmi->ptMaxTrackSize.x = 850; + mmi->ptMaxTrackSize.y = 500; + + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + //Parse the menu selections: + + switch (wmId) + { + +/* + case BPQSTREAMS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_CHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_UNCHECKED); + + StreamDisplay = TRUE; + + break; + + case BPQIPSTATUS: + + CheckMenuItem(hMenu,BPQSTREAMS,MF_UNCHECKED); + CheckMenuItem(hMenu,BPQIPSTATUS,MF_CHECKED); + + StreamDisplay = FALSE; + memset(Screen, ' ', 4000); + + + break; + +*/ + + case BPQCOPY: + + // + // Copy buffer to clipboard + // + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, 33*110); + + if (hMem != 0) + { + if (OpenClipboard(hWnd)) + { +// CopyScreentoBuffer(GlobalLock(hMem)); + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + else + { + GlobalFree(hMem); + } + + } + + break; + + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MAXIMIZE: + + break; + + case SC_MINIMIZE: + + StatusMinimized = TRUE; + break; + + case SC_RESTORE: + + StatusMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + for (i=0; i<33; i++) + { + TextOut(hdc,0,i*14,&Screen[i*108],108); + } + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + +// PostQuitMessage(0); + + break; + + + default: + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + return (0); +} + +VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized) +{ + HKEY hKey=0; + char Size[80]; + char Key[80]; + int retCode, disp; + RECT Rect; + + if (IsWindow(hWnd) == FALSE) + return; + + ShowWindow(hWnd, SW_RESTORE); + + if (GetWindowRect(hWnd, &Rect) == FALSE) + return; + + // Make relative to Frame + + Rect.top -= FRect.top ; + Rect.left -= FRect.left; + Rect.bottom -= FRect.top; + Rect.right -= FRect.left; + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\%s", RegKey); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, + KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d,%d", Rect.left, Rect.right, Rect.top ,Rect.bottom, Minimized); + retCode = RegSetValueEx(hKey, Value, 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + RegCloseKey(hKey); + } +} + +extern int GPSPort; +extern char LAT[]; // in standard APRS Format +extern char LON[]; // in standard APRS Format + +VOID SaveBPQ32Windows() +{ + HKEY hKey=0; + char Size[80]; + int retCode, disp; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + int i; + + retCode = RegCreateKeyEx(REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + sprintf(Size,"%d,%d,%d,%d", FRect.left, FRect.right, FRect.top, FRect.bottom); + retCode = RegSetValueEx(hKey, "FrameWindowSize", 0, REG_SZ, (BYTE *)&Size, strlen(Size)); + + // Save GPS Position + + if (GPSPort) + { + sprintf(Size, "%s, %s", LAT, LON); + retCode = RegSetValueEx(hKey, "GPS", 0, REG_SZ,(BYTE *)&Size, strlen(Size)); + } + + RegCloseKey(hKey); + } + + SaveMDIWindowPos(StatusWnd, "", "StatusWindowSize", StatusMinimized); + SaveMDIWindowPos(hConsWnd, "", "WindowSize", ConsoleMinimized); + + for (i=0; iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); + } + } + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + SaveWindowPos(70); // Rigcontrol + + + if (hIPResWnd) + SaveMDIWindowPos(hIPResWnd, "", "IPResSize", IPMinimized); + + SaveHostSessions(); +} + +DllExport BOOL APIENTRY CheckIfOwner() +{ + // + // Returns TRUE if current process is root process + // that loaded the DLL + // + + if (TimerInst == GetCurrentProcessId()) + + return (TRUE); + else + return (FALSE); +} + +VOID GetParam(char * input, char * key, char * value) +{ + char * ptr = strstr(input, key); + char Param[2048]; + char * ptr1, * ptr2; + char c; + + + if (ptr) + { + ptr2 = strchr(ptr, '&'); + if (ptr2) *ptr2 = 0; + strcpy(Param, ptr + strlen(key)); + if (ptr2) *ptr2 = '&'; // Restore string + + // Undo any % transparency + + ptr1 = Param; + ptr2 = Param; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + *(ptr2++) = m * 16 + n; + } + else if (c == '+') + *(ptr2++) = ' '; + else + *(ptr2++) = c; + + c = *(ptr1++); + } + + *(ptr2++) = 0; + + strcpy(value, Param); + } +} + +int GetListeningPortsPID(int Port) +{ + MIB_TCPTABLE_OWNER_PID * TcpTable = NULL; + PMIB_TCPROW_OWNER_PID Row; + int dwSize = 0; + DWORD n; + + // Get PID of process for this TCP Port + + // Get Length of table + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + TcpTable = malloc(dwSize); + + if (TcpTable == NULL) + return 0; + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + for (n = 0; n < TcpTable->dwNumEntries; n++) + { + Row = &TcpTable->table[n]; + + if (Row->dwLocalPort == Port && Row->dwState == MIB_TCP_STATE_LISTEN) + { + return Row->dwOwningPid; + break; + } + } + return 0; // Not found +} + +DllExport char * APIENTRY GetLOC() +{ + return LOC; +} + +DllExport void APIENTRY GetLatLon(double * lat, double * lon) +{ + *lat = LatFromLOC; + *lon = LonFromLOC; + return; +} + + +// UZ7HO Dll PTT interface + +// 1 ext_PTT_info +// 2 ext_PTT_settings +// 3 ext_PTT_OFF +// 4 ext_PTT_ON +// 5 ext_PTT_close +// 6 ext_PTT_open + +extern struct RIGINFO * DLLRIG; // Rig record for dll PTT interface (currently only for UZ7HO); + +VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState); +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); + +int WINAPI ext_PTT_info() +{ + return 0; +} + +int WINAPI ext_PTT_settings() +{ + return 0; +} + +int WINAPI ext_PTT_OFF(int Port) +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 0, 0); + + return 0; +} + +int WINAPI ext_PTT_ON(int Port) +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 1, 0); + + return 0; +} +int WINAPI ext_PTT_close() +{ + if (DLLRIG) + Rig_PTTEx(DLLRIG, 0, 0); + + return 0; +} + +DllExport INT WINAPI ext_PTT_open() +{ + return 1; +} + +char * stristr (char *ch1, char *ch2) +{ + char *chN1, *chN2; + char *chNdx; + char *chRet = NULL; + + chN1 = _strdup(ch1); + chN2 = _strdup(ch2); + + if (chN1 && chN2) + { + chNdx = chN1; + while (*chNdx) + { + *chNdx = (char) tolower(*chNdx); + chNdx ++; + } + chNdx = chN2; + + while (*chNdx) + { + *chNdx = (char) tolower(*chNdx); + chNdx ++; + } + + chNdx = strstr(chN1, chN2); + + if (chNdx) + chRet = ch1 + (chNdx - chN1); + } + + free (chN1); + free (chN2); + return chRet; +} + diff --git a/.svn/pristine/0c/0c8d0dde1866e8f5b2214eab03930e5891967d4f.svn-base b/.svn/pristine/0c/0c8d0dde1866e8f5b2214eab03930e5891967d4f.svn-base new file mode 100644 index 0000000..07cfa13 --- /dev/null +++ b/.svn/pristine/0c/0c8d0dde1866e8f5b2214eab03930e5891967d4f.svn-base @@ -0,0 +1,424 @@ +// Mail and Chat Server for BPQ32 Packet Switch +// +// Monitor Window(s) Module + +#include "bpqmail.h" + +static char ClassName[]="BPQMONWINDOW"; + +char SYSOPCall[50]; + +static WNDPROC wpOrigInputProc; +static WNDPROC wpOrigOutputProc; + +HWND hMonitor; + +static HWND hwndInput; +static HWND hwndOutput; + +static HMENU hMenu; // handle of menu + + +#define InputBoxHeight 25 +RECT MonitorRect; +RECT OutputRect; + +int Height, Width, LastY; + +static char kbbuf[160]; +static int kbptr=0; + +static char * readbuff; +static int readbufflen; + +static BOOL StripLF = TRUE; +BOOL MonBBS = TRUE; +BOOL MonCHAT = TRUE; +BOOL MonTCP = TRUE; + +BOOL LogBBS = TRUE; +BOOL LogCHAT = TRUE; +BOOL LogTCP = TRUE; + + +static int PartLinePtr=0; +static int PartLineIndex=0; // Listbox index of (last) incomplete line + + +static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +static LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY MonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static void MoveWindows(); +static void MoveMCWindows(); + +#define BGCOLOUR RGB(236,233,216) + +BOOL CreateMonitor() +{ + WNDCLASS wc; + HBRUSH bgBrush; + + if (hMonitor) + { + ShowWindow(hMonitor, SW_SHOWNORMAL); + SetForegroundWindow(hMonitor); + return FALSE; // Alreaqy open + } + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = MonWndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + + hMonitor=CreateDialog(hInst,ClassName,0,NULL); + + if (!hMonitor) + return (FALSE); + + readbuff = zalloc(1000); + readbufflen = 1000; + + hMenu=GetMenu(hMonitor); + + CheckMenuItem(hMenu,MONBBS, MonBBS ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,MONCHAT, MonCHAT ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,MONTCP, MonTCP ? MF_CHECKED : MF_UNCHECKED); + + DrawMenuBar(hWnd); + + // Retrieve the handlse to the edit controls. + + hwndOutput = GetDlgItem(hMonitor, 121); + + // Set our own WndProcs for the controls. + + wpOrigOutputProc = (WNDPROC)SetWindowLong(hwndOutput, GWL_WNDPROC, (LONG)OutputProc); + + if (cfgMinToTray) + AddTrayMenuItem(hMonitor, "Mail Monitor"); + + ShowWindow(hMonitor, SW_SHOWNORMAL); + + if (MonitorRect.right < 100 || MonitorRect.bottom < 100) + { + GetWindowRect(hMonitor, &MonitorRect); + } + + MoveWindow(hMonitor,MonitorRect.left,MonitorRect.top, MonitorRect.right-MonitorRect.left, MonitorRect.bottom-MonitorRect.top, TRUE); + + MoveWindows(); + + return TRUE; + +} + +static void MoveWindows() +{ + RECT rcMain, rcClient; + int ClientHeight, ClientWidth; + + GetWindowRect(hMonitor, &rcMain); + GetClientRect(hMonitor, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + +// MoveWindow(hwndMon,2, 0, ClientWidth-4, SplitPos, TRUE); + MoveWindow(hwndOutput,2, 2, ClientWidth-4, ClientHeight-4, TRUE); +// MoveWindow(hwndSplit,0, SplitPos, ClientWidth, SplitBarHeight, TRUE); +} + +static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + + switch (message) + { + + case WM_ACTIVATE: + + SetFocus(hwndInput); + break; + + case WM_CLOSE: + if (wParam) // Used by Close All Programs. + return 0; + + return (DefWindowProc(hWnd, message, wParam, lParam)); + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case MONBBS: + + ToggleParam(hMenu, hWnd, &MonBBS, MONBBS); + break; + + case MONCHAT: + + ToggleParam(hMenu, hWnd, &MonCHAT, MONCHAT); + break; + + case MONTCP: + + ToggleParam(hMenu, hWnd, &MonTCP, MONTCP); + break; + + + case BPQCLEAROUT: + + SendMessage(hwndOutput,LB_RESETCONTENT, 0, 0); + break; + + case BPQCOPYOUT: + + CopyToClipboard(hwndOutput); + break; + + + + //case BPQHELP: + + // HtmlHelp(hWnd,"BPQTerminal.chm",HH_HELP_FINDER,0); + // break; + + default: + + return 0; + + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case SC_MINIMIZE: + + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Height = lprc->bottom-lprc->top; + Width = lprc->right-lprc->left; + + MoveWindows(); + + return TRUE; + + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &MonitorRect); // For save soutine + + SetWindowLong(hwndInput, GWL_WNDPROC, + (LONG) wpOrigInputProc); + + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + + hMonitor = NULL; + + free(readbuff); + readbufflen = 0; + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + + +LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + + // Trap mouse messages, so we cant select stuff in output and mon windows, + // otherwise scrolling doesnt work. + + if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_LBUTTONDBLCLK) + return TRUE; + + return CallWindowProc(wpOrigOutputProc, hwnd, uMsg, wParam, lParam); +} + +int WritetoMonitorWindow(char * Msg, int len) +{ + char * ptr1, * ptr2; + int index; + + if (len+PartLinePtr > readbufflen) + { + readbufflen += len+PartLinePtr; + readbuff = realloc(readbuff, readbufflen); + } + + if (PartLinePtr != 0) + SendMessage(hwndOutput,LB_DELETESTRING,PartLineIndex,(LPARAM)(LPCTSTR) 0 ); + + memcpy(&readbuff[PartLinePtr], Msg, len); + + len=len+PartLinePtr; + + ptr1=&readbuff[0]; + readbuff[len]=0; + + do { + ptr2=memchr(ptr1,7,len); + + if (ptr2) + *(ptr2)=32; + + } while (ptr2); + +lineloop: + +// if (PartLinePtr > 300) +// PartLinePtr = 0; + + if (len > 0) + { + // copy text to control a line at a time + + ptr2=memchr(ptr1,13,len); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + PartLinePtr=len; + memmove(readbuff,ptr1,len); + PartLineIndex=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) PartLineIndex, MAKELPARAM(FALSE, 0)); + + return (0); + + } + + *(ptr2++)=0; + + index=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + + // if (LogOutput) WriteMonitorLine(ptr1, ptr2 - ptr1); + + PartLinePtr=0; + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + if ((len > 0) && StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + + if (index > 1200) + + do{ + + index=SendMessage(hwndOutput,LB_DELETESTRING, 0, 0); + + } while (index > 1000); + + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); + + goto lineloop; + } + + + return (0); +} + +static int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item) +{ + *Param = !(*Param); + + CheckMenuItem(hMenu,Item, (*Param) ? MF_CHECKED : MF_UNCHECKED); + + return (0); +} + +static void CopyToClipboard(HWND hWnd) +{ + int i,n, len=0; + HGLOBAL hMem; + char * ptr; + // + // Copy List Box to clipboard + // + + n = SendMessage(hWnd, LB_GETCOUNT, 0, 0); + + for (i=0; iTime < Now - 86400) + { + Rec = History; + History = Rec->next; // Remove from front of chain + } + else + Rec = malloc(sizeof (struct HistoryRec)); + + memset(Rec, 0, sizeof (struct HistoryRec)); + + tm = gmtime(&Now); + + if (strlen(text) + strlen(user->name) + strlen(user->call) > 2000) + return 0; // Shouldn't be that long, but protect buffer + + sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min); + sprintf(buf, "%s%-6.6s %s %c %s\r", Stamp, user->call, user->name, ':', text); + + Rec->Time = Now; + + Rec->Topic = _strdup(user->topic->name); + Rec->Message = _strdup(buf); + + if (History == NULL) + History = Rec; + + else + { + ptr = History; + + while (ptr && ptr->next) + { + n++; + ptr = ptr->next; + } + + n++; + ptr->next = Rec; + } + + return n; +} + + + +int ChatIsUTF8(unsigned char *ptr, int len) +{ + int n; + unsigned char * cpt = ptr; + + // This is simpler than the Term version, as it only handles complete lines of text, so cant get split sequences + + cpt--; + + for (n = 0; n < len; n++) + { + cpt++; + + if (*cpt < 128) + continue; + + if ((*cpt & 0xF8) == 0xF0) + { // start of 4-byte sequence + if (((*(cpt + 1) & 0xC0) == 0x80) + && ((*(cpt + 2) & 0xC0) == 0x80) + && ((*(cpt + 3) & 0xC0) == 0x80)) + { + cpt += 3; + n += 3; + continue; + } + return FALSE; + } + else if ((*cpt & 0xF0) == 0xE0) + { // start of 3-byte sequence + if (((*(cpt + 1) & 0xC0) == 0x80) + && ((*(cpt + 2) & 0xC0) == 0x80)) + { + cpt += 2; + n += 2; + continue; + } + return FALSE; + } + else if ((*cpt & 0xE0) == 0xC0) + { // start of 2-byte sequence + if ((*(cpt + 1) & 0xC0) == 0x80) + { + cpt++; + n++; + continue; + } + return FALSE; + } + return FALSE; + } + + return TRUE; +} + +#ifndef LINBPQ + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} + + +VOID * _zalloc(size_t len) +{ + // ?? malloc and clear + + void * ptr; + + ptr=malloc(len); + memset(ptr, 0, len); + + return ptr; +} + + +VOID * _zalloc_dbg(int len, int type, char * file, int line) +{ + // ?? malloc and clear + + void * ptr; + + ptr=_malloc_dbg(len, type, file, line); + + if (ptr == NULL) + CriticalErrorHandler("malloc failed"); + + memset(ptr, 0, len); + + return ptr; +} + +#endif + +VOID __cdecl nprintf(ChatCIRCUIT * conn, const char * format, ...) + +{ + // seems to be printf to a socket + + char buff[65536]; + va_list(arglist); + + va_start(arglist, format); + vsnprintf(buff, sizeof(buff), format, arglist); + + nputs(conn, buff); +} + + +VOID nputc(ChatCIRCUIT * conn, char chr) +{ + // Seems to send chr to socket + + ChatWriteLogLine(conn, '>',&chr, 1, LOG_CHAT); + ChatQueueMsg(conn, &chr, 1); +} + +VOID nputs(ChatCIRCUIT * conn, char * buf) +{ + // Seems to send buf to socket + + ChatQueueMsg(conn, buf, (int)strlen(buf)); + + if (*buf == 0x1b) + buf += 2; // Colour Escape + + ChatWriteLogLine(conn, '>',buf, (int)strlen(buf), LOG_CHAT); +} + +int ChatQueueMsg(ChatCIRCUIT * conn, char * msg, int len) +{ + // Add Message to queue for this connection + + if (conn->rtcflags & p_linked) + conn->u.link->lastMsgReceived = time(NULL); + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Queue Length all is sent - free the buffer and start again. + + // Create or extend buffer + + GetSemaphore(&OutputSEM, 0); + + while (conn->OutputQueueLength + len > conn->OutputQueueSize) + { + // Extend Queue + + conn->OutputQueueSize += 4096; + conn->OutputQueue = realloc(conn->OutputQueue, conn->OutputQueueSize); + } + + memcpy(&conn->OutputQueue[conn->OutputQueueLength], msg, len); + conn->OutputQueueLength += len; + + FreeSemaphore(&OutputSEM); + + return len; +} + +VOID ChatSendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user) +{ + if (!rtloginu (conn, TRUE)) + { + // Already connected - close + + ChatFlush(conn); + Sleep(1000); + Disconnect(conn->BPQStream); + } + return; + +} + +VOID ChatExpandAndSendMessage(ChatCIRCUIT * conn, char * Msg, int LOG) +{ + char NewMessage[10000]; + char * OldP = Msg; + char * NewP = NewMessage; + char * ptr, * pptr; + int len; + char Dollar[] = "$"; + char CR[] = "\r"; + int Msgs = 0, Unread = 0; + + + ptr = strchr(OldP, '$'); + + while (ptr) + { + len = (int)(ptr - OldP); // Chars before $ + memcpy(NewP, OldP, len); + NewP += len; + + switch (*++ptr) + { + case 'I': // First name of the connected user. + + pptr = conn->UserPointer->Name; + break; + + + case 'U': // Callsign of the connected user. + + pptr = conn->UserPointer->Call; + break; + + case 'W': // Inserts a carriage return. + + pptr = CR; + break; + + break; + + default: + + pptr = Dollar; // Just Copy $ + } + + len = (int)strlen(pptr); + memcpy(NewP, pptr, len); + NewP += len; + + OldP = ++ptr; + ptr = strchr(OldP, '$'); + } + + strcpy(NewP, OldP); + + len = RemoveLF(NewMessage, (int)strlen(NewMessage)); + + ChatWriteLogLine(conn, '>', NewMessage, len, LOG); + ChatQueueMsg(conn, NewMessage, len); +} + + + +void chat_link_out (LINK *link) +{ + int n, p; + ChatCIRCUIT * conn; + char Msg[80]; + + for (n = NumberofChatStreams-1; n >= 0 ; n--) + { + conn = &ChatConnections[n]; + + if (conn->Active == FALSE) + { + p = conn->BPQStream; + memset(conn, 0, sizeof(ChatCIRCUIT)); // Clear everything + conn->BPQStream = p; + + conn->Active = TRUE; + circuit_new(conn, p_linkini); + conn->u.link = link; + conn->Flags = CHATMODE | CHATLINK; + + n=sprintf_s(Msg, sizeof(Msg), "Connecting to Chat Node %s", conn->u.link->alias); + + strcpy(conn->Callsign, conn->u.link->alias); + + ChatWriteLogLine(conn, '|',Msg, n, LOG_CHAT); + + link->ScriptIndex = -1; + RunningConnectScript = time(NULL); + link->MoreLines = TRUE; + link->scriptRunning = TRUE; + link->RTLSent = 0; + + ConnectUsingAppl(conn->BPQStream, ChatApplMask); + + // Connected Event will trigger connect to remote system + + return; + } + } + return; +} + + +VOID saywhat(ChatCIRCUIT *circuit) +{ + nputs(circuit, "Invalid Command\r"); +} + +VOID saydone(ChatCIRCUIT *circuit) +{ + nputs(circuit, "Ok\r"); +} + +VOID strnew(char ** new, char *f1) +{ + // seems to allocate a new string, and copy the old one to it + // how is this different to strdup?? + + *new = _strdup(f1); +} + +#define sl_ins_hd(link, hd) \ + if (hd == NULL)\ + hd=link;\ + else\ + {\ + link->next=hd->next;\ + hd->next=link;\ + } + +BOOL matchi(char * p1, char * p2) +{ + // Return TRUE is strings match + + if (_stricmp(p1, p2)) + return FALSE; + else + return TRUE; +} + + +VOID ProcessChatLine(ChatCIRCUIT * conn, struct UserInfo * user, char* OrigBuffer, int len) +{ + ChatCIRCUIT *c; + char * Buffer = OrigBuffer; + WCHAR BufferW[65536]; + UCHAR BufferB[65536]; + + // Sanity Check + + if (len > 32768) + return; + + // Convert to UTF8 if not already in UTF-8 + + if (len == 73 && memcmp(&OrigBuffer[40], " ", 20) == 0) + { + // Chat Signon Message. If Topic is present, switch to it + + char * Context; + char * Appl; + char * topic; + + Appl = strtok_s(OrigBuffer, " ,\r", &Context); + topic = strtok_s(NULL, " ,\r", &Context); + + if (topic == NULL) + return; // Just Chat + + // Have a Topic + + if (conn->Flags & GETTINGUSER) + { + // Need to log in before switching topic, so Give a dummy name here + + conn->Flags &= ~GETTINGUSER; + strcpy(user->Name, "?_name"); + ChatSendWelcomeMsg(conn->BPQStream, conn, user); + } + + OrigBuffer[40] = 0; + sprintf(&OrigBuffer[40],"/t %s\r", topic); + strcpy(OrigBuffer, &OrigBuffer[40]); + len = (int)strlen(OrigBuffer); + } + else + { + // Normal input + + if (conn->Flags & GETTINGUSER) + { + // Check not getting *RTL in response to Name prompt + + if (memcmp(Buffer, "*RTL", 4) == 0) + { + // Other end thinks this is a node-node link + + Logprintf(LOG_CHAT, conn, '!', "Station %s trying to start Node Protocol, but not defined as a Node", + conn->Callsign); + + knownnode_add(conn->Callsign); // So it won't happen again + + Disconnect(conn->BPQStream); + return; + } + + conn->Flags &= ~GETTINGUSER; + memcpy(user->Name, Buffer, len-1); + ChatSendWelcomeMsg(conn->BPQStream, conn, user); + + return; + } + } + + if (ChatIsUTF8(OrigBuffer, len) == FALSE) + { + // With Windows it is simple - convert using current codepage + // I think the only reliable way is to convert to unicode and back + +#ifdef WIN32 + + int wlen; + + wlen = MultiByteToWideChar(CP_ACP, 0, Buffer, len, BufferW, 65536); + len = WideCharToMultiByte(CP_UTF8, 0, BufferW, wlen, BufferB, 63336, NULL, NULL); + Buffer = BufferB; + +#else + size_t left = 65536; + size_t clen = len; + + UCHAR * BufferBP = BufferB; + struct user_t * icu = conn->u.user; + + if (conn->rtcflags & p_user) + { + if (icu->iconv_toUTF8 == NULL) + { + icu->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", icu->Codepage); + + if (icu->iconv_toUTF8 == (iconv_t)-1) + icu->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252"); + } + + iconv(icu->iconv_toUTF8, NULL, NULL, NULL, NULL); // Reset State Machine + iconv(icu->iconv_toUTF8, &Buffer, &clen, (char ** __restrict__)&BufferBP, &left); + } + else + { + if (link_toUTF8 == NULL) + link_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252"); + + iconv(link_toUTF8, NULL, NULL, NULL, NULL); // Reset State Machine + iconv(link_toUTF8, &Buffer, &clen, (char ** __restrict__)&BufferBP, &left); + } + len = 65536 - left; + Buffer = BufferB; + +#endif + + } + ChatWriteLogLine(conn, '<',Buffer, len, LOG_CHAT); + + + Buffer[len] = 0; + + strlop(Buffer, '\r'); + + if (conn->rtcflags == p_linkwait) + { + //waiting for *RTL + + if (memcmp(Buffer, "*RTL", 4) == 0) + { + // Node - Node Connect + + if (rtloginl (conn, conn->Callsign)) + { + // Accepted + + conn->Flags |= CHATLINK; + return; + } + else + { + // Connection refused. rtlogin1 has sent error message and closed link + + return; + } + } + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + return; + + nprintf(conn, "Unexpected Message on Chat Node-Node Link - Disconnecting\r"); + ChatFlush(conn); + Sleep(500); + conn->rtcflags = p_nil; + + Disconnect(conn->BPQStream); + return; + } + + if (conn->Flags & CHATLINK) + { +#ifndef LINBPQ + + struct _EXCEPTION_POINTERS exinfo; + + __try + { + chkctl(conn, Buffer, len); + } + + #define EXCEPTMSG "Process Chat Line" + #include "StdExcept.c" + + Debugprintf("CHAT *** Was procesing Chat Node Message %s", Buffer); + Disconnect(conn->BPQStream); + CheckProgramErrors(); + } +#else + chkctl(conn, Buffer, len); +#endif + return; + } + + if(conn->u.user == NULL) + { + // A node link, but not activated yet, or a chat console which has dosconnected + + if (conn->BPQStream != -2) + return; + + // Log console user in + + if (rtloginu (conn, TRUE)) + conn->Flags |= CHATMODE; + + return; + + } + + if ((len <6) && (memcmp(Buffer, "*RTL", 4) == 0)) + { + // Other end thinks this is a node-node link + + Logprintf(LOG_CHAT, conn, '!', "Station %s trying to start Node Protocol, but not defined as a Node", + conn->Callsign); + + knownnode_add(conn->Callsign); // So it won't happen again + + Disconnect(conn->BPQStream); + return; + } + + if (Buffer[0] == '/') + { + // Process Command + + int cmdLen = 0; + char * param = strchr(&Buffer[1], ' '); + + if (param) + cmdLen = param - &Buffer[1]; + else + cmdLen = strlen(&Buffer[1]); + + if (_memicmp(&Buffer[1], "Bye", 1) == 0) + { + SendUnbuffered(conn->BPQStream, ChatSignoffMsg, (int)strlen(ChatSignoffMsg)); + + if (conn->BPQStream < 0) + { + logout(conn); + conn->Flags = 0; + if (conn->BPQStream == -2) + CloseConsole(conn->BPQStream); + } + else + ReturntoNode(conn->BPQStream); + + return; + } + + if (_memicmp(&Buffer[1], "Quit", 4) == 0) + { + SendUnbuffered(conn->BPQStream, ChatSignoffMsg, (int)strlen(ChatSignoffMsg)); + + if (conn->BPQStream < 0) + { + logout(conn); + conn->Flags = 0; + if (conn->BPQStream == -2) + CloseConsole(conn->BPQStream); + } + + else + { + Sleep(1000); + Disconnect(conn->BPQStream); + } + return; + } + + if (cmdLen > 1 && _memicmp(&Buffer[1], "History", cmdLen) == 0) // Accept Hi but not H + { + // Param is number of minutes to go back (max 24 hours) + + struct HistoryRec * ptr = History; + int interval = 0; + time_t start; + int n = HistoryCount; + + if (param) + interval = atoi(param); + + if (interval < 1) + { + nprintf(conn, "Format is /history n, where n is history time in minutes\r"); + conn->u.user->lastsendtime = time(NULL); + return; + } + + if (interval > 1440) + { + nprintf(conn, "History is only held for 24 Hours (1440 Minutes)\r"); + interval = 1440; // Limit to 1 day + } + + start = time(NULL) - (interval * 60); + + // Find first record to send + + while (ptr) + { + if (ptr->Time > start) + break; + n--; + ptr = ptr->next; + } + + // n is records found + + while (ptr) + { + nprintf(conn, ptr->Message); + ptr = ptr->next; + } + + conn->u.user->lastsendtime = time(NULL); + return; + } + + + + if (_memicmp(&Buffer[1], "Keepalive", 4) == 0) + { + conn->u.user->rtflags ^= u_keepalive; + upduser(conn->u.user); + nprintf(conn, "Keepalive is %s\r", (conn->u.user->rtflags & u_keepalive) ? "Enabled" : "Disabled"); + conn->u.user->lastsendtime = time(NULL); + return; + } + if (_memicmp(&Buffer[1], "AUTOCHARSET", 4) == 0) + { + conn->u.user->rtflags ^= u_auto; + upduser(conn->u.user); + nprintf(conn, "Automatic Character set selection is %s\r", (conn->u.user->rtflags & u_auto) ? "Enabled" : "Disabled"); + conn->u.user->lastsendtime = time(NULL); + return; + } + if (_memicmp(&Buffer[1], "UTF-8", 3) == 0) + { + conn->u.user->rtflags ^= u_noUTF8; + upduser(conn->u.user); + nprintf(conn, "Character set is %s\r", (conn->u.user->rtflags & u_noUTF8) ? "8 Bit" : "UTF-8"); + conn->u.user->lastsendtime = time(NULL); + return; + } + + if ((_memicmp(&Buffer[1], "CodePage", 3) == 0) || (_memicmp(&Buffer[1], "CP", 2) == 0)) + { + char * Context; + char * CP = strtok_s(&Buffer[1], " ,\r", &Context); +#ifndef WIN32 + iconv_t temp = NULL; +#else + int temp = 0; + WCHAR TempW[10]; +#endif + CP = strtok_s(NULL, " ,\r", &Context); + + if (CP == NULL || CP[0] == 0) + { +#ifndef WIN32 + if (conn->u.user->Codepage[0]) + nprintf(conn, "Codepage is %s\r", conn->u.user->Codepage); +#else + if (conn->u.user->Codepage) + nprintf(conn, "Codepage is %d\r", conn->u.user->Codepage); +#endif + else + nprintf(conn, "Codepage is not set\r"); + + return; + } + _strupr(CP); + +#ifndef WIN32 + + // Validate Code Page by trying to open an iconv descriptor + + temp = iconv_open("UTF-8", CP); + + if (temp == (iconv_t)-1) + { + nprintf(conn, "Invalid Codepage %s\r", CP); + return; + } + + iconv_close(conn->u.user->iconv_toUTF8); + iconv_close(conn->u.user->iconv_fromUTF8); + + conn->u.user->iconv_toUTF8 = temp; + conn->u.user->iconv_fromUTF8 = iconv_open(CP, "UTF-8"); + + strcpy(conn->u.user->Codepage, CP); + nprintf(conn, "Codepage set to %s\r", conn->u.user->Codepage); +#else + if (CP[0] == 'C') + CP +=2; + + // Validate by trying ot use it + + temp = atoi(CP); + + if (MultiByteToWideChar(temp, 0, "\r", 2, TempW, 10) == 0) + { + int err = GetLastError(); + + if (err == ERROR_INVALID_PARAMETER) + { + nprintf(conn, "Invalid Codepage %d\r", temp); + return; + } + } + + conn->u.user->Codepage = temp; + nprintf(conn, "Codepage set to %d\r", conn->u.user->Codepage); +#endif + upduser(conn->u.user); + + return; + } + + if (_memicmp(&Buffer[1], "Shownames", 4) == 0) + { + conn->u.user->rtflags ^= u_shownames; + upduser(conn->u.user); + nprintf(conn, "Shownames is %s\r", (conn->u.user->rtflags & u_shownames) ? "Enabled" : "Disabled"); + conn->u.user->lastsendtime = time(NULL); + return; + } + + if (_memicmp(&Buffer[1], "Time", 4) == 0) + { + conn->u.user->rtflags ^= u_showtime; + upduser(conn->u.user); + nprintf(conn, "Show Time is %s\r", (conn->u.user->rtflags & u_showtime) ? "Enabled" : "Disabled"); + conn->u.user->lastsendtime = time(NULL); + return; + } + + if (_memicmp(&Buffer[1], "colours", 4) == 0) + { + int i =0; + + while (i < 100) + { + nprintf(conn, "\x1b%c%02d XXXXX\r", i + 10, i); + i++; + if (i == 3) + i++; + } + return; + } + + rt_cmd(conn, Buffer); + + return; + } + + // Send message to all other connected users on same channel + + text_tellu(conn->u.user, Buffer, NULL, o_topic); // To local users. + + HistoryCount = AddtoHistory(conn->u.user, Buffer); + + conn->u.user->lastrealmsgtime = conn->u.user->lastmsgtime = time(NULL); + + // Send to Linked nodes + + for (c = circuit_hd; c; c = c->next) + { + if ((c->rtcflags & p_linked) && c->refcnt && ct_find(c, conn->u.user->topic)) + nprintf(c, "%c%c%s %s %s\r", FORMAT, id_data, OurNode, conn->u.user->call, Buffer); + } +} + +void upduser(USER *user) +{ + FILE *in, *out; + char *c; + char Buffer[2048]; + char *buf = Buffer; + + in = fopen(RtUsr, "r"); + + if (!(in)) + { + in = fopen(RtUsr, "w"); + if (in) + fclose(in); + in = fopen(RtUsr, "r"); + } + + if (!(in)) return; + + out = fopen(RtUsrTemp, "w"); + + if (!(out)) return; + + while(fgets(buf, 128, in)) + { + if (strstr(buf, "*RTL")) // Tidy user database + continue; + + c = strchr(buf, ' '); + if (c) *c = '\0'; + if (!matchi(buf, user->call)) + { + if (c) *c = ' '; + fputs(buf, out); + } + } + +#ifndef WIN32 + fprintf(out, "%s %d %s %s¬%d¬%s\n", user->call, user->rtflags, user->name, user->qth, user->Colour, user->Codepage); +#else + fprintf(out, "%s %d %s %s¬%d¬%d\n", user->call, user->rtflags, user->name, user->qth, user->Colour, user->Codepage); +#endif + fclose(in); + fclose(out); + + remove(RtUsr); + rename(RtUsrTemp, RtUsr); +} + +char * lookupuser(char * call) +{ + FILE *in; + char *flags; + char Buffer[2048]; + char *buf = Buffer; + char * name; + + in = fopen(RtUsr, "r"); + + if (in) + { + while(fgets(buf, 128, in)) + { + strlop(buf, '\n'); + + flags = strlop(buf, ' '); + if (!matchi(buf, call)) continue; + if (!flags) break; + + fclose(in); + name = strlop(flags, ' '); + strlop(name, ' '); + return _strdup(name); + } + fclose(in); + } + + return NULL; +} + + + +void rduser(USER *user) +{ + FILE *in; + char *name, *flags, *qth; + char Buffer[2048]; + char *buf = Buffer; + char * ptr; + + user->name = _strdup("?_name"); + user->qth = _strdup("?_qth"); + + in = fopen(RtUsr, "r"); + + if (in) + { + while(fgets(buf, 128, in)) + { + strlop(buf, '\n'); + + flags = strlop(buf, ' '); + if (!matchi(buf, user->call)) continue; + if (!flags) break; + + name = strlop(flags, ' '); + user->rtflags = atoi(flags); + + qth = strlop(name, ' '); + strnew(&user->name, name); + + if (!qth) break; + + // Colour Code may follow QTH, and Code Page may follow Colour + + ptr = strchr(qth, '¬'); + if (ptr) + { + *ptr++ = 0; + user->Colour = atoi(ptr); + + ptr = strchr(ptr, '¬'); + + if (ptr) + { + *ptr++ = 0; +#ifndef WIN32 + strcpy(user->Codepage, ptr); +#else + user->Codepage = atoi(ptr); +#endif + } + } + + strnew(&user->qth, qth); + break; + } + fclose(in); + +#ifndef WIN32 + + // Open an iconv decriptor for each conversion + + if (user->Codepage[0]) + user->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", user->Codepage); + else + user->iconv_toUTF8 = (iconv_t)-1; + + if (user->iconv_toUTF8 == (iconv_t)-1) + user->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252"); + + + if (user->Codepage[0]) + user->iconv_fromUTF8 = iconv_open(user->Codepage, "UTF-8"); + else + user->iconv_fromUTF8 = (iconv_t)-1; + + if (user->iconv_fromUTF8 == (iconv_t)-1) + user->iconv_fromUTF8 = iconv_open("CP1252//IGNORE", "UTF-8"); +#endif + } +} + + +void ReportBadJoin(char * ncall, char *ucall) +{ + Logprintf(LOG_CHAT, NULL, '!', "User %s Join from Node %s but already connected", ucall, ncall); +} + +void ReportBadLeave(char * ncall, char * ucall) +{ + Logprintf(LOG_CHAT, NULL, '!', "Node %s reporting Node %s as a leaving user", ncall, ucall); +} + + +struct DUPINFO DupInfo[MAXDUPS]; + +static BOOL CheckforDups(ChatCIRCUIT * circuit, char * Call, char * Msg) +{ + // Primitive duplicate suppression - see if same call and text reeived in last few secons + + time_t Now = time(NULL); + time_t DupCheck = Now - DUPSECONDS; + int i, saveindex = -1; + + for (i = 0; i < MAXDUPS; i++) + { + if (DupInfo[i].DupTime < DupCheck) + { + // too old - use first if we need to save it + + if (saveindex == -1) + { + saveindex = i; + } + continue; + } + + if ((strcmp(Call, DupInfo[i].DupUser) == 0) && (memcmp(Msg, DupInfo[i].DupText, strlen(DupInfo[i].DupText)) == 0)) + { + // Duplicate, so discard, but save time + + DupInfo[i].DupTime = Now; + Logprintf(LOG_CHAT, circuit, '?', "Duplicate Message From %s %s suppressed", Call, Msg); + + return TRUE; // Duplicate + } + + } + + // Not in list + + if (saveindex == -1) // List is full + saveindex = MAXDUPS - 1; // Stick on end + + DupInfo[saveindex].DupTime = Now; + strcpy(DupInfo[saveindex].DupUser, Call); + + if (strlen(Msg) > 99) + { + memcpy(DupInfo[saveindex].DupText, Msg, 99); + DupInfo[saveindex].DupText[99] = 0; + } + else + strcpy(DupInfo[saveindex].DupText, Msg); + + return FALSE; +} + +void chkctl(ChatCIRCUIT *ckt_from, char * Buffer, int Len) +{ + CHATNODE * node, *ln; + ChatCIRCUIT * ckt_to; + USER * user, * su; + time_t Now = time(NULL); + LINK * Link = ckt_from->u.link; + + char * ncall, * ucall, * f1, * f2, * buf; + int i; + + if (Buffer[FORMAT_O] != FORMAT) return; // Not a control message. + + // Check for corruption + + for (i = 1; i < (Len - 1); i++) + { + if (Buffer[i] < 32) + { + if (Buffer[i] == 9) + { + Buffer[i] = 32; + continue; + } + Debugprintf("Corrupt Chat Link Messages %s", Buffer); + return; + } + } + + buf = _strdup(Buffer + DATA_O); + +// FORMAT and TYPE bytes are followed by node and user callsigns. + + ncall = buf; + ucall = strlop(buf, ' '); + if (!ucall) { free(buf); return; } // Not a control message. + +// There may be at least one field after the node and user callsigns. +// Node leave (id_unlink) has no F1. + + f1 = strlop(ucall, ' '); + strlop(ucall, 9); // some have tabs ?? + +// If the frame came from an unknown node ignore it. +// If the frame came from us ignore it (loop breaking). + + node = node_find(ncall); + if (!node || matchi(ncall, OurNode)) { free(buf); return; } + + if (ckt_from->rtcflags & p_linked) + ckt_from->u.link->lastMsgReceived = Now; + + switch(Buffer[TYPE_O]) + { + // Data from user ucall at node ncall. + + case id_data : + + // Check for dups + + if (CheckforDups(ckt_from, ucall, f1)) + break; + + user = user_find(ucall, ncall); + + if (!user) + break; + + user->lastrealmsgtime = user->lastmsgtime = time(NULL); + + text_tellu(user, f1, NULL, o_topic); + HistoryCount = AddtoHistory(user, f1); + + for (ckt_to = circuit_hd; ckt_to; ckt_to = ckt_to->next) + { + if ((ckt_to->rtcflags & p_linked) && ckt_to->refcnt && + !cn_find(ckt_to, node) && ct_find(ckt_to, user->topic)) + nprintf(ckt_to, "%s\r", Buffer); + } + break; + + // User ucall at node ncall changed their Name/QTH info. + + case id_user : + + user = user_find(ucall, ncall); + if (!user) break; + f2 = strlop(f1, ' '); + if (!f2) break; + + if ((strcmp(user->name, f1) == 0) && (strcmp(user->qth, f2) == 0)) // No Change? + break; + + echo(ckt_from, node, Buffer); // Relay to other nodes. + strnew(&user->name, f1); + strnew(&user->qth, f2); + upduser(user); + break; + + // User ucall logged into node ncall. + + case id_join : + + user = user_find(ucall, ncall); + + if (user) + { + // Already Here + + // If last join was less the 5 secs ago don't report - probably a "Join/Leave Storm" + + if (time(NULL) - user->timeconnected > 5) + ReportBadJoin(ncall, ucall); + + //if (strcmp(user->node->call, OurNode) == 0) + //{ + // Locally connected, and at another node + //} + + user->timeconnected = time(NULL); + break; // We have this user as an active Node + } + + // update join time + + echo(ckt_from, node, Buffer); // Relay to other nodes. + f2 = strlop(f1, ' '); + if (!f2) break; + user = user_join(ckt_from, ucall, ncall, NULL, FALSE); + if (!user) break; + ckt_from->refcnt++; + text_tellu_Joined(user); + strnew(&user->name, f1); + strnew(&user->qth, f2); + upduser(user); +// makelinks(); // Bring up our links if not already up + + break; + + // User ucall logged out of node ncall. + + case id_leave : + + user = user_find(ucall, ncall); + if (!user) + { + Debugprintf("CHAT: Leave for %s from %s when not on list", ucall, ncall); + break; + } + + // if connected for for less than 3 seconds ignore. May give stuck nodes but should stop "Join/Leave Storm" + // we can't just silently leave as next join will propagate + + if (time(NULL) - user->timeconnected < 3) + break; + + echo(ckt_from, node, Buffer); // Relay to other nodes. + + f2 = strlop(f1, ' '); + if (!f2) break; + + text_tellu(user, rtleave, NULL, o_all); + ckt_from->refcnt--; + strnew(&user->name, f1); + strnew(&user->qth, f2); + upduser(user); + user_leave(user); + + cn_dec(ckt_from, node); + node_dec(node); + + break; + + // Node ncall lost its link to node ucall, alias f1. + + case id_unlink : + + // Only relay to other nodes if we had node. Could get loop otherwise. + // ?? This could possibly cause stuck nodes + + ln = node_find(ucall); + + // if connected for for less than 3 seconds ignore. May give stuck nodes but should stop "Join/Leave Storm" + // we can't just silently leave as next join will propagate + + if (ln) + { + if (time(NULL) - ln->timeconnected < 3) + break; + + // is it on this circuit? + + if (cn_find(ckt_from, ln)) + { + cn_dec(ckt_from, ln); + node_dec(ln); + echo(ckt_from, node, Buffer); // Relay to other nodes if we had node. COuld get loop if + } + else + { + Debugprintf("CHAT: node %s unlink for %s when not on this link", ncall, ucall); + } + } + else + { + Debugprintf("CHAT: node %s unlink for %s when not on list", ncall, ucall); + } + + break; + + // Node ncall acquired a link to node ucall, alias f1. + // If we are not linked, is no problem, don't link. + // If we are linked, is a loop, do what? (Try ignore!) + + case id_link : + + ln = node_find(ucall); + + if (!ln && !matchi(ncall, OurNode)) + { + f2 = strlop(f1, ' '); + cn_inc(ckt_from, ucall, f1, f2); + echo(ckt_from, node, Buffer); // Relay to other nodes. + } + else + { + // If last join was less the 5 secs ago don't report - probably a "Join/Leave Storm" + + if (time(NULL) - ln->timeconnected > 5) + Debugprintf("CHAT: node %s link for %s when already on list", ncall, ucall); + + // update join time + + ln->timeconnected = time(NULL); + break; + } + + break; + + // User ucall at node ncall sent f2 to user f1. + + case id_send : + user = user_find(ucall, ncall); + if (!user) break; + f2 = strlop(f1, ' '); + if (!f2) break; + su = user_find(f1, NULL); + if (!su) break; + + if (su->circuit->rtcflags & p_user) + text_tellu(user, f2, f1, o_one); + else + echo(ckt_from, node, Buffer); // Relay to other nodes. + break; + + // User ucall at node ncall changed topic. + + case id_topic : + user = user_find(ucall, ncall); + if (user) + { + if (_stricmp(user->topic->name, f1) != 0) + { + echo(ckt_from, node, Buffer); // Relay to other nodes. + topic_chg(user, f1); + } + } + break; + + + case id_keepalive : + + ln = node_find(ncall); + if (ln) + { + if (ln->Version == NULL) + if (f1) + ln->Version = _strdup(f1); + } + + nprintf(ckt_from, "%c%c%s %s\r", FORMAT, id_pollresp, OurNode, Link->call); + break; + + case id_poll: + + // Send Poll Response + + Link->supportsPolls = Now; + nprintf(ckt_from, "%c%c%s %s\r", FORMAT, id_pollresp, OurNode, Link->call); + break; + + case id_pollresp: + + Link->supportsPolls = Now; + Link->RTT = Now - Link->timePollSent; + Link->timePollSent = 0; // Cancel Timeout + break; + + default: + break; + } + + free(buf); +} + +// Tell another node about nodes known by this node. +// Do not tell it about this node, the other node knows who it +// linked to (or who linked to it). +// Tell another node about users known by this node. +// Done at incoming or outgoing link establishment. + +void state_tell(ChatCIRCUIT *circuit, char * Version) +{ + CHATNODE *node; + USER *user; + + node = cn_inc(circuit, circuit->u.link->call, circuit->u.link->alias, Version); + node_tell(node, id_link); // Tell other nodes about this new link + + // Tell the node that just linked here about nodes known on other links. + + for (node = node_hd; node; node = node->next) + { + if (!matchi(node->call, OurNode)) + node_xmit(node, id_link, circuit); + } + + // Tell the node that just linked here about known users, and their topics. + + for (user = user_hd; user; user = user->next) + { + user_xmit(user, id_join, circuit); + topic_xmit(user, circuit); + } +} + +static void circuit_free(ChatCIRCUIT *circuit) +{ + ChatCIRCUIT *c, *cp; + CN *ncn; + CHATNODE *nn; + TOPIC *tn; + + cp = NULL; + + for (c = circuit_hd; c; cp = c, c = c->next) + { + if (c == circuit) + { + if (cp) cp->next = c->next; else circuit_hd = c->next; + + while (c->hnode) + { + ncn = c->hnode->next; + free(c->hnode); + c->hnode = ncn; + } + + break; + } + } + + if (circuit_hd) return; + +// RT has gone inactive. Clean up. + + while (node_hd) + { + nn = node_hd->next; + free(node_hd->alias); + free(node_hd->call); + free(node_hd); + node_hd = nn; + } + + while (topic_hd) + { + tn = topic_hd->next; + free(topic_hd->name); + free(topic_hd); + topic_hd = tn; + } +} + + +// Find a node in the node list. + +CHATNODE *node_find(char *call) +{ + CHATNODE *node; + + for (node = node_hd; node; node = node->next) + { + //if (node->refcnt && matchi(node->call, call)) I don't think this is right!!! + if (matchi(node->call, call)) + break; + } + + return node; +} + +// Add a reference to a node. + +static CHATNODE *node_inc(char *call, char *alias, char * Version) +{ + CHATNODE *node; + + node = node_find(call); + + if (!node) + { + knownnode_add(call); + + node = zalloc(sizeof(CHATNODE)); + sl_ins_hd(node, node_hd); + node->call = _strdup(call); + node->alias = _strdup(alias); + if (Version) + node->Version = _strdup(Version); + + node->timeconnected = time(NULL); + +// Debugprintf("New Node Rec Created at %x for %s %s", node, node->call, node->alias); + } + + node->refcnt++; + return node; +} + +// Remove a reference to a node. + +static void node_dec(CHATNODE *node) +{ + CHATNODE *t, *tp; + USER *user; + + ChatCIRCUIT *circuit; + CN *cn; + + if (--node->refcnt) return; // Other references. + + // Remove the node from the node list. + + tp = NULL; + + // Make sure there aren't any user or circuit records pointing to it + + for (user = user_hd; user; user = user->next) + { + if (user->node == node) + { + Debugprintf("Trying to remove node %s that is linked from user %s", node->call, user->call); + node->refcnt++; + } + } + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked) + { + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node == node) + { + Debugprintf("Trying to remove node %s that is linked from circuit %s", node->call, circuit->Callsign); + node->refcnt++; + } + } + } + } + + if (node->refcnt) return; // Now have other references. + + for (t = node_hd; t; tp = t, t = t->next) + { + if (t == node) + { + if (tp) tp->next = t->next; else node_hd = t->next; + free(t->alias); + t->alias = NULL; + free(t->call); + t->call = NULL; + free(t); + break; + } + } +} + +// User joins a topic. + +static TOPIC *topic_join(ChatCIRCUIT *circuit, char *s) +{ + CT *ct; + TOPIC *topic; + +// Look for an existing topic. + + for (topic = topic_hd; topic; topic = topic->next) + { + if (matchi(topic->name, s)) + break; + } + +// Create a new topic, if needed. + + if (!topic) + { + topic = zalloc(sizeof(TOPIC)); + sl_ins_hd(topic, topic_hd); + topic->name = _strdup(s); + } + + topic->refcnt++; // One more user in this topic. + + Logprintf(LOG_CHAT, circuit, '?', "topic_join complete user %s topic %s addr %x ref %d", + circuit->u.user->call, topic->name, topic, topic->refcnt); + + +// Add the circuit / topic association. + + for (ct = circuit->topic; ct; ct = ct->next) + { + if (ct->topic == topic) + { + ct->refcnt++; + return topic; + } + } + + ct = zalloc(sizeof(CT)); + sl_ins_hd(ct, circuit->topic); + ct->topic = topic; + ct->refcnt = 1; + return topic; +} + +// User leaves a topic. + +static void topic_leave(ChatCIRCUIT *circuit, TOPIC *topic) +{ + CT *ct, *ctp; + TOPIC *t, *tp; + + Logprintf(LOG_CHAT, circuit, '?', "topic_leave user %s topic %s addr %x ref %d", + circuit->u.user->call, topic->name, topic, topic->refcnt); + + topic->refcnt--; + + ctp = NULL; + + for (ct = circuit->topic; ct; ctp = ct, ct = ct->next) + { + if (ct->topic == topic) + { + if (!--ct->refcnt) + { + if (ctp) ctp->next = ct->next; else circuit->topic = ct->next; + free(ct); + break; + } + } + } + + tp = NULL; + + for (t = topic_hd; t; tp = t, t = t->next) + { + if (!t->refcnt && (t == topic)) + { + if (tp) tp->next = t->next; else topic_hd = t->next; + free(t->name); + free(t); + break; + } + } +} + +// Find a circuit/topic association. + +int ct_find(ChatCIRCUIT *circuit, TOPIC *topic) +{ + CT *ct; + + for (ct = circuit->topic; ct; ct = ct->next) + { + if (ct->topic == topic) + return ct->refcnt; + } + return 0; +} + +// Nodes reached from each circuit. Used only if the circuit is a link. + +// Remove a circuit/node association. + +static void cn_dec(ChatCIRCUIT *circuit, CHATNODE *node) +{ + CN *c, *cp; + +// Debugprintf("CHAT: Remove c/n %s ", node->call); + + cp = NULL; + + for (c = circuit->hnode; c; cp = c, c = c->next) + { + if (c->node == node) + { +// CN * cn; +// int len; +// char line[1000]=""; + + if (--c->refcnt) + { +// Debugprintf("CHAT: Remove c/n Node %s still in use refcount %d", node->call, c->refcnt); + return; // Still in use + } + + if (cp) + cp->next = c->next; + else + circuit->hnode = c->next; + + free(c); + + break; + } + } + + if (c == NULL) + { + CN * cn; + int len; + char line[1000]=""; + + // not found?? + + Debugprintf("CHAT: !! Remove c/n Node %s addr %x not found cn chain follows", node->call, node); + + line[0] = 0; + + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node && cn->node->call) + { +#ifndef LINBPQ + __try + { +#endif + len += sprintf(&line[len], " %p %s", cn->node, cn->node->alias); + if (len > 80) + { + Debugprintf("%s", line); + len = sprintf(line, " "); + } +#ifndef LINBPQ + } + __except(EXCEPTION_EXECUTE_HANDLER) + {len = sprintf("%s *PE* Corrupt Rec %x %x ", line, cn, cn->node);} +#endif + } + else + { + len = sprintf("%s Corrupt Rec %x %x ", line, cn, cn->node); + } + } + Debugprintf("%s", line); + + } + + +} + +// Add a circuit/node association. + +static CHATNODE *cn_inc(ChatCIRCUIT *circuit, char *call, char *alias, char * Version) +{ + CHATNODE *node; + CN *cn; + + node = node_inc(call, alias, Version); + + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node == node) + { + cn->refcnt++; +// Debugprintf("cn_inc cn Refcount for %s->%s incremented to %d - adding Call %s", +// circuit->Callsign, node->call, cn->refcnt, call); + + return node; + } + } + + cn = zalloc(sizeof(CN)); + sl_ins_hd(cn, circuit->hnode); + cn->node = node; + cn->refcnt = 1; + +// Debugprintf("cn_inc New cn for %s->%s - adding Call %s", +// circuit->Callsign, node->call, call); + + return node; +} + +// Find a circuit/node association. + +static int cn_find(ChatCIRCUIT *circuit, CHATNODE *node) +{ + CN *cn; + + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node == node) + return cn->refcnt; + } + return 0; +} + +// From a local user to a specific user at another node. + +static void text_xmit(USER *user, USER *to, char *text) +{ + nprintf(to->circuit, "%c%c%s %s %s %s\r", + FORMAT, id_send, OurNode, user->call, to->call, text); +} + +void put_text(ChatCIRCUIT * circuit, USER * user, UCHAR * buf) +{ + UCHAR BufferB[4096]; + + // Text is UTF-8 internally. If user doen't want UTF-8. convert to Node's locale + + if (circuit->u.user->rtflags & u_noUTF8) + { +#ifdef WIN32 + char * Buffer = buf; + WCHAR BufferW[4096]; + int wlen, blen; + BOOL DefaultUsed = FALSE; + char Subst = '?'; + + wlen = MultiByteToWideChar(CP_UTF8, 0, buf, (int)strlen(buf) + 1, BufferW, 4096); + blen = WideCharToMultiByte(circuit->u.user->Codepage, 0, BufferW, wlen, BufferB + 2, 4096, &Subst, &DefaultUsed); + + if (blen == 0) // Probably means invalid code page + blen = WideCharToMultiByte(CP_ACP, 0, BufferW, wlen, BufferB + 2, 4096, &Subst, &DefaultUsed); + + buf = BufferB + 2; + BufferB[blen + 2] = 0; +#else + + size_t left = 4096; + UCHAR * BufferBP = BufferB; + size_t len = strlen(buf) + 1; + struct user_t * icu = circuit->u.user; + + if (icu->iconv_fromUTF8 == NULL) + { + icu->iconv_fromUTF8 = iconv_open(icu->Codepage, "UTF-8"); + + if (icu->iconv_fromUTF8 == (iconv_t)-1) + icu->iconv_fromUTF8 = iconv_open("CP1252//IGNORE", "UTF-8"); + } + + iconv(icu->iconv_fromUTF8, NULL, NULL, NULL, NULL); // Reset State Machine + iconv(icu->iconv_fromUTF8, (char ** __restrict__)&buf, &len, (char ** __restrict__)&BufferBP, &left); + + len = 4096 - left; + buf = BufferB; + +#endif + + } + + + if (circuit->u.user->rtflags & u_colour) // Use Colour + { + // Put a colour header on message + + *(--buf) = user->Colour; + *(--buf) = 0x1b; + nputs(circuit, buf); + buf +=2; + } + else + nputs(circuit, buf); + + + + circuit->u.user->lastsendtime = time(NULL); +} + +void text_tellu(USER *user, char *text, char *to, int who) +{ + ChatCIRCUIT *circuit; + UCHAR Buffer[2048]; + UCHAR *buf = &Buffer[4]; + char * Time; + struct tm * tm; + char Stamp[20]; + time_t T; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min); + +// Send it to all connected users in the same topic. +// Echo to originator if requested. + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (!(circuit->rtcflags & p_user)) continue; // Circuit is a link. + + if ((circuit->u.user == user) && !(user->rtflags & u_echo)) continue; + + if (circuit->u.user->rtflags & u_showtime) + Time = Stamp; + else + Time = ""; + + if (circuit->u.user->rtflags & u_shownames) + sprintf(buf, "%s%-6.6s %s %c %s\r", Time, user->call, user->name, (who == o_one) ? '>' : ':', text); + else + sprintf(buf, "%s%-6.6s %c %s\r", Time, user->call, (who == o_one) ? '>' : ':', text); + + + switch(who) + { + case o_topic : + if (circuit->u.user->topic == user->topic) + put_text(circuit, user, buf); // Send adding Colour if wanted + + break; + + case o_all: + + put_text(circuit, user, buf); // Send adding Colour if wanted + + break; + + case o_one : + if (matchi(circuit->u.user->call, to)) + put_text(circuit, user, buf); // Send adding Colour if wanted + break; + } + } +} + +extern int FlashOnConnect; + +void text_tellu_Joined(USER * user) +{ + ChatCIRCUIT *circuit; + UCHAR Buffer[200]; + UCHAR *buf = &Buffer[4]; + char * Time; + struct tm * tm; + char Stamp[20]; + time_t T; + char prog[256] = ""; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min); + + sprintf(buf, "%s%-6.6s : %s *** Joined Chat, Topic %s", Stamp, user->call, user->name, user->topic->name); + + if (reportChatEvents) + { + +#ifdef WIN32 + if (pRunEventProgram) + pRunEventProgram("ChatNewUser.exe", user->call); +#else + sprintf(prog, "%s/%s", BPQDirectory, "ChatNewUser"); + RunEventProgram(prog, user->call); +#endif + } + +// Send it to all connected users in the same topic. +// Echo to originator if requested. + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (!(circuit->rtcflags & p_user)) continue; // Circuit is a link. + if ((circuit->u.user == user) && !(user->rtflags & u_echo)) continue; + + if (circuit->u.user->rtflags & u_showtime) + Time = Stamp; + else + Time = ""; + + sprintf(buf, "%s%-6.6s : %s *** Joined Chat, Topic %s", Time, user->call, user->name, user->topic->name); + + put_text(circuit, user, buf); // Send adding Colour if wanted + + if (circuit->u.user->rtflags & u_bells) + if (circuit->BPQStream < 0) // Console + { +#ifndef LINBPQ + if (FlashOnConnect) FlashWindow(ConsHeader[1]->hConsole, TRUE); +#endif + nputc(circuit, 7); +// PlaySound ("BPQCHAT_USER_LOGIN", NULL, SND_ALIAS | SND_APPLICATION | SND_ASYNC); + } + else + nputc(circuit, 7); + + nputc(circuit, 13); + } +} +// Tell one link circuit about a local user change of topic. + +static void topic_xmit(USER *user, ChatCIRCUIT *circuit) +{ + nprintf(circuit, "%c%c%s %s %s\r", + FORMAT, id_topic, OurNode, user->call, user->topic->name); +} + +// Tell another node about one known node on a link add or drop +// if that node is from some other link. + +static void node_xmit(CHATNODE *node, char kind, ChatCIRCUIT *circuit) +{ +#ifndef LINBPQ + struct _EXCEPTION_POINTERS exinfo; + + __try + { +#endif + if (!cn_find(circuit, node)) + if (node->Version && (kind == id_link)) + nprintf(circuit, "%c%c%s %s %s %s\r", FORMAT, kind, OurNode, node->call, node->alias, node->Version); + else + nprintf(circuit, "%c%c%s %s %s\r", FORMAT, kind, OurNode, node->call, node->alias); + +#ifndef LINBPQ + } + + #define EXCEPTMSG "node_xmit" + #include "StdExcept.c" + + Debugprintf("Corrupt Rec %x %x %x", node, node->call, node->alias); + } +#endif +} + +// Tell all other nodes about one node known by this node. + +static void node_tell(CHATNODE *node, char kind) +{ + ChatCIRCUIT *circuit; + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked) + node_xmit(node, kind, circuit); + } +} + +// Tell another node about a user login/logout at this node. + +static void user_xmit(USER *user, char kind, ChatCIRCUIT *circuit) +{ + CHATNODE *node; + + node = user->node; + + if (!cn_find(circuit, node)) + nprintf(circuit, "%c%c%s %s %s %s\r", FORMAT, kind, node->call, user->call, user->name, user->qth); +} + +// Tell all other nodes about a user login/logout at this node. + +static void user_tell(USER *user, char kind) +{ + ChatCIRCUIT *circuit; + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked) + user_xmit(user, kind, circuit); + } +} + +// Find the user record for call@node. Node can be NULL, meaning any node + +USER *user_find(char *call, char * node) +{ + USER *user; + + for (user = user_hd; user; user = user->next) + { + if (node) + { + if (matchi(user->call, call) && matchi(user->node->call, node)) + break; + } + else + { + if (matchi(user->call, call)) + break; + } + } + + return user; +} + +static void user_leave(USER *user) +{ + USER *t, *tp; + + topic_leave(user->circuit, user->topic); + + tp = NULL; + + for (t = user_hd; t; tp = t, t = t->next) + { + if (t == user) + { + if (tp) tp->next = t->next; else user_hd = t->next; + + free(t->name); + free(t->call); + free(t->qth); +#ifndef WIN32 + if (t->iconv_fromUTF8) + iconv_close(t->iconv_fromUTF8); + if (t->iconv_toUTF8) + iconv_close(t->iconv_toUTF8); +#endif + free(t); + break; + } + } + + if (user_hd == NULL) + ChatTmr = 59; // If no users, disconnect links after 10-20 secs +} + +// User changed to a different topic. + +static BOOL topic_chg(USER *user, char *s) +{ + char buf[128]; + + if (_stricmp(user->topic->name, s) == 0) return FALSE; // Not Changed + + sprintf(buf, "*** Left Topic: %s", user->topic->name); + text_tellu(user, buf, NULL, o_topic); // Tell everyone in the old topic. + topic_leave(user->circuit, user->topic); + user->topic = topic_join(user->circuit, s); + sprintf(buf, "*** Joined Topic: %s", user->topic->name); + text_tellu(user, buf, NULL, o_topic); // Tell everyone in the new topic. + + return TRUE; +} + +// Create a user record for this user. + +static USER *user_join(ChatCIRCUIT *circuit, char *ucall, char *ncall, char *nalias, BOOL Local) +{ + CHATNODE *node; + USER *user; + + if (Local) + { + node = cn_inc(circuit, ncall, nalias, Verstring); + } + else + node = cn_inc(circuit, ncall, nalias, NULL); + +// Is this user already logged in at this node? + + for (user = user_hd; user; user = user->next) + { + if (matchi(user->call, ucall) && (user->node == node)) + return user; + } + +// User is not logged in, create a user record for them. + + user = zalloc(sizeof(USER)); + sl_ins_hd(user, user_hd); + user->circuit = circuit; + user->call = _strdup(ucall); + _strupr(user->call); + user->node = node; + rduser(user); + + if (user->Colour == 0 || user->Colour == 11) // None or default + { + // Allocate Random + int sum = 0, i; + + for (i = 0; i < 9; i++) + sum += user->call[i]; + sum %= 20; + + user->Colour = AutoColours[sum] + 10; // Best 20 colours + } + + if (circuit->rtcflags & p_user) + circuit->u.user = user; + + user->timeconnected = user->lastrealmsgtime = user->lastmsgtime = time(NULL); + + user->topic = topic_join(circuit, deftopic); + return user; +} + +// Link went away. We dropped it, or the other node dropped it. +// Drop nodes and users connected from this link. +// Tell other (still connected) links what was dropped. + +void link_drop(ChatCIRCUIT *circuit) +{ + USER *user, *usernext; + CN *cn; + +// So we don't try and send anything on this circuit. + + if (circuit->u.link) + if (circuit->rtcflags == p_linkini) + Debugprintf("Chat link %s Link Setup Failed", circuit->u.link->call); + + if (circuit->rtcflags == p_linkini) + circuit->u.link->flags = p_linkfailed; + else + circuit->u.link->flags = 0; + + circuit->rtcflags = p_nil; + +// Users connected on the dropped link are no longer connected. + + for (user = user_hd; user; user = usernext) + { + usernext = user->next; // Save next pointer in case entry is free'd + + if (user->circuit == circuit) + { + CHATNODE *node; + + node = user->node; + + text_tellu(user, rtleave, NULL, o_all); + user_tell(user, id_leave); + user_leave(user); + + circuit->refcnt--; + if (node) + node_dec(node); + } + } + +// Any node known from the dropped link is no longer known. + + for (cn = circuit->hnode; cn; cn = cn->next) + { + node_tell(cn->node, id_unlink); + node_dec(cn->node); + } + +// The circuit is no longer used. + + circuit_free(circuit); + NeedStatus = TRUE; +} + +// Handle an incoming control frame from a linked RT system. + +static void echo(ChatCIRCUIT *fc, CHATNODE *node, char * Buffer) +{ + ChatCIRCUIT *tc; + + for (tc = circuit_hd; tc; tc = tc->next) + { + if ((tc != fc) && (tc->rtcflags & p_linked) && !cn_find(tc, node)) + nprintf(tc, "%s\r", Buffer); + } +} + +char ** SeparateConnectScript(char * MultiString) +{ + char * ptr1 = MultiString; + char ** Value; + int Count = 0; + char * ptr; + + // Convert to string array + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + ptr = MultiString; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + if (strlen(ptr)) + { + Value = realloc(Value, (Count + 2) * sizeof(void *)); + Value[Count++] = _strdup(ptr); + } + ptr = ptr1; + } + + Value[Count] = NULL; + return Value; +} + +// Add an entry to list of link partners + +int rtlink (char * Call) +{ + LINK *link, *temp; + char *c; + char * script; + + _strupr(Call); + script = strlop(Call, '|'); + + c = strlop(Call, ':'); + if (!c) return FALSE; + + link = zalloc(sizeof(LINK)); + + link->alias = _strdup(Call); + link->call = _strdup(c); + + if (script) + { + link->ConnectScript = SeparateConnectScript(script); + link->Lines = 0; + while (link->ConnectScript[++link->Lines]); + } + else + { + // Create Script with one entry to call partner direct; + + link->ConnectScript = zalloc(sizeof(void *) * 2); // always NULL entry on end + link->ConnectScript[0] = malloc(32); + sprintf(link->ConnectScript[0], "C %s", c); + link->Lines = 1; + } + + if (link_hd == NULL) + link_hd = link; + else + { + temp = link_hd; + while(temp->next) + temp = temp->next; + + temp->next = link; + } + + return TRUE; +} + +VOID removelinks() +{ + LINK *link, *nextlink; + + for (link = link_hd; link; link = nextlink) + { + nextlink = link->next; + + if (link->ConnectScript) + { + int n = 0; + while(link->ConnectScript[n]) + free(link->ConnectScript[n++]); + + free(link->ConnectScript); + } + + free(link->alias); + link->alias = 0; + free(link->call); + link->call = 0; + free(link); + link = 0; + } + link_hd = NULL; +} +VOID removeknown() +{ + // Save Known Nodes list and free struct + + KNOWNNODE *node, *nextnode; + FILE *out; + + out = fopen(RtKnown, "w"); + + if (!out) + return; + + for (node = known_hd; node; node = nextnode) + { + fprintf(out, "%s %u\n", node->call, (unsigned int)node->LastHeard); + + nextnode = node->next; + free(node->call); + free(node); + } + known_hd = NULL; + + fclose(out); +} + +VOID LoadKnown() +{ + // Reload Known Nodes list + + FILE *in; + char buf[128]; + char * ptr; + + in = fopen(RtKnown, "r"); + + if (in == NULL) + return; + + while(fgets(buf, 128, in)) + { + ptr = strchr(buf, ' '); + if (ptr) + { + *(ptr) = 0; + knownnode_add(buf); + } + } + + fclose(in); +} + +// We don't allocate memory for circuit, but we do chain it + +ChatCIRCUIT *circuit_new(ChatCIRCUIT *circuit, int flags) +{ + // Make sure circuit isn't already on list + + ChatCIRCUIT *c; + + circuit->rtcflags = flags; + circuit->next = NULL; + + for (c = circuit_hd; c; c = c->next) + { + if (c == circuit) + { + Debugprintf("CHAT: Attempting to add Circuit when already on list"); + return circuit; + } + } + + sl_ins_hd(circuit, circuit_hd); + + return circuit; +} + +// Handle an incoming link. We should only get here if we think the station is a node. + +int rtloginl (ChatCIRCUIT *conn, char * call) +{ + LINK * link; + + if (node_find(call)) + { + Logprintf(LOG_CHAT, conn, '|', "Refusing link from %s to %s to prevent a loop", conn->Callsign, OurNode); + + nprintf(conn, "Refusing link from %s to %s to prevent a loop.\n", conn->Callsign, OurNode); + ChatFlush(conn); + Sleep(500); + conn->rtcflags = p_nil; + Disconnect(conn->BPQStream); + return FALSE; // Already linked. + } + + for (link = link_hd; link; link = link->next) + { + if (matchi(call, link->call)) + break; + } + + if (!link) + { + // We don't link with this system. Shouldn't happen, as we checked earlier + + nprintf(conn, "Node %s does not have %s defined as a node to link to - closing.\r", + OurNode, conn->Callsign); + ChatFlush(conn); + Sleep(500); + conn->rtcflags = p_nil; + Disconnect(conn->BPQStream); + return FALSE; + } + + if (link->flags & (p_linked | p_linkini)) + { + // Already Linked. Used to Disconnect, but that can cause sync errors + // Try closing old link and keeping new + + ChatCIRCUIT *c; + int len; + char Msg[80]; + + for (c = circuit_hd; c; c = c->next) + { + if (c->u.link == link) + { + len=sprintf_s(Msg, sizeof(Msg), "Chat Node %s Connect when Connected - Old Connection Closed", call); + ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); + + c->Active = FALSE; // So we don't try to clear circuit again + Disconnect(c->BPQStream); + link_drop(c); + RefreshMainWindow(); + break; + } + } + } + +// Accept the link request. + + circuit_new(conn, p_linked); + conn->u.link = link; + nputs(conn, "OK\r"); + link->flags = p_linked; + link->delay = 0; // Dont delay first restart + state_tell(conn, NULL); + conn->u.link->timePollSent = time(NULL); // Keepalive is a poll + nprintf(conn, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, conn->u.link->call, Verstring); + + NeedStatus = TRUE; + + return TRUE; +} + +// User connected to chat, or did chat command from BBS + +int rtloginu (ChatCIRCUIT *circuit, BOOL Local) +{ + USER *user; + +// Is this user already logged in to RT somewhere else? + + user = user_find(circuit->UserPointer->Call, NULL); + + if (user) + { + // if connected at this node, kill old connection and allow new login + + if (user->node == node_find(OurNode)) + { + nputs(circuit, "*** Already connected at this node - old session will be closed.\r"); + + if (user->circuit->BPQStream < 0) + { + CloseConsole(user->circuit->BPQStream); + } + else + { + Disconnect(user->circuit->BPQStream); + } + } + else + nputs(circuit, "*** Already connected at another node.\r"); + + return FALSE; + } + +// Create the user entry. + + circuit_new(circuit, p_user); + + user = user_join(circuit, circuit->UserPointer->Call, OurNode, OurAlias, Local); + circuit->u.user = user; + + if (strcmp(user->name, "?_name") == 0) + { + user->name = _strdup(circuit->UserPointer->Name); + } + upduser(user); + + ChatExpandAndSendMessage(circuit, ChatWelcomeMsg, LOG_CHAT); + text_tellu_Joined(user); + user_tell(user, id_join); + show_users(circuit); + user->lastsendtime = time(NULL); +// makelinks(); + + return TRUE; +} + +void logout(ChatCIRCUIT *circuit) +{ + USER *user; + CHATNODE *node; + + circuit->rtcflags = p_nil; + user = circuit->u.user; + + if (user) // May not have logged in if already conencted + { + node = user->node; + + user_tell(user, id_leave); + text_tellu(user, rtleave, NULL, o_all); + user_leave(user); + + // order changed so node_dec can check if a node that is about the be deleted has eny users + + if (node) + { + cn_dec(circuit, node); + node_dec(node); + } + + circuit->u.user = NULL; + } + + circuit_free(circuit); +} + +void show_users(ChatCIRCUIT *circuit) +{ + USER *user; + char * Alias; + char * Topic; + + int i = 0; + + // First count them + + for (user = user_hd; user; user = user->next) + { + i++; + } + + nprintf(circuit, "%d Station(s) connected:\r", i); + + for (user = user_hd; user; user = user->next) + { + if ((user->node == 0) || (user->node->alias == 0)) + Alias = "(Corrupt Alias)"; + else + Alias = user->node->alias; + + if ((user->topic == 0) || (user->topic->name == 0)) + Topic = "(Corrupt Topic)"; + else + Topic = user->topic->name; + +#ifndef LINBPQ + __try + { +#endif + if (circuit->u.user->rtflags & u_colour) // Use Colour + nprintf(circuit, "\x1b%c%-6.6s at %-9.9s %s, %s [%s] Idle for %d seconds\r", + user->Colour, user->call, Alias, user->name, user->qth, Topic, time(NULL) - user->lastrealmsgtime); + else + nprintf(circuit, "%-6.6s at %-9.9s %s, %s [%s] Idle for %d seconds\r", + user->call, Alias, user->name, user->qth, Topic, time(NULL) - user->lastrealmsgtime); +#ifndef LINBPQ + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + Debugprintf("MAILCHAT *** Program Error in show_users"); + CheckProgramErrors(); + } +#endif + } +} + + +static void show_nodes(ChatCIRCUIT *circuit) +{ + CHATNODE *node; + + nputs(circuit, "Known Nodes:\r"); + + for (node = node_hd; node; node = node->next) + { + if (node->refcnt) + if (node->Version) + nprintf(circuit, "%s:%s %s %u\r", node->alias, node->call, node->Version, node->refcnt); + else + nprintf(circuit, "%s:%s %s %u\r", node->alias, node->call, "Not Known", node->refcnt); + } +} + +// /P Command: List circuits and remote RT on them. + +#define xxx "\r " + +static void show_circuits(ChatCIRCUIT *conn, char Flag) +{ + ChatCIRCUIT *circuit; + CHATNODE *node; + LINK *link; + char line[1000]; + int len = 0; + CN *cn; + + int i = 0; + + // First count them + + for (node = node_hd; node; node = node->next) + { + i++; + } + + nprintf(conn, "%d Node(s)\r", i); + + if (Flag == 'c') + len = sprintf(line, "Here %-6.6s <-", OurNode); + else + len = sprintf(line, "Here %-6.6s <-", OurAlias); + + for (node = node_hd; node; node = node->next) if (node->refcnt) + { + if (Flag == 'c') + len += sprintf(&line[len], " %s", node->call); + else + len += sprintf(&line[len], " %s", node->alias); + if (len > 80) + { + nprintf(conn, "%s\r", line); + len = sprintf(line, " "); + } + } + + nprintf(conn, "%s\r", line); + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked) + { + if (Flag == 'c') + len = sprintf(line, "Nodes via %-6.6s(%d) -", circuit->u.link->call, circuit->refcnt); + else + len = sprintf(line, "Nodes via %-6.6s(%d) -", circuit->u.link->alias, circuit->refcnt); + +#ifndef LINBPQ + __try{ + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node && cn->node->alias) + { + __try + { + if (Flag == 'c') + len += sprintf(&line[len], " %s", cn->node->call); + else + len += sprintf(&line[len], " %s", cn->node->alias); + if (len > 80) + { + nprintf(conn, "%s\r", line); + len = sprintf(line, " "); + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + {len += sprintf(&line[len], " *PE* Corrupt Rec %x %x", cn, cn->node);} + } + else + len = sprintf(&line[len], " Corrupt Rec %x %x ", cn, cn->node); + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + {len += sprintf(&line[len], " *PE* Corrupt Rec %x %x ", cn, cn->node);} +#else + for (cn = circuit->hnode; cn; cn = cn->next) + { + if (cn->node && cn->node->alias) + { + if (Flag == 'c') + len += sprintf(&line[len], " %s", cn->node->call); + else + len += sprintf(&line[len], " %s", cn->node->alias); + if (len > 80) + { + nprintf(conn, "%s\r", line); + len = sprintf(line, " "); + } + } + else + len += sprintf(&line[len], " Corrupt Rec %p %p ", cn, cn->node); + } +#endif + nprintf(conn, "%s\r", line); + + } + else if (circuit->rtcflags & p_user) + nprintf(conn, "User %-6.6s\r", circuit->u.user->call); + else if (circuit->rtcflags & p_linkini) + { + if (circuit->u.link) + { if (Flag == 'c') + nprintf(conn, "Link %-6.6s (setup)\r", circuit->u.link->call); + else + nprintf(conn, "Link %-6.6s (setup)\r", circuit->u.link->alias); + + } + else + nprintf(conn, "Link ?? (setup)\r"); + } + } + + nprintf(conn, "Links Defined:\r"); + + for (link = link_hd; link; link = link->next) + { + if (link->flags & p_linked ) + if (link->supportsPolls) + nprintf(conn, " %-10.10s Open RTT %d\r", link->call, link->RTT); + else + nprintf(conn, " %-10.10s Open\r", link->call); + else if (link->flags & (p_linked | p_linkini)) + nprintf(conn, " %-10.10s Connecting\r", link->call); + else if (link->flags & p_linkfailed) + nprintf(conn, " %-10.10s Connect failed\r", link->call); + else + nprintf(conn, " %-10.10s Idle\r", link->call); + } +} + +// /T Command: List topics and users in them. + +static void show_topics(ChatCIRCUIT *conn) +{ + TOPIC *topic; + USER *user; + + nputs(conn, "Active Topics are:\r"); + + for (topic = topic_hd; topic; topic = topic->next) + { + nprintf(conn, "%s\r", topic->name); + + if (topic->refcnt) + { + nputs(conn, " "); + for (user = user_hd; user; user = user->next) + { + if (user->topic == topic) + nprintf(conn, " %s", user->call); + } + nputc(conn, '\r'); + } + } +} + +static void show_users_in_topic(ChatCIRCUIT *conn) +{ + TOPIC *topic; + USER *user; + + nputs(conn, "Users in Topic:\r"); + + topic = conn->u.user->topic; + { + if (topic->refcnt) + { + for (user = user_hd; user; user = user->next) + { + if (user->topic == topic) + nprintf(conn, "%s ", user->call); + } + nputc(conn, '\r'); + } + } +} + +// Do a user command. + +int rt_cmd(ChatCIRCUIT *circuit, char * Buffer) +{ + ChatCIRCUIT *c; + USER *user, *su; + char *f1, *f2; + + user = circuit->u.user; + +// user->lastsendtime = time(NULL); + + switch(tolower(Buffer[1])) + { + case 'a' : + user->rtflags ^= u_bells; + upduser(user); + nprintf(circuit, "Alert %s\r", (user->rtflags & u_bells) ? "Enabled" : "Disabled"); + return TRUE; + + case 'b' : return FALSE; + + case 'c' : + user->rtflags ^= u_colour; + upduser(user); + nprintf(circuit, "Colour Mode %s\r", (user->rtflags & u_colour) ? "Enabled" : "Disabled"); + return TRUE; + + case 'e' : + user->rtflags ^= u_echo; + upduser(user); + nprintf(circuit, "Echo %s\r", (user->rtflags & u_echo) ? "Enabled" : "Disabled"); + return TRUE; + + case 'f' : makelinks(); return TRUE; + + case 'h' : + case '?' : + { + char * Save; + char * MsgBytes = Save = ReadInfoFile("chathelp.txt"); + + if (MsgBytes) + { + int Length; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + ChatQueueMsg(circuit, MsgBytes, Length); + free(Save); + } + else + { + nputs(circuit, "Commands can be in upper or lower case.\r"); + nputs(circuit, "/U - Show Users.\r/N - Enter your Name.\r/Q - Enter your QTH.\r/T - Show Topics.\r"); + nputs(circuit, "/T Name - Join Topic or Create new Topic. Topic Names are not case sensitive\r/P - Show Ports and Links.\r"); + nprintf(circuit, "/A - Toggle Alert on user join - %s.\r", + (user->rtflags & u_bells) ? "Enabled" : "Disabled"); + nprintf(circuit, "/C - Toggle Colour Mode on or off (only works on Console or BPQTerm/TermTCP/QtTermTCP - %s.\r", + (user->rtflags & u_colour) ? "Enabled" : "Disabled"); + nputs(circuit, "/Codepage CPnnnn - Set Codepage to use if UTF-8 is disabled.\r"); + nprintf(circuit, "/E - Toggle Echo - %s .\r", + (user->rtflags & u_echo) ? "Enabled" : "Disabled"); + nprintf(circuit, "/Keepalive - Toggle sending Keepalive messages every 10 minutes - %s.\r", + (user->rtflags & u_keepalive) ? "Enabled" : "Disabled"); + nprintf(circuit, "/ShowNames - Toggle displaying name as well as call on each message - %s\r", + (user->rtflags & u_shownames) ? "Enabled" : "Disabled"); + nprintf(circuit, "/Auto - Toggle Automatic character set selection - %s.\r", + (user->rtflags & u_auto) ? "Enabled" : "Disabled"); + nprintf(circuit, "/UTF-8 - Character set Selection - %s.\r", + (user->rtflags & u_noUTF8) ? "8 Bit" : "UTF-8"); + nprintf(circuit, "/Time - Toggle displaying timestamp on each message - %s.\r", + (user->rtflags & u_showtime) ? "Enabled" : "Disabled"); + nputs(circuit, "/S CALL Text - Send Text to that station only.\r"); + nputs(circuit, "/F - Force all links to be made.\r/K - Show Known nodes.\r"); + nputs(circuit, "/B - Leave Chat and return to node.\r/QUIT - Leave Chat and disconnect from node.\r"); + nputs(circuit, "/History nn - Display chat messages received in last nn minutes.\r"); + } + } + return TRUE; + + case 'k' : show_nodes(circuit); return TRUE; + + case 'n' : + + f1 = &Buffer[2]; + + while ((*f1 != 0) && (*f1 == ' ')) + f1++; + + if (*f1 == 0) + { + nprintf(circuit, "Name is %s\r", user->name); + return TRUE; + } + + strnew(&user->name, f1); + nprintf(circuit, "Name set to %s\r", user->name); + upduser(user); + user_tell(user, id_user); + return TRUE; + + case 'p' : show_circuits(circuit, Buffer[3]); return TRUE; + + case 'q' : + + f1 = &Buffer[2]; + + while ((*f1 != 0) && (*f1 == ' ')) + f1++; + + if (*f1 == 0) + { + nprintf(circuit, "QTH is %s\r", user->qth); + return TRUE; + } + + strnew(&user->qth, f1); + + nprintf(circuit, "QTH set to %s\r", user->qth); + upduser(user); + user_tell(user, id_user); + return TRUE; + + case 's' : + strcat(Buffer, "\r"); + f1 = strlop(Buffer, ' '); // To. + if (!f1) break; + f2 = strlop(f1, ' '); // Text to send. + if (!f2) break; + _strupr(f1); + su = user_find(f1, NULL); + + if (!su) + { + nputs(circuit, "*** That user is not logged in.\r"); + return TRUE; + } + + // Send to the desired user only. + + if (su->circuit->rtcflags & p_user) + text_tellu(user, f2, f1, o_one); + else + text_xmit(user, su, f2); + + return TRUE; + + case 't' : + f1 = strlop(Buffer, ' '); + if (f1) + { + if (topic_chg(user, f1)) + { + nprintf(circuit, "Switched to Topic %s\r", user->topic->name); + show_users_in_topic(circuit); + + // Tell all link circuits about the change of topic. + + for (c = circuit_hd; c; c = c->next) + { + if (c->rtcflags & p_linked) + topic_xmit(user, c); + } + } + else + { + // Already in topic + + nprintf(circuit, "You were already in Topic %s\r", user->topic->name); + } + } + else + show_topics(circuit); + return TRUE; + + case 'u' : show_users(circuit); return TRUE; + + default : break; + } + + saywhat(circuit); + return TRUE; +} + +void makelinks(void) +{ + LINK *link; + ChatCIRCUIT *circuit; + + // Make the links. Called every 10 seconds + + // Make sure previous link has completed or failed + + if (RunningConnectScript) + { + // Make sure Connect Script isn't taking too long + + if (time(NULL) - RunningConnectScript < 30) + return; + + // Running too long - close it + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + // Find the link + + if (circuit->rtcflags & (p_linkini)) + { + link = circuit->u.link; + link->flags = p_linkfailed; + RunningConnectScript = 0; + link->scriptRunning = 0; // so it doesn't get reentered + Logprintf(LOG_CHAT, circuit, '|', "Connect to %s timed out", circuit->Callsign); + + Disconnect(circuit->BPQStream); + } + } + RunningConnectScript = 0; + } + + for (link = link_hd; link; link = link->next) + { + // Is this link already established? + + if (link->flags & (p_linked | p_linkini)) + continue; + + // Already linked through some other node? + // If so, making this link would create a loop. + + if (node_find(link->call)) + continue; + + // Fire up the process to handle this link. + + if (link->delay == 0) + { + link->flags = p_linkini; + link->delay = 12; // 2 mins + chat_link_out(link); + return; // One at a time + } + else + link->delay--; + } +} + +VOID node_close() +{ + // Close all Node-Node Links + + ChatCIRCUIT *circuit; + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & (p_linked | p_linkini | p_linkwait)) + Disconnect(circuit->BPQStream); + } +} + +// Send Keepalives to all connected nodes + +static void node_keepalive() +{ + ChatCIRCUIT *circuit; + + NeedStatus = TRUE; // Send Report to Monitor + + if (user_hd) // Any Users? + { + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (circuit->rtcflags & p_linked && circuit->u.link) + { + nprintf(circuit, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, circuit->u.link->call, Verstring); + circuit->u.link->timePollSent = time(NULL); // Also acts as poll + } + } + } + else + { + // No users. Close links + + node_close(); + } +} + +VOID ChatTimer() +{ + // Entered every 10 seconds + + int i = 0; + ChatCIRCUIT *c; + +#ifndef LINBPQ + int len; + CHATNODE *node; + TOPIC *topic; + char Msg[256]; +#endif + USER *user; + time_t NOW = time(NULL); + + GetSemaphore(&ChatSemaphore, 0); + + if (NeedStatus) + { + NeedStatus = FALSE; + SendChatLinkStatus(); + } + +#ifndef LINBPQ + + ClearDebugWindow(); + + WritetoDebugWindow("Chat Nodes\r\n", 12); + + for (node = node_hd; node; node = node->next) + { + len = sprintf_s(Msg, sizeof(Msg), "%s Version %s Count %d\r\n", + node->call, node->Version, node->refcnt); + WritetoDebugWindow(Msg, len); + + i++; + } + + SetDlgItemInt(hWnd, IDC_NODES, i, FALSE); + + WritetoDebugWindow("Chat Links\r\n", 12); + + i = 0; + for (c = circuit_hd; c; c = c->next) + { + if (c->rtcflags & p_linked) + { + char buff[1000]; + int ptr; + CT * ct; + ptr = sprintf_s(buff, sizeof(buff), "%s Topics: ", c->u.user->call); + + if (c->topic) + { + for (ct = c->topic; ct; ct = ct->next) + { + ptr+= sprintf_s(&buff[ptr], sizeof(buff) - ptr, "%s ", ct->topic->name); + } + } + WritetoDebugWindow(buff, ptr); + WritetoDebugWindow("\r\n", 2); + + i++; + } + } + + SetDlgItemInt(hWnd, IDC_LINKS, i, FALSE); + + WritetoDebugWindow("Chat Topics\r\n", 12); + + i = 0; + for (topic = topic_hd; topic; topic = topic->next) + { + len = sprintf_s(Msg, sizeof(Msg), "%s %d\r\n", topic->name, topic->refcnt); + WritetoDebugWindow(Msg, len); + i++; + } + + WritetoDebugWindow("Chat Users\r\n", 12); + + i = 0; + for (user = user_hd; user; user = user->next) + { + len = sprintf_s(Msg, sizeof(Msg), "%s Topic %s\r\n", user->call, + (user->topic) ? user->topic->name : "** Missing Topic **"); + WritetoDebugWindow(Msg, len); + i++; + + if (user->circuit && user->circuit->rtcflags & p_user) // Local User + { + time_t Idle = NOW - user->lastmsgtime; + + if (Idle > 7200) + { + nprintf(user->circuit, "*** Disconnected - Idle time exceeded\r"); + Sleep(1000); + + if (user->circuit->BPQStream < 0) + { + CloseConsole(user->circuit->BPQStream); + break; + } + else + { + Disconnect(user->circuit->BPQStream); + break; + } + } + + if ((user->rtflags & u_keepalive) && (NOW - user->lastsendtime) > 600) + { + nprintf(user->circuit, "Chat Keepalive\r"); + user->lastsendtime = NOW; + } + } + } + + SetDlgItemInt(hWnd, IDC_USERS, i, FALSE); + +#else + + for (user = user_hd; user; user = user->next) + { + if (user->circuit && user->circuit->rtcflags & p_user) // Local User + { + if ((NOW - user->lastmsgtime) > 7200) + { + nprintf(user->circuit, "*** Disconnected - Idle time exceeded\r"); + Sleep(1000); + + if (user->circuit->BPQStream < 0) + { + CloseConsole(user->circuit->BPQStream); + break; + } + else + { + Disconnect(user->circuit->BPQStream); + break; + } + } + + if ((user->rtflags & u_keepalive) && (NOW - user->lastsendtime) > 600) + { + nprintf(user->circuit, "Chat Keepalive\r"); + user->lastsendtime = NOW; + } + } + } + +#endif + + // if no message on a Node-Node link, send poll + + for (c = circuit_hd; c; c = c->next) + { + if (c->rtcflags & p_linked && c->u.link) + { + time_t Now = time(NULL); + LINK * Link = c->u.link; + + if (Now - Link->lastMsgReceived > 60) + { + // if we have a poll outstanding for ? 30 secs close link + // but check other end can handle polls + + if (Link->supportsPolls && Link->timePollSent && Now - Link->timePollSent > 30) + { + Logprintf(LOG_CHAT, c, '|', "%s No Poll Response for %d Secs - Dropping Link", + c->Callsign, Now - Link->timePollSent); + + Disconnect(c->BPQStream); + continue; + } + + Link->timePollSent = Now; + nprintf(c, "%c%c%s %s\r", FORMAT, id_poll, OurNode, Link->call); + } + } + } + + ChatTmr++; + + if (user_hd) // Any Users? + makelinks(); + + if (ChatTmr > 60) // 10 Mins + { + ChatTmr = 1; + node_keepalive(); + } + + FreeSemaphore(&ChatSemaphore); + + if (NeedINFO) + { + NeedINFO--; + + if (NeedINFO == 0) + { + // Send INFO to Chatmap + + char Msg[500]; + int len; + + NeedINFO = 360; // Send Every Hour + + if (Position[0]) + { + len = sprintf(Msg, "INFO %s|%s|%d|\r", Position, PopupText, PopupMode); + + if (len < 256) + Send_MON_Datagram(Msg, len); + } + } + } +} + +VOID FreeChatMemory() +{ + removelinks(); + removeknown(); +} + +// Find a call in the known node list. + +KNOWNNODE *knownnode_find(char *call) +{ + KNOWNNODE *node; + + for (node = known_hd; node; node = node->next) + { + if (matchi(node->call, call)) + break; + } + + return node; +} + +// Add a known node. + +static KNOWNNODE *knownnode_add(char *call) +{ + KNOWNNODE *node; + + node = knownnode_find(call); + + if (!node) + { + node = zalloc(sizeof(KNOWNNODE)); + sl_ins_hd(node, known_hd); + node->call = _strdup(call); + } + + node->LastHeard = time(NULL); + return node; +} + +static char UIDEST[10] = "DUMMY"; +static char AXDEST[7]; +static char ChatMYCALL[7]; + +#pragma pack(1) + + +typedef struct _MESSAGEX +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGE * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + +// MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + UCHAR DATA[256]; + +}MESSAGEX, *PMESSAGEX; + +#pragma pack() + +SOCKET ChatReportSocket = 0; + + +VOID SetupChat() +{ + u_long param=1; + BOOL bcopt=TRUE; + + ConvToAX25(OurNode, ChatMYCALL); + ConvToAX25(UIDEST, AXDEST); + + sprintf(Verstring, "%d.%d.%d.%d", Ver[0], Ver[1], Ver[2], Ver[3]); + + LoadKnown(); + + ChatReportSocket = socket(AF_INET,SOCK_DGRAM,0); + + if (ChatReportSocket == INVALID_SOCKET) + { + Debugprintf("Failed to create Chat Reporting socket"); + ChatReportSocket = 0; + return; + } + + ioctlsocket (ChatReportSocket, FIONBIO, ¶m); + setsockopt (ChatReportSocket, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt,4); +} + + +VOID Send_MON_Datagram(UCHAR * Msg, DWORD Len) +{ + MESSAGEX AXMSG; + PMESSAGEX AXPTR = &AXMSG; + + if (Len > 256) + { + Debugprintf("Send_MON_Datagram Error Msg = %s Len = %d", Msg, Len); + return; + } + +// ConvToAX25("GM4OAS-5", ChatMYCALL); + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, AXDEST, 7); + memcpy(AXPTR->ORIGIN, ChatMYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->DATA, Msg, Len); + + SendChatReport(ChatReportSocket, (char *)&AXMSG.DEST, Len + 16); + + return; + +} + +VOID SendChatLinkStatus() +{ + char Msg[256] = {0}; + LINK * link; + int len = 0; + ChatCIRCUIT *circuit; + + if (ChatApplNum == 0) + return; + +// if (AXIPPort == 0) +// return; + + if (ChatMYCALL[0] == 0) + return; + + for (link = link_hd; link; link = link->next) + { + if (link->flags & p_linked) + { + // Verify connection + + for (circuit = circuit_hd; circuit; circuit = circuit->next) + { + if (strcmp(circuit->Callsign, link->alias) == 0) + { + if (circuit->Active == 0) + { + // BPQ Session is dead - Simulate a Disconnect + + circuit->Active = TRUE; // So disconnect will work + Disconnected(circuit->BPQStream); + NeedStatus = TRUE; // Reenter + return; // Link Chain has changed + } + break; + } + } + + if (circuit == 0) + { + // No BPQ Session - is the only answer to restart the node? + + // Logprintf(LOG_DEBUGx, NULL, '!', "Stuck Chat Sesion Detected"); + // Logprintf(LOG_DEBUGx, NULL, '!', "Chat is a mess - forcing a restart"); + // ProgramErrors = 26; + // CheckProgramErrors(); + } + } + + len += sprintf(&Msg[len], "%s %c ", link->call, '0' + link->flags); + + if (len > 240) + break; + } + Msg[len++] = '\r'; + + Send_MON_Datagram(Msg, len); +} + +VOID ClearChatLinkStatus() +{ + LINK * link; + + for (link = link_hd; link; link = link->next) + { + link->flags = 0; + } +} + +BOOL ProcessChatConnectScript(ChatCIRCUIT * conn, char * Buffer, int len) +{ + LINK * link = conn->u.link; + char ** Scripts; + + ChatWriteLogLine(conn, '<', Buffer, len-1, LOG_CHAT); + + Buffer[len] = 0; + _strupr(Buffer); + + Scripts = link->ConnectScript; + + if (strstr(Buffer, "BUSY") || strstr(Buffer, "FAILURE") || + (strstr(Buffer, "DOWNLINK") && strstr(Buffer, "ATTEMPTING") == 0) || + strstr(Buffer, "SORRY") || strstr(Buffer, "INVALID") || strstr(Buffer, "RETRIED") || + strstr(Buffer, "NO CONNECTION TO") || strstr(Buffer, "ERROR - ") || + strstr(Buffer, "UNABLE TO CONNECT") || strstr(Buffer, "DISCONNECTED") || + strstr(Buffer, "FAILED TO CONNECT") || strstr(Buffer, "REJECTED")) + { + // Connect Failed + + link->flags = p_linkfailed; + RunningConnectScript = 0; + link->scriptRunning = 0; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + // The pointer is only updated when we get the connect, so we can tell when the last line is acked + // The first entry is always from Connected event, so don't have to worry about testing entry -1 below + + + if (link->RTLSent) + { + RunningConnectScript = 0; + link->scriptRunning = 0; + link->RTLSent = 0; + + if (memcmp(Buffer, "OK", 2) == 0) + { + // Reply to *RTL + + // Make sure node isn't known. There is a window here that could cause a loop + + if (node_find(conn->u.link->call)) + { + Logprintf(LOG_CHAT, conn, '|', "Dropping link with %s to prevent a loop", conn->Callsign); + Disconnect(conn->BPQStream); + return FALSE; + } + + conn->u.link->flags = p_linked; + conn->rtcflags = p_linked; + state_tell(conn, conn->FBBReplyChars); + NeedStatus = TRUE; + + return TRUE; + } + + // Some other response to *RTL - disconnect + + Logprintf(LOG_CHAT, conn, '|', "Unexpected Response %s to *RTL - Dropping link", Buffer); + Disconnect(conn->BPQStream); + return FALSE; + + } + + if (strstr(Buffer, " CONNECTED") || strstr(Buffer, "PACLEN") || strstr(Buffer, "IDLETIME") || + strstr(Buffer, "OK") || strstr(Buffer, "###LINK MADE") || strstr(Buffer, "VIRTUAL CIRCUIT ESTABLISHED")) + { + char * Cmd; + +LoopBack: + + Cmd = Scripts[++link->ScriptIndex]; + + // Only Check until script is finished + + if (Cmd == 0 || link->ScriptIndex >= link->Lines) + { + link->MoreLines = FALSE; + return TRUE; + } + + if (Cmd && (strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#')) + goto LoopBack; // Blank line + + // Replace \ with # so can send commands starting with # + + if (Cmd[0] == '\\') + { + Cmd[0] = '#'; + nprintf(conn, "%s\r", Cmd); + Cmd[0] = '\\'; // Put \ back in script + } + else + nprintf(conn, "%s\r", Cmd); + + return TRUE; + } + + if (memcmp(Buffer, "[BPQCHATSERVER-", 15) == 0) + { + char * ptr = strchr(Buffer, ']'); + if (ptr) + { + *ptr = 0; + strcpy(conn->FBBReplyChars, &Buffer[15]); + } + else + conn->FBBReplyChars[0] = 0; + + // Connected - Send *RTL + + nputs(conn, "*RTL\r"); // Log in to the remote RT system. + + conn->u.link->timePollSent = time(NULL); // Keepalive is a poll + nprintf(conn, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, conn->u.link->call, Verstring); + link->RTLSent = 1; + conn->u.link->lastMsgSent = time(NULL); + + return TRUE; + } + +// Anthing else could be ctext. etc. Ignore + + return TRUE; +} + + + +#ifdef LINBPQ + +// LINCHAT specific code + +extern struct SEM OutputSEM; + +static config_t cfg; +static config_setting_t * group; + +extern char pgm[256]; + +char ChatSYSOPCall[50] = ""; + +VOID ChatSendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user); + + +int ChatConnected(int Stream) +{ + int n; + ChatCIRCUIT * conn; + struct UserInfo * user = NULL; + char callsign[10]; + int port, paclen, maxframe, l4window; + char ConnectedMsg[] = "*** CONNECTED "; + char Msg[100]; + LINK *link; + KNOWNNODE *node; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn = &ChatConnections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active) + { + // Probably an outgoing connect + + if (conn->rtcflags == p_linkini) + { + conn->paclen = chatPaclen; + + // Run first line of connect script + + ProcessChatConnectScript(conn, ConnectedMsg, 15); +// nprintf(conn, "c %s\r", conn->u.link->call); + } + return 0; + } + + memset(conn, 0, sizeof(ChatCIRCUIT)); // Clear everything + conn->Active = TRUE; + conn->BPQStream = Stream; + + conn->Secure_Session = GetConnectionInfo(Stream, callsign, + &port, &conn->SessType, &paclen, &maxframe, &l4window); + + if (paclen == 0) + paclen = 256; + + if (paclen > chatPaclen) + paclen = chatPaclen; + + conn->paclen = paclen; + + strlop(callsign, ' '); // Remove trailing spaces + + memcpy(conn->Callsign, callsign, 10); + + strlop(callsign, '-'); // Remove any SSID + + user = zalloc(sizeof(struct UserInfo)); + + strcpy(user->Call, callsign); + + conn->UserPointer = user; + + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call); + + // Send SID and Prompt + + ChatWriteLogLine(conn, '|',Msg, n, LOG_CHAT); + conn->Flags |= CHATMODE; + + nprintf(conn, ChatSID, Ver[0], Ver[1], Ver[2], Ver[3]); + + // See if from a defined node + + for (link = link_hd; link; link = link->next) + { + if (matchi(conn->Callsign, link->call)) + { + conn->rtcflags = p_linkwait; + return 0; // Wait for *RTL + } + } + + // See if from a previously known node + + node = knownnode_find(conn->Callsign); + + if (node) + { + // A node is trying to link, but we don't have it defined - close + + Logprintf(LOG_CHAT, conn, '!', "Node %s connected, but is not defined as a Node - closing", + conn->Callsign); + + nprintf(conn, "Node %s does not have %s defined as a node to link to - closing.\r", + OurNode, conn->Callsign); + + ChatFlush(conn); + + Sleep(500); + conn->rtcflags = p_nil; + + Disconnect(conn->BPQStream); + + return 0; + } + + if (user->Name[0] == 0) + { + char * Name = lookupuser(user->Call); + + if (Name) + { + if (strlen(Name) > 17) + Name[17] = 0; + + strcpy(user->Name, Name); + free(Name); + } + else + { + conn->Flags |= GETTINGUSER; + nputs(conn, NewUserPrompt); + return TRUE; + } + } + + ChatSendWelcomeMsg(Stream, conn, user); + RefreshMainWindow(); + ChatFlush(conn); + + return 0; + } + } + + return 0; +} + +int ChatDisconnected (ChatCIRCUIT * conn) +{ + struct UserInfo * user = NULL; + int Stream = conn->BPQStream; + char Msg[255]; + int len; + + if (conn->Active == FALSE) + return 0; + + ChatClearQueue(conn); + + conn->Active = FALSE; + + if (conn->Flags & CHATMODE) + { + if (conn->Flags & CHATLINK && conn->u.link) + { + // if running connect script, clear script active + + if (conn->u.link->flags & p_linkini) + { + RunningConnectScript = 0; + conn->u.link->scriptRunning = 0; + } + + len=sprintf_s(Msg, sizeof(Msg), "Chat Node %s Disconnected", conn->u.link->call); + ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); + link_drop(conn); + + } + else + { + len=sprintf_s(Msg, sizeof(Msg), "Chat User %s Disconnected", conn->Callsign); + ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT); + + logout(conn); + + } + + conn->Flags = 0; + conn->u.link = NULL; + conn->UserPointer = NULL; + return 0; + } + + return 0; +} + +int ChatDoReceivedData(ChatCIRCUIT * conn) +{ + int count, InputLen; + UINT MsgLen; + int Stream = conn->BPQStream; + struct UserInfo * user; + char * ptr, * ptr2; + char Buffer[10000]; + + + // May have several messages per packet, or message split over packets + + if (conn->InputLen + 1000 > 10000) // Shouldnt have lines longer than this in text mode + conn->InputLen = 0; // discard + + GetMsg(Stream, &conn->InputBuffer[conn->InputLen], &InputLen, &count); + + if (InputLen == 0) return 0; + + conn->Watchdog = 900; // 15 Minutes + conn->InputLen += InputLen; + +loop: + + if (conn->InputLen == 1 && conn->InputBuffer[0] == 0) // Single Null + { + conn->InputLen = 0; + + if (conn->u.user->circuit && conn->u.user->circuit->rtcflags & p_user) // Local User + conn->u.user->lastmsgtime = time(NULL); + + return 0; + } + + ptr = memchr(conn->InputBuffer, '\r', conn->InputLen); + + if (ptr) // CR in buffer + { + user = conn->UserPointer; + + ptr2 = &conn->InputBuffer[conn->InputLen]; + + if (++ptr == ptr2) + { + // Usual Case - single meg in buffer + + if (conn->rtcflags == p_linkini) // Chat Connect + ProcessChatConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessChatLine(conn, user, conn->InputBuffer, conn->InputLen); + conn->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = conn->InputLen - (int)(ptr2-ptr); + + memcpy(Buffer, conn->InputBuffer, MsgLen); + + if (conn->rtcflags == p_linkini) + ProcessChatConnectScript(conn, Buffer, MsgLen); + else + ProcessChatLine(conn, user, Buffer, MsgLen); + + if (*ptr == 0 || *ptr == '\n') + { + /// CR LF or CR Null + + ptr++; + conn->InputLen--; + } + + memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); + conn->InputLen -= MsgLen; + + goto loop; + + } + } + return 0; +} + + +int ChatPollStreams() +{ + int state,change; + ChatCIRCUIT * conn; + int n; + struct UserInfo * user = NULL; + char ConnectedMsg[] = "*** CONNECTED "; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn = &ChatConnections[n]; + + SessionState(conn->BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) // Connected + { + GetSemaphore(&ConSemaphore, 0); + ChatConnected(conn->BPQStream); + FreeSemaphore(&ConSemaphore); + } + else + { + GetSemaphore(&ConSemaphore, 0); + ChatDisconnected(conn); + FreeSemaphore(&ConSemaphore); + } + } + + ChatDoReceivedData(conn); + } + + return 0; +} + + +BOOL GetChatConfig(char * ConfigName) +{ + config_init(&cfg); + + /* Read the file. If there is an error, report it and exit. */ + + if(! config_read_file(&cfg, ConfigName)) + { + fprintf(stderr, "%d - %s\n", + config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + return(EXIT_FAILURE); + } + + group = config_lookup (&cfg, "Chat"); + + if (group == NULL) + return EXIT_FAILURE; + + ChatApplNum = GetIntValue(group, "ApplNum"); + MaxChatStreams = GetIntValue(group, "MaxStreams"); + reportChatEvents = GetIntValue(group, "reportChatEvents"); + chatPaclen = GetIntValue(group, "chatPaclen"); + GetStringValue(group, "OtherChatNodes", OtherNodesList, 1000); + GetStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg, 1000); + GetStringValue(group, "MapPosition", Position, 81); + GetStringValue(group, "MapPopup", PopupText, 260); + PopupMode = GetIntValue(group, "PopupMode"); + + if (chatPaclen == 0) + chatPaclen = 236; + + if (chatPaclen < 60) + chatPaclen = 60; + + + return EXIT_SUCCESS; +} + +VOID SaveChatConfigFile(char * ConfigName) +{ + config_setting_t *root, *group; + + // Get rid of old config before saving + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "Chat", CONFIG_TYPE_GROUP); + + SaveIntValue(group, "ApplNum", ChatApplNum); + SaveIntValue(group, "MaxStreams", MaxChatStreams); + SaveIntValue(group, "reportChatEvents", reportChatEvents); + SaveIntValue(group, "chatPaclen", chatPaclen); + SaveStringValue(group, "OtherChatNodes", OtherNodesList); + SaveStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg); + + SaveStringValue(group, "MapPosition", Position); + SaveStringValue(group, "MapPopup", PopupText); + SaveIntValue(group, "PopupMode", PopupMode); + + if(! config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + config_destroy(&cfg); +} + +BOOL ChatInit() +{ + char * ptr1 = GetApplCall(ChatApplNum); + char * ptr2; + char * Context; + int i; + ChatCIRCUIT * conn; + + + if (*ptr1 < 0x21) + { + printf("No APPLCALL for Chat APPL\n"); + return FALSE; + } + + memcpy(OurNode, ptr1, 10); + strlop(OurNode, ' '); + + ptr1 = GetApplAlias(ChatApplNum); + memcpy(OurAlias, ptr1,10); + strlop(OurAlias, ' '); + + if (ChatSYSOPCall[0] == 0) + { + strcpy(ChatSYSOPCall, OurNode); + strlop(ChatSYSOPCall, '-'); + } + + sprintf(ChatSignoffMsg, "73 de %s\r", ChatSYSOPCall); + + if (ChatWelcomeMsg[0] == 0) + sprintf(ChatWelcomeMsg, "%s's Chat Server.$WType /h for command summary.$WBringing up links to other nodes.$W" + "This may take a minute or two.$WThe /p command shows what nodes are linked.$W", ChatSYSOPCall); + + ChatApplMask = 1<<(ChatApplNum-1); + + // Set up other nodes list. rtlink messes with the string so pass copy + + // On first run config will have spaces not newlines + + if (strchr(OtherNodesList, '\r')) // Has connect script entries + { + ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), "\r\n", &Context); + + while (ptr1 && ptr1[0]) + { + rtlink(ptr1); + ptr1 = strtok_s(NULL, "\r\n", &Context); + } + } + else + { + ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), " ,\r", &Context); + + while (ptr1) + { + rtlink(ptr1); + ptr1 = strtok_s(NULL, " ,\r", &Context); + } + } + + free(ptr2); + + SetupChat(); + + // Allocate Streams + + strcpy(pgm, "CHAT"); + + for (i = 0; i < MaxChatStreams; i++) + { + conn = &ChatConnections[i]; + conn->BPQStream = FindFreeStream(); + + if (conn->BPQStream == 255) break; + + NumberofChatStreams++; + + SetAppl(conn->BPQStream, 3, ChatApplMask); + Disconnect(conn->BPQStream); + } + + strcpy(pgm, "LINBPQ"); + + return TRUE; +} + +#endif + +void ChatFlush(ChatCIRCUIT * conn) +{ + int tosend, len, sent; + + // Try to send data to user. May be stopped by user paging or node flow control + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // BOOL Paging; // Set if user wants paging + // int LinesSent; // Count when paging + // int PageLen; // Lines per page + + + tosend = conn->OutputQueueLength - conn->OutputGetPointer; + + sent = 0; + + while (tosend > 0) + { + if (TXCount(conn->BPQStream) > 4) + return; // Busy + + if (tosend <= conn->paclen) + len = tosend; + else + len=conn->paclen; + + GetSemaphore(&OutputSEM, 0); + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + + conn->OutputGetPointer += len; + + FreeSemaphore(&OutputSEM); + + tosend -= len; + sent++; + + if (sent > 4) + return; + } + + // All Sent. Free buffers and reset pointers + + ChatClearQueue(conn); +} + +VOID ChatClearQueue(ChatCIRCUIT * conn) +{ + GetSemaphore(&OutputSEM, 0); + + conn->OutputGetPointer = 0; + conn->OutputQueueLength = 0; + + FreeSemaphore(&OutputSEM); +} + +#ifdef LINBPQ +void ChatTrytoSend() +{ + // call Flush on any connected streams with queued data + + ChatCIRCUIT * conn; + + int n; + + for (n = 0; n < NumberofChatStreams; n++) + { + conn = &ChatConnections[n]; + + if (conn->Active == TRUE) + ChatFlush(conn); + } +} + +VOID CloseChat() +{ + int BPQStream, n; + + for (n = 0; n < NumberofChatStreams; n++) + { + BPQStream = ChatConnections[n].BPQStream; + + if (BPQStream) + { + SetAppl(BPQStream, 0, 0); + Disconnect(BPQStream); + DeallocateStream(BPQStream); + } + } + + ClearChatLinkStatus(); + SendChatLinkStatus(); + Sleep(1000); // A bit of time for links to close + SendChatLinkStatus(); // Send again to reduce chance of being missed + FreeChatMemory(); +} + +VOID SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen) +{ + unsigned short int crc = compute_crc(buff, txlen); + + crc ^= 0xffff; + + buff[txlen++] = (crc&0xff); + buff[txlen++] = (crc>>8); + + sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest)); + +} + +#endif + + +#ifndef WIN32 +#define INVALID_HANDLE_VALUE (void *)-1 +#endif + +static FILE * LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + +static time_t LastLogTime[4] = {0, 0, 0, 0}; + +static char FilesNames[4][100] = {"", "", "", ""}; + +static char * Logs[4] = {"BBS", "CHAT", "TCP", "DEBUG"}; + + +BOOL ChatOpenLogfile(int Flags) +{ + UCHAR FN[MAX_PATH]; + time_t LT; + struct tm * tm; + + LT = time(NULL); + tm = gmtime(<); + + sprintf(FN,"%s/logs/log_%02d%02d%02d_%s.txt", GetLogDirectory(), tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]); + + LogHandle[Flags] = fopen(FN, "ab"); + +#ifndef WIN32 + + if (strcmp(FN, &FilesNames[Flags][0])) + { + UCHAR SYMLINK[MAX_PATH]; + + sprintf(SYMLINK,"%s/logLatest_%s.txt", GetBPQDirectory(), Logs[Flags]); + unlink(SYMLINK); + strcpy(&FilesNames[Flags][0], FN); + symlink(FN, SYMLINK); + } + +#endif + + return (LogHandle[Flags] != NULL); +} + +void ChatWriteLogLine(ChatCIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags) +{ + char CRLF[2] = {0x0d,0x0a}; + struct tm * tm; + char Stamp[20]; + time_t T; + +#ifndef LINBPQ + + if (hMonitor) + { + if (Flags == LOG_CHAT) + { + WritetoMonitorWindow((char *)&Flag, 1); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + WritetoMonitorWindow(call, 10); + } + else + WritetoMonitorWindow(" ", 10); + + WritetoMonitorWindow(Msg, MsgLen); + if (Msg[MsgLen-1] != '\r') + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_DEBUGx) + { + WritetoMonitorWindow((char *)&Flag, 1); + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + + } + +#endif + + if (Flags == LOG_CHAT && !LogCHAT) + return; + + if (LogHandle[Flags] == INVALID_HANDLE_VALUE) ChatOpenLogfile(Flags); + + if (LogHandle[Flags] == INVALID_HANDLE_VALUE) return; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Stamp,"%02d%02d%02d %02d:%02d:%02d %c", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, Flag); + + fwrite(Stamp, 1, strlen(Stamp), LogHandle[Flags]); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + fwrite(call, 1, 10, LogHandle[Flags]); + } + else + fwrite(" ", 1, 10, LogHandle[Flags]); + + fwrite(Msg, 1, MsgLen, LogHandle[Flags]); + + if (Flags == LOG_CHAT && Msg[MsgLen-1] == '\r') + fwrite(&CRLF[1], 1, 1, LogHandle[Flags]); + else + fwrite(CRLF, 1, 2, LogHandle[Flags]); + + fclose(LogHandle[Flags]); + LogHandle[Flags] = INVALID_HANDLE_VALUE; +} + + diff --git a/.svn/pristine/0d/0dfe91be8ae70b991986eaaa9e579a14e878d39f.svn-base b/.svn/pristine/0d/0dfe91be8ae70b991986eaaa9e579a14e878d39f.svn-base new file mode 100644 index 0000000..b85b576 --- /dev/null +++ b/.svn/pristine/0d/0dfe91be8ae70b991986eaaa9e579a14e878d39f.svn-base @@ -0,0 +1,1652 @@ +#ifndef WINVER // Allow use of features specific to Windows XP or later. +#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. +#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE. +#endif + + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE +#define _WINSOCK_DEPRECATED_NO_WARNINGS + +#define LIBCONFIG_STATIC +#include + +#ifndef WIN32 +#include +#endif + +#include "compatbits.h" + +#ifndef LINBPQ +#include "bpq32.h" +#include "BPQMailrc.h" +#include "dbghelp.h" +#else +#include "cheaders.h" +#endif + +#include "asmstrucs.h" + +#define MaxBPQPortNo 63 // Port 64 reserved for BBS Mon +#define MAXBPQPORTS 63 + +#define NEWROUTING + +extern int FOURCHARCONT; + +// Standard __except handler for try/except + +VOID CheckProgramErrors(); +VOID WriteMiniDump(); + +extern int ProgramErrors; + +extern struct _EXCEPTION_POINTERS exinfox; + +#ifdef WIN32 +void Dump_Process_State(struct _EXCEPTION_POINTERS * exinfo, char * Msg); + +#define My__except_Routine(Message) \ +__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ + CheckProgramErrors();\ + WriteMiniDump();\ +} + + +/* +#define My__except_Routine(Message) \ +__except(memcpy(&exinfox, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Dump_Process_State(&exinfox, Message);\ + CheckProgramErrors();\ +} + +#define My__except_RoutineWithDisconnect(Message) \ +__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ + FreeSemaphore(&ChatSemaphore);\ + if (conn->BPQStream < 0)\ + CloseConsole(conn->BPQStream);\ + else\ + Disconnect(conn->BPQStream);\ +} +*/ +#define My_except_RoutineWithDiscBBS(Message) \ +__except(memcpy(&exinfo, GetExceptionInformation(), sizeof(struct _EXCEPTION_POINTERS)), EXCEPTION_EXECUTE_HANDLER)\ +{\ + Debugprintf("MAILCHAT *** Program Error %x at %x in %s EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x",\ + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, Message,\ + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx,\ + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi);\ + if (conn->BPQStream < 0)\ + CloseConsole(conn->BPQStream);\ + else\ + Disconnect(conn->BPQStream);\ + CheckProgramErrors();\ +} +#endif +#define MAXUSERNAMELEN 6 + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_CONNECT WM_USER + 2 +#define WSA_DATA WM_USER + 3 +#define NNTP_ACCEPT WM_USER + 4 +#define NNTP_DATA WM_USER + 5 + +#ifdef _DEBUG + +VOID * _malloc_dbg_trace(int len, int type, char * file, int line); + +#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define realloc(p, s) _realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define _recalloc(p, c, s) _recalloc_dbg(p, c, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define _expand(p, s) _expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define free(p) _free_dbg(p, _NORMAL_BLOCK) +#define _strdup(s) _strdup_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) + + +#define zalloc(s) _zalloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) +#else +#define zalloc(s) _zalloc(s) +#endif + +#ifdef LINBPQ + +#undef zalloc +#define zalloc _zalloc + +#endif + +VOID * _zalloc_dbg(size_t len, int type, char * file, int line); + +#define LOG_BBS 0 +#define LOG_CHAT 1 +#define LOG_TCP 2 +#define LOG_DEBUG_X 3 + + +//Chat Duplicate suppression Code + +#define MAXDUPS 10 // Number to keep +#define DUPSECONDS 5 // TIme to Keep + +struct DUPINFO +{ + time_t DupTime; + char DupUser[10]; + char DupText[100]; +}; + + +struct UserRec +{ + char * Callsign; + char * UserName; + char * Password; +}; + + + +typedef struct ConnectionInfo_S +{ + struct ConnectionInfo_S *next; + PROC *proc; + UCHAR RadioOnlyMode; // T or R flag for Radio Only mode. + + int Number; // Number of record - for Connections display + BOOL Active; + int BPQStream; + int paclen; + UCHAR Callsign[11]; // Station call including SSID + BOOL GotHeader; + UCHAR InputMode; // Line by Line or Binary or YAPP + + UCHAR * InputBuffer; + int InputBufferLen; + int InputLen; // Data we have already = Offset of end of an incomplete packet; + + struct UserInfo * UserPointer; + int Retries; + int LoginState; // 1 = user ok, 2 = password ok + int Flags; + + // Data to the user is kept in a malloc'd buffer. This can be appended to, + // and data sucked out under both terminal and system flow control. PACLEN is + // enfored when sending to node. + + UCHAR * OutputQueue; // Messages to user + int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + int OutputGetPointer; // Next byte to send. When Getpointer = Queue Length all is sent - free the buffer and start again. + + int CloseAfterFlush; // Close session when all sent. Set to 100ms intervals to wait. + + int ErrorCount; // Invalid Command count + BOOL Paging; // Set if user wants paging + int LinesSent; // Count when paging + int PageLen; // Lines per page + + UCHAR * MailBuffer; // Mail Message being received + UCHAR * CopyBuffer; // Mail Message being forwarded + int MailBufferSize; // Total Malloc'ed size. Actual size in in Msg Struct + + long lastmsg; // Last Listed. Stored here, updated in user record only on clean close + BOOL sysop; // Set if user is authenticated as a sysop + BOOL Secure_Session; // Set if Local Terminal, or Telnet connect with SYSOP status + UINT BBSFlags; // Set if defined as a bbs and SID received + struct MsgInfo * TempMsg; // Header while message is being received + struct MsgInfo * FwdMsg; // Header while message is being forwarded + + char ** To; // May be several Recipients + int ToCount; + + int BBSNumber; // The BBS number (offset into bitlist of BBSes to forward a message to + int NextMessagetoForward; // Next index to check in forward cycle + BOOL BPQBBS; // Set if SID indicates other end is BPQ + char MSGTYPES[20]; // Any MSGTYPEFLAGS + BOOL SendT; // Send T messages + BOOL SendP; // Send P messages + BOOL SendB; // Send Bulls + BOOL SendWL2KFW; // send ;FW: + int MaxBLen; // Max Size for this session + int MaxPLen; // Max Size for this session + int MaxTLen; // Max Size for this session + BOOL DoReverse; // Request Reverse Forward + char LastForwardType; // Last type of messages forwarded + struct FBBHeaderLine * FBBHeaders; // The Headers from an FFB forward block + char FBBReplyChars[36]; //The +-=!nnnn chars for the 5 proposals + int FBBReplyIndex; // current Reply Pointer + int FBBIndex; // current propopsal number + int RestartFrom; // Restart position + BOOL NeedRestartHeader; // Set if waiting for 6 byte restart header + BOOL DontSaveRestartData; // Set if corrupt data received + BOOL FBBMsgsSent; // Messages need to be maked as complete when next command received + UCHAR FBBChecksum; // Header Checksum + BOOL OpenBCM; // OpenBCM mode (escape -xFF chars) + BOOL InTelnetExcape; // Last Char was 0xff + BOOL LocalMsg; // Set if current Send command is for a local user + BOOL NewUser; // Set if first time user has accessed BBS + BOOL Paclink; // Set if receiving messages from Paclink + BOOL RMSExpress; // Set if receiving messages from RMS Express + BOOL WL2K; // Set if communicating with a CMS + BOOL PAT; // Set if communicating with PAT + char ** PacLinkCalls; // Calls we are getting messages for + BOOL SkipPrompt; // Set if a remote node sends a > at the end of his CTEXT + BOOL SkipConn; // Node sends "connected" in its CTEXT + int Watchdog; // Hung Circuit Detect. + int SessType; // BPQ32 sesstype bits + +#define Sess_L2LINK 1 +#define Sess_SESSION 2 +#define Sess_UPLINK 4 +#define Sess_DOWNLINK 8 +#define Sess_BPQHOST 0x20 +#define Sess_PACTOR 0x40 + + HANDLE DebugHandle; // File Handle for session-based debugging + + char ARQFilename[256]; // Filename from ARQ:FILE:: Header + int ARQClearCount; // To make sure queues are flushed when sending + + int SIDResponseTimer; // Used to detect incomplete handshake + + char PQChallenge[20]; // Secure User logon challange + char SecureMsg[20]; // CMS Secure Signon Response + int MCastListenTime; // Time to run session for + + int YAPPLen; // Bytes sent/received of YAPP Message + long YAPPDate; // Date for received file - if set enables YAPPC + + int SyncCompressedLen; + int SyncXMLLen; + int SyncMsgLen; + char * SyncHost; // Saved so can send "request sync" + int SyncPort; + UCHAR * SyncMessage; // Compressed SYNC message to send + + // These are used to detect CRLF split over a packet boundary + int usingCR; // Session is (normally) using CR as terminator + int lastLineEnd; // Terminator for current line + + struct ConnectionInfo_S * SysopChatStream; // Stream sysop is chatting to + +} ConnectionInfo, CIRCUIT; + +// Flags Equates + +#define GETTINGUSER 1 +#define GETTINGBBS 2 +#define CHATMODE 4 +#define GETTINGTITLE 8 +#define GETTINGMESSAGE 16 +#define CHATLINK 32 // Link to another Chat Node +#define SENDTITLE 64 +#define SENDBODY 128 +#define WAITPROMPT 256 // Waiting for prompt after message +#define PROPOSINGSYNCMSG 512 // Sent proposal to SYNC, waiting response +#define SENDINGSYNCMSG 1024 // Sent message to SYNC, waiting response +#define REQUESTINGSYNC 2048 +#define GETTINGSYNCMESSAGE 4096 // Receiving body of a SYNC message + +// BBSFlags Equates + +#define BBS 1 +#define FBBForwarding 2 +#define FBBCompressed 4 +#define FBBB1Mode 8 +#define FBBB2Mode 16 +#define RunningConnectScript 32 +#define MBLFORWARDING 64 // MBL Style Frwarding- waiting for OK/NO or Prompt following message +#define TEXTFORWARDING 128 // Plain Text forwarding +#define OUTWARDCONNECT 256 // We connected to them +#define FLARQMODE 512 // Message from FLARQ +#define FLARQMAIL 1024 // Sending FLARQ Format Message +#define ARQMAILACK 2048 // Waiting for all data to be acked +#define NEEDLF 4096 // Add LF to forward script commands (fro Telnet +#define MCASTRX 8192 // Stream in Multicast RX Mode +#define DISCONNECTING 16384 // Disconnect sent to Node +#define YAPPTX 0x008000 // Sending YAPP file +#define SYSOPCHAT 0x010000 // Chatting to BBS console +#define WINLINKRO 0x020000 // WL2K RO (no J in SID) +#define SYNCMODE 0x040000 // RMS RELAY SYNC +#define MFJMODE 0x080000 // MFJ PMS +#define NEWPACCOM 0x100000 // PACCOM PMS 3.2 +#define SETCALLTOSENDER 0x200000 // Set calling call to message sender + + +struct FBBRestartData +{ + struct MsgInfo * TempMsg; // Header while message is being received + struct UserInfo * UserPointer; + UCHAR * MailBuffer; // Mail Message being received + int MailBufferSize; // Total Malloc'ed size. Actual size in in Msg Struct + int Count; // Give up if too many restarts +}; + +// We need to keep the B2Message file for B2 messages we are sending until the messages is acked, so +// we can restart it. Otherwise the file may change, resulting in a checksum error + + +struct B2RestartData +{ + int CSize; // Compresses Size (B2 proto) + UCHAR * CompressedMsg; // Compressed Body fo B2 + struct MsgInfo * FwdMsg; + struct UserInfo * UserPointer; + int Count; // Give up if too many restarts +}; + +//------ TAJ ----- +typedef struct PGARGS +{ + CIRCUIT * conn; + struct UserInfo * user; + char InputBuffer[80]; + int Len; +}RUNPGARGS, *RUNPGARGS_PTR; + +//--------------- + +#pragma pack(1) + +struct TempUserInfo +{ + int LastAuthCode; // Protect against playback attack + + // Fields used to allow interrupting and resuming a paged listing + + BOOL ListActive; // Doing a list + BOOL ListSuspended; // Paused doing a list + int LastListedInPagedMode; + char LastListCommand[80]; + char LastListParams[80]; + int LinesSent; + char SendFullFrom; + char ListType; + char ListDirn; + char ListStatus; + char ListSelector; // < > @ etc + + int ListRangeStart; + int ListRangeEnd; + int LLCount; // Number still to send in List Last N + int UpdateLatest; // if set, save last listed as latest + BOOL IncludeKilled; // Show Killed Messages if SYSOP + //--- TAJ --- + int PG_INDEX; // current index of PG server + int PG_SERVER; // PG server to run + RUNPGARGS_PTR RUNPGPARAMS; // pointer to RUNPGARGS for dealloc + //----------- +}; + +#define PMSG 1 +#define BMSG 2 +#define TMSG 3 + +struct OldUserInfo +{ + // Old format - without message type specific traffic counts + + char Call[10]; // Connected call without SSID +// indicat relai[8]; /* 64 Digis path */ + int lastmsg; /* 4 Last L number */ + int ConnectsIn; /* 4 Number of connexions in*/ + int TimeLastConnected; //Last connexion date */ +// long lastyap __a2__ ; /* 4 Last YN date */ + ULONG flags ; /* 4 Flags */ + + UCHAR PageLen; // Lines Per Page + UCHAR lang ; /* 1 Language */ + + int Xnewbanner; /* 4 Last Banner date */ + short Xdownload ; /* 2 download size (KB) = 100 */ + char POP3Locked ; // Nonzero if POP3 server has locked this user (stops other pop3 connections, or BBS user killing messages) + char BBSNumber; // BBS Bitmap Index Number + struct BBSForwardingInfo * ForwardingInfo; + struct UserInfo * BBSNext; // links BBS record + struct TempUserInfo * Temp; // Working Fields - not saved in user file + char xfree[6]; /* 6 Spare */ + char Xtheme; /* 1 Current topic */ + + char Name[18]; /* 18 1st Name */ + char Address[61]; /* 61 Address */ + + // Stats. Was City[31]; /* 31 City */ + + int MsgsReceived; + int MsgsSent; + int MsgsRejectedIn; // Messages we reject + int MsgsRejectedOut; // Messages Rejectd by other end + int BytesForwardedIn; + int BytesForwardedOut; + int ConnectsOut; // Forwarding Connects Out + + USHORT RMSSSIDBits; // SSID's to poll in RMS + + char Spare1; + + char HomeBBS[41]; /* 41 home BBS */ + char QRA[7]; /* 7 Qth Locator */ + char pass[13]; /* 13 Password */ + char ZIP[9]; /* 9 Zipcode */ + BOOL spare; +} ; /* Total : 360 bytes */ + +struct MsgStats +{ + int ConnectsIn; /* 4 Number of connexions in*/ + int ConnectsOut; // Forwarding Connects Out + + // Stats saveed by message type + + int MsgsReceived[4]; + int MsgsSent[4]; + int MsgsRejectedIn[4]; // Messages we reject + int MsgsRejectedOut[4]; // Messages Rejectd by other end + int BytesForwardedIn[4]; + int BytesForwardedOut[4]; +}; + +struct UserInfo +{ + // New Format - with stats maintained by message type and unused fields removed. + + // This is no longer a fixed length record so can't be saved as a binarl + + char Call[10]; // Connected call without SSID + + int Length; // To make subsequent format changes easier + + int lastmsg; /* 4 Last L number */ + int xTimeLastConnected; //Last connexion date */ + ULONG flags ; /* 4 Flags */ + + UCHAR PageLen; // Lines Per Page + + char POP3Locked ; // Nonzero if POP3 server has locked this user (stops other pop3 connections, or BBS user killing messages) + unsigned char BBSNumber; // BBS Bitmap Index Number + struct BBSForwardingInfo * ForwardingInfo; + struct UserInfo * BBSNext; // links BBS record + struct TempUserInfo * Temp; // Working Fields - not saved in user file + char Name[18]; /* 18 1st Name */ + char Address[61]; /* 61 Address */ + + USHORT RMSSSIDBits; // SSID's to poll in RMS + + char HomeBBS[41]; /* 41 home BBS */ + char QRA[7]; /* 7 Qth Locator */ + char pass[13]; /* 13 Password */ + char ZIP[9]; /* 9 Zipcode */ + + struct MsgStats Total; + struct MsgStats Last; + + char CMSPass[16]; // For Secure Signon + int WebSeqNo; + + long long TimeLastConnected; //Last connection date */ + + char Filler[44 - 8]; // So we can add a few fields wirhout another resize +}; + +// flags equates + +#define F_Excluded 0x0001 +#define F_GGG 0x0002 +#define F_Expert 0x0004 +#define F_SYSOP 0x0008 +#define F_BBS 0x0010 +#define F_RMSREDIRECT 0x0020 +#define F_BBB 0x0040 +#define F_CCC 0x0080 +#define F_DDD 0x0100 +#define F_EEE 0x0200 +#define F_FFF 0x0400 +#define F_PMS 0x0800 +#define F_EMAIL 0x1000 +#define F_HOLDMAIL 0x2000 +#define F_POLLRMS 0x4000 +#define F_SYSOP_IN_LM 0x8000 +#define F_Temp_B2_BBS 0x00010000 // "Winlink Express User" +#define F_NOWINLINK 0x00020000 // Don't add Winlink.org +#define F_NOBULLS 0x00040000 +#define F_NTSMPS 0x00080000 +#define F_APRSMFOR 0x00100000 // Send APRS message for new mail +#define F_APRSSSID 0xF0000000 // (Top 4 Bits + + +struct Override +{ + char * Call; + int Days; +}; + +struct ALIAS +{ + char * Alias; + char * Dest; +}; + +typedef struct _MESSAGEX +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGEX * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + +// MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + UCHAR DATA[256]; + UCHAR DIGIS[56]; // Padding in case we have digis + +}MESSAGEX, *PMESSAGEX; + + +#pragma pack() + +// Message Database Entry. Designed to be compatible with FBB + +#define NBBBS 160 // Max BBSes we can forward to. Must be Multiple of 8, and must be 80 for FBB compatibliliy +#define NBMASK NBBBS/8 // Number of bytes in Forward bitlists. + +#pragma pack(1) + +struct OldMsgInfo +{ + char type ; + char status ; + int number ; + int length ; + int datereceived; + char bbsfrom[7] ; // ? BBS we got it from ? + char via[41] ; + char from[7] ; + char to[7] ; + char bid[13] ; + char title[61] ; + char bin; + int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp + + UCHAR B2Flags; + + char free[4]; + unsigned short nblu; + int theme ; + time_t datecreated ; + time_t datechanged ; + char fbbs[10] ; + char forw[10] ; + char emailfrom[41]; +} ; + + +struct MsgInfo +{ + char type; + char status; + int number; + int length; + int xdatereceived; + char bbsfrom[7]; // ? BBS we got it from ? + char via[41]; + char from[7]; + char to[7]; + char bid[13]; + char title[61]; + int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp + + UCHAR B2Flags; // Not all flags specific to B2 + + #define B2Msg 1 // Set if Message File is a formatted B2 message + #define Attachments 2 // Set if B2 message has attachments + #define FromPaclink 4 + #define FromCMS 8 + #define FromRMSExpress 16 + #define RadioOnlyMsg 32 // Received using call-T + #define RadioOnlyFwd 64 // Received using call-R + #define WarnNotForwardedSent 128 + + int xdatecreated; + int xdatechanged; + UCHAR fbbs[NBMASK]; + UCHAR forw[NBMASK]; + char emailfrom[41]; + char Locked; // Set if selected for sending (NTS Pickup) + char Defered; // FBB response '=' received + UCHAR UTF8; // Set if Message is in UTF8 (ie from POP/SMTP) + +// For 64 bit time_t compatibility define as long long +// (so struct is same with 32 or 64 bit time_t) + + int64_t datereceived; + int64_t datecreated; + int64_t datechanged; + + char Spare[61 - 24]; // For future use +} ; + +#define MSGTYPE_B 0 +#define MSGTYPE_P 1 + +#define MSGSTATUS_N 0 +#define MSGSTATUS_Y 1 +#define MSGSTATUS_F 2 +#define MSGSTATUS_K 3 +#define MSGSTATUS_H 4 +#define MSGSTATUS_D 5 +#define MSGSTATUS_$ 6 + +struct NNTPRec +{ + // Used for NNTP access to Bulls + + struct NNTPRec * Next; // Record held in chain, so can be held sorted + char NewsGroup[64]; // = Bull TO.at field + int FirstMsg; // Lowest Number + int LastMsg; // Highest Number + int Count; // Active Msgs + time_t DateCreated; // COntains Creation Date of First Bull in Group +}; + + +typedef struct { + char mode; + char BID[13]; + union + { /* array named screen */ + struct + { + unsigned short msgno; + unsigned short timestamp; + }; + CIRCUIT * conn; + } u; +} BIDRec, *BIDRecP; + + +typedef struct WPDBASE{ /* 194 bytes */ + char callsign[7]; + char name[13]; + unsigned char Type; + unsigned char changed; + unsigned short seen; + long long last_modif; + long long last_seen; + char first_homebbs[41]; + char secnd_homebbs[41]; + char first_zip[9]; + char secnd_zip[9]; + char first_qth[31]; + char secnd_qth[31]; +} WPRec, * WPRecP; + +#pragma pack() + +struct FWDBAND +{ + time_t FWDStartBand; + time_t FWDEndBand; +}; + + + +struct BBSForwardingInfo +{ + // Holds info for forwarding + + BOOL Enabled; // Forwarding Enabled + char ** ConnectScript; // Main Connect Script + char ** TempConnectScript; // Used with FWD command. + int ScriptIndex; // Next line in script + BOOL MoreLines; // Set until script is finsihed + + char ** TOCalls; // Calls in to field + char ** ATCalls; // Calls in ATBBS field + char ** HaddressesP; // Heirarchical Addresses for Personals to forward to (as stored) + char *** HADDRSP; // Heirarchical Addresses for Personals to forward to + char ** Haddresses; // Heirarchical Addresses to forward to (as stored) + char *** HADDRS; // Heirarchical Addresses to forward to + int * HADDROffet; // Elements added to complete the HR. At least n+1 must match to forward + char ** FWDTimes; // Time bands to forward + struct FWDBAND ** FWDBands; + int MsgCount; // Messages for this BBS + BOOL ReverseFlag; // Set if BBS wants to poll for reverse forwarding + BOOL Forwarding; // Forward in progress + int MaxFBBBlockSize; + BOOL AllowBlocked; // Allow FBB Blocked + BOOL AllowCompressed; // Allow FBB Compressed + BOOL AllowB1; // Enable B1 + BOOL AllowB2; // Enable B2 + BOOL SendCTRLZ; // Send Ctrl/z instead of /ex + BOOL PersonalOnly; // Only Forward Personals + BOOL SendNew; // Forward new messages immediately + int FwdInterval; + int RevFwdInterval; + int FwdTimer; + time_t LastReverseForward; + char *BBSHA; // HA of BBS + char ** BBSHAElements; // elements of HA of BBS + int ConTimeout; +// char UserCall[10]; // User we are forwarding on behalf of (Currently only for RMS) +// int UserIndex; // index of User we are forwarding on behalf of (Currently only for RMS) +}; + + +struct FBBHeaderLine +{ + // Holds the info from the (up to) 5 headers presented at the start of a Forward Block + + char Format; // Ascii or Binary + char MsgType; // P B etc + char From[7]; // Sender + char ATBBS[41]; // BBS of recipient (@ Field) + char To[7]; // Recipient + char BID[13]; + int Size; + int CSize; // Compresses Size (B2 proto) + BOOL B2Message; // Set if an FC type + UCHAR * CompressedMsg; // Compressed Body fo B2 + struct MsgInfo * FwdMsg; // Header so we can mark as complete +}; + +#define MAXSTACK 20 +//#define MAXLINE 10000 +#define INPUTLEN 512 + +#define MAXLINES 1000 +#define LINELEN 200 + +extern char RTFHeader[4000]; +extern int RTFHddrLen; + +struct ConsoleInfo +{ + struct ConsoleInfo * next; + CIRCUIT * Console; + int BPQStream; + WNDPROC wpOrigInputProc; + HWND hConsole; + HWND hwndInput; + HWND hwndOutput; + HMENU hMenu; // handle of menu + RECT ConsoleRect; + RECT OutputRect; + + int Height, Width, LastY; + + int ClientHeight, ClientWidth; + char kbbuf[INPUTLEN]; + int kbptr; + + char * readbuff; // Malloc'ed + int readbufflen; // Current Length + char * KbdStack[MAXSTACK]; + + int StackIndex; + + BOOL Bells; + BOOL FlashOnBell; // Flash instead of Beep + BOOL StripLF; + + BOOL WarnWrap; + BOOL FlashOnConnect; + BOOL WrapInput; + BOOL CloseWindowOnBye; + + unsigned int WrapLen; + int WarnLen; + int maxlinelen; + + int PartLinePtr; + int PartLineIndex; // Listbox index of (last) incomplete line + + DWORD dwCharX; // average width of characters + DWORD dwCharY; // height of characters + DWORD dwClientX; // width of client area + DWORD dwClientY; // height of client area + DWORD dwLineLen; // line length + int nCaretPosX; // horizontal position of caret + int nCaretPosY; // vertical position of caret + + COLORREF FGColour; // Text Colour + COLORREF BGColour; // Background Colour + COLORREF DefaultColour; // Default Text Colour + + int CurrentLine; // Line we are writing to in circular buffer. + + int Index; + BOOL SendHeader; + BOOL Finished; + + char OutputScreen[MAXLINES][LINELEN]; + + int Colourvalue[MAXLINES]; + int LineLen[MAXLINES]; + + int CurrentColour; + int Thumb; + int FirstTime; + BOOL Scrolled; // Set if scrolled back + int RTFHeight; // Height of RTF control in pixels + +}; + + +struct MSESSION +{ + struct MSESSION * Next; + unsigned int Key; + char * FileName; + char * OrigTimeStamp; + unsigned char * Message; + int MessageLen; + unsigned char * BlockList; + char * ID; + int BlockSize; + int BlockCount; + int BlocksReceived; + BOOL Completed; + time_t Created; + time_t LastUpdated; + int Index; // Line in Display +}; + +VOID __cdecl nprintf(CIRCUIT * conn, const char * format, ...); +char * strlop(char * buf, char delim); +int rt_cmd(CIRCUIT *circuit, char * Buffer); +CIRCUIT *circuit_new(CIRCUIT *circuit, int flags); +VOID BBSputs(CIRCUIT * conn, char * buf); +VOID FBBputs(CIRCUIT * conn, char * buf); +void makelinks(void); +VOID * _zalloc(size_t len); +VOID FreeChatMemory(); +VOID ChatTimer(); +VOID nputs(CIRCUIT * conn, char * buf); +VOID node_close(); +VOID removelinks(); +VOID SetupChat(); +VOID SendChatLinkStatus(); +VOID ClearChatLinkStatus(); +VOID Send_MON_Datagram(UCHAR * Msg, DWORD Len); + +#define Connect(stream) SessionControl(stream,1,0) +#define Disconnect(stream) SessionControl(stream,2,0) +#define ReturntoNode(stream) SessionControl(stream,3,0) +#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) + +int EncryptPass(char * Pass, char * Encrypt); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); + +// TCP Connections. FOr the moment SMTP or POP3 + +typedef struct SocketConnectionInfo +{ + struct SocketConnectionInfo * Next; + int Number; // Number of record - for Connections display + SOCKET socket; + SOCKADDR_IN sin; + int Type; // SMTP or POP3 + BOOL AMPR; // Set if sending to an AMPR.ORG server + char FromDomain[50]; // Domain we are sending from + struct UserInfo * bbs; // BBS dor forwarding to AMPR + int State; // Transaction State Machine + UCHAR CallSign[10]; + UCHAR TCPBuffer[3000]; // For converting byte stream to messages + int InputLen; // Data we have alreasdy = Offset of end of an incomplete packet; + + char * MailFrom; // Envelope Sender and Receiver + char ** RecpTo; // May be several Recipients + int Recipients; + + UCHAR * MailBuffer; // Mail Message being received. malloc'ed as needed + int MailBufferSize; // Total Malloc'ed size. Actual size is in MailSize + int MailSize; + int Flags; + + struct UserInfo * POP3User; + struct MsgInfo ** POP3Msgs; // Header List of messages for this uaer + int POP3MsgCount; // No of Messages + int POP3MsgNum; // Sequence number of message being received + + struct MsgInfo * SMTPMsg; // message for this SMTP connection + + UCHAR * SendBuffer; // Message being sent if socket is busy. malloc'ed as needed + int SendBufferSize; // Total Malloc'ed size. Actual size is in MailSize + int SendSize; // Bytes in buffer + int SendPtr; // next byte to send when ready + + struct NNTPRec * NNTPGroup; // Currently Selected Group + int NNTPNum; // Currenrly Selected Msg Number + int Timeout; // Used to close a session that is open too long + +} SocketConn; + +// FBB reject.sys like filters + +typedef struct FBBFILTER +{ + struct FBBFILTER * Next; + char Action; + char Type; + char From[10]; + char AT[10]; + char TO[10]; + char BID[16]; + int MaxLen; + +} FBBFilter; + +extern FBBFilter * Filters; + +typedef struct KEYVALUES +{ + char * Key; + char * Value; +} KeyValues; + +typedef struct WEBMAILINFO +{ + // Info for HTML Forms Processing + + struct HtmlFormDir * Dir; // HTML Directory + char * txtFileName; // Template Name for current message + char * InputHTMLName; // Template to input message + char * DisplayHTMLName; // Template to display message + char * ReplyHTMLName; // Template for replying to message + char * txtFile; // Template data + char * OrigTo; // To field when template loaded + char * OrigSubject; // Subject field when template loaded + char * OrigBody; // Msg text when template loaded + char * OrigBID; + char OrigType; + char * To; + char * CC; + char * Subject; + char * Body; + char * BID; + char Type; + struct MsgInfo * Msg; // Msg record if replying + KeyValues txtKeys[1000]; // Key/Value pairs for txt template. Used when creating or displaying + KeyValues XMLKeys[1000]; // Key/Value pairs from XML attachment + BOOL isReply; + char * XMLName; + char * XML; // XML attachment + int XMLLen; + int Files; + char * FileName[100]; // Attachments + char * FileBody[100]; + int FileLen[100]; + + char * Header; + int HeaderLen; + + char * Footer; + int FooterLen; + + char * Reply; // put in here to save passing lots of parameters + int * RLen; + + BOOL Winlink; + BOOL P2P; + BOOL Packet; + + int CurrentMessageIndex; // Index of message currently displayed (for Prev and Next) +#ifdef WIN32 + void * iconv_toUTF8; // Used on Linux for char set conversion +#else + iconv_t * iconv_toUTF8; // Used on Linux for char set conversion +#endif + +}WebMailInfo; + +#define SMTPServer 1 +#define POP3SLAVE 2 +#define SMTPClient 3 +#define POP3Client 4 +#define NNTPServer 5 + +// State Values + +#define GettingUser 1 +#define GettingPass 2 +#define Authenticated 4 + +#define Connecting 8 + +// SMTP Master + +#define WaitingForGreeting 16 +#define WaitingForHELOResponse 32 +#define WaitingForFROMResponse 64 +#define WaitingForTOResponse 128 +#define WaitingForDATAResponse 256 +#define WaitingForBodyResponse 512 +#define WaitingForAUTHResponse 1024 + +// POP3 Master + +#define WaitingForUSERResponse 32 +#define WaitingForPASSResponse 64 +#define WaitingForSTATResponse 128 +#define WaitingForUIDLResponse 256 +#define WaitingForLISTResponse 512 +#define WaitingForRETRResponse 512 +#define WaitingForDELEResponse 1024 +#define WaitingForQUITResponse 2048 + + +#define SE 240 // End of subnegotiation parameters +#define NOP 241 //No operation +//#define DM 242 //Data mark Indicates the position of a Synch event within the data stream. This should always be accompanied by a TCP urgent notification. +#define BRK 243 //Break Indicates that the "break" or "attention" key was hi. +#define IP 244 //Suspend Interrupt or abort the process to which the NVT is connected. +#define AO 245 //Abort output Allows the current process to run to completion but does not send its output to the user. +#define AYT 246 //Are you there Send back to the NVT some visible evidence that the AYT was received. +#define EC 247 //Erase character The receiver should delete the last preceding undeleted character from the data stream. +#define EL 248 //Erase line Delete characters from the data stream back to but not including the previous CRLF. +#define GA 249 //Go ahead Under certain circumstances used to tell the other end that it can transmit. +#define SB 250 //Subnegotiation Subnegotiation of the indicated option follows. +#define WILL 251 //will Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. +#define WONT 252 //wont Indicates the refusal to perform, or continue performing, the indicated option. +#define DOx 253 //do Indicates the request that the other party perform, or confirmation that you are expecting the other party to perform, the indicated option. +#define DONT 254 //dont Indicates the demand that the other party stop performing, or confirmation that you are no longer expecting the other party to perform, the indicated option. +#define IAC 255 + +#define suppressgoahead 3 //858 +//#define Status 5 //859 +//#define echo 1 //857 +#define timingmark 6 //860 +#define terminaltype 24 //1091 +#define windowsize 31 //1073 +#define terminalspeed 32 //1079 +#define remoteflowcontrol 33 //1372 +#define linemode 34 //1184 +#define environmentvariables 36 //1408 + +BOOL Initialise(); +#ifdef WIN32 +INT_PTR CALLBACK ConfigWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +#endif +int DisplaySessions(); +int DoStateChange(int Stream); +int DoReceivedData(int Stream); +int DoBBSMonitorData(int Stream); +int Connected(int Stream); +int Disconnected(int Stream); +//int Socket_Accept(int SocketId); +//int Socket_Data(int SocketId,int error, int eventcode); +int DataSocket_Read(SocketConn * sockptr, SOCKET sock); +int DataSocket_Write(SocketConn * sockptr, SOCKET sock); +int DataSocket_Disconnect(SocketConn * sockptr); +int RefreshMainWindow(); +int Terminate(); +int SendtoSocket(SOCKET sock,char * Msg); +int WriteLog(char * msg); +int ConnectState(int Stream); +UCHAR * EncodeCall(UCHAR * Call); +int ParseIniFile(char * fn); +struct UserInfo * AllocateUserRecord(char * Call); +struct MsgInfo * AllocateMsgRecord(); +BIDRec * AllocateBIDRecord(); +BIDRec * AllocateTempBIDRecord(); +struct UserInfo * LookupCall(char * Call); +BIDRec * LookupBID(char * BID); +BIDRec * LookupTempBID(char * BID); +VOID RemoveTempBIDS(CIRCUIT * conn); +VOID SaveUserDatabase(); +VOID GetUserDatabase(); +VOID GetMessageDatabase(); +VOID SaveMessageDatabase(); +VOID GetBIDDatabase(); +VOID SaveBIDDatabase(); +VOID GetWPDatabase(); +VOID CopyWPDatabase(); +VOID SaveWPDatabase(); +VOID GetBadWordFile(); +WPRec * LookupWP(char * Call); +VOID SendWelcomeMsg(int Stream, ConnectionInfo * conn, struct UserInfo * user); +VOID ProcessLine(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); +VOID ProcessChatLine(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); +VOID SendPrompt(ConnectionInfo * conn, struct UserInfo * user); +int QueueMsg( ConnectionInfo * conn, char * msg, int len); +VOID SendUnbuffered(int stream, char * msg, int len); +//int GetFileList(char * Dir); +BOOL ListMessage(struct MsgInfo * Msg, ConnectionInfo * conn, struct TempUserInfo * Temp); +void DoDeliveredCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +void DoKillCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +void DoListCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, BOOL Resuming, char * Context); +void DoReadCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +void KillMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno); +int KillMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call); +int KillMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call); +void DoUnholdCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); + +VOID FlagAsKilled(struct MsgInfo * Msg, BOOL SaveDB); +int ListMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); +int ListMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); +int ListMessagesAT(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start); +void ListMessagesInRange(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom ); +void ListMessagesInRangeForwards(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom ); +int GetUserMsg(int m, char * Call, BOOL SYSOP); +void Flush(ConnectionInfo * conn); +VOID ClearQueue(ConnectionInfo * conn); +void TrytoSend(); +void ReadMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno); +struct MsgInfo * FindMessage(char * Call, int msgno, BOOL sysop); +char * ReadMessageFile(int msgno); +char * ReadInfoFile(char * File); +char * FormatDateAndTime(time_t Datim, BOOL DateOnly); +int CriticalErrorHandler(char * error); +BOOL DoSendCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context); +BOOL CreateMessage(ConnectionInfo * conn, char * From, char * ToCall, char * ATBBS, char MsgType, char * BID, char * Title); +VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int len); +VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); +VOID CreateMessageFile(ConnectionInfo * conn, struct MsgInfo * Msg); +int ProcessConnecting(CIRCUIT * circuit, char * Buffer, int Len); +VOID SaveConfig(char * ConfigName); +BOOL GetConfig(char * ConfigName); +int GetIntValue(config_setting_t * group, char * name); +//BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen); +BOOL GetConfigFromRegistry(); +VOID Parse_SID(CIRCUIT * conn, char * SID, int len); +VOID ProcessMBLLine(CIRCUIT * conn, struct UserInfo * user, UCHAR* Buffer, int len); +VOID ProcessFBBLine(ConnectionInfo * conn, struct UserInfo * user, UCHAR * Buffer, int len); +VOID SetupNextFBBMessage(CIRCUIT * conn); +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char * To, char ** ATBBS, char ** BID); +int PrintMessages(HWND hDlg, int Count, int * Indexes); +int check_fwd_bit(char *mask, int bbsnumber); +void set_fwd_bit(char *mask, int bbsnumber); +void clear_fwd_bit (char *mask, int bbsnumber); +VOID SetupForwardingStruct(struct UserInfo * user); +BOOL Forward_Message(struct UserInfo * user, struct MsgInfo * Msg); +VOID StartForwarding (int BBSNumber, char ** TempScript); +BOOL Reverse_Forward(); +int ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len); +BOOL FBBDoForward(CIRCUIT * conn); +BOOL FindMessagestoForward(CIRCUIT * conn); +BOOL SeeifMessagestoForward(int BBSNumber, CIRCUIT * Conn); +int CountMessagestoForward(struct UserInfo * user); +int CountBytestoForward(struct UserInfo * user); + +VOID * GetMultiLineDialogParam(HWND hDialog, int DLGItem); + + +VOID * GetMultiStringValue(config_setting_t * hKey, char * ValueName); +VOID * RegGetMultiStringValue(HKEY hKey, char * ValueName); + +int MultiLineDialogToREG_MULTI_SZ(HWND hWnd, int DLGItem, HKEY hKey, char * ValueName); +int Do_BBS_Sel_Changed(HWND hDlg); +VOID FreeForwardingStruct(struct UserInfo * user); +VOID FreeList(char ** Hddr); +int Do_User_Sel_Changed(HWND hDlg); +int Do_Msg_Sel_Changed(HWND hDlg); +VOID Do_Save_Msg(); +VOID Do_Add_User(HWND hDlg); +VOID Do_Delete_User(HWND hDlg); +VOID FlagSentMessages(CIRCUIT * conn, struct UserInfo * user); +VOID HoldSentMessages(CIRCUIT * conn, struct UserInfo * user); +VOID Do_Save_User(HWND hDlg, BOOL ShowBox); +VOID DeleteBBS(struct UserInfo * user); +VOID SaveBBSConfig(); +BOOL GetChatConfig(char * ConfigName); +VOID SaveChatConfig(); +VOID SaveISPConfig(); +VOID SaveFWDConfig(); +VOID SaveMAINTConfig(); +VOID SaveWelcomeMsgs(); +VOID SavePrompts(); +VOID ReinitializeFWDStruct(struct UserInfo * user); +VOID CopyBIDDatabase(); +VOID CopyMessageDatabase(); +VOID CopyUserDatabase(); +VOID FWDTimerProc(); +VOID CreateMessageFromBuffer(CIRCUIT * conn); +VOID __cdecl nodeprintf(ConnectionInfo * conn, const char * format, ...); +VOID __cdecl nodeprintfEx(ConnectionInfo * conn, const char * format, ...); +VOID FreeOverrides(); +VOID SendMessageToSYSOP(char * Title, char * MailBuffer, int Length); +struct UserInfo * FindRMS(); +VOID FindNextRMSUser(struct BBSForwardingInfo * FWDInfo); +BOOL ConnecttoBBS (struct UserInfo * user); +BOOL SetupNewBBS(struct UserInfo * user); +VOID CreateRegBackup(); +VOID SaveFilters(HWND hDlg); +BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type, int Len); +BOOL CheckHoldFilters(struct MsgInfo * Msg, char * From, char * To, char * ATBBS, char * BID); +BOOL CheckifLocalRMSUser(char * FullTo); +VOID DoWPLookup(ConnectionInfo * conn, struct UserInfo * user, char Type, char *Context); +BOOL wildcardcompare(char * Target, char * Match); +VOID SendWarningToSYSOP(struct MsgInfo * Msg); +VOID DoEditUserCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoPollRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoShowRMSCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoSetIdleTime(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoFwdCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID SaveFwdParams(char * Call, struct BBSForwardingInfo * ForwardingInfo); +VOID DoAuthCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID ProcessSuspendedListCommand(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); +VOID DoReroute(CIRCUIT * conn, struct UserInfo * user); + +// FBB Routines + +VOID SendCompressed(CIRCUIT * conn, struct MsgInfo * FwdMsg); +VOID SendCompressedB2(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); +VOID UnpackFBBBinary(CIRCUIT * conn); +void Decode(CIRCUIT * conn, __int16 DecodeOnly); +//long Encode(char * in, char * out, long inlen, BOOL B1Protocol); + +BOOL CreateB2Message(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader, char * Rline); +VOID SaveFBBBinary(CIRCUIT * conn); +BOOL LookupRestart(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); +BOOL DoWeWantIt(CIRCUIT * conn, struct FBBHeaderLine * FBBHeader); + +// Console Routines + +BOOL CreateConsole(int Stream); +int WritetoConsoleWindow(int Stream, char * Msg, int len); +int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item); +void CopyRichTextToClipboard(HWND hWnd); +void CopyToClipboard(HWND hWnd); +VOID CloseConsole(int Stream); + +// Monitor Routines + +BOOL CreateMonitor(); +int WritetoMonitorWindow(char * Msg, int len); + +BOOL CreateDebugWindow(); +VOID WritetoDebugWindow(char * Msg, int len); +VOID ClearDebugWindow(); +int RemoveLF(char * Message, int len); + +// Utilities + +BOOL isdigits(char * string); + + +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); +void FreeSemaphore(struct SEM * Semaphore); + +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Logprintf(int LogMode, CIRCUIT * conn, int InOut, const char * format, ...); + +VOID SortBBSChain(); +VOID ExpandAndSendMessage(CIRCUIT * conn, char * Msg, int LOG); +int ImportMessages(CIRCUIT * conn, char * FN, BOOL Nopopup); + +// TCP Routines + +BOOL InitialiseTCP(); +VOID SetupListenSet(); +VOID TCPTimer(); +VOID TCPFastTimer(); +int Socket_Data(int sock, int error, int eventcode); +static int Socket_Accept(SOCKET SocketId); +int Socket_Connect(SOCKET sock, int Error); +VOID ProcessSMTPServerMessage(SocketConn * sockptr, char * Buffer, int Len); +int CreateSMTPMessage(SocketConn * sockptr, int i, char * MsgTitle, time_t Date, char * MsgBody, int Msglen, BOOL B2Flag); +BOOL CreateSMTPMessageFile(char * Message, struct MsgInfo * Msg); +SOCKET CreateListeningSocket(int Port); +int TidyString(char * MailFrom); +VOID ProcessPOP3ServerMessage(SocketConn * sockptr, char * Buffer, int Len); +char *str_base64_encode(char *str); +int b64decode(char *str); +SocketConn * SMTPConnect(char * Host, int Port, BOOL AMPR, struct MsgInfo * Msg, char * MsgBody); +BOOL POP3Connect(char * Host, int Port); +VOID ProcessSMTPClientMessage(SocketConn * sockptr, char * Buffer, int Len); +VOID ProcessPOP3ClientMessage(SocketConn * sockptr, char * Buffer, int Len); +int CreatePOP3Message(char * From, char * To, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen, BOOL B2Flag); +void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags); +int SendSock(SocketConn * sockptr, char * msg); +VOID __cdecl sockprintf(SocketConn * sockptr, const char * format, ...); +VOID SendFromQueue(SocketConn * sockptr); +VOID SendMultiPartMessage(SocketConn * sockptr, struct MsgInfo * Msg, UCHAR * msgbytes); +int CountMessagesTo(struct UserInfo * user, int * Unread); + +BOOL SendtoISP(); + +// NNTP ROutines + +VOID InitialiseNNTP(); +VOID BuildNNTPList(struct MsgInfo * Msg); +int NNTP_Data(int sock, int error, int eventcode); +int NNTP_Accept(SOCKET SocketId); + +VOID * GetOverrides(config_setting_t * group, char * ValueName); +VOID * RegGetOverrides(HKEY hKey, char * ValueName); + +VOID DoHouseKeeping(BOOL Mainual); +VOID ExpireMessages(); +VOID KillMsg(struct MsgInfo * Msg); +BOOL RemoveKilledMessages(); +VOID Renumber_Messages(); +BOOL ExpireBIDs(); +VOID MailHousekeepingResults(); +VOID CreateBBSTrafficReport(); +VOID CreateWPReport(); + +// WP Routines + +VOID ProcessWPMsg(char * MailBuffer, int Size, char * FisrtRLine); +VOID GetWPInfoFromRLine(char * From, char * FirstRLine, time_t RLineTime); +VOID UpdateWPWithUserInfo(struct UserInfo * user); +VOID GetWPBBSInfo(char * Rline); + +// UI Routines + +VOID SetupUIInterface(); +VOID Free_UI(); +VOID SendLatestUI(int Port); +VOID SendMsgUI(struct MsgInfo * Msg); +static VOID Send_AX_Datagram(UCHAR * Msg, DWORD Len, UCHAR Port, UCHAR * HWADDR, BOOL Queue); +VOID SeeifBBSUIFrame(struct _MESSAGEX * buff, int len); +struct MsgInfo * FindMessageByNumber(int msgno); +int CountConnectionsOnPort(int CheckPort); + +// Message Routing Routtines + +VOID SetupHAElements(struct BBSForwardingInfo * ForwardingInfo); +VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo); +VOID SetupHAddresesP(struct BBSForwardingInfo * ForwardingInfo); +VOID SetupMyHA(); +VOID SetupFwdAliases(); +struct Continent * FindContinent(char * Name); +int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn); +BOOL CheckABBS(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute); +BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo); +BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS); +BOOL CheckBBSHList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char * HRoute); +BOOL CheckBBSHElements(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements); +BOOL CheckBBSHElementsFlood(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements); +int CheckBBSToForNTS(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo); +int CheckBBSATListWildCarded(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS); + +VOID ReRouteMessages(); + +VOID initUTF8(); +int Is8Bit(unsigned char *cpt, int len); +int IsUTF8(unsigned char *ptr, int len); +int IsUTF8(unsigned char *ptr, int len); +int WebIsUTF8(unsigned char *ptr, int len); +int Convert437toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1251toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1252toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int TrytoGuessCode(unsigned char * Char, int Len); + + +VOID FreeWebMailMallocs(); + +extern int _MYTIMEZONE; + +extern HKEY REGTREE; +extern char * REGTREETEXT; + +extern HBRUSH bgBrush; +extern BOOL cfgMinToTray; + +extern CIRCUIT * Console; + +extern ULONG ChatApplMask; +extern char Verstring[]; + +extern char SignoffMsg[]; +extern char AbortedMsg[]; +extern char InfoBoxText[]; // Text to display in Config Info Popup + +extern int LastVer[4]; // In case we need to do somthing the first time a version is run + +extern HWND MainWnd; +extern char BaseDir[]; +extern char BaseDirRaw[]; +extern char MailDir[]; +extern char WPDatabasePath[]; +extern char RlineVer[50]; + +extern BOOL LogBBS; +extern BOOL LogCHAT; +extern BOOL LogTCP; +extern BOOL ForwardToMe; +extern BOOL OnlyKnown; + +extern BOOL AllowAnon; +extern BOOL UserCantKillT; +extern BOOL DontNeedHomeBBS; + +extern int LatestMsg; +extern char BBSName[]; +extern char SYSOPCall[]; +extern char BBSSID[]; +extern char NewUserPrompt[]; + +extern char * WelcomeMsg; +extern char * NewWelcomeMsg; +extern char * ChatWelcomeMsg; +extern char * NewChatWelcomeMsg; +extern char * ExpertWelcomeMsg; + +extern char * Prompt; +extern char * NewPrompt; +extern char * ExpertPrompt; + +// Filter Params + +extern char ** RejFrom; // Reject on FROM Call +extern char ** RejTo; // Reject on TO Call +extern char ** RejAt; // Reject on AT Call +extern char ** RejBID; + +extern char ** HoldFrom; // Hold on FROM Call +extern char ** HoldTo; // Hold on TO Call +extern char ** HoldAt; // Hold on AT Call +extern char ** HoldBID; + +// Send WP Params + +extern BOOL SendWP; +extern char SendWPVIA[81]; +extern char SendWPTO[11]; +extern int SendWPType; + +extern int Ver[4]; + +extern struct MsgInfo ** MsgHddrPtr; + +extern BIDRec ** BIDRecPtr; +extern int NumberofBIDs; + +extern struct NNTPRec * FirstNNTPRec; +//extern int NumberofNNTPRecs; + + +extern int NumberofMessages; +extern int FirstMessageIndextoForward; + +extern WPRec ** WPRecPtr; +extern int NumberofWPrecs; + +extern struct SEM AllocSemaphore; +extern struct SEM ConSemaphore; +extern struct SEM MsgNoSemaphore; + +extern struct MsgInfo * MsgnotoMsg[]; // Message Number to Message Slot List. + + +extern char hostname[]; +extern char RtUsr[]; +extern char RtUsrTemp[]; +extern char RtKnown[]; +extern int AXIPPort; +extern BOOL NeedStatus; + +extern BOOL ISP_Gateway_Enabled; +extern BOOL SMTPAuthNeeded; + + +extern int MaxMsgno; +extern int BidLifetime; +extern int MaxAge; +extern int MaintInterval; +extern int MaintTime; +extern int UserLifetime; + +extern int MaxRXSize; +extern int MaxTXSize; + +extern char OurNode[]; +extern char OurAlias[]; +extern BOOL SMTPMsgCreated; + +extern HINSTANCE hInst; +extern HWND hWnd; +extern RECT MainRect; + +extern char BBSName[]; +extern char HRoute[]; +extern char AMPRDomain[]; +extern BOOL SendAMPRDirect; +extern int BBSApplNum; +extern int SMTPInPort; +extern int POP3InPort; +extern int NNTPInPort; +extern BOOL RemoteEmail; + +extern int MaxStreams; +extern UCHAR * OtherNodes; +extern struct UserInfo * BBSChain; // Chain of users that are BBSes +extern struct UserInfo ** UserRecPtr; +extern int NumberofUsers; +extern struct MsgInfo ** MsgHddrPtr; +extern int NumberofMessages; +extern int HighestBBSNumber; +extern HMENU hFWDMenu; // Forward Menu Handle +extern char zeros[]; // For forward bitmask tests +extern BOOL EnableUI; +extern BOOL RefuseBulls; +extern BOOL SendSYStoSYSOPCall; +extern BOOL SendBBStoSYSOPCall; +extern BOOL DontHoldNewUsers; +extern BOOL DefaultNoWINLINK; +extern BOOL UIEnabled[]; +extern BOOL UINull[]; +extern BOOL UIMF[]; +extern BOOL UIHDDR[]; +extern char * UIDigi[]; +extern int MailForInterval; +extern char MailForText[]; + +extern BOOL ISP_Gateway_Enabled; + +extern char MyDomain[]; // Mail domain for BBS<>Internet Mapping + +extern char ISPSMTPName[]; +extern char ISPEHLOName[]; +extern int ISPSMTPPort; + +extern char ISPPOP3Name[]; +extern int ISPPOP3Port; +extern int ISPPOP3Interval; + +extern char ISPAccountName[]; +extern char ISPAccountPass[]; +extern char EncryptedISPAccountPass[]; +extern int EncryptedPassLen; +extern char *month[]; + +extern HWND hDebug; +extern RECT MonitorRect; +extern RECT DebugRect; +extern HWND hMonitor; +//extern HWND hConsole; +//extern RECT ConsoleRect; +extern int LogAge; +extern BOOL DeletetoRecycleBin; +extern BOOL SuppressMaintEmail; +extern BOOL SaveRegDuringMaint; +extern BOOL SendWP; +extern BOOL OverrideUnsent; +extern BOOL SendNonDeliveryMsgs; +extern BOOL GenerateTrafficReport; + +extern double PR; +extern double PUR; +extern double PF; +extern double PNF; +extern int BF; +extern int BNF; +extern int NTSD; +extern int NTSU; +extern int NTSF; +extern int AP; +extern int AB; + +extern struct Override ** LTFROM; +extern struct Override ** LTTO; +extern struct Override ** LTAT; + +extern time_t LastHouseKeepingTime; +extern time_t LastTrafficTime; + +extern char * MyElements[]; +extern char ** AliasText; +extern struct ALIAS ** Aliases; + +extern BOOL ReaddressLocal; +extern BOOL ReaddressReceived; +extern BOOL WarnNoRoute; +extern BOOL SendPtoMultiple; +extern BOOL Localtime; + +extern struct ConsoleInfo * ConsHeader[2]; + +extern BOOL NeedHomeBBS; +extern char ConfigName[250]; +extern BOOL UsingingRegConfig; + +extern BOOL MulticastRX; + +extern BOOL FilterWPBulls; +extern BOOL NoWPGuesses; +extern char ** SendWPAddrs; // Replacers WP To and VIA + +extern BOOL DontCheckFromCall; + +extern time_t APIClock;; + +// YAPP stuff + +#define SOH 1 +#define STX 2 +#define ETX 3 +#define EOT 4 +#define ENQ 5 +#define ACK 6 +#define DLE 0x10 +#define NAK 0x15 +#define CAN 0x18 diff --git a/.svn/pristine/0e/0ebfb2fa079aeb6cc3c978273fe57af90ded386a.svn-base b/.svn/pristine/0e/0ebfb2fa079aeb6cc3c978273fe57af90ded386a.svn-base new file mode 100644 index 0000000..66a1577 --- /dev/null +++ b/.svn/pristine/0e/0ebfb2fa079aeb6cc3c978273fe57af90ded386a.svn-base @@ -0,0 +1,1856 @@ +/* +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 +*/ + + +// Module to provide a basic Gateway between IP over AX.25 and the Internet. + +// Uses WinPcap on Windows, TAP Driver on Linux + +// Basically operates as a mac level bridge, with headers converted between ax.25 and Ethernet. +// ARP frames are also reformatted, and monitored to build a simple routing table. +// Apps may not use ARP (MSYS is configured with an ax.25 default route, rather than an IP one), +// so the default route must be configured. + +// Intended as a gateway for legacy apps, rather than a full function ip over ax.25 router. +// Suggested config is to use the Internet Ethernet Adapter, behind a NAT/PAT Router. +// The ax.25 applications will appear as single addresses on the Ethernet LAN + +// The code can also switch packets between ax.25 interfaces + +// First Version, July 2008 + +// Version 1.2.1 January 2009 + +// Add IP Address Mapping option + +// June 2014. Convert to Router instead of MAC Bridge, and include a RIP44 decoder +// so packets can be routed from RF to/from encapsulated 44 net subnets. +// Routes may also be learned from received RF packets, or added from config file + +/* +TODo ?Multiple Adapters +*/ + + +// ip tuntap add dev bpqtap mode tap +// ifconfig bpqtap 44.131.4.19 mtu 256 up + + + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#include "cheaders.h" + +#include "ipcode.h" + +#ifdef WIN32 +#include "pcap.h" +#endif + +#ifndef LINBPQ +#include "kernelresource.h" +LRESULT CALLBACK ResWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +#endif + +//#define s_addr S_un.S_addr + +extern BPQVECSTRUC * IPHOSTVECTORPTR; + +BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port); +VOID SENDSABM(struct _LINKTABLE * LINK); +BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK); +BOOL ProcessConfig(); +VOID RemoveARP(PARPDATA Arp); + +VOID ProcessTunnelMsg(PIPMSG IPptr); +VOID ProcessRIP44Message(PIPMSG IPptr); +PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found); +BOOL ProcessROUTELine(char * buf, BOOL Locked); +VOID DoRouteTimer(); +PROUTEENTRY FindRoute(uint32_t IPADDR); +VOID SendIPtoEncap(PIPMSG IPptr, uint32_t Encap); +USHORT Generate_CHECKSUM(VOID * ptr1, int Len); + +static VOID MapRouteIPMsg(PIPMSG IPptr); +BOOL Check_Checksum(VOID * ptr1, int Len); + +static BOOL Send_ETH(VOID * Block, DWORD len); + +VOID ProcessEthARPMsg(PETHARP arpptr); +static VOID SendARPMsg(PARPDATA Arp); +VOID SendICMPTimeExceeded(PIPMSG IPptr); + +#define ARPTIMEOUT 3600 + + +// ARP REQUEST/REPLY (Eth) + +static ETHARP ETHARPREQMSG = {0}; + +static ARPDATA ** ARPRecords = NULL; // ARP Table - malloc'ed as needed + +static int NumberofARPEntries = 0; + +static ROUTEENTRY ** RouteRecords = NULL; + +static int NumberofRoutes = 0; + +//HANDLE hBPQNET = INVALID_HANDLE_VALUE; + +static uint32_t OurIPAddr = 0; + +static uint32_t OurIPBroadcast = 0; +static uint32_t OurNetMask = 0xffffffff; + +static BOOL WantTAP = FALSE; +static BOOL WantEncap = 0; // Run RIP44 and Net44 Encap + +static int IPTTL = 128; + +static int FramesForwarded = 0; +static int FramesDropped = 0; +static int ARPTimeouts = 0; +static int SecTimer = 10; + +static BOOL NeedResolver = FALSE; + +static HMENU hMenu; +extern HMENU hWndMenu; +static HMENU hPopMenu; + +extern HKEY REGTREE; + +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu, hBaseMenu; +extern HWND ClientWnd, FrameWnd; + +static int map_table_len = 0; +//int index=0; // pointer for table search +static int ResolveIndex=-1; // pointer to entry being resolved + +static struct map_table_entry map_table[MAX_ENTRIES]; + +static int Windowlength, WindowParam; + + +static time_t ltime,lasttime; + +static char ConfigClassName[]="CONFIG"; + +HWND hIPResWnd = 0; + +BOOL IPMinimized; + +static int baseline=0; + +static unsigned char hostaddr[64]; + + +// Following two fields used by stats to get round shared memmory problem + +static ARPDATA Arp={0}; +static int ARPFlag = -1; + +// Following Buffer is used for msgs from WinPcap. Put the Enet message part way down the buffer, +// so there is room for ax.25 header instead of Enet header when we route the frame to ax.25 +// Enet Header ia 14 bytes, AX.25 UI is 16 + +// Also used to reassemble NOS Fragmented ax.25 packets + +static UCHAR Buffer[4096] = {0}; + +#define EthOffset 30 // Should be plenty + +static DWORD IPLen = 0; + + +#ifdef WIN32 +static UCHAR ourMACAddr[6] = {02,'B','P','Q',3,48}; +#else +UCHAR ourMACAddr[6] = {02,'B','P','Q',0,1}; +#endif + +static LONG DefaultIPAddr = 0; + +static IPSTATS IPStats = {0}; + +static UCHAR BPQDirectory[260]; + +static char ARPFN[MAX_PATH]; + +static HANDLE handle; + +#ifdef WIN32 +static pcap_t *adhandle = 0; +static pcap_t * (FAR * pcap_open_livex)(const char *, int, int, int, char *); + +static int pcap_reopen_delay; +#endif + +static char Adapter[256]; + +static int Promiscuous = 1; // Default to Promiscuous + +#ifdef WIN32 + +static HINSTANCE PcapDriver=0; + +typedef int (FAR *FARPROCX)(); + +static int (FAR * pcap_sendpacketx)(); + +static FARPROCX pcap_compilex; +static FARPROCX pcap_setfilterx; +static FARPROCX pcap_datalinkx; +static FARPROCX pcap_next_exx; +static FARPROCX pcap_geterrx; + + +static char Dllname[6]="wpcap"; + +FARPROCX GetAddress(char * Proc); + +#else + +#define pcap_compilex pcap_compile +#define pcap_open_livex pcap_open_live +#define pcap_setfilterx pcap_setfilter +#define pcap_datalinkx pcap_datalink +#define pcap_next_exx pcap_next_ex +#define pcap_geterrx pcap_geterr +#define pcap_sendpacketx pcap_sendpacket +#endif +VOID __cdecl Debugprintf(const char * format, ...); + +static HANDLE hInstance; + + + + +void OpenTAP(); + +Dll BOOL APIENTRY Init_PM() +{ + ARPDATA * ARPptr; + + if (hIPResWnd) + { + PostMessage(hIPResWnd, WM_CLOSE,0,0); +// DestroyWindow(hIPResWnd); + + Debugprintf("IP Init Destroying IP Resolver"); + } + + hIPResWnd= NULL; + + ARPRecords = NULL; // ARP Table - malloc'ed as needed + NumberofARPEntries=0; + + RouteRecords = NULL; + NumberofRoutes = 0; + + ReadConfigFile(); + + // Clear old packets + + memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6); + memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); + ETHARPREQMSG.MSGHDDR.ETYPE = 0x0608; // ARP + + ETHARPREQMSG.HWTYPE=0x0100; // Eth + ETHARPREQMSG.PID=0x0008; + ETHARPREQMSG.HWADDRLEN = 6; + ETHARPREQMSG.IPADDRLEN = 4; + +#ifdef WIN32 + + // + // Open PCAP Driver + + if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch + { + char buf[80]; + + if (OpenPCAP()) + sprintf(buf,"Portmapper Using %s\n", Adapter); + else + sprintf(buf," Portmapper Unable to open %s\n", Adapter); + + WritetoConsoleLocal(buf); + + if (adhandle == NULL) + { + WritetoConsoleLocal("Failed to open pcap device - Portmapper Disabled\n"); + return FALSE; + } + + // Allocate ARP Entry for Default Gateway, and send ARP for it + + if (DefaultIPAddr) + { + ARPptr = AllocARPEntry(); + + if (ARPptr != NULL) + { + ARPptr->ARPINTERFACE = 255; + ARPptr->ARPTYPE = 'E'; + ARPptr->IPADDR = DefaultIPAddr; + ARPptr->LOCKED = TRUE; + + SendARPMsg(ARPptr); + } + } + } + +#else + + // Linux - if TAP requested, open it +#ifndef MACBPQ + + if (WantTAP) + OpenTAP(); + +#endif +#endif + + +#ifndef LINBPQ + + if (NeedResolver) + { + WNDCLASS wc; + int i; + char WindowTitle[100]; + int retCode, Type, Vallen; + HKEY hKey; + char Size[80]; + RECT Rect = {0,0,0,0}; + + retCode = RegOpenKeyEx (REGTREE, "SOFTWARE\\G8BPQ\\BPQ32", 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"IPResSize",0, + (uint32_t *)&Type,(UCHAR *)&Size,(uint32_t *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &IPMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + RegCloseKey(hKey); + } + + // Fill in window class structure with parameters that describe + // the main window. + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)ResWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = NULL ; + wc.lpszClassName = "IPAppName"; + + // Register the window classes + + RegisterClass(&wc); + + i=GetLastError(); + + Windowlength=(map_table_len)*14+100; + WindowParam=WS_OVERLAPPEDWINDOW | WS_VSCROLL; + + sprintf(WindowTitle,"PortM Resolver"); + + hIPResWnd = CreateMDIWindow("IPAppName", WindowTitle, WindowParam, + Rect.left - (OffsetW /2), Rect.top - OffsetH, Rect.right - Rect.left, Rect.bottom - Rect.top, + ClientWnd, hInstance, 1234); + + hPopMenu = CreatePopupMenu(); + AppendMenu(hPopMenu, MF_STRING, BPQREREAD, "ReRead Config"); + + SetScrollRange(hIPResWnd,SB_VERT,0, map_table_len,TRUE); + + if (IPMinimized) + ShowWindow(hIPResWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hIPResWnd, SW_RESTORE); + + _beginthread(IPResolveNames, 0, NULL ); + } +#endif + + WritetoConsoleLocal("Portmapper Enabled\n"); + + return TRUE; + +} + +VOID PMClose() +{ +} + +union +{ + struct sockaddr_in rxaddr; + struct sockaddr_in6 rxaddr6; +} RXaddr; + + +Dll BOOL APIENTRY Poll_PM() +{ + int res; + struct pcap_pkthdr *header; + const u_char *pkt_data; + + // Entered every 100 mS + + // if ARPFlag set, copy requested ARP record (For BPQStatus GUI) + + if (ARPFlag != -1) + { + memcpy(&Arp, ARPRecords[ARPFlag], sizeof (ARPDATA)); + ARPFlag = -1; + } + + SecTimer--; + + if (SecTimer == 0) + { + SecTimer = 10; + DoARPTimer(); + DoRouteTimer(); + } + +Pollloop: + +#ifdef WIN32 + + if (adhandle) + { + res = pcap_next_exx(adhandle, &header, &pkt_data); + + if (res > 0) + { + PETHMSG ethptr = (PETHMSG)&Buffer[EthOffset]; + + if (header->len > 1514) + { +// Debugprintf("Ether Packet Len = %d", header->len); + goto Pollloop; + } + + memcpy(&Buffer[EthOffset],pkt_data, header->len); + + if (ethptr->ETYPE == 0x0008) + { + ProcessEthIPMsg((PETHMSG)&Buffer[EthOffset]); + // PIPMSG ipptr = (PIPMSG)&Buffer[EthOffset+14]; + // ProcessIPMsg(ipptr, ethptr->SOURCE, 'E', 255); + goto Pollloop; + } + + if (ethptr->ETYPE == 0x0608) + { + ProcessEthARPMsg((PETHARP)ethptr); + goto Pollloop; + } + + // Ignore anything else + + goto Pollloop; + } + else + { + if (res < 0) + { + char * error = (char *)pcap_geterrx(adhandle) ; + Debugprintf(error); + if (OpenPCAP() == FALSE) + pcap_reopen_delay = 300; + } + } + } + else + { + // No handle. + + if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch + { + // Try reopening periodically + + pcap_reopen_delay --; + + if (pcap_reopen_delay < 0) + if (OpenPCAP() == FALSE) + pcap_reopen_delay = 300; // Retry every 30 seconds + } + } + +#endif + + + return TRUE; +} + + +static BOOL Send_ETH(VOID * Block, DWORD len) +{ +#ifdef WIN32 + if (adhandle) + { +// if (len < 60) len = 60; + + // Send down the packet + + pcap_sendpacketx(adhandle, // Adapter + Block, // buffer with the packet + len); // size + } +#endif + return TRUE; +} + + +static VOID SendIPtoBPQDEV(PIPMSG IPptr, UCHAR * HWADDR) +{ + // AX.25 headers are bigger, so there will always be room in buffer for enet header + + PETHMSG Ethptr = (PETHMSG)IPptr; + int Len; + + (UCHAR *)Ethptr--; + + Len = ntohs(IPptr->IPLENGTH); + + Len+=14; // Add eth Header + + memcpy(Ethptr->DEST, HWADDR, 6); + memcpy(Ethptr->SOURCE, ourMACAddr, 6); + Ethptr->ETYPE= 0x0008; + + Send_ETH(Ethptr,Len); + + return; +} + +static VOID ProcessEthIPMsg(PETHMSG Buffer) + +{ + PIPMSG ipptr = (PIPMSG)&Buffer[1]; + + if (memcmp(Buffer, ourMACAddr,6 ) != 0) + return; // Not for us + + if (memcmp(&Buffer[6], ourMACAddr,6 ) == 0) + return; // Discard our sends + + // if Checkum offload is active we get the packet before the NIC sees it (from PCAP) + + if (ipptr->IPCHECKSUM == 0) // Windows seems to do this + { + // Generate IP and TCP/UDP checksums + + int Len = ntohs(ipptr->IPLENGTH); + Len-=20; + + ipptr->IPCHECKSUM = Generate_CHECKSUM(ipptr, 20); + + if (ipptr->IPPROTOCOL == 6) // TCP + { + PTCPMSG TCP = (PTCPMSG)&ipptr->Data; + PHEADER PH = {0}; + + PH.IPPROTOCOL = 6; + PH.LENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &ipptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &ipptr->IPDEST, 4); + + TCP->CHECKSUM = ~Generate_CHECKSUM(&PH, 12); + TCP->CHECKSUM = Generate_CHECKSUM(TCP, Len); + } + } + ProcessIPMsg(ipptr, Buffer->SOURCE, 'E', 255); +} + +static VOID ProcessEthARPMsg(PETHARP arpptr) +{ + int i=0; + PARPDATA Arp; + BOOL Found; + + if (memcmp(&arpptr->MSGHDDR.SOURCE, ourMACAddr,6 ) == 0 ) + return; // Discard our sends + + switch (arpptr->ARPOPCODE) + { + case 0x0100: + + // We should only accept requests from our subnet - we might have more than one net on iterface + + if ((arpptr->SENDIPADDR & OurNetMask) != (OurIPAddr & OurNetMask)) + return; + + if (arpptr->TARGETIPADDR == 0) // Request for 0.0.0.0 + return; + + // Add to our table, as we will almost certainly want to send back to it + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto AlreadyThere; // Already there + + if (Arp == NULL) return; // No point of table full + + Arp->IPADDR = arpptr->SENDIPADDR; + Arp->ARPTYPE = 'E'; + Arp->ARPINTERFACE = 255; + Arp->ARPTIMER = ARPTIMEOUT; + + SaveARP(); + +AlreadyThere: + + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6); + Arp->ARPVALID = TRUE; + + if (arpptr->TARGETIPADDR == OurIPAddr) + { + uint32_t Save = arpptr->TARGETIPADDR; + + arpptr->ARPOPCODE = 0x0200; + memcpy(arpptr->TARGETHWADDR, arpptr->SENDHWADDR ,6); + memcpy(arpptr->SENDHWADDR, ourMACAddr ,6); + + arpptr->TARGETIPADDR = arpptr->SENDIPADDR; + arpptr->SENDIPADDR = Save; + + memcpy(arpptr->MSGHDDR.DEST, arpptr->MSGHDDR.SOURCE ,6); + memcpy(arpptr->MSGHDDR.SOURCE, ourMACAddr ,6); + + Send_ETH(arpptr,42); + + return; + + } + + break; + + + case 0x0200: + + if (memcmp(&arpptr->MSGHDDR.DEST, ourMACAddr,6 ) != 0 ) + return; // Not for us + + // Update ARP Cache + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto Update; + + if (Arp == NULL) + goto SendBack; + +Update: + Arp->IPADDR = arpptr->SENDIPADDR; + + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6); + Arp->ARPTYPE = 'E'; + Arp->ARPINTERFACE = 255; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + SaveARP(); + +SendBack: + + // Send Back to Originator of ARP Request + + if (arpptr->TARGETIPADDR == OurIPAddr) // Reply to our request? + break; + + default: + break; + } + return; +} + +static int CheckSumAndSend(PIPMSG IPptr, PTCPMSG TCPmsg, USHORT Len) +{ + struct _IPMSG PH = {0}; + IPptr->IPCHECKSUM = 0; + + PH.IPPROTOCOL = 6; + PH.IPLENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &IPptr->IPDEST, 4); + + TCPmsg->CHECKSUM = ~Generate_CHECKSUM(&PH, 20); + TCPmsg->CHECKSUM = Generate_CHECKSUM(TCPmsg, Len); + + // No need to do IP checksum as RouteIPMessage doesit + +// CHECKSUM IT + +// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + MapRouteIPMsg(IPptr); + return 0; +} + + +static VOID ProcessIPMsg(PIPMSG IPptr, UCHAR * MACADDR, char Type, UCHAR Port) +{ + uint32_t Dest; + PARPDATA Arp; + BOOL Found; + int index, Len; + PTCPMSG TCPptr; + PUDPMSG UDPptr; + + + if (IPptr->VERLEN != 0x45) return; // Only support Type = 4, Len = 20 + + if (!CheckIPChecksum(IPptr)) return; + + // Make sure origin ia in ARP Table + + Arp = LookupARP(IPptr->IPSOURCE.addr, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Arp != NULL) + { + Arp->IPADDR = IPptr->IPSOURCE.addr; + + if (Type == 'E') + { + memcpy(Arp->HWADDR, MACADDR, 6); + } + else + { + memcpy(Arp->HWADDR, MACADDR, 7); + Arp->HWADDR[6] &= 0x7e; + } + Arp->ARPTYPE = Type; + Arp->ARPINTERFACE = Port; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + + SaveARP(); + } + } + else + Arp->ARPTIMER = ARPTIMEOUT; // Refresh + + // See if for us - if not pass to router + + Dest = IPptr->IPDEST.addr; + + if (Dest == OurIPAddr || Dest == 0xffffffff || Dest == OurIPBroadcast) + goto ForUs; + + return; + +ForUs: + +// if (IPptr->IPPROTOCOL == 4) // AMPRNET Tunnelled Packet +// { +// ProcessTunnelMsg(IPptr); +// return; +// } + + if (IPptr->IPPROTOCOL == 1) // ICMP + { + ProcessICMPMsg(IPptr); + return; + } + + // Support UDP for SNMP + + if (IPptr->IPPROTOCOL == 17) // UDP + { + UDPptr = (PUDPMSG)&IPptr->Data; + + if (UDPptr->DESTPORT == htons(161)) + { + ProcessSNMPMessage(IPptr); + return; + } + } + + // See if for a mapped Address + + if (IPptr->IPPROTOCOL != 6) return; // Only TCP + + TCPptr = (PTCPMSG)&IPptr->Data; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + for (index=0; index < map_table_len; index++) + { + if ((map_table[index].sourceport == TCPptr->DESTPORT) && + map_table[index].sourceipaddr == IPptr->IPSOURCE.addr) + { + // Outgoing Message - replace Dest IP address and Port. Source Port remains unchanged + + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPDEST.addr = map_table[index].mappedipaddr; + TCPptr->DESTPORT = map_table[index].mappedport; + CheckSumAndSend(IPptr, TCPptr, Len); + return; + } + + if ((map_table[index].mappedport == TCPptr->SOURCEPORT) && + map_table[index].mappedipaddr == IPptr->IPSOURCE.addr) + { + // Incomming Message - replace Dest IP address and Source Port + + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPDEST.addr = map_table[index].sourceipaddr; + TCPptr->SOURCEPORT = map_table[index].sourceport; + CheckSumAndSend(IPptr, TCPptr, Len); + return; + } + } +} + +static VOID ProcessICMPMsg(PIPMSG IPptr) +{ + int Len; + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + Check_Checksum(ICMPptr, Len); + + if (ICMPptr->ICMPTYPE == 8) + { + // ICMP_ECHO + + ICMPptr->ICMPTYPE = 0; // Convert to Reply + + // CHECKSUM IT + + ICMPptr->ICMPCHECKSUM = 0; + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, Len); + + // Swap Dest to Origin + + IPptr->IPDEST = IPptr->IPSOURCE; + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPTTL = IPTTL; + +// IPptr->IPCHECKSUM = 0; +// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); // RouteIPMsg redoes checksum + + MapRouteIPMsg(IPptr); // Send Back + } + + if (ICMPptr->ICMPTYPE == 0) + { + // ICMP_REPLY: + + // I don't see how Portmapper should be getting ping responses + +/* + UCHAR * BUFFER = GetBuff(); + UCHAR * ptr1; + struct _MESSAGE * Msg; + TRANSPORTENTRY * Session = L4TABLE; + char IP[20]; + unsigned char work[4]; + + Session += ICMPptr->ICMPID; + + if (BUFFER == NULL) + return; + + ptr1 = &BUFFER[7]; + + memcpy(work, &IPptr->IPSOURCE, 4); + sprintf(IP, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + *ptr1++ = 0xf0; // PID + + ptr1 += sprintf(ptr1, "Ping Response from %s", IP); + + *ptr1++ = 0x0d; // CR + + Len = ptr1 - BUFFER; + + Msg = (struct _MESSAGE *)BUFFER; + Msg->LENGTH = Len; + Msg->CHAIN = NULL; + + C_Q_ADD(&Session->L4TX_Q, (UINT *)BUFFER); + + PostDataAvailable(Session); +*/ + return; + } +} + + +static VOID SendICMPMessage(PIPMSG IPptr, int Type, int Code, int P2) +{ + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + UCHAR * ptr; + + if (OurIPAddr == 0) + return; // Can't do much without one + + if (IPptr->IPPROTOCOL == ICMP && ICMPptr->ICMPTYPE == 11) + return; // Don't send Time Exceeded for TimeExceded + + // Copy the Original IP Header and first 8 bytes of packet down the buffer + + ptr = (UCHAR *) ICMPptr; + + memmove(ptr + 8, IPptr, 28); // IP header plus 8 data + +// We swap Souce to Dest, Convert to ICMP 11 and send back first 8 bytes of packet after header + + IPptr->IPDEST = IPptr->IPSOURCE; + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPPROTOCOL = ICMP; + IPptr->IPTTL = IPTTL; + IPptr->FRAGWORD = 0; + IPptr->IPLENGTH = htons(56); // IP Header ICMP Header IP Header 8 Data + + memset (ICMPptr, 0, 8); + ICMPptr->ICMPTYPE = Type; + ICMPptr->ICMPCODE = Code; + ICMPptr->ICMPSEQUENCE = htons(P2); + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, 36); + + MapRouteIPMsg(IPptr); +} + +static VOID MapRouteIPMsg(PIPMSG IPptr) +{ + PARPDATA Arp; + BOOL Found; + + // We rely on the ARP messages generated by either end to route frames. + // If address is not in ARP cache (say call originated by MSYS), send to our default route + + // Decremnent TTL and Recalculate header checksum + + IPptr->IPTTL--; + + if (IPptr->IPTTL == 0) + { + SendICMPTimeExceeded(IPptr); + return; // Should we send time exceeded???? + } + + IPptr->IPCHECKSUM = 0; + IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + // Look up ARP + + Arp = LookupARP(IPptr->IPDEST.addr, FALSE, &Found); + + // If enabled, look in Net44 Encap Routes + + if (!Found && DefaultIPAddr) + Arp = LookupARP(DefaultIPAddr, FALSE, &Found); + + if (!Found) + return; // No route or default + + if (Arp == NULL) + return; // Should we try to ARP it? + + if (Arp->ARPVALID) + { + SendIPtoBPQDEV(IPptr, Arp->HWADDR); + } + + return; +} + +static PROUTEENTRY AllocRouteEntry() +{ + PROUTEENTRY Routeptr; + + if (NumberofRoutes == 0) + + RouteRecords = malloc(sizeof(void *)); + else + RouteRecords = realloc(RouteRecords,(NumberofRoutes + 1) * sizeof(void *)); + + Routeptr = zalloc(sizeof(ROUTEENTRY)); + + if (Routeptr == NULL) return NULL; + + RouteRecords[NumberofRoutes++] = Routeptr; + + return Routeptr; +} + + +static PARPDATA AllocARPEntry() +{ + ARPDATA * ARPptr; + + if (NumberofARPEntries == 0) + + ARPRecords = malloc(sizeof(void *)); + else + ARPRecords = realloc(ARPRecords, (NumberofARPEntries+1)*sizeof(void *)); + + ARPptr = malloc(sizeof(ARPDATA)); + + if (ARPptr == NULL) return NULL; + + memset(ARPptr, 0, sizeof(ARPDATA)); + + ARPRecords[NumberofARPEntries++] = ARPptr; + + return ARPptr; +} + + static VOID SendARPMsg(PARPDATA Arp) + { + // Send ARP. Initially used only to find default gateway + + Arp->ARPTIMER = 5; // Retry periodically + + ETHARPREQMSG.ARPOPCODE = 0x0100; // ; REQUEST + + ETHARPREQMSG.TARGETIPADDR = Arp->IPADDR; + memset(ETHARPREQMSG.TARGETHWADDR, 0, 6); + + ETHARPREQMSG.SENDIPADDR = OurIPAddr; + memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr, 6); + + memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); + memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6); + + Send_ETH(ÐARPREQMSG, 42); + + return; + } + +static PROUTEENTRY FindRoute(uint32_t IPADDR) +{ + PROUTEENTRY Route = NULL; + int i; + + for (i = 0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + + if ((IPADDR & Route->SUBNET) == Route->NETWORK) + return Route; + } + return NULL; +} + + + +static PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found) +{ + PROUTEENTRY Route = NULL; + int i; + + for (i = 0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + + if (Route->NETWORK == IPADDR && Route->SUBNET == Mask) + { + *Found = TRUE; + return Route; + } + } + + // Not Found + + *Found = FALSE; + + if (Add) + { + Route = AllocRouteEntry(); + return Route; + } + else + return NULL; +} + +static PARPDATA LookupARP(uint32_t IPADDR, BOOL Add, BOOL * Found) +{ + PARPDATA Arp = NULL; + int i; + + for (i=0; i < NumberofARPEntries; i++) + { + Arp = ARPRecords[i]; + + if (Arp->IPADDR == IPADDR) + { + *Found = TRUE; + return Arp; + } + } + + // Not Found + + *Found = FALSE; + + if (Add) + { + Arp = AllocARPEntry(); + return Arp; + } + else + return NULL; +} +static VOID RemoveARP(PARPDATA Arp); + +static VOID RemoveRoute(PROUTEENTRY Route) +{ + int i; + + for (i=0; i < NumberofRoutes; i++) + { + if (Route == RouteRecords[i]) + { + while (i < NumberofRoutes) + { + RouteRecords[i] = RouteRecords[i+1]; + i++; + } + + if (Route->ARP) + { + PARPDATA Arp = Route->ARP; + Route->ARP->ARPROUTE = NULL; // Avoid recursion + RemoveARP(Arp); + } + + free(Route); + NumberofRoutes--; + return; + } + } +} + + +static VOID RemoveARP(PARPDATA Arp) +{ + int i; + + if (Arp->IPADDR == DefaultIPAddr) + { + // Dont remove Default Gateway. Set to re-resolve + + Arp->ARPVALID = FALSE; + Arp->ARPTIMER = 5; + return; + } + + for (i=0; i < NumberofARPEntries; i++) + { + if (Arp == ARPRecords[i]) + { + while (i < NumberofARPEntries) + { + ARPRecords[i] = ARPRecords[i+1]; + i++; + } + + // Remove linked route + + if (Arp->ARPROUTE) + { + PROUTEENTRY Route = Arp->ARPROUTE; + Arp->ARPROUTE->ARP = NULL; // Avoid recursion + RemoveRoute(Route); + } + + free(Arp); + NumberofARPEntries--; + return; + } + } +} + + +static BOOL ReadConfigFile() +{ + +// IPAddr 192.168.0.129 +// IPBroadcast 192.168.0.255 +// IPGateway 192.168.0.1 +// IPPorts 1,4 + +// MAP 192.168.0.100 1001 n9pmo.dyndns.org 1000 + + char * Config; + char * ptr1, * ptr2; + + char buf[256],errbuf[256]; + + map_table_len = 0; // For reread + + Config = PortConfig[PortMapConfigSlot]; // Config from bpq32.cfg + + if (Config) + { + // Using config from bpq32.cfg + + ptr1 = Config; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!ProcessLine(buf)) + { + WritetoConsoleLocal("PortMapper bad config record "); + strcat(errbuf, "\n"); + WritetoConsoleLocal(errbuf); + } + } + } + return (TRUE); +} + + +static int ProcessLine(char * buf) +{ + char * ptr, * p_value, * p_origport, * p_host, * p_port; + int port, mappedport, ipad; + + ptr = strtok(buf, " \t\n\r"); + p_value = strtok(NULL, " \t\n\r"); + + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if(_stricmp(ptr,"ADAPTER") == 0) + { +#ifndef WIN32 + WritetoConsoleLocal("IPGating to Ethernet is not supported in this build\n"); + return TRUE; +#endif + strcpy(Adapter,p_value); + return (TRUE); + } + + if(_stricmp(ptr,"promiscuous") == 0) + { + Promiscuous = atoi(p_value); + return (TRUE); + } + + if (_stricmp(ptr,"IPAddr") == 0) + { + OurIPAddr = inet_addr(p_value); + + if (OurIPAddr == INADDR_NONE) return (FALSE); + + return (TRUE); + } + if (_stricmp(ptr,"IPBroadcast") == 0) + { + OurIPBroadcast = inet_addr(p_value); + + if (OurIPBroadcast == INADDR_NONE) return (FALSE); + + return (TRUE); + } + + if (_stricmp(ptr,"IPNetMask") == 0) + { + OurNetMask = inet_addr(p_value); + + if (OurNetMask == INADDR_NONE) return (FALSE); + + return (TRUE); + } + + + if (_stricmp(ptr,"IPGateway") == 0) + { + DefaultIPAddr = inet_addr(p_value); + + if (DefaultIPAddr == INADDR_NONE) return (FALSE); + + return (TRUE); + } + +// ARP 44.131.4.18 GM8BPQ-7 1 D + + if (_stricmp(ptr,"MAP") == 0) + { +#ifdef LINBPQ + + WritetoConsoleLocal("MAP not supported in LinBPQ IP Gateway\n"); + return TRUE; +#endif + if (!p_value) return FALSE; + + p_origport = strtok(NULL, " ,\t\n\r"); + if (!p_origport) return FALSE; + + p_host = strtok(NULL, " ,\t\n\r"); + if (!p_host) return FALSE; + + p_port = strtok(NULL, " ,\t\n\r"); + if (!p_port) return FALSE; + + port=atoi(p_origport); + if (port == 0) return FALSE; + + mappedport=atoi(p_port); + if (mappedport == 0) return FALSE; + + ipad = inet_addr(p_value); + + map_table[map_table_len].sourceipaddr = ipad; + strcpy(map_table[map_table_len].hostname, p_host); + map_table[map_table_len].sourceport = ntohs(port); + map_table[map_table_len++].mappedport = ntohs(mappedport); + + NeedResolver = TRUE; + + return (TRUE); + } + + // + // Bad line + // + return (FALSE); + +} + +static VOID DoARPTimer() +{ + PARPDATA Arp = NULL; + int i; + + for (i=0; i < NumberofARPEntries; i++) + { + Arp = ARPRecords[i]; + + if (!Arp->ARPVALID) + { + Arp->ARPTIMER--; + + if (Arp->ARPTIMER == 0) + { + // Retry Request + + SendARPMsg(Arp); + } + continue; + } + + // Time out active entries + + if (Arp->LOCKED == 0) + { + Arp->ARPTIMER--; + + if (Arp->ARPTIMER == 0) + { + // Remove Entry + + RemoveARP(Arp); + SaveARP(); + } + } + } +} + +static VOID DoRouteTimer() +{ + int i; + PROUTEENTRY Route; + + for (i=0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + if (Route->RIPTIMOUT) + Route->RIPTIMOUT--; + } +} + + +// PCAP Support Code + + +#ifdef WIN32 + +static FARPROCX GetAddress(char * Proc) +{ + FARPROCX ProcAddr; + int err=0; + char buf[256]; + int n; + + + ProcAddr=(FARPROCX) GetProcAddress(PcapDriver,Proc); + + if (ProcAddr == 0) + { + err=GetLastError(); + + n=sprintf(buf,"Error finding %s - %d", Proc,err); + WritetoConsoleLocal(buf); + + return(0); + } + + return ProcAddr; +} + + +static void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); + +static int OpenPCAP() +{ + u_long param=1; + BOOL bcopt=TRUE; + int i=0; + char errbuf[PCAP_ERRBUF_SIZE]; + u_int netmask; + char packet_filter[64]; + struct bpf_program fcode; + char buf[256]; + int n; + + + PcapDriver=LoadLibrary(Dllname); + + if (PcapDriver == NULL) return(FALSE); + + if ((pcap_sendpacketx=GetAddress("pcap_sendpacket")) == 0 ) return FALSE; + + if ((pcap_datalinkx=GetAddress("pcap_datalink")) == 0 ) return FALSE; + + if ((pcap_compilex=GetAddress("pcap_compile")) == 0 ) return FALSE; + + if ((pcap_setfilterx=GetAddress("pcap_setfilter")) == 0 ) return FALSE; + + pcap_open_livex = (pcap_t * (__cdecl *)(const char *, int, int, int, char *)) GetProcAddress(PcapDriver,"pcap_open_live"); + + if (pcap_open_livex == NULL) return FALSE; + + if ((pcap_geterrx=GetAddress("pcap_geterr")) == 0 ) return FALSE; + + if ((pcap_next_exx=GetAddress("pcap_next_ex")) == 0 ) return FALSE; + + /* Open the adapter */ + + adhandle = pcap_open_livex(Adapter, // name of the device + 65536, // portion of the packet to capture. + // 65536 grants that the whole packet will be captured on all the MACs. + Promiscuous, // promiscuous mode (nonzero means promiscuous) + 1, // read timeout + errbuf // error buffer + ); + + if (adhandle == NULL) + return FALSE; + + /* Check the link layer. We support only Ethernet for simplicity. */ + + if(pcap_datalinkx(adhandle) != DLT_EN10MB) + { + n=sprintf(buf,"\nThis program works only on Ethernet networks.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } + + netmask=0xffffff; + +// sprintf(packet_filter,"ether[12:2]=0x0800 or ether[12:2]=0x0806"); + + sprintf(packet_filter,"ether broadcast or ether dst %02x:%02x:%02x:%02x:%02x:%02x", + ourMACAddr[0], ourMACAddr[1], ourMACAddr[2], + ourMACAddr[3], ourMACAddr[4], ourMACAddr[5]); + + //compile the filter + + if (pcap_compilex(adhandle, &fcode, packet_filter, 1, netmask) <0 ) + { + n=sprintf(buf,"\nUnable to compile the packet filter. Check the syntax.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } + + //set the filter + + if (pcap_setfilterx(adhandle, &fcode)<0) + { + n=sprintf(buf,"\nError setting the filter.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } + + return TRUE; +} +#endif + + +int CompareMasks (const VOID * a, const VOID * b); + + +#ifndef LINBPQ + +extern HFONT hFont; +struct tagMSG Msg; +char buf[1024]; + +static LRESULT CALLBACK ResWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + struct hostent * hostptr; + struct in_addr ipad; + char line[100]; + int index,displayline; + MINMAXINFO * mmi; + + int i=1; + + int nScrollCode,nPos; + + switch (message) + { + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 400; + mmi->ptMaxSize.y = Windowlength; + mmi->ptMaxTrackSize.x = 400; + mmi->ptMaxTrackSize.y = Windowlength; + break; + + case WM_USER+199: + + i=WSAGETASYNCERROR(lParam); + + map_table[ResolveIndex].error=i; + + if (i ==0) + { + // resolved ok + + hostptr=(struct hostent *)&buf; + memcpy(&map_table[ResolveIndex].mappedipaddr,hostptr->h_addr,4); + } + + InvalidateRect(hWnd,NULL,FALSE); + + while (ResolveIndex < map_table_len) + { + ResolveIndex++; + + WSAAsyncGetHostByName (hWnd,WM_USER+199, + map_table[ResolveIndex].hostname, + buf,MAXGETHOSTSTRUCT); + + break; + } + break; + + case WM_MDIACTIVATE: + { + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (WPARAM)hPopMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM)hWndMenu); + } + else + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM)hMainFrameMenu, (LPARAM)NULL); + + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + + + if (wmId == BPQREREAD) + { + if (ProcessConfig()) + { + FreeConfig(); + + ReadConfigFile(); + PostMessage(hIPResWnd, WM_TIMER,0,0); + InvalidateRect(hWnd,NULL,TRUE); + } + else + Consoleprintf("Failed to reread config file - leaving config unchanged"); + + return 0; + } +/* + if (wmId == BPQADDARP) + { + if (ConfigWnd == 0) + { + ConfigWnd=CreateDialog(hInstance,ConfigClassName,0,NULL); + + if (!ConfigWnd) + { + i=GetLastError(); + return (FALSE); + } + ShowWindow(ConfigWnd, SW_SHOW); + UpdateWindow(ConfigWnd); + } + + SetForegroundWindow(ConfigWnd); + + return(0); + } + return 0; +*/ + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case SC_RESTORE: + + IPMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + break; + + case SC_MINIMIZE: + + IPMinimized = TRUE; + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_VSCROLL: + + nScrollCode = (int) LOWORD(wParam); // scroll bar value + nPos = (short int) HIWORD(wParam); // scroll box position + + //hwndScrollBar = (HWND) lParam; // handle of scroll bar + + if (nScrollCode == SB_LINEUP || nScrollCode == SB_PAGEUP) + { + baseline--; + if (baseline <0) + baseline=0; + } + + if (nScrollCode == SB_LINEDOWN || nScrollCode == SB_PAGEDOWN) + { + baseline++; + if (baseline > map_table_len) + baseline = map_table_len; + } + + if (nScrollCode == SB_THUMBTRACK) + { + baseline=nPos; + } + + SetScrollPos(hWnd,SB_VERT,baseline,TRUE); + + InvalidateRect(hWnd,NULL,TRUE); + break; + + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + index = baseline; + displayline=0; + + while (index < map_table_len) + { + if (map_table[index].ResolveFlag && map_table[index].error != 0) + { + // resolver error - Display Error Code + sprintf(hostaddr,"Error %d",map_table[index].error); + } + else + { + memcpy(&ipad,&map_table[index].mappedipaddr,4); + strncpy(hostaddr,inet_ntoa(ipad),16); + } + + memcpy(&ipad,&map_table[index].mappedipaddr,4); + + i=sprintf(line,"%.64s = %-.30s", + map_table[index].hostname, + hostaddr); + + TextOut(hdc,0,(displayline++)*14+2,line,i); + + index++; + } + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + + +// PostQuitMessage(0); + + break; + + + case WM_TIMER: + + for (ResolveIndex=0; ResolveIndex < map_table_len; ResolveIndex++) + { + WSAAsyncGetHostByName (hWnd,WM_USER+199, + map_table[ResolveIndex].hostname, + buf,MAXGETHOSTSTRUCT); + break; + } + + default: + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} + +static void IPResolveNames( void *dummy ) +{ + SetTimer(hIPResWnd,1,15*60*1000,0); + + PostMessage(hIPResWnd, WM_TIMER,0,0); + + while (GetMessage(&Msg, hIPResWnd, 0, 0)) + { + TranslateMessage(&Msg); + DispatchMessage(&Msg); + } +} + +#endif + +/* +; DO PSEUDO HEADER FIRST +; + MOV DX,600H ; PROTOCOL (REVERSED) + MOV AX,TCPLENGTH ; TCP LENGTH + XCHG AH,AL + ADD DX,AX + MOV AX,WORD PTR LOCALADDR[BX] + ADC DX,AX + MOV AX,WORD PTR LOCALADDR+2[BX] + ADC DX,AX + MOV AX,WORD PTR REMOTEADDR[BX] + ADC DX,AX + MOV AX,WORD PTR REMOTEADDR+2[BX] + ADC DX,AX + ADC DX,0 + + MOV PHSUM,DX + + PUSH BX + + MOV BX,TXBUFFER ; HEADER + + MOV CX,TCPLENGTH ; PUT LENGTH INTO HEADER + MOV BUFFLEN[BX],CX +; + MOV SI,BUFFPTR[BX] + + INC CX ; ROUND UP + SHR CX,1 ; WORD COUNT + + CALL DO_CHECKSUM + + ADD DX,PHSUM + ADC DX,0 + NOT DX + + MOV SI,BUFFPTR[BX] + MOV CHECKSUM[SI],DX + + +*/ diff --git a/.svn/pristine/0e/0efb7b73d192e43b1c3e8d52089e20083efa2b19.svn-base b/.svn/pristine/0e/0efb7b73d192e43b1c3e8d52089e20083efa2b19.svn-base new file mode 100644 index 0000000..cae501a --- /dev/null +++ b/.svn/pristine/0e/0efb7b73d192e43b1c3e8d52089e20083efa2b19.svn-base @@ -0,0 +1,161 @@ + +/* pngrio.c - functions for data input + * + * libpng 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all input. Users who need + * special handling are expected to write a function that has the same + * arguments as this and performs a similar function, but that possibly + * has a different input method. Note that you shouldn't change this + * function, but rather write a replacement function and then make + * libpng use it at run time with png_set_read_fn(...). + */ + +#define PNG_INTERNAL +#include "png.h" + +/* Read the data from whatever input you are using. The default routine + reads from a file pointer. Note that this routine sometimes gets called + with very small lengths, so you should implement some kind of simple + buffering if you are using unbuffered reads. This should never be asked + to read more then 64K on a 16 bit machine. */ +void /* PRIVATE */ +png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_debug1(4,"reading %d bytes\n", (int)length); + if (png_ptr->read_data_fn != NULL) + (*(png_ptr->read_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL read function"); +} + +#if !defined(PNG_NO_STDIO) +/* This is the function that does the actual reading of data. If you are + not reading from a standard C stream, you should create a replacement + read_data function and use it at run time with png_set_read_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = (png_size_t)fread(data, (png_size_t)1, length, + (png_FILE_p)png_ptr->io_ptr); +#endif + + if (check != length) + png_error(png_ptr, "Read Error"); +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void /* PRIVATE */ +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + int check; + png_byte *n_data; + png_FILE_p io_ptr; + + /* Check if data really is near. If so, use usual code. */ + n_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)n_data == data) + { +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = fread(n_data, 1, length, io_ptr); +#endif + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t read, remaining, err; + check = 0; + remaining = length; + do + { + read = MIN(NEAR_BUF_SIZE, remaining); +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(io_ptr), buf, read, &err, NULL) ) + err = 0; +#else + err = fread(buf, (png_size_t)1, read, io_ptr); +#endif + png_memcpy(data, buf, read); /* copy far buffer to near buffer */ + if(err != read) + break; + else + check += err; + data += read; + remaining -= read; + } + while (remaining != 0); + } + if ((png_uint_32)check != (png_uint_32)length) + png_error(png_ptr, "read Error"); +} +#endif +#endif + +/* This function allows the application to supply a new input function + for libpng if standard C streams aren't being used. + + This function takes as its arguments: + png_ptr - pointer to a png input data structure + io_ptr - pointer to user supplied structure containing info about + the input functions. May be NULL. + read_data_fn - pointer to a new input function that takes as its + arguments a pointer to a png_struct, a pointer to + a location where input data can be stored, and a 32-bit + unsigned int that is the number of bytes to be read. + To exit and output any fatal error messages the new write + function should call png_error(png_ptr, "Error msg"). */ +void PNGAPI +png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn) +{ + png_ptr->io_ptr = io_ptr; + +#if !defined(PNG_NO_STDIO) + if (read_data_fn != NULL) + png_ptr->read_data_fn = read_data_fn; + else + png_ptr->read_data_fn = png_default_read_data; +#else + png_ptr->read_data_fn = read_data_fn; +#endif + + /* It is an error to write to a read device */ + if (png_ptr->write_data_fn != NULL) + { + png_ptr->write_data_fn = NULL; + png_warning(png_ptr, + "It's an error to set both read_data_fn and write_data_fn in the "); + png_warning(png_ptr, + "same structure. Resetting write_data_fn to NULL."); + } + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_ptr->output_flush_fn = NULL; +#endif +} diff --git a/.svn/pristine/0f/0f360bb978fa349027d73174ce2a1e0fd2560283.svn-base b/.svn/pristine/0f/0f360bb978fa349027d73174ce2a1e0fd2560283.svn-base new file mode 100644 index 0000000..6711b53 --- /dev/null +++ b/.svn/pristine/0f/0f360bb978fa349027d73174ce2a1e0fd2560283.svn-base @@ -0,0 +1,964 @@ +// +// Common definitons for Pactor-like Modules + +#include "kernelresource.h" + +#include "rigcontrol.h" + +#define MAXBLOCK 4096 + +#define MAXFREQS 20 // RigControl freqs to scan + +extern char HFCTEXT[81]; +extern int HFCTEXTLEN; + +extern HANDLE hInstance; + +extern HMENU hMainFrameMenu; +extern HMENU hWndMenu; + +/* +struct WL2KInfo +{ + struct WL2KInfo * Next; + + char * Host; + short WL2KPort; + + char RMSCall[10]; + char BaseCall[10]; + char GridSquare[7]; + char Times[80]; + char ServiceCode[17]; + + BOOL UseRigCtrlFreqs; + char WL2KFreq[12]; + char WL2KMode; // WL2K reporting mode + char WL2KModeChar; // W or N + BOOL DontReportNarrowOnWideFreqs; + +// char NARROWMODE; +// char WIDEMODE; // Mode numbers to report to WL2K + +// struct WL2KInfo WL2KInfoList[MAXFREQS]; // Freqs for sending to WL2K + + int Freq; + char Bandwidth; +// char * TimeList; // eg 06-10,12-15 + int mode; // see below (an integer) + int baud; // see below (an integer) + int power; // actual power if known, default to 100 for HF, 30 for VHF/UHF (an integer) + int height; // antenna height in feet if known, default to 25 + int gain; // antenna gain if known, default to 0 + int direction; // primary antenna direction in degrees if known, use 000 for omni (an integer) + BOOL RPonPTC; // Set if scanning for Robust Packet on a PTC +}; + +*/ +#pragma pack(1) + +// AGWPE Header Structure + +struct AGWHEADER +{ + UCHAR Port; + UCHAR filler1[3]; + char DataKind; + UCHAR filler2; + unsigned char PID; + UCHAR filler3; + unsigned char callfrom[10]; + unsigned char callto[10]; + int DataLength; + int reserved; +}; + +#pragma pack() + +// Telnet Server User Record + +struct UserRec +{ + char * Callsign; + char * UserName; + char * Password; + char * Appl; // Autoconnect APPL + BOOL Secure; // Authorised User +}; + +struct LOCALNET +{ + struct LOCALNET * Next; + uint32_t Network; + uint32_t Mask; +}; + + +#define MaxCMS 10 // Number of addresses we can keep - currently 4 are used. + +struct TCPINFO +{ + int NumberofUsers; + struct UserRec ** UserRecPtr; + int CurrentConnections; + + struct UserRec RelayUser; + + int CurrentSockets; + + int TCPPort; + int FBBPort[100]; + int RelayPort; + int HTTPPort; + int APIPort; + int TriModePort; + int SyncPort; + int SNMPPort; + int DRATSPort; + int CMDPort[33]; + char RELAYHOST[64]; + char CMSServer[64]; + BOOL FallbacktoRelay; // Use Relsy if can't connect to CMS + + BOOL IPV4; // Allow Connect using IPV4 + BOOL IPV6; // Allow Connect using IPV6 + BOOL CMS; // Allow Connect to CMS + BOOL CMSOK; // Internet link is ok. + BOOL UseCachedCMSAddrs; + struct in_addr CMSAddr[MaxCMS]; + BOOL CMSFailed[MaxCMS]; // Set if connect to CMS failed. + char * CMSName[MaxCMS]; // Reverse DNS Name of Server + int NumberofCMSAddrs; + int NextCMSAddr; // Round Robin Pointer + int CheckCMSTimer; // CMS Poll Timer + + char SecureCMSPassword[80]; // For Secure CMS Signin + char GatewayCall[10]; // Call for CMS access + char GatewayLoc[10]; // Loc - Needed to report Hybrid Mode + int ReportHybrid; // Report as Hybrod Station + char * HybridServiceCode; + char * HybridFrequencies; + char * HybridCoLocatedRMS; + + BOOL DisconnectOnClose; + + char PasswordMsg[100]; + + char cfgHOSTPROMPT[100]; + + char cfgCTEXT[300]; + + char cfgLOCALECHO[100]; + + int MaxSessions; + + char LoginMsg[100]; + + char RelayAPPL[20]; + char SyncAPPL[20]; + + SOCKET TCPSock; + SOCKET FBBsock[100]; + SOCKET Relaysock; + SOCKET HTTPsock; + SOCKET APIsock; + SOCKET TriModeSock; + SOCKET TriModeDataSock; + SOCKET Syncsock; + SOCKET DRATSsock; + SOCKET SNMPsock; + + struct ConnectionInfo * TriModeControlSession; + SOCKET sock6; + SOCKET FBBsock6[100]; + SOCKET Relaysock6; + SOCKET HTTPsock6; + SOCKET APIsock6; + SOCKET Syncsock6; + SOCKET DRATSsock6; + + fd_set ListenSet; + SOCKET maxsock; + + HMENU hActionMenu; + HMENU hLogMenu; + HMENU hDisMenu; // Disconnect Menu Handle + HWND hCMSWnd; + + int SecureTelnet; + int ReportRelayTraffic; // Send WL2K Reports for Relay Traffic + + char * WebTermCSS; // css override for web terminal + struct LOCALNET * LocalNets; + +}; + + +struct STREAMINFO +{ +// TRANSPORTENTRY * AttachedSession; + + void * PACTORtoBPQ_Q; // Frames for BPQ + void * BPQtoPACTOR_Q; // Frames for PACTOR + int FramesOutstanding; // Frames Queued - used for flow control + int FramesQueued; // Frames Queued - used for flow control + BOOL InternalCmd; // Last Command was generated internally + int IntCmdDelay; // To limit internal commands + BOOL CheckingCall; // Set on PTC if waiting for I response after a Connect RXed + + BOOL Attached; // Set what attached to a BPQ32 stream + BOOL Connected; // When set, all data is passed as data instead of commands + BOOL Connecting; // Set when Outward Connect in progress + BOOL Disconnecting; // Set when disconnect in progress + // Used when appplication disconnects the bpq session, and + // prevents new attaches while a dirty disconnect is in progress + int DisconnectingTimeout; // A hard disconnect occurs if this expires before the disconnect complete + int ReportDISC; // Need to report an incoming DISC to kernel + BOOL DiscWhenAllSent; // Close session when all msgs have been sent to node + BOOL ARQENDSent; // Set when V4 ARQEND Sent + + int DEDStream; // Stream number for DED interface (same as index except for pactor) + + char MyCall[10] ; // Call we are using + char RemoteCall[10]; // Callsign + + char callingCall[10]; // for reporting. Link and Our calls depand on which end connected + char receivingCall[10]; // for reporting. Link and Our calls depand on which end connected + char Direction[4]; // In or Out + + + + char AGWKey[21]; // Session Key for AGW Session Based Drivers + + time_t ConnectTime; // Time connection made + time_t AttachTime; + + int bytesTXed; + int BytesAcked; + int bytesRXed; + int PacketsSent; + int BytesResent; + int BytesOutstanding; // For Packet Channels + + UCHAR PTCStatus0; // Status Bytes + UCHAR PTCStatus1; // Status Bytes + UCHAR PTCStatus2; // Status Bytes + UCHAR PTCStatus3; // Status Bytes + + char * CmdSet; // A series of commands to send to the TNC + char * CmdSave; // Base address for free + + struct ConnectionInfo * ConnectionInfo; // TCP Server Connection Info + + int TimeInRX; // Too long in send mode timer + int NeedDisc; // Timer to send DISC if appl not available + + BOOL NoCMSFallback; // Dont use relay if CMS not available + struct ARQINFO * ARQInfo; // FLDIGI/FLARQ Stream Mode Specific Data + + HWND xIDC_MYCALL; + HWND xIDC_DESTCALL; + HWND xIDC_STATUS; + HWND xIDC_SEND; + HWND xIDC_RXED; + HWND xIDC_RESENT; + HWND xIDC_ACKED; + HWND xIDC_DIRN; + + int RelaySyncStream; + int VaraACMode; +}; + +typedef struct AGWINFO +{ + // Fields for AGW Session based Ports (eg UZ7HO Modem) + + struct AGWHEADER TXHeader; + struct AGWHEADER RXHeader; + int MaxSessions; + int ConnTimeOut; + int PollDelay; + time_t LastParamTime; + +#ifdef WIN32 + + // For selecting UZ7HO Mode and Freq + + COMBOBOXINFO cbinfo; // UZ7HO Modem Combo Box info + HWND hFreq; // UZ7HO Frequency Box + HWND hSpin; // UZ7HO Spin Button + +#endif + + int isQTSM; // Flag to Identify QtSM + + int CenterFreq; + int Modem; // Modem number in list + char ModemName[20]; + unsigned char Version[4]; + unsigned char fx25Flags; + unsigned char il2pFlags; + unsigned char il2pcrc; + +} *PAGWINFO; + +typedef struct ARQINFO +{ + // Fields for FLDIGI/FLARQ Ports + + // Max window is 64, though often will use less + + char OurStream; + char FarStream; + + PMSGWITHLEN TXHOLDQ[64]; // Frames waiting ACK + PMSGWITHLEN RXHOLDQ[64]; // Frames waiting missing frames. + + int TXWindow; + int RXWindow; + int MaxBlock; // Max sending block size + + int TXSeq; + int TXLastACK; // Last frame ACK'ed + + int RXHighest; + int RXNext; + int RXNoGaps; + + int Retries; + int NoAckRetries; // Status received but no data acked + int ARQTimer; + int ARQState; + +#define ARQ_ACTIVE 1 // Have a session of some type + + int ARQTimerState; + +#define ARQ_CONNECTING 1 +#define ARQ_CONNECTACK 2 +#define ARQ_DISC 3 +#define ARQ_WAITACK 4 +#define ARQ_WAITDATA 5 // Waiting for more data before polling + + char LastMsg[80]; // Last message sent that expects an ack + int LastLen; + char TXMsg[80]; // Message to aend after TXDELAY + int TXLen; + int TurnroundTimer; // RX to TX delay. + int TXDelay; + +} *ARQINFO; + +typedef struct FLINFO +{ + // Fields for MPSK Session Ports ) + + BOOL TX; // Set when FLDigi is transmitting + char DefaultMode[64]; // Mode to return to after session + int DefaultFreq; // Freq to return to after session + BOOL Beacon; // Use ALE Beacons + char LastXML[128]; // Last XML Request Sent + int XMLControl; // Controlls polling FLDigi by XML + int CmdControl; // Controlls polling FLDigi by KISS Command + BOOL FLARQ; // Connection from FLARQ + BOOL Busy; + BOOL CONOK; // Allow incoming connects + BOOL KISSMODE; // Using KISS instead of socket interface + BOOL RAW; // Raw (ARQ Socket or KISS RAW, depening on above) + int CenterFreq; + char CurrentMode[20]; // Mode to return to after session + int Responding; // If FLDigi is responding to conmands + BOOL MCASTMODE; // If port is in MCAST RX MOde + +} *FLINFO; + +typedef struct MPSKINFO +{ + // Fields for MPSK Session Ports ) + + int ConnTimeOut; + BOOL TX; // Set when Multipsk is transmitting + char DefaultMode[20]; // Mode to return to after session + BOOL Beacon; // Use ALE Beacons + int MaxSessions; +} *MPSKINFO; + +struct FreeDataINFO +{ + int startingTNC; + int TNCRunning; + int Conecting; + int Connected; + char ourCall[10]; + char toCall[10]; + char farCall[10]; // TNC Call + char useBaseCall; // Use base call (without ssid) for TNC Call + char * Capture; // Capture Device Name + char * Playback; // Playback Device Name + char * hamlibHost; + int hamlibPort; + + unsigned char toSendData[8192]; // Buffer data from node for more efficiency + int toSendCount; + int toSendTimeout; + unsigned char toSendMsg[256]; // Buffer data from node for more efficiency + int toSendMsgCount; + int toSendMsgTimeout; + char * RXDir; // Directory for Received Files + int CONOK; // Virtual Lisren Flag + int Chat; // In Chat Mode + char ChatCall[10]; + int needPoll; // Set if get data needed + int arqstate; // 1 = Disc / 2 - connecting 3 - connected + int TuningRange; // Must be 50, 100, 150, 200, 250 + int LimitBandWidth; + int TXLevel; + int Explorer; // Enable reporting to Freedata Explorer + char SSIDList[256]; + char * SSIDS[16]; +}; + +struct sixPackInfo; + +typedef struct TNCINFO +{ + HWND hDlg; // Status Window Handle + int (FAR * WebWindowProc)(struct TNCINFO * TNC, char * Buff, BOOL LOCAL); // Routine to build web status window + int WebWinX; + int WebWinY; // Size of window + char * WebBuffer; // Buffer for logs + int RigControlRow; // Rig Control Line in Dialog + struct _EXTPORTDATA * PortRecord; // BPQ32 port record for this port + struct RIGINFO * RIG; // Pointer to Rig Control RIG record for RX or Both + struct RIGINFO * TXRIG; // Pointer to Rig Control RIG record for TX + char * InitScript; // Initialisation Commands + int InitScriptLen; // Length + time_t SessionTimeLimit; // Optional limit to total session time + time_t DefaultSessionTimeLimit; // Configured value + + time_t AttachTimeLimit; // to trap port left attached for a long time without other activity + time_t AttachTime; + + int Hardware; // Hardware Type + +#define H_WINMOR 1 +#define H_SCS 2 +#define H_KAM 3 +#define H_AEA 4 +#define H_HAL 5 +#define H_TELNET 6 +#define H_TRK 7 +#define H_TRKM 7 +#define H_V4 8 +#define H_UZ7HO 9 +#define H_MPSK 10 +#define H_FLDIGI 11 +#define H_UIARQ 12 +#define H_ARDOP 13 +#define H_VARA 14 +#define H_SERIAL 15 +#define H_KISSHF 16 +#define H_WINRPR 17 +#define H_HSMODEM 18 +#define H_FREEDATA 19 +#define H_SIXPACK 20 + + + int Port; // BPQ Port Number + + struct RIGINFO DummyRig; // Used if not using Rigcontrol + + BOOL Minimized; // Start Minimized flag + + void * WINMORtoBPQ_Q; // Frames for BPQ, indexed by BPQ Port + void * BPQtoWINMOR_Q; // Frames for WINMOR. indexed by WINMOR port. Only used it TCP session is blocked + + SOCKET TCPSock; // Control Socket + SOCKET TCPDataSock; // Data Socket + SOCKET PacketSock; // Packet Over TCP (ARDOP) + + char * WINMORSignon; // Pointer to message for secure signin + char * HostName; // WINMOR Host - may be dotted decimal or DNS Name + int TCPPort; // + int PacketPort; // Packet Over TCP (ARDOP) + char * ApplCmd; // Application to connect to on incoming connect (null = leave at command handler) + BOOL SwallowSignon; // Set to suppress *** connected to APPL + + union + { + UCHAR TCPBuffer[1000]; // For converting byte stream to messages + UCHAR DEDBuffer[1000]; // For converting byte stream to messages + UCHAR KISSBuffer[1000]; // For KISS over Host Mode + }; + + UCHAR * ARDOPBuffer; // Needs to be pretty big, so Malloc + UCHAR * ARDOPDataBuffer; // Needs to be pretty big, so Malloc + + int InputLen; // Data we have already = Offset of end of an incomplete packet; + int DataInputLen; // Data we have already = Offset of end of an incomplete packet; + int KISSInputLen; // Data we have already = Offset of end of an incomplete packet; + int ESCFLAG; // KISS Escape received + + int MSGCOUNT; // DED WORKING FIELD + int MSGLENGTH; // DED Msg Len + int MSGCHANNEL; // DED Msg Channel Number + int MSGTYPE; // DED Msg Type + + int HOSTSTATE; // ded HOST state machine + + + BOOL StartSent; // Codec Start send (so will get a disconnect) + int ConnectPending; // Set if Connect Pending Received. If so, mustn't allow freq change. + BOOL GavePermission; // Set if we allowed freq change + BOOL DiscPending; // Set if Disconnect Pending Received. So we can time out stuck in Disconnecting + BOOL HadConnect; // Flag to say have been in session + BOOL FECMode; // In FEC Mode + BOOL FEC1600; // Use 1600 Hz FEC Mode + int FECIDTimer; // Time in FEC Mode. Used to trigger ID broadcasts + BOOL RestartAfterFailure; + BOOL StartInRobust; // For WINMOR, set to Robust Mode for first few packets + + int Busy; // Channel Busy Timer/Counter . Non-zero = Busy + + int BusyFlags; // Channel Busy Flags + +#define CDBusy 1 // For WINMOR - reported busy (set till reported clear) +#define PTTBusy 2 // PTT Active + + BOOL FECPending; // Need an FEC Send when channel is next idle + + time_t lasttime; + + BOOL CONNECTING; // TCP Session Flags + BOOL CONNECTED; + BOOL Alerted; // Connect Failed Prompt sent + BOOL DATACONNECTING; + BOOL DATACONNECTED; + + BOOL TNCCONNECTING; // For FreeData + BOOL TNCCONNECTED; + + BOOL QtSMConnected; + + char NodeCall[10]; // Call we listen for (PORTCALL or NODECALL + char CurrentMYC[10]; // Save current call so we don't change it unnecessarily + char * LISTENCALLS; // Calls TNC will respond to (currently only for VARA) + + char TargetCall[10]; // Call incoming connect is addressed to (for appl call support) + + struct sockaddr_in destaddr; + struct sockaddr_in Datadestaddr; + + int PTTMode; // PTT Mode Flags + int PTTState; // Current State + uint64_t PTTActivemS; // For Stats + uint64_t PTTonTime; // + + uint64_t BusyActivemS; // For channel busy stats + uint64_t BusyonTime; + + char PTTOn[60]; // Port override of RIGCONTROL config + char PTTOff[60]; + int PTTOnLen; + int PTTOffLen; + + int TXRadio; // Rigcontrol Radio Number for TX + int RXRadio; // Rigcontrol Radio Number for RX + + long long int TXFreq; // Freq to set on tx before ptt + double ActiveTXFreq; // Freq to set on tx after attach + double ActiveRXFreq; // Freq to set on rx after attach + + double DefaultTXFreq; // Freq to set on tx after close + double DefaultRXFreq; // Freq to set on rx after close + + char ** DisconnectScript; // May replace above 2 params + + int TXOffset; // Correction to TXFreq + + int PID; // Process ID for Software TNC + HWND hWnd; // Main window handle for Software TNC + + char * CaptureDevices; + char * PlaybackDevices; + char * ProgramPath; + BOOL WeStartedTNC; + + int Restarts; // TNC Kill/Restarts done + time_t LastRestart; + + int TimeSinceLast; // Time since last message from TNC (10ths of a sec) + int HeartBeat; + +// int Interlock; // Port Interlock Group + + HWND hMonitor; // Handle to Monitor control +// HMENU hPopMenu; // Actions Menu Handle + + int MaxConReq; // For ARDOP + int BusyHold; // Hold Time from SCS reporting channel free till we call + int BusyWait; // Time to wait for clear channel before connect + + BOOL OverrideBusy; + int BusyDelay; // Timer for busy timeout + int AutoStartDelay; // Time to wait for TNC to start + char * ConnectCmd; // Saved command if waiting for busy to clear + BOOL UseAPPLCalls; // Robust Packet to use Applcalls + BOOL UseAPPLCallsforPactor; // Pactor to use Applcalls + + // Fields for reporting to WL2K Map + + struct WL2KInfo * WL2K; + +/* + char * Host; + short WL2KPort; + + int UpdateWL2KTimer; + BOOL UpdateWL2K; + char RMSCall[10]; + char BaseCall[10]; + char GridSquare[7]; + char Comment[80]; + char ServiceCode[17]; + + BOOL UseRigCtrlFreqs; + char WL2KFreq[12]; + char WL2KModeChar; // W or N + BOOL DontReportNarrowOnWideFreqs; + +// char NARROWMODE; +// char WIDEMODE; // Mode numbers to report to WL2K + + struct WL2KInfo WL2KInfoList[MAXFREQS]; // Freqs for sending to WL2K +*/ + char WL2KMode; // WL2K reporting mode + + struct STREAMINFO Streams[27]; // 0 is Pactor 1 - 10 are ax.25. + int LastStream; // Last one polled for status or send + + void * BPQtoRadio_Q; // Frames to Rig Interface + void * RadiotoBPQ_Q; // Frames from Rig Interface + + void * KISSTX_Q; // Frames to Host Mode KISS interface + struct PORTCONTROL * VirtualPORT; // Pointer to Virtual Packet Port of Host Mode KISS + + char * InitPtr; // Next Command + int ReinitState; // Reinit State Machine + int ReinitCount; // Count for DED Recovery + int TermReinitCount; // Count for DED Term Mode Recovery + BOOL TNCOK; // TNC is reponding + int FramesOutstanding; // Frames Queued - used for flow control + BOOL InternalCmd; // Last Command was generated internally + int IntCmdDelay; // To limit internal commands + + + HANDLE hDevice; + int ReopenTimer; // Used to reopen device if failed (eg USB port removed) + BOOL HostMode; // Set if in DED Host Mode +// BOOL CRCMode; // Set if using SCS Extended DED Mode (JHOST4) + BOOL UsingTermMode; // Set if tnc should be left in term mode + int Timeout; // Timeout response counter + int Retries; + int Window; // Window Size for ARQ + UCHAR TXBuffer[500]; // Last message sent - saved for Retry + int TXLen; // Len of last sent + UCHAR RXBuffer[520]; // Message being received - may not arrive all at once + UINT RXLen; // Data in RXBUffer + UCHAR Toggle; // Sequence bit + int Buffers; // Free buffers in TNC + BOOL WantToChangeFreq; // Request from Scanner to Change + int OKToChangeFreq; // 1 = SCS Says OK to change, -1 = Dont Change zero = still waiting + BOOL DontWantToChangeFreq; // Change done - ok to SCS + BOOL DontReleasePermission; // Hold Permission to prevent calls on this frequency + time_t TimeEnteredSYNCMode; // To detect scan lock when using applcalls on PTC + BOOL SyncSupported; // TNC reports sync + time_t TimeScanLocked; // ditto for TNCs that don't report SYNC + int PTCStatus; // Sync, Idle, Traffic, etc + UCHAR NexttoPoll[20]; // Streams with data outstanding (from General Poll) + BOOL PollSent; // Toggle to ensure we issue a general poll regularly + int StreamtoPoll; + + char Bandwidth; // Currently set Mode W or N + + int Mode; // Mode Flag + + BOOL Dragon; // Set if P4Dragon + BOOL DragonSingle; // Set if P4Dragon using Pactor and Packet on same port + BOOL DragonKISS; // Set if P4Dragon supports sending KISS frames in Hostmode + BOOL EnterExit; // Switching to Term mode to change bandwidth + int PktStream; // Stream in use for Packet when in single port mode + BOOL MaxLevel; // Pactor Level to set for Wide Mode (3 or 4) + int MinLevel; // Mimimum accepted Pactor Level + int MinLevelTimer; // Time left to achieve Min Level + int PacketChannels; + int RobustTime; // For PTC, Spend this part of scan cycle (in 10th secs) in Robust Packet Mode + int SwitchToPactor; // Countdown to switch + + BOOL OldMode; // Use PACTOR instead of TOR (for old software) + BOOL VeryOldMode; // Use MYCALL instead of MYPTCALL (for old software) + + int Mem1; // Free Bytes (VHF /HF) + int Mem2; + + BOOL HFPacket; // Set if HF port is in Packet mode instead of Pactor Mode + BOOL Robust; // Set if SCS Tracker is in Robust Packet mode or WINMOR TNC is in Robust Mode + BOOL RobustDefault; // Set if SCS Tracker default is Robust Packet mode + BOOL ForceRobust; // Don't allow Normal Packet even if scan requests it. + BOOL TeensyRPR; // Teensy RPR TNC - don't send %R + char NormSpeed[8]; // Speed Param for Normal Packet on Tracker + char RobustSpeed[8]; // Speed Param for Robust Packet on Tracker + BOOL RPBEACON; // Send Beacon after each session + + int TimeInRX; // Time waiting for ISS before sending + char TXRXState; // Current ISS/IRS State + + BOOL NeedPACTOR; // Set if need to send PACTOR to put into Standby Mode + int CmdStream; // Stream last command was issued on + + union + { + struct TCPINFO * TCPInfo; // Telnet Server Specific Data + struct AGWINFO * AGWInfo; // AGW Stream Mode Specific Data + struct MPSKINFO * MPSKInfo; // MPSK Stream Mode Specific Data + struct FLINFO * FLInfo; // FLDIGI Stream Mode Specific Data + }; + + struct ARQINFO * ARQInfo; // FLDIGI/FLARQ Stream Mode Specific Data + + BOOL DataBusy; // Waiting for Data Ack - Don't send any more data + BOOL CommandBusy; // Waiting for Command ACK + + BOOL TEXTMODE; // Set if AEA in text mode + BOOL NeedTurnRound; // Set if we have sent data, so need to send ctrl/z + BOOL NeedTRANS; // Set if we have to send TRANS when ctrl/z is acked. + + char * CmdSet; // A series of commands to send to the TNC + char * CmdSave; // Base address for free + + BOOL PktUpdateMap; // Set if Packet MH data to be sent to NodeMap + + int DefaultMode; + int CurrentMode; // Used on HAL + + char * DefaultRadioCmd; // RADIO command to send at end of session + char * Frequency; + // For Mode Map if no Rigcontrol + // Mode Equates + + #define Clover 'C' + #define Pactor 'P' + #define AMTOR 'A' + + UCHAR DataBuffer[500]; // Data Chars split from received stream + UCHAR CmdBuffer[500]; // Cmd/Response chars split from received stream + int DataLen; // Data in DataBuffer + int CmdLen; // Data in CmdBuffer + BOOL CmdEsc; // Set if last char rxed was 0x80 + BOOL DataEsc; // Set if last char rxed was 0x81 + int PollDelay; // Don't poll too often; + int InData; // FLDigi - MCAST <....> received + int InPacket; // FLDigi - SOH or < received. + int MCASTLen; // Data still to get + + int DataMode; // How to treat data + +#define RXDATA 0x30 // Switch to Receive Data characters +#define TXDATA 0x31 // Switch to Transmit Data characters +#define SECDATA 0x32 // Switch to RX data from secondary port + + int TXMode; // Where to send data + +#define TXMODEM 0x33 // Send TX data to modem +#define TXSEC 0x34 // Send TX data to secondary port + + BOOL XONXOFF; // Set if hardware is using XON/XOFF + + double LastFreq; // Used by V4 to see if freq has changed + int ModemCentre; // Modem centre frequency + int ClientHeight; + int ClientWidth; + HWND xIDC_TNCSTATE; + HWND xIDC_COMMSSTATE; + HWND xIDC_MODE; + HWND xIDC_LEDS; + HWND xIDC_TRAFFIC; + HWND xIDC_BUFFERS; + HWND xIDC_CHANSTATE; + HWND xIDC_LEVELS; + HWND xIDC_STATE; + HWND xIDC_TXRX; + HWND xIDC_PROTOSTATE; + HWND xIDC_RESTARTTIME; + HWND xIDC_RESTARTS; + HWND xIDC_PACTORLEVEL; + HWND xIDC_TXTUNE; + HWND xIDC_TXTUNEVAL; + + char * WEB_TNCSTATE; + char * WEB_COMMSSTATE; + char * WEB_MODE; + char * WEB_LEDS; + char * WEB_TRAFFIC; + char * WEB_BUFFERS; + char * WEB_CHANSTATE; + char * WEB_STATE; + char * WEB_TXRX; + char * WEB_PROTOSTATE; + char * WEB_RESTARTTIME; + char * WEB_RESTARTS; + char * WEB_PACTORLEVEL; + char * WEB_LEVELS; + int WEB_CHANGED; // Used to speed up refresh when active + + HMENU hMenu; + HMENU hWndMenu; + + VOID (* SuspendPortProc) (struct TNCINFO * TNC, struct TNCINFO * ThisTNC); + VOID (* ReleasePortProc) (struct TNCINFO * TNC); + VOID (* ForcedCloseProc) (struct TNCINFO * TNC, int Stream); + + time_t WinmorRestartCodecTimer; + int WinmorCurrentMode; + char ARDOPCurrentMode[10]; + char ARDOPCommsMode; + char * ARDOPSerialPort; // Have Bus/Device for I2C + int ARDOPSerialSpeed; + BOOL TCPCONNECTED; // ARDOP over TCP Connected + int SlowTimer; + int ARQPorts[32]; // For ARQ over KISS + char * LogPath; + FILE * LogHandle; // Ardop Logging File + FILE * DebugHandle; // Ardop Debug File + char LastLogType; // For split packets + + UCHAR * ARDOPAPRS; // Used to reconstruct APRS datagram from FEC packets + int ARDOPAPRSLen; + + BOOL WRITELOG; // Enable debug logging + int InputLevelMin; // Sound card levels + int InputLevelMax; // Sound card levels + + int DiscardNextOK; // Used by VARA to suppress OK response to LISTEN commands + int SeenCancelPending; // Used by VARA to suppress duplicate cancel pendings + + MESSAGE * Monframe; // Used by DED Host for receiving Packet Monitor Frame + // split over 2 packets + + struct HSMODEMINFO * HSModemInfo; + struct FreeDataINFO * FreeDataInfo; + + int DontRestart; // Don't automatically restart failed TNC + int SendTandRtoRelay; // Send T and R suffix messages to RELAY instead of CMS + + double SNR; // S/N Ratio (VARA) + + int NetRomMode; + unsigned char * NetRomTxBuffer; // For Netrom over VARA + int NetRomTxLen; + char * NRNeighbour; + int NRCloseTimer; + struct _LINKTABLE * DummyLink; // Simulated link to simplify interface to ax,25 netrom code + struct sixPackPortInfo * sixPack; + int VaraACAllowed; // Set by config + int VaraACMode; // Set by first message received + int VaraModeSet; // Have decicded if VarAC mode or not + char * VARACMsg; // to build message from packets + int VarACTimer; // delayed send timer + size_t VARACSize; // malloc'ed size + +} *PTNCINFO; + +VOID * zalloc(int len); + +BOOL ReadConfigFile(int Port, int ProcLine(char * buf, int Port)); +int GetLine(char * buf); +BOOL CreatePactorWindow(struct TNCINFO * TNC, char * ClassName, char * WindowTitle, int RigControlRow, WNDPROC WndProc, + int Width, int Height, VOID ForcedCloseProc(struct TNCINFO * TNC, int Stream)); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +BOOL SendReporttoWL2K(struct TNCINFO * TNC); +struct WL2KInfo * DecodeWL2KReportLine(char * buf); +VOID UpdateMH(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction); +VOID UpdateMHEx(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * LOC, BOOL Report); +VOID SaveWindowPos(int port); +VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized); +BOOL ProcessIncommingConnect(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT); +BOOL ProcessIncommingConnectEx(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT, BOOL AllowTR); +VOID ShowTraffic(struct TNCINFO * TNC); +int OpenCOMMPort(struct TNCINFO * conn, char * Port, int Speed, BOOL Quiet); +VOID SendMH(struct TNCINFO * TNC, char * call, char * freq, char * LOC, char * Mode); +VOID MoveWindows(struct TNCINFO * TNC); + +static VOID TidyClose(struct TNCINFO * TNC, int Stream); +static VOID ForcedClose(struct TNCINFO * TNC, int Stream); +static VOID CloseComplete(struct TNCINFO * TNC, int Stream); + +VOID CheckForDetach(struct TNCINFO * TNC, int Stream, struct STREAMINFO * STREAM, + VOID TidyCloseProc(struct TNCINFO * TNC, int Stream), VOID ForcedCloseProc(struct TNCINFO * TNC, int Stream), + VOID CloseComplete(struct TNCINFO * TNC, int Stream)); + + +BOOL InterlockedCheckBusy(struct TNCINFO * ThisTNC); + +extern UINT CRCTAB; +int BPQTRACE(MESSAGE * Msg, BOOL TOAPRS); + + +static int ProcessLine(char * buf, int Port); +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Consoleprintf(const char * format, ...); + +extern BOOL MinimizetoTray; + +int standardParams(struct TNCINFO * TNC, char * buf); +void DecodePTTString(struct TNCINFO * TNC, char * ptr); + +int Rig_Command(TRANSPORTENTRY * Session, char * Command); + +BOOL Rig_Poll(); + +VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState); +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); + +struct RIGINFO * Rig_GETPTTREC(int Port); + +struct ScanEntry ** CheckTimeBands(struct RIGINFO * RIG); + +#ifndef LINBPQ +LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +#endif + +#define Report_P1 11 +#define Report_P12 12 +#define Report_P123 13 +#define Report_P2 14 +#define Report_P23 15 +#define Report_P3 16 + +#define Report_P1234 17 +#define Report_P234 18 +#define Report_P34 19 +#define Report_P4 20 + +#define Report_WINMOR500 21 +#define Report_WINMOR1600 22 + +#define Report_Robust 30 + +#define IOCTL_SERIAL_IS_COM_OPEN CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_SERIAL_SET_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x803,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x805,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_SERIAL_CLR_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x806,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x807,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x808,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_BPQ_ADD_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x809,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQ_DELETE_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80a,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define W98_SERIAL_GETDATA 0x801 +#define W98_SERIAL_SETDATA 0x802 diff --git a/.svn/pristine/10/108655b013214695f1c2c29f5492cd4f17755b62.svn-base b/.svn/pristine/10/108655b013214695f1c2c29f5492cd4f17755b62.svn-base new file mode 100644 index 0000000..3aec083 --- /dev/null +++ b/.svn/pristine/10/108655b013214695f1c2c29f5492cd4f17755b62.svn-base @@ -0,0 +1,22 @@ +Chat : +{ + ApplNum = 2; + MaxStreams = 11; + OtherChatNodes = "RDGCHT:GB7RDG-1|C STHGTE|C 1 MB7NCR-2|C RDGCHT\r\nRDGCHT:GB7RDG-1|C STHGTE|C 1 MB7NCR-2|C RDGCHT\r\n\r\n"; + ChatWelcomeMsg = "G8BPQ Chat Server.$WType /h for command summary.$WBringing up links to other nodes.$WThis may take a minute or two.$WThe /p command shows what nodes are linked.$W"; + MapPosition = "MapPosition=5259.04N, 00107.01W"; + MapPopup = "MapPopup=G8BPQ Nottingham

BPQ32 Home PageEip; + SPPtr = exinfo.ContextRecord->Esp; + + __asm + { + mov eax, SPPtr + mov SPVal,eax + lea edi,Stack + mov esi,eax + mov ecx,64 + rep movsb + + lea edi,CodeDump + mov esi,eip + mov ecx,64 + rep movsb + } + + + + Debugprintf("BPQ32 *** Program Error %x at %x in %s", + exinfo.ExceptionRecord->ExceptionCode, exinfo.ExceptionRecord->ExceptionAddress, EXCEPTMSG); + + Debugprintf("EAX %x EBX %x ECX %x EDX %x ESI %x EDI %x ESP %x", + exinfo.ContextRecord->Eax, exinfo.ContextRecord->Ebx, exinfo.ContextRecord->Ecx, + exinfo.ContextRecord->Edx, exinfo.ContextRecord->Esi, exinfo.ContextRecord->Edi, SPVal); + +#endif + + Debugprintf("Stack:"); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + SPVal, Stack[0], Stack[1], Stack[2], Stack[3], Stack[4], Stack[5], Stack[6], Stack[7]); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + SPVal+32, Stack[8], Stack[9], Stack[10], Stack[11], Stack[12], Stack[13], Stack[14], Stack[15]); + + Debugprintf("Code:"); + + for (i = 0; i < 16; i++) + { + rev = (CodeDump[i] & 0xff) << 24; + rev |= (CodeDump[i] & 0xff00) << 8; + rev |= (CodeDump[i] & 0xff0000) >> 8; + rev |= (CodeDump[i] & 0xff000000) >> 24; + + CodeDump[i] = rev; + } + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + eip, CodeDump[0], CodeDump[1], CodeDump[2], CodeDump[3], CodeDump[4], CodeDump[5], CodeDump[6], CodeDump[7]); + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + eip+32, CodeDump[8], CodeDump[9], CodeDump[10], CodeDump[11], CodeDump[12], CodeDump[13], CodeDump[14], CodeDump[15]); + + WriteMiniDump(); + + // Note - no closing } so additional code may be run in the __except block + +#ifdef MDIKERNEL + if (CloseOnError == 1) + CloseAllNeeded = 1; +#endif + +#undef EXCEPTMSG diff --git a/.svn/pristine/11/11df31525d8096af91b6003047c846fb996c508a.svn-base b/.svn/pristine/11/11df31525d8096af91b6003047c846fb996c508a.svn-base new file mode 100644 index 0000000..83819b4 --- /dev/null +++ b/.svn/pristine/11/11df31525d8096af91b6003047c846fb996c508a.svn-base @@ -0,0 +1,2201 @@ +/* +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 +*/ + + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + + +#include "kernelresource.h" +#include "cheaders.h" +#include "tncinfo.h" +#ifndef LINBPQ +#include +#endif +//#include +#include "bpq32.h" +#include "adif.h" + + +HANDLE hInstance; +extern HBRUSH bgBrush; +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + +extern int Ver[]; + + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); + +char * GetChallengeResponse(char * Call, char * ChallengeString); + +VOID __cdecl Debugprintf(const char * format, ...); +int FromLOC(char * Locator, double * pLat, double * pLon); +BOOL ToLOC(double Lat, double Lon , char * Locator); + +int GetPosnFromAPRS(char * Call, double * Lat, double * Lon); +char * stristr (char *ch1, char *ch2); + + +static RECT Rect; + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +int Winmor_Socket_Data(int sock, int error, int eventcode); + +struct WL2KInfo * WL2KReports; + +int WL2KTimer = 0; + +int ModetoBaud[31] = {0,0,0,0,0,0,0,0,0,0,0, // 0 = 10 + 200,600,3200,600,3200,3200, // 11 - 16 + 0,0,0,0,0,0,0,0,0,0,0,0,0,600}; // 17 - 30 + +extern char HFCTEXT[]; +extern int HFCTEXTLEN; + + +extern char WL2KCall[10]; +extern char WL2KLoc[7]; + + +VOID MoveWindows(struct TNCINFO * TNC) +{ +#ifndef LINBPQ + RECT rcClient; + int ClientHeight, ClientWidth; + + GetClientRect(TNC->hDlg, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + if (TNC->hMonitor) + MoveWindow(TNC->hMonitor,2 , TNC->RigControlRow + 3, ClientWidth-4, ClientHeight - (TNC->RigControlRow + 3), TRUE); +#endif +} + +char * Config; +static char * ptr1, * ptr2; + +#ifndef LINBPQ + +LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + + int i; + struct TNCINFO * TNC; + + HKEY hKey; + char Key[80]; + int retCode, disp; + + for (i=0; i<41; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->hDlg == hWnd) + break; + } + + if (TNC == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) { + + case WM_CREATE: + + break; + + case WM_PAINT: + +// hdc = BeginPaint (hWnd, &ps); + +// SelectObject( hdc, hFont) ; + +// EndPaint (hWnd, &ps); +// +// wParam = hdc; + + break; + + + case WM_GETMINMAXINFO: + + if (TNC->ClientHeight) + { + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = TNC->ClientWidth; + mmi->ptMaxSize.y = TNC->ClientHeight; + mmi->ptMaxTrackSize.x = TNC->ClientWidth; + mmi->ptMaxTrackSize.y = TNC->ClientHeight; + } + + break; + + + case WM_MDIACTIVATE: + { + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + + if (TNC->hMenu) + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + +// SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + // call DrawMenuBar after the menu items are set + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)TNC->hMenu) + { + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") + || strstr(TNC->ProgramPath, "VARA") || stristr(TNC->ProgramPath, "FREEDATA")) + { + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED); + + break; + } + } + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED); + } + + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case WINMOR_KILL: + + TNC->DontRestart = TRUE; + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + TNC->DontRestart = FALSE; + KillTNC(TNC); + RestartTNC(TNC); + break; + + case WINMOR_RESTARTAFTERFAILURE: + + TNC->RestartAfterFailure = !TNC->RestartAfterFailure; + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4); + RegCloseKey(hKey); + } + break; + + case ARDOP_ABORT: + + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SIZING: + case WM_SIZE: + + MoveWindows(TNC); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + + case SC_RESTORE: + + TNC->Minimized = FALSE; + break; + + case SC_MINIMIZE: + + TNC->Minimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_HSCROLL: + { + char value[16]; + + switch (LOWORD(wParam)) + { + case TB_ENDTRACK: + case TB_THUMBTRACK: + + TNC->TXOffset = SendMessage(TNC->xIDC_TXTUNE, TBM_GETPOS, 0, 0); + sprintf(value, "%d", TNC->TXOffset); + MySetWindowText(TNC->xIDC_TXTUNEVAL, value); + + break; + } + + default: + break; + } + case WM_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +#endif + +BOOL CreatePactorWindow(struct TNCINFO * TNC, char * ClassName, char * WindowTitle, int RigControlRow, WNDPROC WndProc, int Width, int Height, + VOID ForcedCloseProc(struct TNCINFO * TNC, int Stream)) +{ +#ifdef LINBPQ + return FALSE; +#else + WNDCLASS wc; + char Title[80]; + int retCode, Type, Vallen; + HKEY hKey=0; + char Key[80]; + char Size[80]; + int Top, Left; + HANDLE hDlg = 0; + static int LP = 1235; + + if (TNC->hDlg) + { + ShowWindow(TNC->hDlg, SW_SHOWNORMAL); + SetForegroundWindow(TNC->hDlg); + return FALSE; // Already open + } + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + +// if (TNC->Hardware == H_WINMOR || TNC->Hardware == H_TELNET ||TNC->Hardware == H_ARDOP || +// TNC->Hardware == H_V4 || TNC->Hardware == H_FLDIGI || TNC->Hardware == H_UIARQ || TNC->Hardware == H_VARA) + if (TNC->PortRecord) + sprintf(Title, "%s Status - Port %d %s", WindowTitle, TNC->Port, TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + else + sprintf(Title, "Rigcontrol"); + + if (TNC->Hardware == H_MPSK) + sprintf(Title, "Rigcontrol for MultiPSK Port %d", TNC->Port); + + TNC->hDlg = hDlg = CreateMDIWindow(ClassName, Title, 0, + 0, 0, Width, Height, ClientWnd, hInstance, ++LP); + + // CreateDialog(hInstance,ClassName,0,NULL); + + Rect.top = 100; + Rect.left = 20; + Rect.right = Width + 20; + Rect.bottom = Height + 100; + + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"Size",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + { + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &TNC->Minimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + if (Rect.top < OffsetH) + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + } + + if (TNC->Hardware == H_WINMOR || TNC->Hardware == H_ARDOP|| TNC->Hardware == H_VARA) + retCode = RegQueryValueEx(hKey,"TNC->RestartAfterFailure",0, + (ULONG *)&Type,(UCHAR *)&TNC->RestartAfterFailure,(ULONG *)&Vallen); + + RegCloseKey(hKey); + } + + Top = Rect.top; + Left = Rect.left; + +// GetWindowRect(hDlg, &Rect); // Get the real size + + MoveWindow(hDlg, Left - (OffsetW /2), Top - OffsetH, Rect.right - Rect.left, Rect.bottom - Rect.top, TRUE); + + if (TNC->Minimized) + ShowWindow(hDlg, SW_SHOWMINIMIZED); + else + ShowWindow(hDlg, SW_RESTORE); + + TNC->RigControlRow = RigControlRow; + + SetWindowText(TNC->xIDC_TNCSTATE, "Free"); + + TNC->ForcedCloseProc = ForcedCloseProc; + + return TRUE; +#endif +} + + +// WL2K Reporting Code. + +static SOCKADDR_IN sinx; + + +VOID SendReporttoWL2KThread(void * unused); +VOID SendHTTPReporttoWL2KThread(void * unused); + +VOID CheckWL2KReportTimer() +{ + if (WL2KReports == NULL) + return; // Shouldn't happen! + + WL2KTimer--; + + if (WL2KTimer != 0) + return; + +#ifdef WIN32 + WL2KTimer = 2 * 32910; // Every 2 Hours - PC Tick is a bit slow +#else + WL2KTimer = 2 * 36000; // Every 2 Hours +#endif + + if (CheckAppl(NULL, "RMS ") == NULL) + if (CheckAppl(NULL, "RELAY ") == NULL) + return; + + _beginthread(SendHTTPReporttoWL2KThread, 0, 0); + + return; +} + +static char HeaderTemplate[] = "POST %s HTTP/1.1\r\n" + "Accept: application/json\r\n" +// "Accept-Encoding: gzip,deflate,gzip, deflate\r\n" + "Content-Type: application/json\r\n" + "Host: %s:%d\r\n" + "Content-Length: %d\r\n" + //r\nUser-Agent: BPQ32(G8BPQ)\r\n" +// "Expect: 100-continue\r\n" + "\r\n{%s}"; + +char Missing[] = "** Missing **"; + +VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value) +{ + char * ptr1, * ptr2; + + strcpy(Value, Missing); + + ptr1 = strstr(_REPLYBUFFER, Name); + + if (ptr1 == 0) + return; + + ptr1 += (strlen(Name) + 1); + + ptr2 = strchr(ptr1, '"'); + + if (ptr2) + { + size_t ValLen = ptr2 - ptr1; + memcpy(Value, ptr1, ValLen); + Value[ValLen] = 0; + } + + return; +} + + +// Send Winlink Session Record + +extern char LOC[7]; +extern char TextVerstring[50]; + +double Distance(double laa, double loa, double lah, double loh, BOOL KM); +double Bearing(double lat2, double lon2, double lat1, double lon1); +VOID SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return); +SOCKET OpenWL2KHTTPSock(); + + + +struct WL2KMode +{ + int Mode; + char * WL2KString; + char * ADIFString; + char * BPQString; +}; + +struct WL2KMode WL2KModeList[] = +{ + {0,"Packet 1200"}, + {1,"Packet 2400"}, + {2, "Packet 4800"}, + {3, "Packet 9600"}, + {4, "Packet 19200"}, + {5, "Packet 38400"}, + {11, "Pactor 1"}, + {12, "Pactor 1,2"}, + {13, "Pactor 1,2,3"}, + {14, "Pactor 2"}, + {15, "Pactor 2,3"}, + {16, "Pactor 3"}, + {17, "Pactor 1,2,3,4"}, + {18, "Pactor 2,3,4"}, + {19, "Pactor 3,4"}, + {20, "Pactor 4"}, + {21, "WINMOR 500"}, + {22, "WINMOR 1600"}, + {30, "Robust Packet"}, + {40, "ARDOP 200"}, + {41, "ARDOP 500"}, + {42, "ARDOP 1000"}, + {43, "ARDOP 2000"}, + {44, "ARDOP 2000 FM"}, + {50, "VARA"}, + {51, "VARA FM"}, + {52, "VARA FM WIDE"}, + {53, "VARA 500"} +}; + +char WL2KModes [55][18] = { + "Packet 1200", "Packet 2400", "Packet 4800", "Packet 9600", "Packet 19200", "Packet 38400", "High Speed Packet", "", "", "", "", + "Pactor 1", "Pactor", "Pactor", "Pactor 2", "Pactor", "Pactor 3", "Pactor", "Pactor", "Pactor", "Pactor 4", // 11 - 20 + "Winmor 500", "Winmor 1600", "", "", "", "", "", "", "", // 21 - 29 + "Robust Packet", "", "", "", "", "", "", "", "", "", // 30 - 39 + "ARDOP 200", "ARDOP 500", "ARDOP 1000", "ARDOP 2000", "ARDOP 2000 FM", "", "", "", "", "", // 40 - 49 + "VARA", "VARA FM", "VARA FM WIDE", "VARA 500", "VARA 2750"}; + + +VOID SendWL2KSessionRecordThread(void * param) +{ + SOCKET sock; + char Message[512]; + + strcpy(Message, param); + free(param); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + SendHTTPRequest(sock, "/session/add", (char *)Message, (int)strlen(Message), NULL); + closesocket(sock); + } + + return; +} + +VOID SendWL2KRegisterHybridThread(void * param) +{ + SOCKET sock; + char Message[512]; + + strcpy(Message, param); + free(param); + + Debugprintf("Sending %s", Message); + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + SendHTTPRequest(sock, "/radioNetwork/params/add", (char *)Message, (int)strlen(Message), NULL); + closesocket(sock); + } + + return; +} + +VOID SendWL2KRegisterHybrid(struct TNCINFO * TNC) +{ + char Message[512]; + char Date[80] ; + int Len; + struct TCPINFO * TCP = TNC->TCPInfo; + time_t T; + struct tm * tm; + char Call[10]; + + if (TCP == NULL || TCP->GatewayLoc[0] == 0) + return; + + strcpy(Call, TCP->GatewayCall); + strlop(Call, '-'); + + T = time(NULL); + tm = gmtime(&T); + + //2021-10-31-14=35=29 + + sprintf(Date, "%04d-%02d-%02d-%02d:%02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + +// "Callsign":"String","Password":"String","Param":"String","Value":"String","Key":"String" + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"RMSRelayVersion\",\"Value\":\"%s|%s|*HARMNNNN|%s|%s|\"", + Call, TCP->SecureCMSPassword, Date, "3.1.11.2", + TCP->HybridServiceCode, TCP->GatewayLoc); + + SendWL2KRegisterHybridThread(_strdup(Message)); + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"CoLocatedRMS\",\"Value\":\"%s\"", + Call, TCP->SecureCMSPassword, TCP->HybridCoLocatedRMS); + + SendWL2KRegisterHybridThread(_strdup(Message)); + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Password\":\"%s\",\"Param\":\"AllowFreq\",\"Value\":\"%s\"", + Call, TCP->SecureCMSPassword, TCP->HybridFrequencies); + + SendWL2KRegisterHybridThread(_strdup(Message)); + + return; +} + +BOOL NoSessionAccount = FALSE; +BOOL SessionAccountChecked = FALSE; + +BOOL SendWL2KSessionRecord(ADIF * ADIF, int BytesSent, int BytesReceived) +{ +/* +The API is /session/add https://api.winlink.org/json/metadata?op=SessionAdd + +The important parameters are (others can be omitted): + +Application (gateway program name) +Server (gateway callsign) +ServerGrid +Client (client callsign) +ClientGrid +Mode (Pactor, winmor, vara, etc) +Frequency +MessagesSent +MessagesReceived +BytesSent +BytesReceived +HoldingSeconds (duration of connection) +IdTag (random alphanumeric, 12 chars) + +"Application":"RMS Trimode", +"Version":"1.3.25.0", +"Cms":"CMS-A", +"Server":"AB4NX", +"ServerGrid":"EM73WT", +"Client":"VE2SCA","ClientGrid":"", +"Sid":"","Mode":"WINMOR16", +"Frequency":10145000, +"Kilometers":0, +"Degrees":0, +"LastCommand":">", +"MessagesSent":0, +"MessagesReceived":0, +"BytesSent":179, +"BytesReceived":0, +"HoldingSeconds":126, +"IdTag":"ATK9S3QGL2E1"} +*/ + time_t T; + + char Message[4096] = ""; + char * MessagePtr; + int MessageLen; + int Dist = 0; + int intBearing = 0; + + double Lat, Lon; + double myLat, myLon; + + char Tag[32]; + + SOCKET sock; + char Response[1024]; + int Len; + + // Only report if the CMSCall has a WL2KAccount + + if (NoSessionAccount) + return TRUE; + + if (!SessionAccountChecked) + { + // only check once + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + SessionAccountChecked = TRUE; + + Len = sprintf(Message, "\"Callsign\":\"%s\"", ADIF->CMSCall); + + SendHTTPRequest(sock, "/account/exists", Message, Len, Response); + closesocket(sock); + + if (strstr(Response, "\"CallsignExists\":false")) + { + WritetoConsole("WL2K Traffic Reporting disabled - Gateway "); + WritetoConsole(ADIF->CMSCall); + WritetoConsole(" does not have a Winlink Account\r\n"); + Debugprintf("WL2K Traffic Reporting disabled - Gateway %s does not have a Winlink Account", ADIF->CMSCall); + NoSessionAccount = TRUE; + return TRUE; + } + } + } + + if (ADIF == NULL || ADIF->LOC[0] == 0 || ADIF->Call[0] == 0) + return TRUE; + + if (ADIF->StartTime == 0 || ADIF->ServerSID[0] == 0 || ADIF->CMSCall[0] == 0) + return TRUE; + + T = time(NULL); + + // Extract Info we need + + // Distance and Bearing + + if (LOC[0] && ADIF->LOC[0]) + { + if (FromLOC(LOC, &myLat, &myLon) == 0) // Basic checks on LOCs + return TRUE; + if (FromLOC(ADIF->LOC, &Lat, &Lon) == 0) + return TRUE; + + Dist = (int)Distance(myLat, myLon, Lat, Lon, TRUE); + intBearing = (int)Bearing(Lat, Lon, myLat, myLon); + } + + MessageLen = sprintf(Message, "\"Application\":\"%s\",", "BPQ32"); + MessageLen += sprintf(&Message[MessageLen], "\"Version\":\"%s\",", TextVerstring); + MessageLen += sprintf(&Message[MessageLen], "\"Cms\":\"%s\",", "CMS"); + MessageLen += sprintf(&Message[MessageLen], "\"Server\":\"%s\",", ADIF->CMSCall); + MessageLen += sprintf(&Message[MessageLen], "\"ServerGrid\":\"%s\",", LOC); + MessageLen += sprintf(&Message[MessageLen], "\"Client\":\"%s\",", ADIF->Call); + MessageLen += sprintf(&Message[MessageLen], "\"ClientGrid\":\"%s\",", ADIF->LOC); + MessageLen += sprintf(&Message[MessageLen], "\"Sid\":\"%s\",", ADIF->UserSID); + MessageLen += sprintf(&Message[MessageLen], "\"Mode\":\"%s\",", WL2KModes[ADIF->Mode]); + MessageLen += sprintf(&Message[MessageLen], "\"Frequency\":%lld,", ADIF->Freq); + MessageLen += sprintf(&Message[MessageLen], "\"Kilometers\":%d,", Dist); + MessageLen += sprintf(&Message[MessageLen], "\"Degrees\":%d,", intBearing); + MessageLen += sprintf(&Message[MessageLen], "\"LastCommand\":\"%s\",", ADIF->Termination); + MessageLen += sprintf(&Message[MessageLen], "\"MessagesSent\":%d,", ADIF->Sent); + MessageLen += sprintf(&Message[MessageLen], "\"MessagesReceived\":%d,", ADIF->Received); + MessageLen += sprintf(&Message[MessageLen], "\"BytesSent\":%d,", BytesSent); + MessageLen += sprintf(&Message[MessageLen], "\"BytesReceived\":%d,", BytesReceived); + MessageLen += sprintf(&Message[MessageLen], "\"HoldingSeconds\":%d,", (int)(T - ADIF->StartTime)); + sprintf(Tag, "%012X", (int)T * (rand() + 1)); + MessageLen += sprintf(&Message[MessageLen], "\"IdTag\":\"%s\"", Tag); + + MessagePtr = _strdup(Message); + _beginthread(SendWL2KSessionRecordThread, 0, (void *)MessagePtr); + + return TRUE; +} + +char APIKey[] = ",\"Key\":\"0D0C7AD6B38C45A7A9534E67111C38A7\""; + + +VOID SendHTTPRequest(SOCKET sock, char * Request, char * Params, int Len, char * Return) +{ + int InputLen = 0; + int inptr = 0; + char Buffer[2048]; + char Header[2048]; + char * ptr, * ptr1; + int Sent; + + strcat(Params, APIKey); + Len += (int)strlen(APIKey); + + sprintf(Header, HeaderTemplate, Request, "api.winlink.org", 80, Len + 2, Params); + Sent = send(sock, Header, (int)strlen(Header), 0); + + if (Sent == -1) + { + int Err = WSAGetLastError(); + Debugprintf("Error %d from WL2K Update send()", Err); + return; + } + + while (InputLen != -1) + { + InputLen = recv(sock, &Buffer[inptr], 2048 - inptr, 0); + + if (InputLen == -1 || InputLen == 0) + { + int Err = WSAGetLastError(); + Debugprintf("Error %d from WL2K Update recv()", Err); + return; + } + + // As we are using a persistant connection, can't look for close. Check + // for complete message + + inptr += InputLen; + + Buffer[inptr] = 0; + + ptr = strstr(Buffer, "\r\n\r\n"); + + if (ptr) + { + // got header + + int Hddrlen = (int)(ptr - Buffer); + + ptr1 = strstr(Buffer, "Content-Length:"); + + if (ptr1) + { + // Have content length + + int ContentLen = atoi(ptr1 + 16); + + if (ContentLen + Hddrlen + 4 == inptr) + { + // got whole response + + if (strstr(Buffer, " 200 OK")) + { + if (Return) + { + memcpy(Return, ptr + 4, ContentLen); + Return[ContentLen] = 0; + } + else + Debugprintf("WL2K Database update ok"); + + } + else + { + strlop(Buffer, 13); + Debugprintf("WL2K Update Params - %s", Params); + Debugprintf("WL2K Update failed - %s", Buffer); + } + return; + } + } + else + { + ptr1 = strstr(_strlwr(Buffer), "transfer-encoding:"); + + if (ptr1) + { + // Just accept anything until I've sorted things with Lee + Debugprintf("%s", ptr1); + Debugprintf("WL2K Database update ok"); + return; + } + } + } + } +} + +BOOL WL2KAccountChecked = FALSE; +BOOL NoWL2KAccount = FALSE; + +VOID SendHTTPReporttoWL2KThread(void * unused) +{ + // Uses HTTP/JSON Interface + + struct WL2KInfo * WL2KReport = WL2KReports; + char * LastHost = NULL; + char * LastRMSCall = NULL; + char Message[512]; + int LastSocket = 0; + SOCKET sock = 0; + struct sockaddr_in destaddr; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + int err; + u_long param=1; + BOOL bcopt=TRUE; + int Len; + + // Send all reports in list + + char Response[1024]; + + // Only report if the CMSCall has a WL2KAccount + + if (NoWL2KAccount) + return; + + if (!WL2KAccountChecked) + { + // only check once + + sock = OpenWL2KHTTPSock(); + + if (sock) + { + WL2KAccountChecked = TRUE; + + Len = sprintf(Message, "\"Callsign\":\"%s\"", + WL2KReport->BaseCall); + + SendHTTPRequest(sock, "/account/exists", Message, Len, Response); + closesocket(sock); + + if (strstr(Response, "\"CallsignExists\":false")) + { + WritetoConsole("WL2K Reporting disabled - Gateway "); + WritetoConsole(WL2KReport->BaseCall); + WritetoConsole(" does not have a Winlink Account\r\n"); + NoWL2KAccount = TRUE; + return; + } + } + } + + while (WL2KReport) + { + // Resolve Name if needed + + if (LastHost && strcmp(LastHost, WL2KReport->Host) == 0) // Same host? + goto SameHost; + + // New Host - Connect to it + + LastHost = WL2KReport->Host; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(WL2KReport->Host); + destaddr.sin_port = htons(WL2KReport->WL2KPort); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + Debugprintf("Resolving %s", WL2KReport->Host); + HostEnt = gethostbyname (WL2KReport->Host); + + if (!HostEnt) + { + err = WSAGetLastError(); + + Debugprintf("Resolve Failed for %s %d %x", WL2KReport->Host, err, err); + return; // Resolve failed + } + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + // Allocate a Socket entry + + if (sock) + closesocket(sock); + + sock = socket(AF_INET, SOCK_STREAM, 0); + + if (sock == INVALID_SOCKET) + return; + +// ioctlsocket(sock, FIONBIO, ¶m); + + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt, 4); + + destaddr.sin_family = AF_INET; + + if (sock == INVALID_SOCKET) + { + sock = 0; + return; + } + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + // Connect to Host + + if (connect(sock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) != 0) + { + err=WSAGetLastError(); + + // + // Connect failed + // + + Debugprintf("Connect Failed"); + closesocket(sock); + sock = 0; + break; + } + + SameHost: + + Len = sprintf(Message, + "\"Callsign\":\"%s\"," + "\"BaseCallsign\":\"%s\"," + "\"GridSquare\":\"%s\"," + "\"Frequency\":%lld," + "\"Mode\":%d," + "\"Baud\":%d," + "\"Power\":%d," + "\"Height\":%d," + "\"Gain\":%d," + "\"Direction\":%d," + "\"Hours\":\"%s\"," + "\"ServiceCode\":\"%s\"", + + WL2KReport->RMSCall, WL2KReport->BaseCall, WL2KReport->GridSquare, + WL2KReport->Freq, WL2KReport->mode, WL2KReport->baud, WL2KReport->power, + WL2KReport->height, WL2KReport->gain, WL2KReport->direction, + WL2KReport->Times, WL2KReport->ServiceCode); + + Debugprintf("Sending %s", Message); + + SendHTTPRequest(sock, "/channel/add", Message, Len, NULL); + + + // Send Version Message + + + if (LastRMSCall == NULL || strcmp(WL2KReport->RMSCall, LastRMSCall) != 0) + { + int Len; + + LastRMSCall = WL2KReport->RMSCall; + + // "Callsign":"String","Program":"String","Version":"String","Comments":"String" + + Len = sprintf(Message, "\"Callsign\":\"%s\",\"Program\":\"BPQ32\"," + "\"Version\":\"%d.%d.%d.%d\",\"Comments\":\"Test Comment\"", + WL2KReport->RMSCall, Ver[0], Ver[1], Ver[2], Ver[3]); + + Debugprintf("Sending %s", Message); + + SendHTTPRequest(sock, "/version/add", Message, Len, NULL); + } + + WL2KReport = WL2KReport->Next; + } + + Sleep(100); + closesocket(sock); + sock = 0; + +} + +struct WL2KInfo * DecodeWL2KReportLine(char * buf) +{ + //06'', '', '', , , , , + // , , , '', , '' + + // WL2KREPORT service, api.winlink.org, 80, GM8BPQ, IO68VL, 00-23, 144800000, PKT1200, 10, 20, 5, 0, BPQTEST + + char * Context; + char * p_cmd; + char * param; + char errbuf[256]; + struct WL2KInfo * WL2KReport = zalloc(sizeof(struct WL2KInfo)); + char * ptr; + char Param[8][256]; + char * ptr1, * ptr2; + int n = 0; + + memset(Param, 0, 2048); + + strcpy(errbuf, buf); + + p_cmd = strtok_s(&buf[10], ", \t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + + strcpy(WL2KReport->ServiceCode, p_cmd); + + // Can default Host and Port, so cant use strtok for them + + ptr1 = Context; + + while (ptr1 && *ptr1 && n < 2) + { + while(ptr1 && *ptr1 && *ptr1 == ' ') + ptr1++; + + ptr2 = strchr(ptr1, ','); + if (ptr2) *ptr2++ = 0; + + strcpy(&Param[n][0], ptr1); + strlop(Param[n++], ' '); + ptr1 = ptr2; + + } + + if (n < 2) + goto BadLine; + + if (Param[1][0] == 0) + WL2KReport->WL2KPort = 80; // HTTP Interface + else + WL2KReport->WL2KPort = atoi(&Param[1][0]); + + if (Param[0][0] == 0) + WL2KReport->Host = _strdup("api.winlink.org"); + else + { + _strlwr(&Param[0][0]); + + if (strstr(&Param[0][0], "winlink.org")) + { + WL2KReport->WL2KPort = 80; // HTTP Interface + WL2KReport->Host = _strdup("api.winlink.org"); + } + else + WL2KReport->Host = _strdup(&Param[0][0]); + } + + Context = ptr1; + + p_cmd = strtok_s(NULL, ", \t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + + if (WL2KReport->WL2KPort == 0) goto BadLine; + + strcpy(WL2KReport->RMSCall, p_cmd); + strcpy(WL2KReport->BaseCall, p_cmd); + strlop(WL2KReport->BaseCall, '-'); // Remove any SSID + + strcpy(WL2KCall, WL2KReport->BaseCall); // For SYSOP Update + + p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + if (strlen(p_cmd) != 6) goto BadLine; + + strcpy(WL2KReport->GridSquare, p_cmd); + strcpy(WL2KLoc, p_cmd); + + p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + if (strlen(p_cmd) > 79) goto BadLine; + + // Convert any : in times to comma + + ptr = strchr(p_cmd, ':'); + + while (ptr) + { + *ptr = ','; + ptr = strchr(p_cmd, ':'); + } + + strcpy(WL2KReport->Times, p_cmd); + + p_cmd = strtok_s(NULL, " ,\t\n\r", &Context); + if (p_cmd == NULL) goto BadLine; + + WL2KReport->Freq = strtoll(p_cmd, NULL, 10); + + if (WL2KReport->Freq == 0) // Invalid + goto BadLine; + + param = strtok_s(NULL, " ,\t\n\r", &Context); + + // Mode Designator - one of + + // PKTnnnnnn + // WINMOR500 + // WINMOR1600 + // ROBUST + // P1 P12 P123 P1234 etc + + if (memcmp(param, "PKT", 3) == 0) + { + int Speed, Mode; + + Speed = atoi(¶m[3]); + + WL2KReport->baud = Speed; + + if (Speed <= 1200) + Mode = 0; // 1200 + else if (Speed <= 2400) + Mode = 1; // 2400 + else if (Speed <= 4800) + Mode = 2; // 4800 + else if (Speed <= 9600) + Mode = 3; // 9600 + else if (Speed <= 19200) + Mode = 4; // 19200 + else if (Speed <= 38400) + Mode = 5; // 38400 + else + Mode = 6; // >38400 + + WL2KReport->mode = Mode; + } + else if (_stricmp(param, "WINMOR500") == 0) + WL2KReport->mode = 21; + else if (_stricmp(param, "WINMOR1600") == 0) + WL2KReport->mode = 22; + else if (_stricmp(param, "ROBUST") == 0) + { + WL2KReport->mode = 30; + WL2KReport->baud = 600; + } + else if (_stricmp(param, "ARDOP200") == 0) + WL2KReport->mode = 40; + else if (_stricmp(param, "ARDOP500") == 0) + WL2KReport->mode = 41; + else if (_stricmp(param, "ARDOP1000") == 0) + WL2KReport->mode = 42; + else if (_stricmp(param, "ARDOP2000") == 0) + WL2KReport->mode = 43; + else if (_stricmp(param, "ARDOP2000FM") == 0) + WL2KReport->mode = 44; + else if (_stricmp(param, "P1") == 0) + WL2KReport->mode = 11; + else if (_stricmp(param, "P12") == 0) + WL2KReport->mode = 12; + else if (_stricmp(param, "P123") == 0) + WL2KReport->mode = 13; + else if (_stricmp(param, "P2") == 0) + WL2KReport->mode = 14; + else if (_stricmp(param, "P23") == 0) + WL2KReport->mode = 15; + else if (_stricmp(param, "P3") == 0) + WL2KReport->mode = 16; + else if (_stricmp(param, "P1234") == 0) + WL2KReport->mode = 17; + else if (_stricmp(param, "P234") == 0) + WL2KReport->mode = 18; + else if (_stricmp(param, "P34") == 0) + WL2KReport->mode = 19; + else if (_stricmp(param, "P4") == 0) + WL2KReport->mode = 20; + else if (_stricmp(param, "VARA") == 0) + WL2KReport->mode = 50; + else if (_stricmp(param, "VARA2300") == 0) + WL2KReport->mode = 50; + else if (_stricmp(param, "VARAFM") == 0) + WL2KReport->mode = 51; + else if (_stricmp(param, "VARAFM12") == 0) + WL2KReport->mode = 51; + else if (_stricmp(param, "VARAFM96") == 0) + WL2KReport->mode = 52; + else if (_stricmp(param, "VARA500") == 0) + WL2KReport->mode = 53; + else if (_stricmp(param, "VARA2750") == 0) + WL2KReport->mode = 54; + else + goto BadLine; + + param = strtok_s(NULL, " ,\t\n\r", &Context); + + // Optional Params + + WL2KReport->power = (param)? atoi(param) : 0; + param = strtok_s(NULL, " ,\t\n\r", &Context); + WL2KReport->height = (param)? atoi(param) : 0; + param = strtok_s(NULL, " ,\t\n\r", &Context); + WL2KReport->gain = (param)? atoi(param) : 0; + param = strtok_s(NULL, " ,\t\n\r", &Context); + WL2KReport->direction = (param)? atoi(param) : 0; + + WL2KTimer = 60; + + WL2KReport->Next = WL2KReports; + WL2KReports = WL2KReport; + + return WL2KReport; + +BadLine: + + WritetoConsole(" Bad config record "); + WritetoConsole(errbuf); + WritetoConsole("\r\n"); + + return 0; +} + +VOID UpdateMHSupport(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * Loc, BOOL Report, BOOL Digis); + +VOID UpdateMHwithDigis(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction) +{ + UpdateMHSupport(TNC, Call, Mode, Direction, NULL, TRUE, TRUE); +} +VOID UpdateMHEx(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * LOC, BOOL Report) +{ + UpdateMHSupport(TNC, Call, Mode, Direction, LOC, Report, FALSE); +} + +VOID UpdateMH(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction) +{ + UpdateMHSupport(TNC, Call, Mode, Direction, NULL, TRUE, FALSE); +} + +VOID UpdateMHSupport(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction, char * Loc, BOOL Report, BOOL Digis) +{ + PMHSTRUC MH = TNC->PortRecord->PORTCONTROL.PORTMHEARD; + PMHSTRUC MHBASE = MH; + UCHAR AXCall[72] = ""; + int i; + char * LOC, * LOCEND; + char ReportMode[20]; + char NoLOC[7] = ""; + double Freq; + char ReportFreq[350] = ""; + int OldCount = 0; + char ReportCall[16]; + + if (MH == 0) return; + + if (Digis) + { + // Call is an ax.25 digi string not a text call + + memcpy(AXCall, Call, 7 * 9); + ReportCall[ConvFromAX25(Call, ReportCall)] = 0; + + // if this is a UI frame with a locator or APRS position + // we could derive a position from it + + } + else + { + strcpy(ReportCall, Call); + ConvToAX25(Call, AXCall); + AXCall[6] |= 1; // Set End of address + } + + // Adjust freq to centre + +// if (Mode != ' ' && TNC->RIG->Valchar[0]) + if (TNC->RIG->Valchar[0]) + { + if (TNC->Hardware == H_UZ7HO) + { + // See if we have Center Freq Info + if (TNC->AGWInfo->CenterFreq) + { + Freq = atof(TNC->RIG->Valchar) + ((TNC->AGWInfo->CenterFreq * 1.0) / 1000000.0); + } +#ifdef WIN32 + else if (TNC->AGWInfo->hFreq) + { + char Centre[16]; + double ModemFreq; + + SendMessage(TNC->AGWInfo->hFreq, WM_GETTEXT, 15, (LPARAM)Centre); + + ModemFreq = atof(Centre); + + Freq = atof(TNC->RIG->Valchar) + (ModemFreq / 1000000); + } +#endif + else + Freq = atof(TNC->RIG->Valchar) + 0.0015; // Assume 1500 + } + else + + // Not UZ7HO or Linux + + Freq = atof(TNC->RIG->Valchar) + 0.0015; + + _gcvt(Freq, 9, ReportFreq); + } + + if (TNC->Hardware == H_ARDOP) + { + LOC = memchr(Call, '[', 20); + + if (LOC) + { + LOCEND = memchr(Call, ']', 30); + if (LOCEND) + { + LOC--; + *(LOC++) = 0; + *(LOCEND) = 0; + LOC++; + if (strlen(LOC) != 6 && strlen(LOC) != 0) + { + Debugprintf("Corrupt LOC %s %s", Call, LOC); + LOC = NoLOC; + } + goto NOLOC; + } + } + } + + else if (TNC->Hardware != H_WINMOR) // Only WINMOR has a locator + { + LOC = NoLOC; + goto NOLOC; + } + + LOC = memchr(Call, '(', 20); + + if (LOC) + { + LOCEND = memchr(Call, ')', 30); + if (LOCEND) + { + LOC--; + *(LOC++) = 0; + *(LOCEND) = 0; + LOC++; + if (strlen(LOC) != 6 && strlen(LOC) != 0) + { + Debugprintf("Corrupt LOC %s %s", Call, LOC); + LOC = NoLOC; + } + } + } + else + LOC = NoLOC; + +NOLOC: + + if (Loc) + LOC = Loc; // Supplied Locator overrides + + for (i = 0; i < MHENTRIES; i++) + { + if (Mode == ' ' || Mode == '*') // Packet + { + if ((MH->MHCALL[0] == 0) || ((memcmp(AXCall, MH->MHCALL, 7) == 0) && MH->MHDIGI == Mode)) // Spare or our entry + { + OldCount = MH->MHCOUNT; + goto DoMove; + } + } + else + { + if ((MH->MHCALL[0] == 0) || ((memcmp(AXCall, MH->MHCALL, 7) == 0) && + MH->MHDIGI == Mode && strcmp(MH->MHFreq, ReportFreq) == 0)) // Spare or our entry + { + OldCount = MH->MHCOUNT; + goto DoMove; + } + } + MH++; + } + + // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP + + i = MHENTRIES - 1; + + // Move others down and add at front +DoMove: + + if (i != 0) // First + memmove(MHBASE + 1, MHBASE, i * sizeof(MHSTRUC)); + +// memcpy (MHBASE->MHCALL, Buffer->ORIGIN, 7 * 9); + memcpy (MHBASE->MHCALL, AXCall, 7 * 9); // Save Digis + MHBASE->MHDIGI = Mode; + MHBASE->MHTIME = time(NULL); + MHBASE->MHCOUNT = ++OldCount; + + memcpy(MHBASE->MHLocator, LOC, 6); + strcpy(MHBASE->MHFreq, ReportFreq); + + // Report to NodeMap + + if (Report == FALSE) + return; + + if (Mode == '*') + return; // Digi'ed Packet + + if (Mode == ' ') // Packet Data + { + if (TNC->PktUpdateMap == 1) + Mode = '!'; + else + return; + } + + ReportMode[0] = TNC->Hardware + '@'; + ReportMode[1] = Mode; + if (TNC->Hardware == H_HAL) + ReportMode[2] = TNC->CurrentMode; + else + ReportMode[2] = (TNC->RIG->CurrentBandWidth) ? TNC->RIG->CurrentBandWidth : '?'; + ReportMode[3] = Direction; + ReportMode[4] = 0; + + // If no position see if we have an APRS posn + + if (LOC[0] == 0) + { + double Lat, Lon; + + if (GetPosnFromAPRS(ReportCall, &Lat, &Lon) && Lat != 0.0) + { + ToLOC(Lat, Lon, LOC); + } + } + + SendMH(TNC, ReportCall, ReportFreq, LOC, ReportMode); + + return; +} + +VOID CloseDriverWindow(int port) +{ +#ifndef LINBPQ + + struct TNCINFO * TNC; + + TNC = TNCInfo[port]; + if (TNC == NULL) + return; + + if (TNC->hDlg == NULL) + return; + + PostMessage(TNC->hDlg, WM_CLOSE,0,0); +// DestroyWindow(TNC->hDlg); + + TNC->hDlg = NULL; +#endif + return; +} + +VOID SaveWindowPos(int port) +{ +#ifndef LINBPQ + + struct TNCINFO * TNC; + char Key[80]; + + TNC = TNCInfo[port]; + + if (TNC == NULL) + return; + + if (TNC->hDlg == NULL) + return; + + sprintf(Key, "PACTOR\\PORT%d", port); + + SaveMDIWindowPos(TNC->hDlg, Key, "Size", TNC->Minimized); + +#endif + return; +} + +VOID ShowTraffic(struct TNCINFO * TNC) +{ + char Status[80]; + + sprintf(Status, "RX %d TX %d ACKED %d ", + TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); +#ifndef LINBPQ + SetDlgItemText(TNC->hDlg, IDC_TRAFFIC, Status); +#endif +} + +BOOL InterlockedCheckBusy(struct TNCINFO * ThisTNC) +{ + // See if this port, or any interlocked ports are reporting channel busy + + struct TNCINFO * TNC; + int i; + int rxInterlock = ThisTNC->RXRadio; + int txInterlock = ThisTNC->TXRadio; + + if (ThisTNC->Busy) + return TRUE; // Our port is busy + + if (rxInterlock == 0 && txInterlock == 0) + return ThisTNC->Busy; // No Interlock + + for (i=1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + + if (TNC == NULL) + continue; + + if (TNC == ThisTNC) + continue; + + if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group + if (TNC->Busy) + return TRUE; // Interlocked port is busy + + } + return FALSE; // None Busy +} + +char ChallengeResponse[13]; + +char * GetChallengeResponse(char * Call, char * ChallengeString) +{ + // Generates a response to the CMS challenge string... + + long long Challenge = _atoi64(ChallengeString); + long long CallSum = 0; + long long Mask; + long long Response; + long long XX = 1065484730; + + char CallCopy[10]; + UINT i; + + + if (Challenge == 0) + return "000000000000"; + +// Calculate Mask from Callsign + + memcpy(CallCopy, Call, 10); + strlop(CallCopy, '-'); + strlop(CallCopy, ' '); + + for (i = 0; i < strlen(CallCopy); i++) + { + CallSum += CallCopy[i]; + } + + Mask = CallSum + CallSum * 4963 + CallSum * 782386; + + Response = (Challenge % 930249781); + Response ^= Mask; + + sprintf(ChallengeResponse, "%012lld", Response); + + return ChallengeResponse; +} + +SOCKET OpenWL2KHTTPSock() +{ + SOCKET sock = 0; + struct sockaddr_in destaddr; + struct sockaddr_in sinx; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + int err; + u_long param=1; + BOOL bcopt=TRUE; + + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(80); + + // Resolve name to address + + HostEnt = gethostbyname ("api.winlink.org"); + + if (!HostEnt) + { + err = WSAGetLastError(); + + Debugprintf("Resolve Failed for %s %d %x", "api.winlink.org", err, err); + return 0 ; // Resolve failed + } + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + // Allocate a Socket entry + + sock = socket(AF_INET,SOCK_STREAM,0); + + if (sock == INVALID_SOCKET) + return 0; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 ) + return FALSE; + + if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) != 0) + { + err=WSAGetLastError(); + closesocket(sock); + return 0; + } + + return sock; +} + +BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER) +{ + SOCKET sock = 0; + int Len; + char Message[1000]; + + sock = OpenWL2KHTTPSock(); + + if (sock == 0) + return 0; + + // {"Callsign":"String"} + + Len = sprintf(Message, "\"Callsign\":\"%s\"", Call); + + SendHTTPRequest(sock, "/sysop/get", Message, Len, _REPLYBUFFER); + + closesocket(sock); + + return _REPLYBUFFER[0]; +} + +BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL) +{ + SOCKET sock = 0; + struct sockaddr_in destaddr; + struct sockaddr_in sinx; + int len = 100; + int addrlen=sizeof(sinx); + struct hostent * HostEnt; + int err; + u_long param=1; + BOOL bcopt=TRUE; + char Buffer[1000]; + char SendBuffer[1000]; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr("api.winlink.org"); + destaddr.sin_port = htons(80); + + HostEnt = gethostbyname ("api.winlink.org"); + + if (!HostEnt) + { + err = WSAGetLastError(); + + Debugprintf("Resolve Failed for %s %d %x", "api.winlink.org", err, err); + return 0 ; // Resolve failed + } + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + // Allocate a Socket entry + + sock = socket(AF_INET,SOCK_STREAM,0); + + if (sock == INVALID_SOCKET) + return 0; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 ) + return FALSE; + + if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) != 0) + { + err=WSAGetLastError(); + closesocket(sock); + return 0; + } + + len = recv(sock, &Buffer[0], len, 0); + + len = sprintf(SendBuffer, "02%07d%-12s%s%s", (int)strlen(SQL), Call, GetChallengeResponse(Call, Buffer), SQL); + + send(sock, SendBuffer, len, 0); + + len = 1000; + + len = recv(sock, &Buffer[0], len, 0); + + Buffer[len] = 0; + Debugprintf(Buffer); + + closesocket(sock); + + return TRUE; + +} +// http://server.winlink.org:8085/csv/reply/ChannelList?Modes=40,41,42,43,44&ServiceCodes=BPQTEST,PUBLIC + +// Process config lines that are common to a number of HF modes + +static char ** SeparateMultiString(char * MultiString) +{ + char ** Value; + int Count = 0; + char * ptr, * ptr1; + + // Convert to string array + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + strlop(MultiString, 13); + ptr = MultiString; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + if (strlen(ptr)) + { + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count++] = _strdup(ptr); + } + ptr = ptr1; + } + + Value[Count] = NULL; + return Value; +} + + + + +extern int nextDummyInterlock; + +int standardParams(struct TNCINFO * TNC, char * buf) +{ + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + TNC->WL2K = DecodeWL2KReportLine(buf); + else if (_memicmp(buf, "SESSIONTIMELIMIT", 16) == 0) + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit = atoi(&buf[17]) * 60; + else if (_memicmp(buf, "ATTACHTIMELIMIT", 15) == 0) + TNC->AttachTimeLimit = atoi(&buf[16]) * 60; + else if (_memicmp(buf, "BUSYHOLD", 8) == 0) // Hold Time for Busy Detect + TNC->BusyHold = atoi(&buf[8]); + else if (_memicmp(buf, "BUSYWAIT", 8) == 0) // Wait time before failing connect if busy + TNC->BusyWait = atoi(&buf[8]); + else if (_memicmp(buf, "AUTOSTARTDELAY", 14) == 0) // Time to wait for TNC to start + TNC->AutoStartDelay = atoi(&buf[15]); + else if (_memicmp(buf, "DEFAULTRADIOCOMMAND", 19) == 0) + TNC->DefaultRadioCmd = _strdup(&buf[20]); + else if (_memicmp(buf, "MYCALLS", 7) == 0) + { + TNC->LISTENCALLS = _strdup(&buf[8]); + strlop(TNC->LISTENCALLS, '\r'); + } + else if (_memicmp(buf, "NRNEIGHBOUR", 11) == 0) + TNC->NRNeighbour = _strdup(&buf[12]); + else if (_memicmp(buf, "MAXCONREQ", 9) == 0) // Hold Time for Busy Detect + TNC->MaxConReq = atoi(&buf[9]); + + else if (_memicmp(buf, "FREQUENCY", 9) == 0) + TNC->Frequency = _strdup(&buf[10]); + else if (_memicmp(buf, "SendTandRtoRelay", 16) == 0) + TNC->SendTandRtoRelay = atoi(&buf[17]); + else if (_memicmp(buf, "Radio", 5) == 0) // Rig Control RADIO for TX amd RX (Equiv to INTERLOCK) + TNC->RXRadio = TNC->TXRadio = atoi(&buf[6]); + else if (_memicmp(buf, "TXRadio", 7) == 0) // Rig Control RADIO for TX + TNC->TXRadio = atoi(&buf[8]); + else if (_memicmp(buf, "RXRadio", 7) == 0) // Rig Control RADIO for RXFRETRIES + TNC->RXRadio = atoi(&buf[8]); + else if (_memicmp(buf, "TXFreq", 6) == 0) // For PTT Sets Freq mode + TNC->TXFreq = strtoll(&buf[7], NULL, 10); + else if (_memicmp(buf, "DefaultTXFreq", 13) == 0) // Set at end of session + TNC->DefaultTXFreq = atof(&buf[14]); + else if (_memicmp(buf, "DefaultRXFreq", 13) == 0) // Set at end of session + TNC->DefaultRXFreq = atof(&buf[14]); + else if (_memicmp(buf, "ActiveTXFreq", 12) == 0) // Set at start of session + TNC->ActiveTXFreq = atof(&buf[13]); + else if (_memicmp(buf, "ActiveRXFreq", 12) == 0) // Set at start of session + TNC->ActiveRXFreq = atof(&buf[13]); + else if (_memicmp(buf, "DisconnectScript", 16) == 0) // Set at start of session + TNC->DisconnectScript = SeparateMultiString(&buf[17]); + else if (_memicmp(buf, "PTTONHEX", 8) == 0) + { + // Hex String to use for PTT on for this port + + char * ptr1 = &buf[9]; + char * ptr2 = TNC->PTTOn; + int i, j, len; + + _strupr(ptr1); + + TNC->PTTOnLen = len = strlen(ptr1) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + } + else if (_memicmp(buf, "PTTOFFHEX", 9) == 0) + { + // Hex String to use for PTT off + + char * ptr = &buf[10]; + char * ptr2 = TNC->PTTOff; + int i, j, len; + + _strupr(ptr); + + TNC->PTTOffLen = len = strlen(ptr) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + } + else + return FALSE; + + return TRUE; +} + +void DecodePTTString(struct TNCINFO * TNC, char * ptr) +{ + if (_stricmp(ptr, "CI-V") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "CAT") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "RTS") == 0) + TNC->PTTMode = PTTRTS; + else if (_stricmp(ptr, "DTR") == 0) + TNC->PTTMode = PTTDTR; + else if (_stricmp(ptr, "DTRRTS") == 0) + TNC->PTTMode = PTTDTR | PTTRTS; + else if (_stricmp(ptr, "CM108") == 0) + TNC->PTTMode = PTTCM108; + else if (_stricmp(ptr, "HAMLIB") == 0) + TNC->PTTMode = PTTHAMLIB; + else if (_stricmp(ptr, "FLRIG") == 0) + TNC->PTTMode = PTTFLRIG; +} + +extern SOCKET ReportSocket; +extern char LOCATOR[80]; +extern char ReportDest[7]; +extern int NumberofPorts; +extern struct RIGPORTINFO * PORTInfo[34]; // Records are Malloc'd + +time_t LastModeReportTime; +time_t LastFreqReportTime; + +VOID SendReportMsg(char * buff, int txlen); + +void sendModeReport() +{ + // if TNC is connected send mode and frequencies to Node Map as a MODE record + // Are we better sending scan info as a separate record ?? + + // MODE Port, HWType, Interlock + + struct PORTCONTROL * PORT = PORTTABLE; + + struct TNCINFO * TNC; + MESSAGE AXMSG; + PMESSAGE AXPTR = &AXMSG; + char Msg[300] = "MODE "; + int i, Len = 5; + + if ((CurrentSecs - LastModeReportTime) < 900) // Every 15 Mins + return; + + LastModeReportTime = CurrentSecs; + + for (i = 0; i < NUMBEROFPORTS; i++) + { + if (PORT->PROTOCOL == 10) + { + PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT; + TNC = TNCInfo[PORT->PORTNUMBER]; + PORT = PORT->PORTPOINTER; + + if (TNC == NULL) + continue; + + if (TNC->CONNECTED == 0 && TNC->TNCOK == 0) + continue; + + if (ReportSocket == 0 || LOCATOR[0] == 0) + continue; + + if (TNC->Frequency) + Len += sprintf(&Msg[Len], "%d,%d,%d,%.6f/", TNC->Port, TNC->Hardware, TNC->RXRadio, atof(TNC->Frequency)); + else + Len += sprintf(&Msg[Len], "%d,%d,%d/", TNC->Port, TNC->Hardware, TNC->RXRadio); + + if (Len > 240) + break; + } + else + PORT = PORT->PORTPOINTER; + } + + if (Len == 5) + return; // Nothing to send + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; +} + +void sendFreqReport(char * From) +{ + // Send info from rig control or Port Frequency info to Node Map for Mode page. + + MESSAGE AXMSG; + PMESSAGE AXPTR = &AXMSG; + char Msg[300] = "FREQ "; + int i, Len = 5, p; + + struct RIGPORTINFO * RIGPORT; + struct RIGINFO * RIG; + struct TimeScan * Band; + struct PORTCONTROL * PORT = PORTTABLE; + struct TNCINFO * TNC; + + if ((CurrentSecs - LastFreqReportTime) < 7200) // Every 2 Hours + return; + + LastFreqReportTime = CurrentSecs; + + for (p = 0; p < NumberofPorts; p++) + { + RIGPORT = PORTInfo[p]; + + for (i = 0; i < RIGPORT->ConfiguredRigs; i++) + { + int j = 1, k = 0; + + RIG = &RIGPORT->Rigs[i]; + + if (RIG->reportFreqs) + { + Len += sprintf(&Msg[Len], "%d/00:00/%s,\\|",RIG->Interlock,RIG->reportFreqs); + } + else + { + if (RIG->TimeBands) + { + Len += sprintf(&Msg[Len], "%d/",RIG->Interlock); + while (RIG->TimeBands[j]) + { + Band = RIG->TimeBands[j]; + k = 0; + + if (Band->Scanlist[0]) + { + Len += sprintf(&Msg[Len], "%02d:%02d/", Band->Start / 3600, Band->Start % 3600); + + while (Band->Scanlist[k]) + { + Len += sprintf(&Msg[Len],"%.0f,", Band->Scanlist[k]->Freq + RIG->rxOffset); + k++; + } + Len += sprintf(&Msg[Len], "\\"); + } + j++; + } + Len += sprintf(&Msg[Len], "|"); + } + } + } + } + + // Look for Port freq info + + for (i = 0; i < NUMBEROFPORTS; i++) + { + if (PORT->PROTOCOL == 10) + { + PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT; + TNC = TNCInfo[PORT->PORTNUMBER]; + PORT = PORT->PORTPOINTER; + + if (TNC == NULL) + continue; + + if (TNC->Frequency == NULL) + continue; + + if (TNC->RIG->TimeBands && TNC->RIG->TimeBands[1]->Scanlist) + continue; // Have freq info from Rigcontrol + + if (TNC->RXRadio == 0) // Replace with dummy + TNC->RXRadio = nextDummyInterlock++; + + // Use negative port no instead of interlock group + + Len += sprintf(&Msg[Len], "%d/00:00/%.0f|", TNC->RXRadio, atof(TNC->Frequency) * 1000000.0); + } + else + PORT = PORT->PORTPOINTER; + } + + if (Len == 5) + return; // Nothing to send + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(AXPTR->DEST, ReportDest, 7); + memcpy(AXPTR->ORIGIN, MYCALL, 7); + AXPTR->DEST[6] &= 0x7e; // Clear End of Call + AXPTR->DEST[6] |= 0x80; // set Command Bit + + AXPTR->ORIGIN[6] |= 1; // Set End of Call + AXPTR->CTL = 3; //UI + AXPTR->PID = 0xf0; + memcpy(AXPTR->L2DATA, Msg, Len); + + SendReportMsg((char *)&AXMSG.DEST, Len + 16) ; +} + + diff --git a/.svn/pristine/12/129588435d73a332d9a80b3904b723c535bcd9c1.svn-base b/.svn/pristine/12/129588435d73a332d9a80b3904b723c535bcd9c1.svn-base new file mode 100644 index 0000000..95563d4 --- /dev/null +++ b/.svn/pristine/12/129588435d73a332d9a80b3904b723c535bcd9c1.svn-base @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.svn/pristine/13/133f0644e7f4f8c80ccfe00872403b0abf4bd029.svn-base b/.svn/pristine/13/133f0644e7f4f8c80ccfe00872403b0abf4bd029.svn-base new file mode 100644 index 0000000..d3caae4 --- /dev/null +++ b/.svn/pristine/13/133f0644e7f4f8c80ccfe00872403b0abf4bd029.svn-base @@ -0,0 +1,277 @@ +/******************************************************************************* + * Copyright (c) 2009, 2020 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * https://www.eclipse.org/legal/epl-2.0/ + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * @file + * \brief This structure represents a persistent data store, used to store + * outbound and inbound messages, in order to achieve reliable messaging. + * + * The MQTT Client persists QoS1 and QoS2 messages in order to meet the + * assurances of delivery associated with these @ref qos levels. The messages + * are saved in persistent storage + * The type and context of the persistence implementation are specified when + * the MQTT client is created (see MQTTClient_create()). The default + * persistence type (::MQTTCLIENT_PERSISTENCE_DEFAULT) uses a file system-based + * persistence mechanism. The persistence_context argument passed to + * MQTTClient_create() when using the default peristence is a string + * representing the location of the persistence directory. If the context + * argument is NULL, the working directory will be used. + * + * To use memory-based persistence, an application passes + * ::MQTTCLIENT_PERSISTENCE_NONE as the persistence_type to + * MQTTClient_create(). This can lead to message loss in certain situations, + * but can be appropriate in some cases (see @ref qos). + * + * Client applications can provide their own persistence mechanism by passing + * ::MQTTCLIENT_PERSISTENCE_USER as the persistence_type. To implement a + * custom persistence mechanism, the application must pass an initialized + * ::MQTTClient_persistence structure as the persistence_context + * argument to MQTTClient_create(). + * + * If the functions defined return an ::MQTTCLIENT_PERSISTENCE_ERROR then the + * state of the persisted data should remain as it was prior to the function + * being called. For example, if Persistence_put() returns + * ::MQTTCLIENT_PERSISTENCE_ERROR, then it is assumed tha tthe persistent store + * does not contain the data that was passed to the function. Similarly, if + * Persistence_remove() returns ::MQTTCLIENT_PERSISTENCE_ERROR then it is + * assumed that the data to be removed is still held in the persistent store. + * + * It is up to the persistence implementation to log any error information that + * may be required to diagnose a persistence mechanism failure. + */ + +/* +/// @cond EXCLUDE +*/ +#if !defined(MQTTCLIENTPERSISTENCE_H) +#define MQTTCLIENTPERSISTENCE_H +/* +/// @endcond +*/ + +/** + * This persistence_type value specifies the default file system-based + * persistence mechanism (see MQTTClient_create()). + */ +#define MQTTCLIENT_PERSISTENCE_DEFAULT 0 +/** + * This persistence_type value specifies a memory-based + * persistence mechanism (see MQTTClient_create()). + */ +#define MQTTCLIENT_PERSISTENCE_NONE 1 +/** + * This persistence_type value specifies an application-specific + * persistence mechanism (see MQTTClient_create()). + */ +#define MQTTCLIENT_PERSISTENCE_USER 2 + +/** + * Application-specific persistence functions must return this error code if + * there is a problem executing the function. + */ +#define MQTTCLIENT_PERSISTENCE_ERROR -2 + +/** + * @brief Initialize the persistent store. + * + * Either open the existing persistent store for this client ID or create a new + * one if one doesn't exist. If the persistent store is already open, return + * without taking any action. + * + * An application can use the same client identifier to connect to many + * different servers. The clientid in conjunction with the + * serverURI uniquely identifies the persistence store required. + * + * @param handle The address of a pointer to a handle for this persistence + * implementation. This function must set handle to a valid reference to the + * persistence following a successful return. + * The handle pointer is passed as an argument to all the other + * persistence functions. It may include the context parameter and/or any other + * data for use by the persistence functions. + * @param clientID The client identifier for which the persistent store should + * be opened. + * @param serverURI The connection string specified when the MQTT client was + * created (see MQTTClient_create()). + * @param context A pointer to any data required to initialize the persistent + * store (see ::MQTTClient_persistence). + * @return Return 0 if the function completes successfully, otherwise return + * ::MQTTCLIENT_PERSISTENCE_ERROR. + */ +typedef int (*Persistence_open)(void** handle, const char* clientID, const char* serverURI, void* context); + +/** + * @brief Close the persistent store referred to by the handle. + * + * @param handle The handle pointer from a successful call to + * Persistence_open(). + * @return Return 0 if the function completes successfully, otherwise return + * ::MQTTCLIENT_PERSISTENCE_ERROR. + */ +typedef int (*Persistence_close)(void* handle); + +/** + * @brief Put the specified data into the persistent store. + * + * @param handle The handle pointer from a successful call to + * Persistence_open(). + * @param key A string used as the key for the data to be put in the store. The + * key is later used to retrieve data from the store with Persistence_get(). + * @param bufcount The number of buffers to write to the persistence store. + * @param buffers An array of pointers to the data buffers associated with + * this key. + * @param buflens An array of lengths of the data buffers. buflen[n] + * gives the length of buffer[n]. + * @return Return 0 if the function completes successfully, otherwise return + * ::MQTTCLIENT_PERSISTENCE_ERROR. + */ +typedef int (*Persistence_put)(void* handle, char* key, int bufcount, char* buffers[], int buflens[]); + +/** + * @brief Retrieve the specified data from the persistent store. + * + * @param handle The handle pointer from a successful call to + * Persistence_open(). + * @param key A string that is the key for the data to be retrieved. This is + * the same key used to save the data to the store with Persistence_put(). + * @param buffer The address of a pointer to a buffer. This function sets the + * pointer to point at the retrieved data, if successful. + * @param buflen The address of an int that is set to the length of + * buffer by this function if successful. + * @return Return 0 if the function completes successfully, otherwise return + * ::MQTTCLIENT_PERSISTENCE_ERROR. + */ +typedef int (*Persistence_get)(void* handle, char* key, char** buffer, int* buflen); + +/** + * @brief Remove the data for the specified key from the store. + * + * @param handle The handle pointer from a successful call to + * Persistence_open(). + * @param key A string that is the key for the data to be removed from the + * store. This is the same key used to save the data to the store with + * Persistence_put(). + * @return Return 0 if the function completes successfully, otherwise return + * ::MQTTCLIENT_PERSISTENCE_ERROR. + */ +typedef int (*Persistence_remove)(void* handle, char* key); + +/** + * @brief Returns the keys in this persistent data store. + * + * @param handle The handle pointer from a successful call to + * Persistence_open(). + * @param keys The address of a pointer to pointers to strings. Assuming + * successful execution, this function allocates memory to hold the returned + * keys (strings used to store the data with Persistence_put()). It also + * allocates memory to hold an array of pointers to these strings. keys + * is set to point to the array of pointers to strings. + * @param nkeys A pointer to the number of keys in this persistent data store. + * This function sets the number of keys, if successful. + * @return Return 0 if the function completes successfully, otherwise return + * ::MQTTCLIENT_PERSISTENCE_ERROR. + */ +typedef int (*Persistence_keys)(void* handle, char*** keys, int* nkeys); + +/** + * @brief Clears the persistence store, so that it no longer contains any + * persisted data. + * + * @param handle The handle pointer from a successful call to + * Persistence_open(). + * @return Return 0 if the function completes successfully, otherwise return + * ::MQTTCLIENT_PERSISTENCE_ERROR. + */ +typedef int (*Persistence_clear)(void* handle); + +/** + * @brief Returns whether any data has been persisted using the specified key. + * + * @param handle The handle pointer from a successful call to + * Persistence_open(). + * @param key The string to be tested for existence in the store. + * @return Return 0 if the key was found in the store, otherwise return + * ::MQTTCLIENT_PERSISTENCE_ERROR. + */ +typedef int (*Persistence_containskey)(void* handle, char* key); + +/** + * @brief A structure containing the function pointers to a persistence + * implementation and the context or state that will be shared across all + * the persistence functions. + */ +typedef struct { + /** + * A pointer to any data required to initialize the persistent store. + */ + void* context; + /** + * A function pointer to an implementation of Persistence_open(). + */ + Persistence_open popen; + /** + * A function pointer to an implementation of Persistence_close(). + */ + Persistence_close pclose; + /** + * A function pointer to an implementation of Persistence_put(). + */ + Persistence_put pput; + /** + * A function pointer to an implementation of Persistence_get(). + */ + Persistence_get pget; + /** + * A function pointer to an implementation of Persistence_remove(). + */ + Persistence_remove premove; + /** + * A function pointer to an implementation of Persistence_keys(). + */ + Persistence_keys pkeys; + /** + * A function pointer to an implementation of Persistence_clear(). + */ + Persistence_clear pclear; + /** + * A function pointer to an implementation of Persistence_containskey(). + */ + Persistence_containskey pcontainskey; +} MQTTClient_persistence; + + +/** + * A callback which is invoked just before a write to persistence. This can be + * used to transform the data, for instance to encrypt it. + * @param context The context as set in ::MQTTAsync_setBeforePersistenceWrite + * @param bufcount The number of buffers to write to the persistence store. + * @param buffers An array of pointers to the data buffers. + * @param buflens An array of lengths of the data buffers. + * @return Return 0 if the function completes successfully, otherwise non 0. + */ +typedef int MQTTPersistence_beforeWrite(void* context, int bufcount, char* buffers[], int buflens[]); + + +/** + * A callback which is invoked just after a read from persistence. This can be + * used to transform the data, for instance to decrypt it. + * @param context The context as set in ::MQTTAsync_setAfterPersistenceRead + * @param buffer The address of a pointer to a buffer. + * @param buflen The address of an int that is the length of the buffer. + * @return Return 0 if the function completes successfully, otherwise non 0. + */ +typedef int MQTTPersistence_afterRead(void* context, char** buffer, int* buflen); + +#endif diff --git a/.svn/pristine/13/1340af3f5aabaa27f5c011eb6fba97496d3ca296.svn-base b/.svn/pristine/13/1340af3f5aabaa27f5c011eb6fba97496d3ca296.svn-base new file mode 100644 index 0000000..12abf4b --- /dev/null +++ b/.svn/pristine/13/1340af3f5aabaa27f5c011eb6fba97496d3ca296.svn-base @@ -0,0 +1,1329 @@ +/* +Copyright 2001-2018 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 +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// White Pages Database Support Routines + +#include "bpqmail.h" + +VOID __cdecl Debugprintf(const char * format, ...); +VOID ReleaseSock(SOCKET sock); +void MQTTMessageEvent(void* message); + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); + +struct NNTPRec * FirstNNTPRec = NULL; + +//int NumberofNNTPRecs=0; + +SOCKET nntpsock; + +extern SocketConn * Sockets; // Chain of active sockets + +int NNTPInPort = 0; + +char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + + +VOID ReleaseNNTPSock(SOCKET sock); + +int NNTPSendSock(SocketConn * sockptr, char * msg) +{ + int len = (int)strlen(msg); + char * newmsg = malloc(len+10); + + WriteLogLine(NULL, '>',msg, len, LOG_TCP); + + strcpy(newmsg, msg); + + strcat(newmsg, "\r\n"); + + len+=2; + + // Attempt to fix Thunderbird - Queue all and send at end + + if ((sockptr->SendSize + len) > sockptr->SendBufferSize) + { + sockptr->SendBufferSize += (10000 + len); + sockptr->SendBuffer = realloc(sockptr->SendBuffer, sockptr->SendBufferSize); + } + + memcpy(&sockptr->SendBuffer[sockptr->SendSize], newmsg, len); + sockptr->SendSize += len; + free (newmsg); + return len; +} + +void NNTPFlush(SocketConn * sockptr) +{ + int sent; + + sent = send(sockptr->socket, sockptr->SendBuffer, sockptr->SendSize, 0); + + if (sent < sockptr->SendSize) + { + int error, remains; + + // Not all could be sent - queue rest + + if (sent == SOCKET_ERROR) + { + error = WSAGetLastError(); + if (error == WSAEWOULDBLOCK) + sent=0; + + // What else?? + } + + remains = sockptr->SendSize - sent; + + sockptr->SendBufferSize += (10000 + remains); + sockptr->SendBuffer = malloc(sockptr->SendBufferSize); + + memmove(sockptr->SendBuffer, &sockptr->SendBuffer[sent], remains); + + sockptr->SendSize = remains; + sockptr->SendPtr = 0; + + return; + } + + free(sockptr->SendBuffer); + sockptr->SendBuffer = NULL; + sockptr->SendSize = 0; + sockptr->SendBufferSize = 0; + return; +} + +VOID __cdecl NNTPsockprintf(SocketConn * sockptr, const char * format, ...) +{ + // printf to a socket + + char buff[1000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(buff, format, arglist); + + NNTPSendSock(sockptr, buff); +} + +struct NNTPRec * LookupNNTP(char * Group) +{ + struct NNTPRec * ptr = FirstNNTPRec; + + while (ptr) + { + if (_stricmp(ptr->NewsGroup, Group) == 0) + return ptr; + + ptr = ptr->Next; + } + + return NULL; +} + + +VOID BuildNNTPList(struct MsgInfo * Msg) +{ + struct NNTPRec * REC; + struct NNTPRec * OLDREC; + struct NNTPRec * PREVREC = 0; + + char FullGroup[100]; + + if (Msg->type != 'B' || Msg->status == 'K' || Msg->status == 'H') + return; + + sprintf(FullGroup, "%s.%s", Msg->to, Msg->via); + + REC = LookupNNTP(FullGroup); + + if (REC == NULL) + { + // New Group. Allocate a record, and put at correct place in chain (alpha order) + + GetSemaphore(&AllocSemaphore, 0); + + REC = zalloc(sizeof (struct NNTPRec)); + OLDREC = FirstNNTPRec; + + if (OLDREC == 0) // First record + { + FirstNNTPRec = REC; + goto DoneIt; + } + else + { + // Follow chain till we find one with a later name + + while(OLDREC) + { + if (strcmp(OLDREC->NewsGroup, FullGroup) > 0) + { + // chain in here + + REC->Next = OLDREC; + if (PREVREC) + PREVREC->Next = REC; + else + FirstNNTPRec = REC; + goto DoneIt; + + } + else + { + PREVREC = OLDREC; + OLDREC = OLDREC->Next; + } + } + + // Run off end - chain to PREVREC + + PREVREC->Next = REC; + } +DoneIt: + strcpy(REC->NewsGroup, FullGroup); + REC->FirstMsg = Msg->number; + REC->DateCreated = (time_t)Msg->datecreated; + + FreeSemaphore(&AllocSemaphore); + } + + REC->LastMsg = Msg->number; + REC->Count++; +} + +void RebuildNNTPList() +{ + struct NNTPRec * NNTPREC = FirstNNTPRec; + struct NNTPRec * SaveNNTPREC; + struct MsgInfo * Msg; + int i; + + // Free old list + + while (NNTPREC) + { + SaveNNTPREC = NNTPREC->Next; + free(NNTPREC); + NNTPREC = SaveNNTPREC; + } + + FirstNNTPRec = NULL; + + for (i = 1; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + BuildNNTPList(Msg); + } +} + + + +char * GetPathFromHeaders(char * MsgBytes) +{ + char * Path = zalloc(10000); + char * ptr1; + + ptr1 = MsgBytes; + +nextline: + + if (memcmp(ptr1, "R:", 2) == 0) + { + char * ptr4 = strchr(ptr1, '\r'); + char * ptr5 = strchr(ptr1, '.'); + ptr1 = strchr(ptr1, '@'); + + if (!ptr1) + return Path; + + if (*++ptr1 == ':') + ptr1++; // Format 2 + + *(ptr5) = 0; + + strcat(Path, "|"); + strcat(Path, ptr1); + + *(ptr5) = '.'; + + ptr1 = ptr4; + + ptr1++; + if (*ptr1 == '\n') ptr1++; + + goto nextline; + } + + return Path; +} + +char * FormatNNTPDateAndTime(time_t Datim) +{ + struct tm *tm; + static char Date[30]; + + // Fri, 19 Nov 82 16:14:55 GMT + // A#asctime gives Wed Jan 02 02:03:55 1980\n\0. + + tm = gmtime(&Datim); + + + + if (tm) + sprintf_s(Date, sizeof(Date), "%s, %02d %3s %02d %02d:%02d:%02d Z", + day[tm->tm_wday], tm->tm_mday, month[tm->tm_mon], tm->tm_year - 100, tm->tm_hour, tm->tm_min, tm->tm_sec); + + return Date; +} + +VOID InitialiseNNTP() + +{ + if (NNTPInPort) + nntpsock = CreateListeningSocket(NNTPInPort); +} + +int CreateNNTPMessage(char * From, char * To, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen) +{ + struct MsgInfo * Msg; + BIDRec * BIDRec; + char * Via; + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + Msg->length = MsgLen; + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + Msg->type = 'B'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + if (Date) + Msg->datecreated = Date; + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + + TidyString(To); + Via = strlop(To, '@'); + + if (strlen(To) > 6) To[6]=0; + + strcpy(Msg->to, To); + strcpy(Msg->from, From); + strcpy(Msg->title, MsgTitle); + strcpy(Msg->via, Via); + + // Set up forwarding bitmap + + MatchMessagetoBBSList(Msg, 0); + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + + BuildNNTPList(Msg); // Build NNTP Groups list + +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(Msg); +#endif + + + return CreateSMTPMessageFile(MsgBody, Msg); + +} + + + +VOID ProcessNNTPServerMessage(SocketConn * sockptr, char * Buffer, int Len) +{ + SOCKET sock; + time_t Date = 0; + + sock=sockptr->socket; + + WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP); + + if (sockptr->Flags == GETTINGMESSAGE) + { + if(memcmp(Buffer, ".\r\n", 3) == 0) + { + // File Message + + char * ptr1, * ptr2; + int linelen, MsgLen; + char MsgFrom[62], MsgTo[62], Msgtitle[62]; + + // Scan headers for From: To: and Subject: Line (Headers end at blank line) + + ptr1 = sockptr->MailBuffer; + Loop: + ptr2 = strchr(ptr1, '\r'); + + if (ptr2 == NULL) + { + NNTPSendSock(sockptr, "500 Eh"); + return; + } + + linelen = (int)(ptr2 - ptr1); + + // From: "John Wiseman" + // To: + // + + + if (_memicmp(ptr1, "From:", 5) == 0) + { + if (linelen > 65) linelen = 65; + memcpy(MsgFrom, ptr1, linelen); + MsgFrom[linelen]=0; + } + else + if (_memicmp(ptr1, "Newsgroups:", 3) == 0) + { + char * sep = strchr(ptr1, '.'); + + if (sep) + *(sep) = '@'; + + if (linelen > 63) linelen = 63; + memcpy(MsgTo, &ptr1[11], linelen-11); + MsgTo[linelen-11]=0; + } + else + if (_memicmp(ptr1, "Subject:", 8) == 0) + { + if (linelen > 68) linelen = 68; + memcpy(Msgtitle, &ptr1[9], linelen-9); + Msgtitle[linelen-9]=0; + } + else + if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char * Context; + char seps[] = " ,\t\r"; + char Offset[10] = ""; + int i, HH, MM; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: Tue, 9 Jun 2009 20:54:55 +0100 + + ptr1 = strtok_s(&ptr1[5], seps, &Context); // Skip Day + ptr1 = strtok_s(NULL, seps, &Context); // Day + + rtime.tm_mday = atoi(ptr1); + + ptr1 = strtok_s(NULL, seps, &Context); // Month + + for (i=0; i < 12; i++) + { + if (strcmp(month[i], ptr1) == 0) + { + rtime.tm_mon = i; + break; + } + } + + sscanf(Context, "%04d %02d:%02d:%02d%s", + &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = 0; + else + { + if ((Offset[0] == '+') || (Offset[0] == '-')) + { + MM = atoi(&Offset[3]); + Offset[3] = 0; + HH = atoi(&Offset[1]); + MM = MM + (60 * HH); + + if (Offset[0] == '+') + Date -= (60*MM); + else + Date += (60*MM); + + + } + } + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip crlf + goto Loop; + } + + ptr1 = sockptr->MailBuffer; + + MsgLen = sockptr->MailSize - (int)(ptr2 - ptr1); + + ptr1 = strchr(MsgFrom, '<'); + + if (ptr1) + { + char * ptr3 = strchr(ptr1, '@'); + ptr1++; + if (ptr3) + *(ptr3) = 0; + } + else + ptr1 = MsgFrom; + + CreateNNTPMessage(_strupr(ptr1), _strupr(MsgTo), Msgtitle, Date, ptr2, MsgLen); + + free(sockptr->MailBuffer); + sockptr->MailBufferSize=0; + sockptr->MailBuffer=0; + sockptr->MailSize = 0; + + sockptr->Flags &= ~GETTINGMESSAGE; + + NNTPSendSock(sockptr, "240 OK"); + + return; + } + + if ((sockptr->MailSize + Len) > sockptr->MailBufferSize) + { + sockptr->MailBufferSize += 10000; + sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to extend Message Buffer"); + shutdown(sock, 0); + return; + } + } + + memcpy(&sockptr->MailBuffer[sockptr->MailSize], Buffer, Len); + sockptr->MailSize += Len; + + return; + } + + Buffer[Len-2] = 0; + + if(_memicmp(Buffer, "AUTHINFO USER", 13) == 0) + { + if (Len > 22) Buffer[22]=0; + strcpy(sockptr->CallSign, &Buffer[14]); + sockptr->State = GettingPass; + NNTPsockprintf(sockptr, "381 More authentication information required"); + return; + } + + if (sockptr->State == GettingUser) + { + NNTPsockprintf(sockptr, "480 Authentication required"); + return; + } + + if (sockptr->State == GettingPass) + { + struct UserInfo * user = NULL; + + if(_memicmp(Buffer, "AUTHINFO PASS", 13) == 0) + { + user = LookupCall(sockptr->CallSign); + + if (user) + { + if (strcmp(user->pass, &Buffer[14]) == 0) + { + NNTPsockprintf(sockptr, "281 Authentication accepted"); + + sockptr->State = Authenticated; + sockptr->POP3User = user; + return; + } + } + NNTPSendSock(sockptr, "482 Authentication rejected"); + sockptr->State = GettingUser; + return; + } + + NNTPsockprintf(sockptr, "480 Authentication required"); + return; + } + + + if(_memicmp(Buffer, "GROUP", 5) == 0) + { + struct NNTPRec * REC = FirstNNTPRec; + + while (REC) + { + if (_stricmp(REC->NewsGroup, &Buffer[6]) == 0) + { + NNTPsockprintf(sockptr, "211 %d %d %d %s", REC->Count, REC->FirstMsg, REC->LastMsg, REC->NewsGroup); + sockptr->NNTPNum = 0; + sockptr->NNTPGroup = REC; + return; + } + REC =REC->Next; + } + + NNTPsockprintf(sockptr, "411 no such news group"); + return; + } + + if(_memicmp(Buffer, "LISTGROUP", 9) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgNo ; + + // Either currently selected, or a param follows + + if (REC == NULL && Buffer[10] == 0) + { + NNTPsockprintf(sockptr, "412 No Group Selected"); + return; + } + + if (Buffer[10] == 0) + goto GotGroup; + + REC = FirstNNTPRec; + + while(REC) + { + if (_stricmp(REC->NewsGroup, &Buffer[10]) == 0) + { + GotGroup: + + NNTPsockprintf(sockptr, "211 Article Numbers Follows"); + sockptr->NNTPNum = 0; + sockptr->NNTPGroup = REC; + + for (MsgNo = REC->FirstMsg; MsgNo <= REC->LastMsg; MsgNo++) + { + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + char FullGroup[100]; + sprintf(FullGroup, "%s.%s", Msg->to, Msg->via ); + if (_stricmp(FullGroup, REC->NewsGroup) == 0) + { + NNTPsockprintf(sockptr, "%d", MsgNo); + } + } + } + NNTPSendSock(sockptr,"."); + return; + } + REC = REC->Next; + } + NNTPsockprintf(sockptr, "411 no such news group"); + return; + } + + if(_memicmp(Buffer, "MODE READER", 11) == 0) + { + NNTPSendSock(sockptr, "200 Hello"); + return; + } + + if(_memicmp(Buffer, "LIST",4) == 0) + { + struct NNTPRec * REC = FirstNNTPRec; + + NNTPSendSock(sockptr, "215 list of newsgroups follows"); + + while (REC) + { + NNTPsockprintf(sockptr, "%s %d %d y", REC->NewsGroup, REC->LastMsg, REC->FirstMsg); + REC = REC->Next; + } + + NNTPSendSock(sockptr,"."); + return; + } + + //NEWGROUPS YYMMDD HHMMSS [GMT] [] + + if(_memicmp(Buffer, "NEWGROUPS", 9) == 0) + { + struct NNTPRec * REC = FirstNNTPRec; + struct tm rtime; + char Offset[20] = ""; + time_t Time; + + memset(&rtime, 0, sizeof(struct tm)); + + sscanf(&Buffer[10], "%02d%02d%02d %02d%02d%02d %s", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, + &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset); + + rtime.tm_year+=100; + rtime.tm_mon--; + + if (_stricmp(Offset, "GMT") == 0) + Time = mktime(&rtime) - (time_t)_MYTIMEZONE; + else + Time = mktime(&rtime); + + NNTPSendSock(sockptr, "231 list of new newsgroups follows"); + + while(REC) + { + if (REC->DateCreated > Time) + NNTPsockprintf(sockptr, "%s %d %d y", REC->NewsGroup, REC->LastMsg, REC->FirstMsg); + REC = REC->Next; + } + + NNTPSendSock(sockptr,"."); + return; + } + + if(_memicmp(Buffer, "HEAD",4) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgNo = atoi(&Buffer[5]); + + if (REC == NULL) + { + NNTPSendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + if (MsgNo == 0) + { + MsgNo = sockptr->NNTPNum; + + if (MsgNo == 0) + { + NNTPSendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgNo; + } + + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + NNTPsockprintf(sockptr, "221 %d <%s>", MsgNo, Msg->bid); + + NNTPsockprintf(sockptr, "From: %s", Msg->from); + NNTPsockprintf(sockptr, "Date: %s", FormatNNTPDateAndTime((time_t)Msg->datecreated)); + NNTPsockprintf(sockptr, "Newsgroups: %s.s", Msg->to, Msg->via); + NNTPsockprintf(sockptr, "Subject: %s", Msg->title); + NNTPsockprintf(sockptr, "Message-ID: <%s>", Msg->bid); + NNTPsockprintf(sockptr, "Path: %s", BBSName); + + NNTPSendSock(sockptr,"."); + return; + } + + NNTPSendSock(sockptr,"423 No such article in this newsgroup"); + return; + } + + if(_memicmp(Buffer, "ARTICLE", 7) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgNo = atoi(&Buffer[8]); + char * msgbytes; + char * Path; + + if (REC == NULL) + { + NNTPSendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + if (MsgNo == 0) + { + MsgNo = sockptr->NNTPNum; + + if (MsgNo == 0) + { + NNTPSendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgNo; + } + + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + NNTPsockprintf(sockptr, "220 %d <%s>", MsgNo, Msg->bid); + msgbytes = ReadMessageFile(Msg->number); + + Path = GetPathFromHeaders(msgbytes); + + NNTPsockprintf(sockptr, "From: %s", Msg->from); + NNTPsockprintf(sockptr, "Date: %s", FormatNNTPDateAndTime((time_t)Msg->datecreated)); + NNTPsockprintf(sockptr, "Newsgroups: %s.%s", Msg->to, Msg->via); + NNTPsockprintf(sockptr, "Subject: %s", Msg->title); + NNTPsockprintf(sockptr, "Message-ID: <%s>", Msg->bid); + NNTPsockprintf(sockptr, "Path: %s", &Path[1]); + + NNTPSendSock(sockptr,""); + + + NNTPSendSock(sockptr,msgbytes); + NNTPSendSock(sockptr,""); + + NNTPSendSock(sockptr,"."); + + free(msgbytes); + free(Path); + + return; + + } + NNTPSendSock(sockptr,"423 No such article in this newsgroup"); + return; + } + + if(_memicmp(Buffer, "BODY", 4) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgNo = atoi(&Buffer[8]); + char * msgbytes; + char * Path; + + if (REC == NULL) + { + NNTPSendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + if (MsgNo == 0) + { + MsgNo = sockptr->NNTPNum; + + if (MsgNo == 0) + { + NNTPSendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgNo; + } + + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + NNTPsockprintf(sockptr, "222 %d <%s>", MsgNo, Msg->bid); + msgbytes = ReadMessageFile(Msg->number); + + Path = GetPathFromHeaders(msgbytes); + + NNTPSendSock(sockptr,msgbytes); + NNTPSendSock(sockptr,""); + + NNTPSendSock(sockptr,"."); + + free(msgbytes); + free(Path); + + return; + + } + NNTPSendSock(sockptr,"423 No such article in this newsgroup"); + return; + } + + if(_memicmp(Buffer, "XHDR",4) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgStart, MsgEnd, MsgNo, fields; + char Header[100]; + + if (REC == NULL) + { + NNTPSendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + // XHDR subject nnnn-nnnn + + fields = sscanf(&Buffer[5], "%s %d-%d", &Header[0], &MsgStart, &MsgEnd); + + if (fields > 1) + MsgNo = MsgStart; + + if (fields == 2) + MsgEnd = MsgStart; + + if (MsgNo == 0) + { + MsgStart = MsgEnd = sockptr->NNTPNum; + + if (MsgStart == 0) + { + NNTPSendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgEnd; + } + + NNTPsockprintf(sockptr, "221 "); + + for (MsgNo = MsgStart; MsgNo <= MsgEnd; MsgNo++) + { + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + char FullGroup[100]; + sprintf(FullGroup, "%s.%s", Msg->to, Msg->via ); + if (_stricmp(FullGroup, REC->NewsGroup) == 0) + { + if (_stricmp(Header, "subject") == 0) + NNTPsockprintf(sockptr, "%d Subject: %s", MsgNo, Msg->title); + else if (_stricmp(Header, "from") == 0) + NNTPsockprintf(sockptr, "%d From: %s", MsgNo, Msg->from); + else if (_stricmp(Header, "date") == 0) + NNTPsockprintf(sockptr, "%d Date: %s", MsgNo, FormatNNTPDateAndTime((time_t)Msg->datecreated)); + else if (_stricmp(Header, "message-id") == 0) + NNTPsockprintf(sockptr, "%d Message-ID: <%s>", MsgNo, Msg->bid); + else if (_stricmp(Header, "lines") == 0) + NNTPsockprintf(sockptr, "%d Lines: %d", MsgNo, Msg->length); + } + } + } + + NNTPSendSock(sockptr,"."); + return; + + } + + if(_memicmp(Buffer, "XOVER", 5) == 0) + { + struct NNTPRec * REC = sockptr->NNTPGroup; + struct MsgInfo * Msg; + int MsgStart, MsgEnd, MsgNo, fields; + + if (REC == NULL) + { + NNTPSendSock(sockptr,"412 no newsgroup has been selected"); + return; + } + + fields = sscanf(&Buffer[6], "%d-%d", &MsgStart, &MsgEnd); + + if (fields > 0) + MsgNo = MsgStart; + + if (fields == 1) + MsgEnd = MsgStart; + + if (MsgNo == 0) + { + MsgStart = MsgEnd = sockptr->NNTPNum; + + if (MsgStart == 0) + { + NNTPSendSock(sockptr,"420 no current article has been selected"); + return; + } + } + else + { + sockptr->NNTPNum = MsgEnd; + } + + NNTPsockprintf(sockptr, "224 "); + + for (MsgNo = MsgStart; MsgNo <= MsgEnd; MsgNo++) + { + Msg=MsgnotoMsg[MsgNo]; + + if (Msg) + { + char FullGroup[100]; + sprintf(FullGroup, "%s.%s", Msg->to, Msg->via ); + if (_stricmp(FullGroup, REC->NewsGroup) == 0) + { + // subject, author, date, message-id, references, byte count, and line count. + NNTPsockprintf(sockptr, "%d\t%s\t%s\t%s\t%s\t%s\t%d\t%d", + MsgNo, Msg->title, Msg->from, FormatNNTPDateAndTime((time_t)Msg->datecreated), Msg->bid, + "", Msg->length, Msg->length); + } + } + } + + NNTPSendSock(sockptr,"."); + return; + + } + + + /* + 240 article posted ok + 340 send article to be posted. End with . + 440 posting not allowed + 441 posting failed +*/ + if(_memicmp(Buffer, "POST", 4) == 0) + { + if (sockptr->State != Authenticated) + { + NNTPsockprintf(sockptr, "480 Authentication required"); + return; + } + + sockptr->MailBuffer=malloc(10000); + sockptr->MailBufferSize=10000; + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to create POP3 Message Buffer"); + NNTPSendSock(sockptr, "QUIT"); + sockptr->State = WaitingForQUITResponse; + shutdown(sock, 0); + + return; + } + + sockptr->Flags |= GETTINGMESSAGE; + + NNTPSendSock(sockptr, "340 OK"); + return; + } + + + + if(_memicmp(Buffer, "QUIT", 4) == 0) + { + NNTPSendSock(sockptr, "205 OK"); + Sleep(500); + shutdown(sock, 0); + return; + } + +/* if(memcmp(Buffer, "RSET\r\n", 6) == 0) + { + NNTPSendSock(sockptr, "250 Ok"); + sockptr->State = 0; + sockptr->Recipients; + return; + } +*/ + + if(memcmp(Buffer, "DATE", 4) == 0) + { + //This command returns a one-line response code of 111 followed by the + //GMT date and time on the server in the form YYYYMMDDhhmmss. + // 111 YYYYMMDDhhmmss + + struct tm *tm; + char Date[32]; + time_t Time = time(NULL); + + tm = gmtime(&Time); + + if (tm) + { + sprintf_s(Date, sizeof(Date), "111 %04d%02d%02d%02d%02d%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + NNTPSendSock(sockptr, Date); + } + else + NNTPSendSock(sockptr, "500 command not recognized"); + + return; + } + + NNTPSendSock(sockptr, "500 command not recognized"); + + return; +} + + + + +int NNTP_Read(SocketConn * sockptr, SOCKET sock) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[2000]; + + // May have several messages per packet, or message split over packets + + if (sockptr->InputLen > 1000) // Shouldnt have lines longer than this in text mode + { + sockptr->InputLen=0; + } + + InputLen=recv(sock, &sockptr->TCPBuffer[sockptr->InputLen], 1000, 0); + + if (InputLen <= 0) + { + int x = WSAGetLastError(); + + closesocket(sock); + ReleaseSock(sock); + + return 0; // Does this mean closed? + } + + sockptr->InputLen += InputLen; + +loop: + + ptr = memchr(sockptr->TCPBuffer, '\n', sockptr->InputLen); + + if (ptr) // CR in buffer + { + ptr2 = &sockptr->TCPBuffer[sockptr->InputLen]; + ptr++; // Assume LF Follows CR + + if (ptr == ptr2) + { + // Usual Case - single meg in buffer + + ProcessNNTPServerMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen); + sockptr->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = sockptr->InputLen - (int)(ptr2-ptr); + + memcpy(Buffer, sockptr->TCPBuffer, MsgLen); + + ProcessNNTPServerMessage(sockptr, Buffer, MsgLen); + + memmove(sockptr->TCPBuffer, ptr, sockptr->InputLen-MsgLen); + + sockptr->InputLen -= MsgLen; + + goto loop; + + } + } + + NNTPFlush(sockptr); + + return 0; +} + + +int NNTP_Accept(SOCKET SocketId) +{ + int addrlen; + SocketConn * sockptr; + u_long param = 1; + + SOCKET sock; + + addrlen=sizeof(struct sockaddr); + + // Allocate a Socket entry + + sockptr=zalloc(sizeof(SocketConn)+100); + + sockptr->Next = Sockets; + Sockets=sockptr; + + sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen); + + if (sock == INVALID_SOCKET) + { + Logprintf(LOG_TCP, NULL, '|', "NNTP accept() failed Error %d", WSAGetLastError()); + + // get rid of socket record + + Sockets = sockptr->Next; + free(sockptr); + + return FALSE; + } + + + sockptr->Type = NNTPServer; + + ioctl(sock, FIONBIO, ¶m); + sockptr->socket = sock; + sockptr->State = 0; + + NNTPSendSock(sockptr, "200 BPQMail NNTP Server ready"); + Logprintf(LOG_TCP, NULL, '|', "Incoming NNTP Connect Socket = %d", sock); + + NNTPFlush(sockptr); + + return 0; +} +/* +int NNTP_Data(int sock, int error, int eventcode) +{ + SocketConn * sockptr; + + // Find Connection Record + + sockptr=Sockets; + + while (sockptr) + { + if (sockptr->socket == sock) + { + switch (eventcode) + { + case FD_READ: + + return NNTP_Read(sockptr,sock); + + case FD_WRITE: + + // Either Just connected, or flow contorl cleared + + if (sockptr->SendBuffer) + // Data Queued + + SendFromQueue(sockptr); + else + { + NNTPSendSock(sockptr, "200 BPQMail NNTP Server ready"); +// sockptr->State = GettingUser; + } + + return 0; + + case FD_OOB: + + return 0; + + case FD_ACCEPT: + + return 0; + + case FD_CONNECT: + + return 0; + + case FD_CLOSE: + + closesocket(sock); + ReleaseNNTPSock(sock); + return 0; + } + return 0; + } + else + sockptr=sockptr->Next; + } + + return 0; +} +*/ +VOID ReleaseNNTPSock(SOCKET sock) +{ + // remove and free the socket record + + SocketConn * sockptr, * lastptr; + + sockptr=Sockets; + lastptr=NULL; + + while (sockptr) + { + if (sockptr->socket == sock) + { + if (lastptr) + lastptr->Next=sockptr->Next; + else + Sockets=sockptr->Next; + + free(sockptr); + return; + } + else + { + lastptr=sockptr; + sockptr=sockptr->Next; + } + } + + return; +} + +VOID SendFromQueue(SocketConn * sockptr) +{ + int bytestosend = sockptr->SendSize - sockptr->SendPtr; + int bytessent; + + Debugprintf("TCP - Sending %d bytes from buffer", bytestosend); + + bytessent = send(sockptr->socket, &sockptr->SendBuffer[sockptr->SendPtr], bytestosend, 0); + + if (bytessent == bytestosend) + { + // All Sent + + free(sockptr->SendBuffer); + sockptr->SendBuffer = NULL; + } + else + { + sockptr->SendPtr += bytessent; + } + + return; +} diff --git a/.svn/pristine/13/1368bcb6660d3eb378f376ef8fa9a0acfad8af7a.svn-base b/.svn/pristine/13/1368bcb6660d3eb378f376ef8fa9a0acfad8af7a.svn-base new file mode 100644 index 0000000..1a7dea1 --- /dev/null +++ b/.svn/pristine/13/1368bcb6660d3eb378f376ef8fa9a0acfad8af7a.svn-base @@ -0,0 +1,237 @@ +/* +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 +*/ + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#define _CRT_SECURE_NO_DEPRECATE + +#include "compatbits.h" +#include +#include "asmstrucs.h" +#include "tncinfo.h" + +VOID __cdecl Debugprintf(const char * format, ...); + +#ifndef WIN32 + +#define APIENTRY +#define DllExport +#define VOID void + +#else +#include +#endif + +extern BOOL EventsEnabled; +void MQTTReportSession(char * Msg); +extern int MQTT; + + +extern char Modenames[19][10]; + +// Runs use specified routine on certain event +#ifndef WIN32 + +void RunEventProgram(char * Program, char * Param) +{ + char * arg_list[] = {Program, NULL, NULL}; + pid_t child_pid; + + if (EventsEnabled == 0) + return; + + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + if (Param && Param[0]) + arg_list[1] = Param; + + // Fork and Exec Specified program + + // Duplicate this process. + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("Event fork() Failed\n"); + return; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + // The execvp function returns only if an error occurs. + + printf ("Failed to run %s\n", arg_list[0]); + exit(0); // Kill the new process + } + +#else + +DllExport void APIENTRY RunEventProgram(char * Program, char * Param) +{ + int n = 0; + char cmdLine[256]; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + + if (EventsEnabled == 0) + return; + + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + sprintf(cmdLine, "%s %s", Program, Param); + + if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo)) + Debugprintf("Failed to Start %s Error %d ", Program, GetLastError()); + +#endif + + return; +} + +void hookL2SessionAccepted(int Port, char * remotecall, char * ourcall, struct _LINKTABLE * LINK) +{ + // Incoming SABM + + LINK->ConnectTime = time(NULL); + LINK->bytesTXed = LINK->bytesRXed = 0; + + strcpy(LINK->callingCall, remotecall); + strcpy(LINK->receivingCall, ourcall); + strcpy(LINK->Direction, "In"); +} + +void hookL2SessionDeleted(struct _LINKTABLE * LINK) +{ + // calculate session time and av bytes/min in and out + + if (LINK->ConnectTime) + { + if (LINK->bytesTXed == 0 && LINK->bytesRXed == 0) + { + // assume failed connect and ignore for now - maybe log later + + } + else + { + char Msg[256]; + char timestamp[16]; + time_t sessionTime = time(NULL) - LINK->ConnectTime; + double avBytesSent = LINK->bytesTXed / (sessionTime / 60.0); + double avBytesRXed = LINK->bytesRXed / (sessionTime / 60.0); + time_t Now = time(NULL); + struct tm * TM = localtime(&Now); + + sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + if (sessionTime == 0) + sessionTime = 1; // Or will get divide by zero error + + Debugprintf("KISS Session Stats Port %d %s %s %d secs Bytes Sent %d BPM %4.2f Bytes Received %d %4.2f BPM ", + LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime, LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp); + + + sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d," + "\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}", + "KISS", LINK->Direction, LINK->LINKPORT->PORTNUMBER, LINK->callingCall, LINK->receivingCall, sessionTime, + LINK->bytesTXed, avBytesSent, LINK->bytesRXed, avBytesRXed, timestamp); + + if (MQTT) + MQTTReportSession(Msg); + } + + LINK->ConnectTime = 0; + } +} + +void hookL2SessionAttempt(int Port, char * ourcall, char * remotecall, struct _LINKTABLE * LINK) +{ + LINK->ConnectTime = time(NULL); + LINK->bytesTXed = LINK->bytesRXed = 0; + + strcpy(LINK->callingCall, ourcall); + strcpy(LINK->receivingCall, remotecall); + strcpy(LINK->Direction, "Out"); +} + +void hookL4SessionAttempt(struct STREAMINFO * STREAM, char * remotecall, char * ourcall) +{ + // Outgoing Connect + + STREAM->ConnectTime = time(NULL); + STREAM->bytesTXed = STREAM->bytesRXed = 0; + + strcpy(STREAM->callingCall, ourcall); + strcpy(STREAM->receivingCall, remotecall); + strcpy(STREAM->Direction, "Out"); +} + +void hookL4SessionAccepted(struct STREAMINFO * STREAM, char * remotecall, char * ourcall) +{ + // Incoming Connect + + STREAM->ConnectTime = time(NULL); + STREAM->bytesTXed = STREAM->bytesRXed = 0; + + strcpy(STREAM->callingCall, remotecall); + strcpy(STREAM->receivingCall, ourcall); + strcpy(STREAM->Direction, "In"); +} + +void hookL4SessionDeleted(struct TNCINFO * TNC, struct STREAMINFO * STREAM) +{ + char Msg[256]; + + char timestamp[16]; + + if (STREAM->ConnectTime) + { + time_t sessionTime = time(NULL) - STREAM->ConnectTime; + double avBytesRXed = STREAM->bytesRXed / (sessionTime / 60.0); + double avBytesSent = STREAM->bytesTXed / (sessionTime / 60.0); + time_t Now = time(NULL); + struct tm * TM = localtime(&Now); + sprintf(timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + if (sessionTime == 0) + sessionTime = 1; // Or will get divide by zero error + + sprintf(Msg, "{\"mode\": \"%s\", \"direction\": \"%s\", \"port\": %d, \"callfrom\": \"%s\", \"callto\": \"%s\", \"time\": %d, \"bytesSent\": %d," + "\"BPMSent\": %4.2f, \"BytesReceived\": %d, \"BPMReceived\": %4.2f, \"timestamp\": \"%s\"}", + Modenames[TNC->Hardware - 1], STREAM->Direction, TNC->Port, STREAM->callingCall, STREAM->receivingCall, sessionTime, + STREAM->bytesTXed, avBytesSent, STREAM->bytesRXed, avBytesRXed, timestamp); + + if (MQTT) + MQTTReportSession(Msg); + + STREAM->ConnectTime = 0; + } +} + + diff --git a/.svn/pristine/13/136e510d90c53fc02930e5a4a63a91f73204a63a.svn-base b/.svn/pristine/13/136e510d90c53fc02930e5a4a63a91f73204a63a.svn-base new file mode 100644 index 0000000..eca826f --- /dev/null +++ b/.svn/pristine/13/136e510d90c53fc02930e5a4a63a91f73204a63a.svn-base @@ -0,0 +1,988 @@ + +// Program to Convert RTS changes on Virtual COM Port to hamlib PTT commands + + +// Version 1. 0. 0. 1 December 2020 + +// Version 1. 0. 2. 1 April 2018 + + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include + +#include +#include +#include +#include +#include + +#define LIBCONFIG_STATIC +#include "libconfig.h" + + +#include "BPQRemotePTTRes.h" + +#define BPQICON 400 + +WSADATA WsaData; // receives data from WSAStartup + +#define WSA_READ WM_USER + 1 + +HINSTANCE hInst; +char AppName[] = "BPQRemotePTT"; +char Title[80] = "BPQRemotePTT"; + +TCHAR szTitle[]="GPSMuxPC"; // The title bar text +TCHAR szWindowClass[]="GPSMAINWINDOW"; // the main window class name + + +// Foward declarations of functions included in this code module: + +ATOM MyRegisterClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); +VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap); +VOID WINAPI txCompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap); +VOID CATThread(); +VOID runTimer(); + +int TimerHandle = 0; + +LOGFONT LFTTYFONT ; + +HFONT hFont; + +struct sockaddr_in sinx; +struct sockaddr rx; + +int addrlen = sizeof(struct sockaddr_in); + +int HAMLIBPORT; // Port Number for HAMLIB (rigctld) Emulator +char * HAMLIBHOST[128]; + +BOOL MinimizetoTray=FALSE; + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[255]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + strcat(Mess, "\r\n"); + + OutputDebugString(Mess); + + return; +} + + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++=0; + + return ptr; +} + +char * RigPort = NULL; +char * RigSpeed = NULL; +HANDLE RigHandle = 0; +int RigType = 0; // Flag for possible RTS/DTR + + +char BPQHostIP[128]; + +char PTTCATPort[4][16]; +HANDLE PTTCATHandle[4]; +short HamLibPort[4]; +SOCKET HamLibSock[4]; // rigctld socket + +HWND comWnd[4]; +HWND portWnd[4]; +HWND stateWnd[4]; +int stateCtl[4]; + +struct sockaddr_in remoteDest[4]; + +int RealMux[4]; // BPQ Virtual or Real + +int EndPTTCATThread = 0; + +char ConfigName[256] = "BPQRemotePTT.cfg"; + +BOOL GetStringValue(config_setting_t * group, char * name, char * value) +{ + const char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + { + str = config_setting_get_string (setting); + strcpy(value, str); + return TRUE; + } + return FALSE; +} + +int GetIntValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return 0; +} + +VOID SaveStringValue(config_setting_t * group, char * name, char * value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, value); + +} + + +VOID SaveIntValue(config_setting_t * group, char * name, int value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT); + if(setting) + config_setting_set_int(setting, value); +} +VOID GetConfig() +{ + config_setting_t *group; + config_t cfg; + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + if(!config_read_file(&cfg, ConfigName)) + { + fprintf(stderr, "Config read error line %d - %s\n", config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + return; + } + + group = config_lookup(&cfg, "main"); + + if (group == NULL) + { + config_destroy(&cfg); + return; + } + + + GetStringValue(group, "BPQHostIP", BPQHostIP); + + GetStringValue(group, "COM1", PTTCATPort[0]); + GetStringValue(group, "COM2", PTTCATPort[1]); + GetStringValue(group, "COM3", PTTCATPort[2]); + GetStringValue(group, "COM4", PTTCATPort[3]); + + HamLibPort[0] = GetIntValue(group, "HamLibPort1"); + HamLibPort[1] = GetIntValue(group, "HamLibPort2"); + HamLibPort[2] = GetIntValue(group, "HamLibPort3"); + HamLibPort[3] = GetIntValue(group, "HamLibPort4"); + + config_destroy(&cfg); +} + +VOID SaveConfig() +{ + config_setting_t *root, *group; + config_t cfg; + + // Get rid of old config before saving + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); + + SaveStringValue(group, "BPQHostIP", BPQHostIP); + + SaveStringValue(group, "COM1", PTTCATPort[0]); + SaveStringValue(group, "COM2", PTTCATPort[1]); + SaveStringValue(group, "COM3", PTTCATPort[2]); + SaveStringValue(group, "COM4", PTTCATPort[3]); + + SaveIntValue(group, "HamLibPort1", HamLibPort[0]); + SaveIntValue(group, "HamLibPort2", HamLibPort[1]); + SaveIntValue(group, "HamLibPort3", HamLibPort[2]); + SaveIntValue(group, "HamLibPort4", HamLibPort[3]); + + if(!config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + + config_destroy(&cfg); +} + + + + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + MSG msg; + + if (lpCmdLine[0]) + { + // Port Name and Speed for Remote CAT + + RigPort = _strdup(lpCmdLine); + RigSpeed = strlop(RigPort, ':'); + } + + MyRegisterClass(hInstance); + + GetConfig(); + + if (!InitInstance(hInstance, nCmdShow)) + return (FALSE); + + // Main message loop: + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + KillTimer(NULL, TimerHandle); + + return (msg.wParam); +} + +// + +// +// FUNCTION: InitApplication(HANDLE) +// +// PURPOSE: Initializes window data and registers window class +// +// COMMENTS: +// +// In this function, we initialize a window class by filling out a data +// structure of type WNDCLASS and calling either RegisterClass or +// the internal MyRegisterClass. +// + +#define BGCOLOUR RGB(236,233,216) +HBRUSH bgBrush; +HBRUSH RedBrush; +HBRUSH GreenBrush; + +HWND hWnd; + +BOOL InitApplication(HINSTANCE hInstance) +{ + return TRUE; +} + +// +// FUNCTION: InitInstance(HANDLE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// + +HFONT FAR PASCAL MyCreateFont( void ) +{ + CHOOSEFONT cf; + LOGFONT lf; + HFONT hfont; + + // Initialize members of the CHOOSEFONT structure. + + cf.lStructSize = sizeof(CHOOSEFONT); + cf.hwndOwner = (HWND)NULL; + cf.hDC = (HDC)NULL; + cf.lpLogFont = &lf; + cf.iPointSize = 0; + cf.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY; + cf.rgbColors = RGB(0,0,0); + cf.lCustData = 0L; + cf.lpfnHook = (LPCFHOOKPROC)NULL; + cf.lpTemplateName = (LPSTR)NULL; + cf.hInstance = (HINSTANCE) NULL; + cf.lpszStyle = (LPSTR)NULL; + cf.nFontType = SCREEN_FONTTYPE; + cf.nSizeMin = 0; + cf.nSizeMax = 0; + + // Display the CHOOSEFONT common-dialog box. + + ChooseFont(&cf); + + // Create a logical font based on the user's + // selection and return a handle identifying + // that font. + + hfont = CreateFontIndirect(cf.lpLogFont); + return (hfont); +} + +VOID Rig_PTT(int n, BOOL PTTState) +{ + + char Msg[16]; + int Len = sprintf(Msg, "T %d\n", PTTState); + + if (HamLibSock[n]) + { + send(HamLibSock[n], Msg, Len, 0); + + if (PTTState) + SetDlgItemText(hWnd, stateCtl[n], "PTT"); + else + SetDlgItemText(hWnd, stateCtl[n], ""); + } +} + + + +VOID PTTCATThread() +{ + DWORD dwLength = 0; + int Length, ret, i; + UCHAR * ptr1; + UCHAR * ptr2; + UCHAR c; + UCHAR Block[4][80]; + UCHAR CurrentState[4] = {0}; +#define RTS 2 +#define DTR 4 + HANDLE Event; + DWORD EvtMask[4]; + OVERLAPPED Overlapped[4]; + char Port[32]; + int PIndex = 0; + int HIndex = 0; + int rc; + + EndPTTCATThread = FALSE; + + while (PIndex < 4 && PTTCATPort[PIndex][0]) + { + RealMux[HIndex] = 0; + + sprintf(Port, "\\\\.\\pipe\\BPQ%s", PTTCATPort[PIndex]); + + PTTCATHandle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + + if (PTTCATHandle[HIndex] == INVALID_HANDLE_VALUE) + { + int Err = GetLastError(); +// Consoleprintf("PTTMUX port BPQCOM%s Open failed code %d", RIG->PTTCATPort[PIndex], Err); + + // See if real com port + + sprintf(Port, "\\\\.\\\\%s", PTTCATPort[PIndex]); + + PTTCATHandle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + RealMux[HIndex] = 1; + + if (PTTCATHandle[HIndex] == INVALID_HANDLE_VALUE) + { + int Err = GetLastError(); + PTTCATHandle[HIndex] = 0; + Debugprintf("PTTMUX port COM%s Open failed code %d", PTTCATPort[PIndex], Err); + } + else + { + rc = SetCommMask(PTTCATHandle[HIndex], EV_CTS | EV_DSR); // Request notifications + HIndex++; + } + } + else + HIndex++; + + PIndex++; + + } + + if (PIndex == 0) + return; // No ports + + Event = CreateEvent(NULL, TRUE, FALSE, NULL); + + for (i = 0; i < HIndex; i ++) + { + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + + if (RealMux[i]) + { + // Request Interface change notifications + + rc = WaitCommEvent(PTTCATHandle[i], &EvtMask[i], &Overlapped[i]); + rc = GetLastError(); + + } + else + { + // Prime a read on each PTTCATHandle + + ReadFile(PTTCATHandle[i], Block[i], 80, &Length, &Overlapped[i]); + } + } + + while (EndPTTCATThread == FALSE) + { + +WaitAgain: + + ret = WaitForSingleObject(Event, 1000); + + if (ret == WAIT_TIMEOUT) + { + if (EndPTTCATThread) + { + for (i = 0; i < HIndex; i ++) + { + CancelIo(PTTCATHandle[i]); + CloseHandle(PTTCATHandle[i]); + PTTCATHandle[i] = INVALID_HANDLE_VALUE; + } + CloseHandle(Event); + return; + } + goto WaitAgain; + } + + ResetEvent(Event); + + // See which request(s) have completed + + for (i = 0; i < HIndex; i ++) + { + ret = GetOverlappedResult(PTTCATHandle[i], &Overlapped[i], &Length, FALSE); + + if (ret) + { + if (RealMux[i]) + { + // Request Interface change notifications + + DWORD Mask; + + GetCommModemStatus(PTTCATHandle[i], &Mask); + + if (Mask & MS_CTS_ON) + Rig_PTT(i, TRUE); + else + Rig_PTT(i, FALSE); + + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + WaitCommEvent(PTTCATHandle[i], &EvtMask[i], &Overlapped[i]); + + } + else + { + + ptr1 = Block[i]; + ptr2 = Block[i]; + + while (Length > 0) + { + c = *(ptr1++); + + Length--; + + if (c == 0xff) + { + c = *(ptr1++); + Length--; + + if (c == 0xff) // ff ff means ff + { + Length--; + } + else + { + // This is connection / RTS/DTR statua from other end + // Convert to CAT Command + + if (c == CurrentState[i]) + continue; + + if (c & RTS) + Rig_PTT(i, TRUE); + else + Rig_PTT(i, FALSE); + + CurrentState[i] = c; + continue; + } + } + } + + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + + ReadFile(PTTCATHandle[i], Block[i], 80, &Length, &Overlapped[i]); + } + } + } + } + EndPTTCATThread = FALSE; +} + +char ClassName[]="BPQMAIL"; + +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASS wc; + + bgBrush = CreateSolidBrush(BGCOLOUR); + RedBrush = CreateSolidBrush(RGB(255,0,0)); + GreenBrush = CreateSolidBrush(RGB(0,255,0)); +// BlueBrush = CreateSolidBrush(RGB(0,0,255)); + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + wc.lpfnWndProc = WndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hIcon = NULL; //LoadIcon( hInstance, MAKEINTRESOURCE(IDI_GPSMUXPC)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL;//"MENU_1"; + wc.lpszClassName = szWindowClass; + +// RegisterClass(&wc); + + // wc.lpfnWndProc = TraceWndProc; + // wc.lpszClassName = TraceClassName; + +// RegisterClass(&wc); + +// wc.lpfnWndProc = ConfigWndProc; +// wc.lpszClassName = ConfigClassName; + + return (RegisterClass(&wc)); + +} + + +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + u_long param=1; + BOOL bcopt=TRUE; + int ret; + WNDCLASS wc = {0}; + int n = 0; + + + hInst = hInstance; // Store instance handle in our global variable + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + + hWnd=CreateDialog(hInst,szWindowClass,0,NULL); + + if (!hWnd) + { + ret=GetLastError(); + return FALSE; + } + + // setup default font information + + LFTTYFONT.lfHeight = 12; + LFTTYFONT.lfWidth = 8 ; + LFTTYFONT.lfEscapement = 0 ; + LFTTYFONT.lfOrientation = 0 ; + LFTTYFONT.lfWeight = 0 ; + LFTTYFONT.lfItalic = 0 ; + LFTTYFONT.lfUnderline = 0 ; + LFTTYFONT.lfStrikeOut = 0 ; + LFTTYFONT.lfCharSet = OEM_CHARSET ; + LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; + LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; + LFTTYFONT.lfQuality = DEFAULT_QUALITY ; + LFTTYFONT.lfPitchAndFamily = FIXED_PITCH | FF_MODERN ; + lstrcpy( LFTTYFONT.lfFaceName, "Fixedsys" ) ; + + hFont = CreateFontIndirect(&LFTTYFONT) ; +// hFont = MyCreateFont(); + + SetWindowText(hWnd,Title); + + comWnd[0] = GetDlgItem(hWnd, IDC_COM1); + comWnd[1] = GetDlgItem(hWnd, IDC_COM2); + comWnd[2] = GetDlgItem(hWnd, IDC_COM3); + comWnd[3] = GetDlgItem(hWnd, IDC_COM4); + + portWnd[0] = GetDlgItem(hWnd, IDC_HAMLIBPORT1); + portWnd[1] = GetDlgItem(hWnd, IDC_HAMLIBPORT2); + portWnd[2] = GetDlgItem(hWnd, IDC_HAMLIBPORT3); + portWnd[3] = GetDlgItem(hWnd, IDC_HAMLIBPORT4); + + stateWnd[0] = GetDlgItem(hWnd, IDC_STATE1); + stateWnd[1] = GetDlgItem(hWnd, IDC_STATE2); + stateWnd[2] = GetDlgItem(hWnd, IDC_STATE3); + stateWnd[3] = GetDlgItem(hWnd, IDC_STATE4); + + + stateCtl[0] = IDC_STATE1; + stateCtl[1] = IDC_STATE2; + stateCtl[2] = IDC_STATE3; + stateCtl[3] = IDC_STATE4; + + ShowWindow(hWnd, nCmdShow); + + SetDlgItemText(hWnd, IDC_BPQHOST, BPQHostIP); + + SetDlgItemText(hWnd, IDC_COM1, PTTCATPort[0]); + SetDlgItemText(hWnd, IDC_COM2, PTTCATPort[1]); + SetDlgItemText(hWnd, IDC_COM3, PTTCATPort[2]); + SetDlgItemText(hWnd, IDC_COM4, PTTCATPort[3]); + + SetDlgItemInt(hWnd, IDC_HAMLIBPORT1, HamLibPort[0], 0); + SetDlgItemInt(hWnd, IDC_HAMLIBPORT2, HamLibPort[1], 0); + SetDlgItemInt(hWnd, IDC_HAMLIBPORT3, HamLibPort[2], 0); + SetDlgItemInt(hWnd, IDC_HAMLIBPORT4, HamLibPort[3], 0); + +// ioctlsocket (sock, FIONBIO, ¶m); + + if (RigPort) + { + int Speed = 9600; + + if (RigSpeed) + Speed = atoi(RigSpeed); + } + + TimerHandle = SetTimer(hWnd, 1, 10000, NULL); + + runTimer(); // Open Hamlib connections + + _beginthread(PTTCATThread, 0); + + return TRUE; +} + +VOID ConnecttoHAMLIB(int n); + +VOID runTimer() +{ + int n; + + for (n = 0; n < 4; n++) + { + if (HamLibPort[n] && HamLibSock[n] == 0) + { + // try to connect + + ConnecttoHAMLIB(n); + + } + } +} +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + + switch (message) + { + case WM_TIMER: + + runTimer(); + break; + + case WM_CTLCOLOREDIT: + { + HDC hdc = (HDC) wParam; // handle to display context + HWND hwnd = (HWND) lParam; // handle to control window + int n; + + for (n = 0; n < 4; n++) + { + if (hwnd == comWnd[n] && PTTCATPort[n][0]) + if (PTTCATHandle[n]) + return (INT_PTR)GreenBrush; + else + return (INT_PTR)RedBrush; + + if (hwnd == portWnd[n] && HamLibPort[n]) + if (HamLibSock[n]) + return (INT_PTR)GreenBrush; + else + return (INT_PTR)RedBrush; + } + + //return (INT_PTR)RedBrush; + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_MINIMIZE: + + if (MinimizetoTray) + + return ShowWindow(hWnd, SW_HIDE); + else + return (DefWindowProc(hWnd, message, wParam, lParam)); + + + break; + + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_CLOSE: + + PostQuitMessage(0); + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + int OK; + + case IDC_TEST1: + + Rig_PTT(0, 1); + Sleep(1500); + Rig_PTT(0, 0); + break; + + case IDC_TEST2: + + Rig_PTT(1, 1); + Sleep(1500); + Rig_PTT(1, 0); + break; + + case IDC_TEST3: + + Rig_PTT(2, 1); + Sleep(1500); + Rig_PTT(2, 0); + break; + + case IDC_TEST4: + + Rig_PTT(3, 1); + Sleep(1500); + Rig_PTT(3, 0); + break; + + case IDOK: + + GetDlgItemText(hWnd, IDC_BPQHOST, BPQHostIP, 127); + + GetDlgItemText(hWnd, IDC_COM1, PTTCATPort[0], 15); + GetDlgItemText(hWnd, IDC_COM2, PTTCATPort[1], 15); + GetDlgItemText(hWnd, IDC_COM3, PTTCATPort[2], 15); + GetDlgItemText(hWnd, IDC_COM4, PTTCATPort[3], 15); + + HamLibPort[0] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT1, &OK, 0); + HamLibPort[1] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT2, &OK, 0); + HamLibPort[2] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT3, &OK, 0); + HamLibPort[3] = GetDlgItemInt(hWnd, IDC_HAMLIBPORT4, &OK, 0); + + SaveConfig(); + + // EndPTTCATThread = 1; + + // Sleep(1100); + + // _beginthread(PTTCATThread, 0); + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + } + + return (DefWindowProc(hWnd, message, wParam, lParam)); + +} + +void HAMLIBProcessMessage(int n) +{ + char RXBuffer[256]; + + int InputLen = recv(HamLibSock[n], RXBuffer, 256, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + if (HamLibSock[n]) + closesocket(HamLibSock[n]); + + HamLibSock[n] = 0; + RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); + + return; + } +} + + +VOID HAMLIBThread(int n); + +VOID ConnecttoHAMLIB(int n) +{ + _beginthread(HAMLIBThread, 0, n); + return ; +} + +VOID HAMLIBThread(int n) +{ + // Opens sockets and looks for data + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + + if (HamLibSock[n]) + closesocket(HamLibSock[n]); + + // Param is IPADDR:PORT. Only Allow numeric addresses + + if (HamLibPort[n] == 0) + return; + + remoteDest[n].sin_family = AF_INET; + remoteDest[n].sin_addr.s_addr = inet_addr(BPQHostIP); + remoteDest[n].sin_port = htons(HamLibPort[n]); + + HamLibSock[n] = 0; + HamLibSock[n] = socket(AF_INET,SOCK_STREAM,0); + + if (HamLibSock[n] == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for HAMLIB socket - error code = %d\r\n", WSAGetLastError()); + Debugprintf(Msg); + + return; + } + + setsockopt(HamLibSock[n], SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(HamLibSock[n], IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(HamLibSock[n],(LPSOCKADDR)&remoteDest[n],sizeof(remoteDest[n])) == 0) + { + // + // Connected successful + // + } + else + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for HAMLIB socket %d - error code = %d\r\n", n, err); + Debugprintf(Msg); + + closesocket(HamLibSock[n]); + + HamLibSock[n] = 0; + + RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); + + return; + } + + RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE); + + ret = GetLastError(); + + while (HamLibSock[n]) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(HamLibSock[n],&readfs); + FD_SET(HamLibSock[n],&errorfs); + + timeout.tv_sec = 60; + timeout.tv_usec = 0; + + ret = select((int)HamLibSock[n] + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("HAMLIB Select failed %d ", WSAGetLastError()); + goto Lost; + } + + if (ret > 0) + { + // See what happened + + if (FD_ISSET(HamLibSock[n], &readfs)) + { + HAMLIBProcessMessage(n); + } + + if (FD_ISSET(HamLibSock[n], &errorfs)) + { +Lost: + sprintf(Msg, "HAMLIB Connection lost for Port %d\r\n", n); + Debugprintf(Msg); + + closesocket(HamLibSock[n]); + HamLibSock[n] = 0; + RedrawWindow(hWnd, NULL, NULL, 0); + + return; + } + continue; + } + else + { + } + } + sprintf(Msg, "HAMLIB Thread Terminated Port %d\r\n", n); + Debugprintf(Msg); +} diff --git a/.svn/pristine/14/147067e4beec11a2c197551503067c2e0ff303a1.svn-base b/.svn/pristine/14/147067e4beec11a2c197551503067c2e0ff303a1.svn-base new file mode 100644 index 0000000..fdffe2a --- /dev/null +++ b/.svn/pristine/14/147067e4beec11a2c197551503067c2e0ff303a1.svn-base @@ -0,0 +1,45 @@ +// +// HTTP Session Control. Used In Kernel HTTPCode, BBSHTMLConfig +// and ChatHTMLConfig + +// On Windows changes to layout or length of this struct require rebuilding BPQ32.dll, BPQMail and BPQChat + +struct HTTPConnectionInfo // Used for Web Server for thread-specific stuff +{ + struct HTTPConnectionInfo * Next; + struct STATIONRECORD * SelCall; // Station Record for individual station display + char Callsign[12]; + int WindDirn, WindSpeed, WindGust, Temp, RainLastHour, RainLastDay, RainToday, Humidity, Pressure; //WX Fields + char * ScreenLines[100]; // Screen Image for Teminal access mode - max 100 lines (cyclic buffer) + int ScreenLineLen[100]; // Length of each lime + int LastLine; // Pointer to last line of data + BOOL PartLine; // Last line does not have CR on end + char HTTPCall[10]; // Call of HTTP user + BOOL Changed; // Changed since last poll. If set, reply immediately, else set timer and wait + SOCKET sock; // Socket for pending send + int ResponseTimer; // Timer for delayed response + int KillTimer; // Clean up timer (no activity timeout) + int Stream; // BPQ Stream Number + char Key[20]; // Session Key + BOOL Connected; + // Use by Mail Module +#ifdef MAIL + struct UserInfo * User; // Selected User + struct MsgInfo * Msg; // Selected Message + WPRec * WP; // Selected WP record + WebMailInfo * WebMail; // Webmail Forms Info +#else + VOID * User; // Selected User + VOID * Msg; // Selected Message + VOID * WP; // Selected WP record + VOID * WebMail; // Webmail Forms Info +#endif + struct UserRec * USER; // Telnet Server USER record + int WebMailSkip; // Number to skip at start of list (for paging) + char WebMailTypes[4]; // Types To List + BOOL WebMailMine; // List all meessage to or from me + BOOL WebMailMyTX; // List all meessage from me + BOOL WebMailMyRX; // List all meessage to me + time_t WebMailLastUsed; + struct TNCINFO * TNC; // Session -> TNC link +}; diff --git a/.svn/pristine/14/148f6414536f7829e65ade1ec3c7a09edd84765d.svn-base b/.svn/pristine/14/148f6414536f7829e65ade1ec3c7a09edd84765d.svn-base new file mode 100644 index 0000000..3e3d08b --- /dev/null +++ b/.svn/pristine/14/148f6414536f7829e65ade1ec3c7a09edd84765d.svn-base @@ -0,0 +1,1907 @@ +/* +Copyright 2001-2018 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 +*/ + +// +// DLL to inteface HAL Communications Corp Clover/Pacor controllers to BPQ32 switch +// +// Uses BPQ EXTERNAL interface +// + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include "time.h" + +#include "CHeaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + +#define HAL 1 + +#define SetMYCALL 0x13 +#define ConnectEnable 0x52 +#define ConnectDisable 0x42 +#define SetEAS 0x59 // Echo as Sent +#define SetTones 0xec +#define ClearOnDisc 0x57 + +static char ClassName[]="HALSTATUS"; + +static char WindowTitle[] = "HAL"; +static int RigControlRow = 185; + +struct TNCINFO * TNCInfo[34]; // Records are Malloc'd + +#define SOH 0x01 // CONTROL CODES +#define ETB 0x17 +#define DLE 0x10 + +//int MaxStreams = 0; + +#ifndef LINBPQ +extern HFONT hFont; +#endif + +static char status[23][50] = {"IDLE", "TFC", "RQ", "ERR", "PHS", "OVER", "FSK TX", + "FSK RX", "P-MODE100", "P-MODE200", "HUFMAN ON", "HUFMAN OFF", "P-MODE SBY(LISTEN ON)", + "P-MODE SBY(LISTEN OFF)", "ISS", "IRS", + "AMTOR SBY(LISTEN ON)", "AMTOR SBY(LISTEN OFF)", "AMTOR FEC TX", "AMTOR FEC RX", "P-MODE FEC TX", + "FREE SIGNAL TX (AMTOR)", "FREE SIGNAL TX TIMED OUT (AMTOR)"}; + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL OpenConnection(int); +BOOL SetupConnection(int); +static BOOL WriteCommBlock(struct TNCINFO * TNC); +static void CheckRX(struct TNCINFO * TNC); +VOID HALPoll(int Port); +VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); +VOID ProcessTermModeResponse(struct TNCINFO * TNC); +static VOID DoTNCReinit(struct TNCINFO * TNC); +VOID DoTermModeTimeout(struct TNCINFO * TNC); +VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length); +VOID ProcessHALCmd(struct TNCINFO * TNC); +VOID ProcessHALData(struct TNCINFO * TNC); +VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); +VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); + +BOOL HALConnected(struct TNCINFO * TNC, char * Call); +VOID HALDisconnected(struct TNCINFO * TNC); + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +int DLEDecode(UCHAR * inbuff, UCHAR * outbuff, int len); + +VOID COMClearDTR(HANDLE fd); +VOID COMClearRTS(HANDLE fd); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); + + + +//static HANDLE LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + +//char * Logs[4] = {"1", "2", "3", "4"}; + +//char BaseDir[]="c:"; + +static VOID CloseLogfile(int Flags) +{ +// CloseHandle(LogHandle[Flags]); +// LogHandle[Flags] = INVALID_HANDLE_VALUE; +} + +static VOID OpenLogfile(int Flags) +{ +/* +UCHAR FN[MAX_PATH]; + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(FN,"%s\\HALLog_%02d%02d%02d_%s.bin", BaseDir, tm->tm_mday, tm->tm_hour, tm->tm_min, Logs[Flags]); + + LogHandle[Flags] = CreateFile(FN, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + SetFilePointer(LogHandle[Flags], 0, 0, FILE_END); + + return (LogHandle[Flags] != INVALID_HANDLE_VALUE); +*/ +} + +static void WriteLogLine(int Flags, char * Msg, int MsgLen) +{ +// int cnt; +// WriteFile(LogHandle[Flags] ,Msg , MsgLen, &cnt, NULL); +} + + + +int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + ptr = strtok(NULL, " \t\n\r"); + + if (_stricmp(buf, "APPL") == 0) // Using BPQ32 COnfig + { + BPQport = Port; + p_cmd = ptr; + } + else + if (_stricmp(buf, "PORT") != 0) // Using Old Config + { + // New config without a PORT or APPL - this is a Config Command + + strcpy(buf, errbuf); + strcat(buf, "\r"); + + BPQport = Port; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + goto ConfigLine; + } + else + + { + + // Old Config from file + + BPQport=0; + BPQport = atoi(ptr); + + p_cmd = strtok(NULL, " \t\n\r"); + + if (Port && Port != BPQport) + { + // Want a particular port, and this isn't it + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + } + } + } + if(BPQport > 0 && BPQport < 33) + { + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_cmd != NULL) + { + if (p_cmd[0] != ';' && p_cmd[0] != '#') + TNC->ApplCmd=_strdup(p_cmd); + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; +ConfigLine: + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + { + TNC->WL2K = DecodeWL2KReportLine(buf); + continue; + } + if (_memicmp(buf, "NEEDXONXOFF", 10) == 0) + { + TNC->XONXOFF = TRUE; + continue; + } + + if (_memicmp(buf, "TONES", 5) == 0) + { + int tone1 = 0, tone2 = 0; + + ptr = strtok(&buf[6], " ,/\t\n\r"); + if (ptr) + { + tone1 = atoi(ptr); + ptr = strtok(NULL, " ,/\t\n\r"); + if (ptr) + { + tone2 = atoi(ptr); + ptr = &TNC->InitScript[TNC->InitScriptLen]; + + // Try putting into FSK mode first + + *(ptr++) = 0x84; + *(ptr++) = SetTones; // Set Tones (Mark, Space HI byte first) + *(ptr++) = tone1 >> 8; + *(ptr++) = tone1 & 0xff; + *(ptr++) = tone2 >> 8; + *(ptr++) = tone2 & 0xff; + + TNC->InitScriptLen += 6; + + continue; + } + } + goto BadLine; + } + if (_memicmp(buf, "DEFAULTMODE ", 12) == 0) + { + + ptr = strtok(&buf[12], " ,\t\n\r"); + if (ptr) + { + if (_stricmp(ptr, "CLOVER") == 0) + TNC->DefaultMode = Clover; + else if (_stricmp(ptr, "PACTOR") == 0) + TNC->DefaultMode = Pactor; + else if (_stricmp(ptr, "AMTOR") == 0) + TNC->DefaultMode = AMTOR; + else goto BadLine; + + continue; + } + goto BadLine; + } + } + BadLine: + WritetoConsole(" Bad config record "); + WritetoConsole(errbuf); + WritetoConsole("\r\n"); + } + + return (TRUE); +} + +static size_t ExtProc(int fn, int port , PDATAMESSAGE buff) +{ + int txlen = 0; + PMSGWITHLEN buffptr; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM; + int Stream; + + if (TNC == NULL) + return 0; + + if (fn < 4 || fn > 5) + if (TNC->hDevice == 0) + return 0; // Port not open + + STREAM = &TNC->Streams[0]; + + switch (fn) + { + case 1: // poll + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = 0; + + return -1; + } + } + + CheckRX(TNC); + HALPoll(port); + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (STREAM->PACTORtoBPQ_Q !=0) + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + + ReleaseBuffer(buffptr); + + return (1); + } + } + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + // Find TNC Record + + Stream = buff->PORT; + + if (!TNC->TNCOK) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 27; + memcpy(&buffptr->Data[0], "No Connection to PACTOR TNC\r", 27); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4); + + buffptr->Len = txlen; + memcpy(&buffptr->Data[0], &buff->L2DATA[0], txlen); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + STREAM->FramesQueued++; + + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + + if (STREAM->FramesQueued > 4) + return (1 | TNC->HostMode << 8); + + return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + case 4: // reinit + + return (0); + + case 5: // Close + + CloseCOMPort(TNCInfo[port]->hDevice); + return (0); + + case 6: // Scan Control + + return 0; // None Yet + + } + return 0; + +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "HAL Status

HAL Status

"); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_STATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TXRX); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], "", TNC->WEB_LEDS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Status%s
TX/RX State%s
Traffic%s
LEDSSTBY CALL LINK ERROR TX RX
%s
"); + + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +VOID * HALExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * ptr; + int len; + char Msg[80]; +#ifndef LINBPQ + HWND x; +#endif + // + // Will be called once for each Pactor Port + // The COM port number is in IOBASE + // + + sprintf(msg,"HAL Driver %s", PortEntry->PORTCONTROL.SerialPortName); + WritetoConsole(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port"); + WritetoConsole(msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->Hardware = H_HAL; + + TNC->Interlock = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->MAXHOSTMODESESSIONS = 1; // Default + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + { + memcpy(TNC->NodeCall, MYNODECALL, 10); + } + else + { + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + } + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + if (TNC->DefaultMode) + TNC->CurrentMode = TNC->DefaultMode; + else + TNC->CurrentMode = Clover; + + TNC->PollDelay = 999999999; + + // Set Disable +?, ExpandedStatus , Channel Stats Off, ClearOnDisc, EAS and MYCALL + + len = sprintf(Msg, "%c%c%c%c%c%c%s", 0xcc, 0x56, 0x41, ClearOnDisc, SetEAS, SetMYCALL, TNC->NodeCall); + len++; // We include the NULL + + memcpy(&TNC->InitScript[TNC->InitScriptLen], Msg, len); + TNC->InitScriptLen += len; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 510; + TNC->WebWinY = 280; + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + strcpy(TNC->WEB_TNCSTATE, "Free"); + TNC->WEB_MODE = zalloc(100); + TNC->WEB_TRAFFIC = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_STATE = zalloc(100); + TNC->WEB_TXRX = zalloc(100); + TNC->WEB_LEDS = zalloc(100); + strcpy(TNC->WEB_LEDS, " X X X X X X"); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 233, ForcedClose); + + x = CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "LEDS", WS_CHILD | WS_VISIBLE,10,138,60,20, TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + x = CreateWindowEx(0, "STATIC", "STBY CALL LINK ERROR TX RX", WS_CHILD | WS_VISIBLE,116,138,280,20, TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + x = TNC->xIDC_LEDS = CreateWindowEx(0, "STATIC", " X X X X X X", WS_CHILD | WS_VISIBLE,116,158,280,20 , TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + + TNC->ClientHeight = 233; + TNC->ClientWidth = 500; + + MoveWindows(TNC); +#endif + + OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + + SendCmd(TNC, "\x09" , 1); // Reset + + WritetoConsole("\n"); + + return ExtProc; +} + + + +static VOID KISSCLOSE(int Port) +{ + struct TNCINFO * conn = TNCInfo[Port]; + + // drop DTR and RTS + + COMClearDTR(conn->hDevice); + COMClearRTS(conn->hDevice); + + // purge any outstanding reads/writes and close device handle + + CloseCOMPort(conn->hDevice); + + return; +} + + +static void CheckRX(struct TNCINFO * TNC) +{ + int Length, Len; + UCHAR * Xptr; + + // only try to read number of bytes in queue + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + // We need to konw whether data is received or echoed, so we can't split commands and data here. + // Pass everything to the Command Handler. It will check that there are enough bytes for the command, + // and wait for more if not. + + // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 + + // If USB version, we might get unescaped xon and xoff, which we must ignore + + if (TNC->XONXOFF) + { + Xptr = memchr(&TNC->RXBuffer, 0x11, Length); + + while(Xptr) + { + Debugprintf("XON Port %d", TNC->Port); + memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); + Xptr = memchr(&TNC->RXBuffer, 0x11, Length); + } + + Xptr = memchr(&TNC->RXBuffer, 0x13, Length); + + while(Xptr) + { + Debugprintf("XOFF Port %d", TNC->Port); + memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); + Xptr = memchr(&TNC->RXBuffer, 0x13, Length); + } + + Xptr = memchr(&TNC->RXBuffer, 0x91, Length); // See if packet contains 0x91 escape + + if (Xptr) + + // Make sure we have the escaped char as well + + if ((Xptr - &TNC->RXBuffer[0]) == Length - 1) // x91 is last char + return; + } + + ProcessHALBuffer(TNC, Length); + + TNC->RXLen = 0; + + return; + +} + + + +static BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + return TRUE; +} + +VOID HALPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + UCHAR * Poll = TNC->TXBuffer; + char Status[80]; + UCHAR TXMsg[1000]; + int datalen; + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + // Timed Out + + TNC->TNCOK = FALSE; + TNC->HostMode = 0; + + sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[0]) // Connected + { + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + } + } + + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (TNC->TNCOK) + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + STREAM->Attached = TRUE; + + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + + datalen = sprintf(TXMsg, "%c%s", SetMYCALL, STREAM->MyCall); + SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + SendCmd(TNC, "\x42", 1); // Connect Enable off + + return; + + } + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + + if (STREAM->Attached) + CheckForDetach(TNC, 0, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (TNC->NeedPACTOR) + { + TNC->NeedPACTOR--; + + if (TNC->NeedPACTOR == 0) + { + int datalen; + + UCHAR TXMsg[80]; + + datalen = sprintf(TXMsg, "%c%s", SetMYCALL, TNC->NodeCall); + SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL + + // Set Listen Mode + + switch (TNC->CurrentMode) + { + case Pactor: + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + SendCmd(TNC, "\x58", 1); // Listen + + break; + + case Clover: + + SendCmd(TNC, "\x80", 1); // Clover + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x41", 1); // No Statistics + SendCmd(TNC, "\x60\x09", 2); // Robust Retries + SendCmd(TNC, "\x61\x09", 2); // Normal Retries + + break; + } + + SendCmd(TNC, "\x52", 1); // ConnectEnable + + // Restart Scanning + + sprintf(Status, "%d SCANSTART 15", TNC->Port); + + Rig_Command(-1, Status); + + return; + } + } + +#define MAXHALTX 256 + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q && (STREAM->BytesTXed - STREAM->BytesAcked < 600)) + { + int datalen; + UINT * buffptr; + UCHAR * MsgPtr; + unsigned char TXMsg[500]; + + buffptr = (UINT * )STREAM->BPQtoPACTOR_Q; + datalen=buffptr[1]; + MsgPtr = (UCHAR *)&buffptr[2]; + + if (STREAM->Connected) + { + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; + if (strstr(MsgPtr, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + STREAM->FramesQueued--; + return; + } + } + + // Must send data in small chunks - the Hal has limited buffer space + + // If in IRS force a turnround + + if (TNC->TXRXState == 'R' && TNC->CurrentMode != Clover) + { + if (TNC->TimeInRX++ > 15) + SendCmd(TNC, "\x87", 1); // Changeover to ISS + else + goto Poll; + } + + TNC->TimeInRX = 0; + + EncodeAndSend(TNC, MsgPtr, datalen); + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + ReleaseBuffer(buffptr); + WriteLogLine(2, MsgPtr, datalen); + + STREAM->BytesTXed += datalen; + STREAM->FramesQueued--; + + ShowTraffic(TNC); + + return; + } + else + { + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + STREAM->FramesQueued--; + + // Command. Do some sanity checking and look for things to process locally + + datalen--; // Exclude CR + MsgPtr[datalen] = 0; // Null Terminate + _strupr(MsgPtr); + + if (memcmp(MsgPtr, "RADIO ", 6) == 0) + { + sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]); + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, &MsgPtr[40])) + { + ReleaseBuffer(buffptr); + } + else + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s", &MsgPtr[40]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return; + } + + if (memcmp(MsgPtr, "MODE CLOVER", 11) == 0) + { + TNC->CurrentMode = Clover; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + MySetWindowText(TNC->xIDC_MODE, "Clover"); + strcpy(TNC->WEB_MODE, "Clover"); + + SendCmd(TNC, "\x80", 1); // Clover + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x41", 1); // No Statistics + + return; + } + + if (memcmp(MsgPtr, "MODE PACTOR", 11) == 0) + { + TNC->CurrentMode = Pactor; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + SendCmd(TNC, "\x48", 1); // Listen Off + + return; + } + if (memcmp(MsgPtr, "MODE AMTOR", 11) == 0) + { + TNC->CurrentMode = AMTOR; + buffptr[1] = sprintf((UCHAR *)&buffptr[2],"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return; + } + + if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect + { + memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); + + switch (TNC->CurrentMode) + { + case Pactor: + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + + datalen = sprintf(TXMsg, "\x19%s", STREAM->RemoteCall); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - PACTOR", STREAM->MyCall, STREAM->RemoteCall); + + // DOnt set connecting till we get the 19 response so we can trap listen as a fail + break; + + case Clover: + + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", STREAM->MyCall, STREAM->RemoteCall); + + break; + } + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + SendCmd(TNC, TXMsg, datalen + 1); // Include NULL + + ReleaseBuffer(buffptr); + + return; + } + + if (memcmp(MsgPtr, "CLOVER ", 7) == 0) + { + memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); + + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); + SendCmd(TNC, TXMsg, datalen + 1); // Include NULL + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", + STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + ReleaseBuffer(buffptr); + + return; + } + + if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect + { + SendCmd(TNC, "\x07", 1); // Normal Disconnect + TNC->NeedPACTOR = 50; + + STREAM->Connecting = FALSE; + STREAM->ReportDISC = TRUE; + ReleaseBuffer(buffptr); + + return; + } + + // Other Command ?? Treat as HEX string + + datalen = sscanf(MsgPtr, "%X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X", + (UINT *)&TXMsg[0], (UINT *)&TXMsg[1], (UINT *)&TXMsg[2], (UINT *)&TXMsg[3], (UINT *)&TXMsg[4], + (UINT *)&TXMsg[5], (UINT *)&TXMsg[6], (UINT *)&TXMsg[7], (UINT *)&TXMsg[8], (UINT *)&TXMsg[9], + (UINT *)&TXMsg[10], (UINT *)&TXMsg[11], (UINT *)&TXMsg[12], (UINT *)&TXMsg[13], + (UINT *)&TXMsg[14], (UINT *)&TXMsg[15]); + +// SendCmd(TNC, TXMsg, datalen); + ReleaseBuffer(buffptr); + TNC->InternalCmd = 0; + } + } + } +Poll: + // Nothing doing - send Poll (but not too often) + + TNC->PollDelay++; + + if (TNC->PollDelay < 20) + return; + + TNC->PollDelay = 0; + + if (TNC->TNCOK) + SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll + else + SendCmd(TNC, "\x09" , 1); // Reset + + TNC->Timeout = 100; + + return; +} + +static VOID DoTNCReinit(struct TNCINFO * TNC) +{ + // TNC Has Restarted, send init commands (can probably send all at once) + +// TNC->TXBuffer[0] = 0x1b; +// TNC->TXLen = 1; + + WriteCommBlock(TNC); + + SendCmd(TNC, TNC->InitScript, TNC->InitScriptLen); + + TNC->HostMode = TRUE; // Should now be in Host Mode + TNC->NeedPACTOR = 20; // Need to set Calls and start scan + + TNC->DataMode = RXDATA; // Start with RX Data + + SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll +// SendCmd(TNC, "\xc9" , 1); // Huffman Off + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + SendCmd(TNC, "\x60\x06", 2); // Robust Mode Retries + +// SendCmd(TNC, "\x6f\x03" , 2); // Undocumented XON/XOFF On - used to see if old or new style modem + + TNC->Timeout = 50; + + return; + +} + +VOID ProcessHALData(struct TNCINFO * TNC) +{ + // Received Data just pass to Appl + + UINT * buffptr; + int Len = TNC->DataLen; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + TNC->DataLen = 0; + + if (TNC->DataMode == TXDATA) + { + STREAM->BytesAcked += Len; +// Debugprintf("Acked %d", Len); + + if (STREAM->BytesAcked > STREAM->BytesTXed) + Debugprintf("Too Much Acked"); + + if ((STREAM->BPQtoPACTOR_Q == 0) && STREAM->BytesAcked >= STREAM->BytesTXed) + { + // All sent + + if (STREAM->Disconnecting) + TidyClose(TNC, 0); + else + if (TNC->CurrentMode != Clover) + + // turn round link + + SendCmd(TNC, "\x0c" , 1); // Turnround + + } + } + else + { + if (TNC->DataMode == RXDATA) + { +// Debugprintf("RXed %d", Len); + buffptr = GetBuff(); + if (buffptr == NULL) + return; // No buffers, so ignore + + buffptr[1] = Len; // Length + + WriteLogLine(1, TNC->DataBuffer, Len); + + STREAM->BytesRXed += Len; + + memcpy(&buffptr[2], TNC->DataBuffer, Len); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + } + + ShowTraffic(TNC); + + return; +} + + + +VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length) +{ + UCHAR Char; + UCHAR * inptr; + UCHAR * cmdptr; + UCHAR * dataptr; + BOOL CmdEsc, DataEsc; + + inptr = TNC->RXBuffer; + + cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; + dataptr = &TNC->DataBuffer[TNC->DataLen]; + CmdEsc = TNC->CmdEsc; + DataEsc = TNC->DataEsc; + + // HAL uses HEX 80 as a command escape, 81 to ESCAPE 80 and 81 + + // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 + + // Command Responses can be variable length + + // Command Handler will check for each command/response if it has enough - if not it will wait till more arrives + + while(Length--) + { + Char = *(inptr++); + + if (CmdEsc) + { + CmdEsc = FALSE; + + if (TNC->XONXOFF && Char == 0x91) + { + // XON/XOFF escape. We ensured above that data follows so we can process it inline + + Length--; + Char = *(inptr++) - 0x20; + } + *(cmdptr++) = Char; + } + else if (DataEsc) + { + DataEsc = FALSE; + goto DataChar; + } + else +NotData: + if (Char == 0x80) // Next Char is Command + CmdEsc = TRUE; + else if (Char == 0x81) // Next Char is escaped data (80 or 81) + DataEsc = TRUE; + else + { + // This is a Data Char. We must process any Commands received so far, so we know the type of data + + DataChar: + + TNC->CmdLen = (int)(cmdptr - TNC->CmdBuffer); + ProcessHALCmd(TNC); + cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; + dataptr = &TNC->DataBuffer[TNC->DataLen]; + + *(dataptr++) = Char; // Normal Data + + // Now process any other data chars + + while(Length--) + { + Char = *(inptr++); + + if (TNC->XONXOFF && Char == 0x91) + { + // XON/XOFF escape within data. We ensured above that data follows so we + // can process it here + + Length--; + Char = *(inptr++) - 0x20; + } + + if (Char == 0x80 || Char == 0x81) + { + // Process any data we have, then loop back + + TNC->DataLen = (int)(dataptr - TNC->DataBuffer); + ProcessHALData(TNC); + + goto NotData; + } + *(dataptr++) = Char; // Normal Data + } + + // Used all data + + TNC->DataLen = (int)(dataptr - TNC->DataBuffer); + + ProcessHALData(TNC); + TNC->CmdEsc = CmdEsc; + TNC->DataEsc = DataEsc; + + return; + } + } + + // Save State + + TNC->CmdLen = (int)(cmdptr - TNC->CmdBuffer); + + TNC->CmdEsc = CmdEsc; + TNC->DataEsc = DataEsc; + + if (TNC->DataLen) + ProcessHALData(TNC); + + if (TNC->CmdLen) + ProcessHALCmd(TNC); +} + +VOID mySetWindowText(struct TNCINFO * TNC, char * Msg) +{ + MySetWindowText(TNC->xIDC_STATE, Msg); + strcpy(TNC->WEB_STATE, Msg); +} + +VOID ProcessHALCmd(struct TNCINFO * TNC) +{ + char * Call; + int Stream = 0; + int Opcode; + int StatusByte; + int Leds; + int Len; + int Used; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + +CmdLoop: + + Opcode = TNC->CmdBuffer[0]; + Len = TNC->CmdLen; + + if (Len == 0) + return; + + TNC->TNCOK = TRUE; + TNC->Timeout = 0; + + sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // We may have more than one response in the buffer, and only each cmd/response decoder knows how many it needs + + switch(Opcode) + { + case 0x09: //Hardware Reset - equivalent to power on reset + + // Hardware has reset - need to reinitialise + + TNC->HostMode = 0; // Force Reinit + + Used = 1; + break; + + case 0x7a: // FSK Modes Status + + // Mixture of mode and state - eg listen huffman on/off irs/iss, so cant just display + + if (Len < 2) return; // Wait for more + + StatusByte = TNC->CmdBuffer[1]; + + switch (StatusByte) + { + case 0x06: // FSK TX (RTTY) + case 0x07: // FSK RX (RTTY) + case 0x10: // AMTOR STANDBY (LISTEN ON) + case 0x11: // AMTOR STANDBY (LISTEN OFF) + case 0x12: // AMTOR FEC TX (AMTOR) + case 0x13: // AMTOR FEC RX (AMTOR) + case 0x14: // P-MODE FEC TX (P-MODE) + case 0x15: // FREE SIGNAL TX (AMTOR) + case 0x16: // FREE SIGNAL TX TIMED OUT (AMTOR) + + // Diaplay Linke Status + + MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + break; + + case 0x0C: // P-MODE STANDBY (LISTEN ON) + case 0x0D: // P-MODE STANDBY (LISTEN OFF) + + // if we were connecting, this means connect failed. + + MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + if (STREAM->Connecting) + HALDisconnected(TNC); + + break; + + case 0x0E: // ISS (AMTOR/P-MODE) + + MySetWindowText(TNC->xIDC_TXRX,"ISS"); + strcpy(TNC->WEB_TXRX, "ISS"); + TNC->TXRXState = 'S'; + break; + + case 0x0F: // IRS (AMTOR/P-MODE) + + MySetWindowText(TNC->xIDC_TXRX,"IRS"); + strcpy(TNC->WEB_TXRX, "IRS"); + TNC->TXRXState = 'R'; + break; + + case 0x00: // IDLE (AMTOR/P-MODE) + case 0x01: // TFC (AMTOR/P-MODE) + case 0x02: // RQ (AMTOR/P-MODE) + case 0x03: // ERR (AMTOR/P-MODE) + case 0x04: // PHS (AMTOR/P-MODE) + case 0x05: // OVER (AMTOR/P-MODE) (not implemented) + + MySetWindowText(TNC->xIDC_STATE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + + +//$807A $8008 P-MODE100 (P-MODE) +//$807A $8009 P-MODE200 (P-MODE) +//$807A $800A HUFFMAN ON (P-MODE) +//$807A $800B HUFFMAN OFF (P-MODE) + ; + } + Used = 2; + break; + + + case 0x7d: // Get LED Status + + // We use Get LED Status as a Poll + + if (Len < 2) return; // Wait for more + + Leds = TNC->CmdBuffer[1]; + sprintf(TNC->WEB_LEDS," %c %c %c %c %c %c ", + (Leds & 0x20)? 'X' : ' ', + (Leds & 0x10)? 'X' : ' ', + (Leds & 0x08)? 'X' : ' ', + (Leds & 0x04)? 'X' : ' ', + (Leds & 0x02)? 'X' : ' ', + (Leds & 0x01)? 'X' : ' '); + +// STBY CALL LINK ERROR TX RX + MySetWindowText(TNC->xIDC_LEDS, TNC->WEB_LEDS); + + Used = 2; + break; + + case 0x21: // Monitored FEC CCB + case 0x22: // Monitored ARQ CCB + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = (int)strlen(Call) + 2; // Opcode and Null + + UpdateMH(TNC, Call, '!', 0); + + break; + + case 0x27: // Clover ARQ LINK REQUEST status message + + //indicates an incoming link request to either MYCALL ($8027 $8000), or MYALTCALL ($8027 $8001). + + if (Len < 2) return; // Wait for more + + // Don't need to do anything (but may eventally use ALTCALL as an APPLCALL + Used = 2; + break; + + case 0x2D: // FSK ARQ Link Request status message + + // $802D $8001 $8000 CLOVER Link Request (not implemented) + // $802D $8002 $8000 AMTOR CCIR-476 Link Request + // $802D $8003 $8000 AMTOR CCIR-625 Link Request + // $802D $8004 $8000 P-MODE Link Request + + if (Len < 3) return; // Wait for more + + // Don't need to do anything (but may save Session type later + + Used = 3; + break; + + + case 0x28: // Monitored Call + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = strlen(Call) + 2; // Opcode and Null + + // Could possibly be used for APPLCALLS by changing MYCALL when we see a call to one of our calls + + break; + + + case 0x20: // Clover Linked with - Call Connected + case 0x29: // The Linked 476 message indicates the start of a CCIR 476 linked session. + case 0x2A: // The Linked 625 message indicates the start of a CCIR 625 linked session to . + case 0x2B: // P-MODE link to + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = (int)strlen(Call) + 2; // Opcode and Null + + HALConnected(TNC, Call); + + break; + + case 0x23: // Normal Disconnected - followed by $8000 + case 0x24: // Link failed (any of the link errors) + case 0x25: // Signal Lost (LOS) + + if (Len < 2) return; // Wait for more + + HALDisconnected(TNC); + + Used = 2; + break; + + + // Stream Switch Reports - we will need to do something with these if Echo as Sent is set + // or we do something with the secondary port + + case 0x30: // Switch to Receive Data characters + case 0x31: // Switch to Transmit Data characters + case 0x32: // Switch to RX data from secondary port + + TNC->DataMode = Opcode; + Used = 1; + break; + + case 0x33: // Send TX data to modem + case 0x34: // Send TX data to secondary port + + TNC->TXMode = Opcode; + Used = 1; + break; + + case 0x70: // Channel Spectra Data + // $807F $80xx $8030 Invalid or unimplemented command code + if (Len < 9) return; // Wait for more + + Used = 9; + break; + + case 0x71: // SelCall On/Off + + if (Len < 2) return; // Wait for more + + Used = 2; + break; + + case 0x72: // Channel Spectra Data + // $807F $80xx $8030 Invalid or unimplemented command code + if (Len < 15) return; // Wait for more + + Used = 15; + break; + + case 0x73: // Clover Link state + + if (Len < 2) return; // Wait for more + + StatusByte = TNC->CmdBuffer[1]; + + switch (StatusByte) + { + case 0x00: mySetWindowText(TNC, "Channel idle"); break; + case 0x01: mySetWindowText(TNC, "Channel occupied with non-Clover signal"); break; + case 0x42: mySetWindowText(TNC, "Linked stations monitored"); break; + case 0x64: mySetWindowText(TNC, "Attempting normal link"); break; + case 0x65: mySetWindowText(TNC, "Attempting robust link"); break; + case 0x66: mySetWindowText(TNC, "Calling ARQ CQ"); break; + case 0x78: mySetWindowText(TNC, "Clover Control Block (CCB) send retry"); break; + case 0x79: mySetWindowText(TNC, "Clover Control Block (CCB) receive retry"); break; + case 0x7D: mySetWindowText(TNC, "Clover Control Block (CCB) received successfully"); break; + case 0x8A: mySetWindowText(TNC, "TX data block sent"); break; + case 0x8B: mySetWindowText(TNC, "RX data block received ok (precedes data block)"); break; + case 0x8C: mySetWindowText(TNC, "TX data block re-sent"); break; + case 0x8D: mySetWindowText(TNC, "RX data block decode failed (precedes data block)"); break; + case 0x8E: mySetWindowText(TNC, "TX idle"); break; + case 0x8F: mySetWindowText(TNC, "RX idle"); break; + case 0x9C: mySetWindowText(TNC, "Link failed: CCB send retries exceeded"); break; + case 0x9D: mySetWindowText(TNC, "Link failed: CCB receive retries exceeded"); break; + case 0x9E: mySetWindowText(TNC, "Link failed: protocol error"); break; + case 0xA0: mySetWindowText(TNC, "Receiving FEC SYNC sequence"); break; + } + + Used = 2; + break; + + case 0x75: // Clover waveform format + + if (Len < 5) return; // Wait for more + + Used = 5; + break; + + case 0x7F: // Error $80xx $80yy Error in command $80xx of type $80yy + // $807F $80xx $8030 Invalid or unimplemented command code + // $807F $80xx $8031 Invalid parameter value + // $807F $80xx $8032 Not allowed when connected + // $807F $80xx $8033 Not allowed when disconnected + // $807F $80xx $8034 Not valid in this mode + // $807F $80xx $8035 Not valid in this code + // $807F $8096 $8036 EEPROM write error + + if (Len < 3) return; // Wait for more + + if (TNC->CmdBuffer[1] == 0x6f && TNC->CmdBuffer[2] == 0x31) + { + // Reject of XON/XOFF enable + +// TNC->XONXOFF = FALSE; +// Debugprintf("BPQ32 HAL Port %d - Disabling XON/XOFF mode", TNC->Port); + } + else + Debugprintf("HAL Port %d Command Error Cmd %X Error %X", TNC->Port, TNC->CmdBuffer[1], TNC->CmdBuffer[2]); + + Used = 3; + break; + + // Following are all immediate commands - response is echo of command + + case 0x6f: // XON/XOFF on + +// TNC->XONXOFF = TRUE; // And drop through +// Debugprintf("BPQ32 HAL Port %d - Enabling XON/XOFF mode", TNC->Port); + + case 0x19: // Call P-MODE to + case 0x10: // Robust Link to using MYCALL + case 0x11: // Normal Link to using MYCALL + + STREAM->Connecting = TRUE; + + case 0x00: // P Load LOD file + case 0x01: // P Load S28 file + case 0x02: //Check Unit Error Status + case 0x03: //F Check System Clock + case 0x04: //C Close PTT and transmit Clover waveform + case 0x05: //Open PTT and stop transmit test + case 0x06: //Immediate Abort (Panic Kill) + case 0x07: //Normal disconnect (wait for ACK) + case 0x08: //Software reset - restore all program defaults + case 0x0A: //Send CW ID + case 0x0B: //Close PTT and transmit Single Tone + case 0x0C: //F Normal OVER (AMTOR,P-MODE) + case 0x0D: //F Force RTTY TX (Baudot/ASCII) + case 0x0E: //F Go to RTTY RX (Baudot/ASCII) + case 0x0F: //Go to LOD/S28 file loader + case SetMYCALL: // Set MYCALL Response + + case 0x1E: // Set MYALTCALL Response + + case 0x41: + case 0x42: + case 0x46: + case 0x47: + case 0x48: + case 0x4d: + case 0x52: // Enable adaptive Clover format + case 0x54: // Enable adaptive Clover format + + case 0x56: // Expanded Link State Reports OFF/ON + case 0x57: // Clear buffers on disc + case 0x58: + case 0x59: + case 0x60: // Robust Mode Retries + case 0x61: // Normal Mode Retries + case 0x80: //Switch to CLOVER mode + case 0x81: //Select AMTOR Standby + case 0x82: //Select AMTOR FEC + case 0x83: //Select P-MODE Standby + case 0x84: //Switch to FSK modes + case 0x85: //Select Baudot + case 0x86: //Select ASCII + case 0x87: //Forced OVER (AMTOR, P-MODE) + case 0x88: //Forced END (AMTOR, P-MODE) + case 0x89: //Force LTRS shift + case 0x8A: //Force FIGS shift + case 0x8B: //Send MARK tone + case 0x8C: //Send SPACE tone + case 0x8D: //Send MARK/SPACE tones + case 0x8E: //Received first character on line + case 0x8F: //Close PTT only (no tones) + + case 0xC9: //Huffman Off/On + case 0xCC: + case 0xD9: //Close PTT only (no tones) + + case SetTones: + + Used = 1; + break; + + case 0x91: // ???? + +// if (Len < 2) return; // Wait for more + + Used = 1; + break; + + default: + + // We didn't recognise command, so don't know how long it is - disaster! + + Debugprintf("HAL Port %d Unrecognised Command %x", TNC->Port, Opcode); + TNC->CmdLen = 0; + + return; + } + + if (Used == Len) + { + // All used - most likely case + + TNC->CmdLen = 0; + return; + } + + // Move Command Down buffer, and reenter + + TNC->CmdLen -= Used; + + memmove(TNC->CmdBuffer, &TNC->CmdBuffer[Used], TNC->CmdLen); + + goto CmdLoop; + + +} + + +VOID HALDisconnected(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + CloseLogfile(0); + CloseLogfile(1); + CloseLogfile(2); + + if ((STREAM->Connecting | STREAM->Connected) == 0) + { + // Not connected or Connecting. Probably response to going into Pactor Listen Mode + + return; + } + + if (STREAM->Connecting && STREAM->Disconnecting == FALSE) + { + UINT * buffptr; + + // Connect Failed - actually I think HAL uses another code for connect failed, but leave here for now + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // In case! + STREAM->FramesQueued = 0; + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + } + + // Connected, or Disconnecting - Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->FramesQueued = 0; + + if (STREAM->Disconnecting == FALSE) + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + + // Need to reset Pactor Call in case it was changed + + TNC->NeedPACTOR = 20; +} + +BOOL HALConnected(struct TNCINFO * TNC, char * Call) +{ + char Msg[80]; + UINT * buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char CallCopy[80]; + + strcpy(CallCopy, Call); + strcat(CallCopy, " "); // Some routines expect 10 char calls + + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = 0; + STREAM->ConnectTime = time(NULL); + + // Stop Scanner + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + ShowTraffic(TNC); + + TNC->DataMode = RXDATA; + + OpenLogfile(0); + OpenLogfile(1); + OpenLogfile(2); + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + // Incoming Connect + + ProcessIncommingConnect(TNC, CallCopy, 0, TRUE); + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", STREAM->RemoteCall, TNC->NodeCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (TNC->CurrentMode != Clover) + SendCmd(TNC, "\x87", 1); // Changeover to ISS + + // If an autoconnect APPL is defined, send it + + if (TNC->ApplCmd) + { + buffptr = GetBuff(); + if (buffptr == 0) return TRUE; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "%s\r", TNC->ApplCmd); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + + return TRUE; + } + + if (FULL_CTEXT && HFCTEXTLEN == 0) + { + EncodeAndSend(TNC, CTEXTMSG, CTEXTLEN); + WriteLogLine(2, CTEXTMSG, CTEXTLEN); + + STREAM->BytesTXed += CTEXTLEN; + } + return TRUE; + } + + // Connect Complete + + buffptr = GetBuff(); + if (buffptr == 0) return TRUE; // No buffers, so ignore + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "*** Connected to %s\r", Call);; + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->Connecting = FALSE; + STREAM->Connected = TRUE; // Subsequent data to data channel + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->NodeCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, CallCopy, '+', 'O'); + + + return TRUE; +} + + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) +{ + // Send A Packet With DLE Encoding Encoding + + TNC->TXLen = DLEEncode(txbuffer, TNC->TXBuffer, Len); + + WriteCommBlock(TNC); +} + +VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) +{ + // Send A Packet With Command Encoding (preceed each with 0x80 + + int i,txptr=0; + UCHAR * outbuff = TNC->TXBuffer; + + for (i=0; iTXLen = txptr; + WriteCommBlock(TNC); +} + +int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + + // Escape x80 and x81 with x81 + +// outbuff[0] = 0x80; +// outbuff[1] = 0x33; // Send data to modem + + for (i=0;iNeedPACTOR = 30; +} + + + + diff --git a/.svn/pristine/14/14b226e8aa8a9b46d989b00b887bcc82fd050d7a.svn-base b/.svn/pristine/14/14b226e8aa8a9b46d989b00b887bcc82fd050d7a.svn-base new file mode 100644 index 0000000..ea562b6 --- /dev/null +++ b/.svn/pristine/14/14b226e8aa8a9b46d989b00b887bcc82fd050d7a.svn-base @@ -0,0 +1,706 @@ +/* +Copyright 2001-2015 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 +*/ + +// +// DLL to provide BPQEther support for G8BPQ switch in a +// 32bit environment, +// +// Uses BPQ EXTERNAL interface +// +// Uses WINPACAP library + +// Version 1.0 December 2005 + +// Version 1.1 January 2006 +// +// Get config file location from Node (will check bpq directory) + +// Version 1.2 October 2006 + +// Write diagnostics to BPQ console window instead of STDOUT + +// Version 1.3 February 2008 + +// Changes for dynamic unload of bpq32.dll + +// Version 1.3.1 Spetmber 2010 + +// Add option to get config from bpq32.dll + + +#define _CRT_SECURE_NO_DEPRECATE + +#include + +//#include + +#include "CHeaders.h" +#include +#include "pcap.h" + +#include "../CommonSource/bpq32.h" + +//#include "packet32.h" +//#include "ntddndis.h" + +extern char * PortConfig[33]; + +typedef struct PCAPStruct +{ + pcap_t *adhandle; + UCHAR EthSource[6]; + UCHAR EthDest[6]; + short EtherType; + BOOL RLITX; + BOOL RLIRX; + BOOL Promiscuous; + int pcap_reopen_delay; + char Adapter[256]; + +} PCAPINFO, *PPCAPINFO ; + +PCAPINFO * PCAPInfo[32]; + +//PPCAPINFO PCAPInfo[16]={0}; + +UCHAR EthDest[7]={01,'B','P','Q',0,0}; + +char EtherType[10]="0x08FF"; + +//pcap_t *adhandle; + +struct tagMSG Msg; + +short udpport=0; + +extern UCHAR BPQDirectory[]; + +unsigned int OurInst = 0; + +HWND hWnd; +BOOL GotMsg; + +DWORD n; + +#ifdef WIN32 + +static HINSTANCE PcapDriver=0; + +typedef int (FAR *FARPROCX)(); + +int (FAR * pcap_sendpacketx)(); + +FARPROCX pcap_compilex; +FARPROCX pcap_setfilterx; +FARPROCX pcap_datalinkx; +FARPROCX pcap_next_exx; +FARPROCX pcap_geterrx; +pcap_t * (FAR * pcap_open_livex)(const char *, int, int, int, char *); + +static char Dllname[6]="wpcap"; + +FARPROCX GetAddress(char * Proc); + +#else + +#define pcap_compilex pcap_compile +#define pcap_open_livex pcap_open_live +#define pcap_setfilterx pcap_setfilter +#define pcap_datalinkx pcap_datalink +#define pcap_next_exx pcap_next_ex +#define pcap_geterrx pcap_geterr +#define pcap_sendpacketx pcap_sendpacket +#endif + +int InitPCAP(void); +static FARPROCX GetAddress(char * Proc); + +static BOOL ReadConfigFile(int Port); +static int ProcessLine(char * buf,int Port, BOOL CheckPort); +static int OpenPCAP(PPCAPINFO PCAP); + + +int ExtProc(int fn, int port,unsigned char * buff) +{ + int len,txlen=0,res; + char txbuff[500]; + struct pcap_pkthdr *header; + u_char *pkt_data; + PPCAPINFO PCAP = PCAPInfo[port]; + + // char dcall[10],scall[10]; + + if (PCAP->adhandle == 0) + { + // No handle. + + if (PCAP->Adapter[0]) + { + // Try reopening periodically + + PCAP->pcap_reopen_delay --; + + if (PCAP->pcap_reopen_delay < 0) + if (OpenPCAP(PCAP) == FALSE) + PCAP->pcap_reopen_delay = 300; // Retry every 30 seconds + } + return 0; + } + switch (fn) + { + case 1: // poll + + res = pcap_next_exx(PCAP->adhandle, &header, &pkt_data); + + if (res == 0) + /* Timeout elapsed */ + return 0; + + if (res == -1) + { + // Failed - try to reopen + + if (OpenPCAP(PCAP) == FALSE) + PCAP->pcap_reopen_delay = 300; + return 0; + } + + if (PCAP->RLIRX) + + // RLI MODE - An extra 3 bytes before len, seem to be 00 00 41 + + { + len=pkt_data[18]*256 + pkt_data[17]; + + if ((len < 16) || (len > 320)) return 0; // Probably BPQ Mode Frame + + len-=3; + + memcpy(&buff[7],&pkt_data[19],len); + + len+=5; + } + else + { + len=pkt_data[15]*256 + pkt_data[14]; + + if ((len < 16) || (len > 320)) return 0; // Probably RLI Mode Frame + + len-=3; + + memcpy(&buff[7],&pkt_data[16],len); + + len+=5; + } + + buff[5]=(len & 0xff); + buff[6]=(len >> 8); + + return 1; + + + case 2: // send + + if (PCAP->RLITX) + + // RLI MODE - An extra 3 bytes before len, seem to be 00 00 41 + + { + txlen=(buff[6]<<8) + buff[5]; // BPQEther is DOS-based - chain word is 2 bytes + + txlen-=2; + txbuff[16]=0x41; + txbuff[17]=(txlen & 0xff); + txbuff[18]=(txlen >> 8); + + if (txlen < 1 || txlen > 400) + return 0; + + memcpy(&txbuff[19],&buff[7],txlen); + + } + else + { + txlen=(buff[6]<<8) + buff[5]; // BPQEther is DOS-based - chain word is 2 bytes + + txlen-=2; + + txbuff[14]=(txlen & 0xff); + txbuff[15]=(txlen >> 8); + + if (txlen < 1 || txlen > 400) + return 0; + + + memcpy(&txbuff[16],&buff[7],txlen); + } + + memcpy(&txbuff[0],&PCAP->EthDest[0],6); + memcpy(&txbuff[6],&PCAP->EthSource[0],6); + memcpy(&txbuff[12],&PCAP->EtherType,2); + + txlen+=14; + + + if (txlen < 60) txlen = 60; + + // Send down the packet + + if (pcap_sendpacketx(PCAP->adhandle, // Adapter + txbuff, // buffer with the packet + txlen // size + ) != 0) + { + + // n=sprintf(buf,"\nError sending the packet: \n", pcap_geterrx(PCAPInfo[port].adhandle)); + // WritetoConsole(buf); + + return 3; + } + + + return (0); + + + case 3: // CHECK IF OK TO SEND + + return (0); // OK + + case 4: // reinit + + return 0; + + case 5: // reinit + + return 0; + } + + return (0); +} + + +UINT ETHERExtInit(struct PORTCONTROL * PortEntry) +{ + // Can have multiple ports, each mapping to a different Ethernet Adapter + + // The Adapter number is in IOADDR + + int Port = PortEntry->PORTNUMBER; + PPCAPINFO PCAP; + char buf[80]; + + if (InitPCAP()) // Make sure pcap is installed + { + WritetoConsole("BPQEther "); + + // + // Read config first, to get UDP info if needed + // + + PCAP = PCAPInfo[Port] = zalloc(sizeof(struct PCAPStruct)); + + if (!ReadConfigFile(Port)) + return (FALSE); + + if (OpenPCAP(PCAP)) + sprintf(buf,"Using %s\n", PCAP->Adapter); + else + sprintf(buf,"Failed to open %s\n", PCAP->Adapter); + + WritetoConsole(buf); + } + return ((int) ExtProc); +} + + +InitPCAP() +{ + char Msg[255]; + int err; + u_long param=1; + BOOL bcopt=TRUE; + +// Use LoadLibrary/GetProcADDR to get round link problem + +#ifndef WIN32 + + return TRUE; + +#endif + + if (PcapDriver) + return TRUE; // Already loaded + + PcapDriver=LoadLibrary(Dllname); + + if (PcapDriver == NULL) + { + err=GetLastError(); + sprintf(Msg,"Error loading Driver %s - Error code %d", Dllname,err); + WritetoConsole(Msg); + return(FALSE); + } + + if ((pcap_sendpacketx=GetAddress("pcap_sendpacket")) == 0 ) return FALSE; + + if ((pcap_datalinkx=GetAddress("pcap_datalink")) == 0 ) return FALSE; + + if ((pcap_compilex=GetAddress("pcap_compile")) == 0 ) return FALSE; + + if ((pcap_setfilterx=GetAddress("pcap_setfilter")) == 0 ) return FALSE; + + if ((pcap_open_livex = (pcap_t * (__cdecl *)()) + GetProcAddress(PcapDriver,"pcap_open_live")) == 0 ) return FALSE; + + if ((pcap_geterrx=GetAddress("pcap_geterr")) == 0 ) return FALSE; + + if ((pcap_next_exx=GetAddress("pcap_next_ex")) == 0 ) return FALSE; + + return (TRUE); +} + +#ifdef WIN32 + +FARPROCX GetAddress(char * Proc) +{ + FARPROCX ProcAddr; + int err=0; + char buf[256]; + int n; + + + ProcAddr=(FARPROCX) GetProcAddress(PcapDriver,Proc); + + if (ProcAddr == 0) + { + err=GetLastError(); + + n=sprintf(buf,"Error finding %s - %d\n", Proc,err); + WritetoConsole(buf); + + return(0); + } + + return ProcAddr; +} +#endif + +#include + +#pragma comment(lib, "IPHLPAPI.lib") + +void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); + +int OpenPCAP(PPCAPINFO PCAP) +{ + int i=0; + char errbuf[PCAP_ERRBUF_SIZE]; + u_int netmask; + char packet_filter[30]; + struct bpf_program fcode; + char buf[256]; + int n; + + /* Open the adapter */ + if ((PCAP->adhandle= pcap_open_livex(PCAP->Adapter, // name of the device + 65536, // portion of the packet to capture. + // 65536 grants that the whole packet will be captured on all the MACs. + PCAP->Promiscuous, // promiscuous mode (nonzero means promiscuous) + 1, // read timeout + errbuf // error buffer + )) == NULL) + { + return FALSE; + } + + /* Check the link layer. We support only Ethernet for simplicity. */ + if(pcap_datalinkx(PCAP->adhandle) != DLT_EN10MB) + { + n=sprintf(buf,"This program works only on Ethernet networks.\n"); + WritetoConsole(buf); + + return FALSE; + } + + netmask=0xffffff; + + sprintf(packet_filter,"ether[12:2]=0x%x", + ntohs(PCAP->EtherType)); + + //compile the filter + if (pcap_compilex(PCAP->adhandle, &fcode, packet_filter, 1, netmask) <0 ) + { + n=sprintf(buf,"Unable to compile the packet filter. Check the syntax.\n"); + WritetoConsole(buf); + + /* Free the device list */ + return FALSE; + } + + //set the filter + + if (pcap_setfilterx(PCAP->adhandle, &fcode)<0) + { + n=sprintf(buf,"Error setting the filter.\n"); + WritetoConsole(buf); + + /* Free the device list */ + return FALSE; + } + + return TRUE; + +} + + +static BOOL ReadConfigFile(int Port) +{ +//TYPE 1 08FF # Ethernet Type +//ETH 1 FF:FF:FF:FF:FF:FF # Target Ethernet AddrMAP G8BPQ-7 10.2.77.1 # IP 93 for compatibility +//ADAPTER 1 \Device\NPF_{21B601E8-8088-4F7D-96 29-EDE2A9243CF4} # Adapter Name + + char buf[256],errbuf[256]; + char * Config; + PPCAPINFO PCAP = PCAPInfo[Port]; + + Config = PortConfig[Port]; + + PCAP->Promiscuous = 1; // Defaults + PCAP->EtherType=htons(0x08FF); + memset(PCAP->EthDest, 0xff, 6); + + if (Config) + { + // Using config from bpq32.cfg + + char * ptr1 = Config, * ptr2; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!ProcessLine(buf, Port, FALSE)) + { + WritetoConsole("BPQEther - Bad config record "); + WritetoConsole(errbuf); + WritetoConsole("\n"); + } + } + return (TRUE); + } + + n=sprintf(buf,"No config info found in bpq32.cfg\n"); + WritetoConsole(buf); + + return (FALSE); +} + + +static int ProcessLine(char * buf, int Port, BOOL CheckPort) +{ + char * ptr; + char * p_port; + char * p_mac; + char * p_Adapter; + char * p_type; + + int port; + int a,b,c,d,e,f,num; + + PPCAPINFO PCAP = PCAPInfo[Port]; + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (CheckPort) + { + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + port = atoi(p_port); + + if (Port != port) return TRUE; // Not for us + } + + if(_stricmp(ptr,"ADAPTER") == 0) + { + IP_ADAPTER_INFO AdapterInfo[16]; // Allocate information for up to 16 NICs + DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer + DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufLen); + + PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo; + + p_Adapter = strtok(NULL, " \t\n\r"); + + strcpy(PCAP->Adapter, p_Adapter); + + // Look for MAC Address + + do + { + if (strcmp(pAdapterInfo->AdapterName, &PCAP->Adapter[12]) == 0) + { + memcpy(PCAP->EthSource, pAdapterInfo->Address, 6); + break; + } + + pAdapterInfo = pAdapterInfo->Next; // Progress through linked list + + } while(pAdapterInfo); // Terminate if last adapter + + return (TRUE); + } + + if(_stricmp(ptr,"TYPE") == 0) + { + p_type = strtok(NULL, " \t\n\r"); + + if (p_type == NULL) return (FALSE); + + num=sscanf(p_type,"%x",&a); + + if (num != 1) return FALSE; + + PCAP->EtherType=htons(a); + return (TRUE); + + } + + if(_stricmp(ptr,"promiscuous") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr == NULL) return (FALSE); + + PCAP->Promiscuous = atoi(ptr); + + return (TRUE); + + } + + if(_stricmp(ptr,"RXMODE") == 0) + { + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + if(_stricmp(p_port,"RLI") == 0) + { + PCAP->RLIRX=TRUE; + return (TRUE); + } + + if(_stricmp(p_port,"BPQ") == 0) + { + PCAP->RLIRX=FALSE; + return (TRUE); + } + + return FALSE; + + } + + if(_stricmp(ptr,"TXMODE") == 0) + { + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + if(_stricmp(p_port,"RLI") == 0) + { + PCAP->RLITX=TRUE; + return (TRUE); + } + + if(_stricmp(p_port,"BPQ") == 0) + { + PCAP->RLITX=FALSE; + return (TRUE); + } + + return FALSE; + + } + + if(_stricmp(ptr,"DEST") == 0) + { + p_mac = strtok(NULL, " \t\n\r"); + + if (p_mac == NULL) return (FALSE); + + num=sscanf(p_mac,"%x-%x-%x-%x-%x-%x",&a,&b,&c,&d,&e,&f); + + if (num != 6) return FALSE; + + PCAP->EthDest[0]=a; + PCAP->EthDest[1]=b; + PCAP->EthDest[2]=c; + PCAP->EthDest[3]=d; + PCAP->EthDest[4]=e; + PCAP->EthDest[5]=f; + + + // strcpy(Adapter,p_Adapter); + + return (TRUE); + } + + if(_stricmp(ptr,"SOURCE") == 0) + { + p_mac = strtok(NULL, " \t\n\r"); + + if (p_mac == NULL) return (FALSE); + + num=sscanf(p_mac,"%x-%x-%x-%x-%x-%x",&a,&b,&c,&d,&e,&f); + + if (num != 6) return FALSE; + + PCAP->EthSource[0]=a; + PCAP->EthSource[1]=b; + PCAP->EthSource[2]=c; + PCAP->EthSource[3]=d; + PCAP->EthSource[4]=e; + PCAP->EthSource[5]=f; + + // strcpy(Adapter,p_Adapter); + + return (TRUE); + + } + // + // Bad line + // + return (FALSE); + +} + + + diff --git a/.svn/pristine/15/1580b3e11cb49512d3712e74430d22e39ec8115c.svn-base b/.svn/pristine/15/1580b3e11cb49512d3712e74430d22e39ec8115c.svn-base new file mode 100644 index 0000000..a359cd1 --- /dev/null +++ b/.svn/pristine/15/1580b3e11cb49512d3712e74430d22e39ec8115c.svn-base @@ -0,0 +1,333 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/.svn/pristine/15/159b79788c0f452c2993299caa5d0cd96da1e8d9.svn-base b/.svn/pristine/15/159b79788c0f452c2993299caa5d0cd96da1e8d9.svn-base new file mode 100644 index 0000000..5516baa --- /dev/null +++ b/.svn/pristine/15/159b79788c0f452c2993299caa5d0cd96da1e8d9.svn-base @@ -0,0 +1,1514 @@ +/* +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 +*/ + +/* +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 +*/ + +// +// C replacement for L3Code.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "cheaders.h" +#include "tncinfo.h" + +VOID UPDATEDESTLIST(); +VOID MOVEALL(dest_list * DEST); +VOID MOVE3TO2(dest_list * DEST); +VOID CLEARTHIRD(dest_list * DEST); +VOID L3TRYNEXTDEST(struct ROUTE * ROUTE); +VOID SendNETROMRoute(struct PORTCONTROL * PORT, unsigned char * axcall); +void SendVARANetromNodes(struct TNCINFO * TNC, MESSAGE *Buffer); +void SendVARANetromMsg(struct TNCINFO * TNC,L3MESSAGEBUFFER * Buffer); + +extern BOOL NODESINPROGRESS ;; +PPORTCONTROL L3CURRENTPORT; +extern dest_list * CURRENTNODE; + +int L3_10SECS = 10; + + +VOID L3BG() +{ + // TRANSFER MESSAGES FROM DEST TO LINK + + int n = MAXDESTS; + struct DEST_LIST * DEST = DESTS; // NODE LIST + struct PORTCONTROL * PORT = PORTTABLE; + struct ROUTE * ROUTE; + struct TNCINFO * TNC; + + struct _LINKTABLE * LINK; + + while (n--) + { + if (DEST->DEST_CALL[0]) // Active entry? + { + while(DEST->DEST_Q) // FRAMES TO SEND? + { + int ActiveRoute = DEST->DEST_ROUTE; + + if (ActiveRoute) + { + ROUTE = DEST->NRROUTE[ActiveRoute - 1].ROUT_NEIGHBOUR; + + // if NetROM over VARA pass direct to the driver + + if (ROUTE) + { + TNC = TNCInfo[ROUTE->NEIGHBOUR_PORT]; + + if (TNC && TNC->NetRomMode) + { + PL3MESSAGEBUFFER MSG = (PL3MESSAGEBUFFER)Q_REM(&DEST->DEST_Q); + SendVARANetromMsg(TNC, MSG); + return; + } + } + if (ROUTE) + LINK = ROUTE->NEIGHBOUR_LINK; + else + LINK= NULL; + + if (LINK) + { + if (LINK->L2STATE == 0) + { + // LINK ENTRY IS INVALID - IT PROBABLY HAS BEEN 'ZAPPED', SO CANCEL IT + + ROUTE->NEIGHBOUR_LINK = NULL; + } + else + { + if (LINK->L2STATE < 5) + { + goto NextDest; // Wait for it to activate + } + else + { + ROUTE->NBOUR_IFRAMES++; + C_Q_ADD(&LINK->TX_Q, Q_REM(&DEST->DEST_Q)); + continue; // See if more + } + } + } + // Drop through to Activate + } + + if (ACTIVATE_DEST(DEST) == FALSE) + { + // Node has no routes - get rid of it + + REMOVENODE(DEST); + return; // Avoid riskof looking at lod entries + } + } + } + +NextDest: + DEST++; + } +} + +BOOL ACTIVATE_DEST(struct DEST_LIST * DEST) +{ + int n = MAXDESTS; + struct PORTCONTROL * PORT = PORTTABLE; + struct ROUTE * ROUTE; + struct _LINKTABLE * LINK; + struct TNCINFO * TNC; + + int ActiveRoute; + + if (DEST->DEST_ROUTE == 0) // ALREADY HAVE A SELECTED ROUTE? + DEST->DEST_ROUTE = 1; // TRY TO ACTIVATE FIRST + + ActiveRoute = DEST->DEST_ROUTE - 1; + + ROUTE = DEST->NRROUTE[ActiveRoute].ROUT_NEIGHBOUR; + + if (ROUTE == 0) + { + // Currnet Route not present + // If current route is 1, then we must have INP3 routes (or entry is corrupt) + + if (DEST->DEST_ROUTE != 1) + goto NOROUTETODEST; + + // Current Route is 1 + + if (DEST->ROUTE[0].ROUT_NEIGHBOUR == 0) + return FALSE; // No INP3 so No Routes + + DEST->DEST_ROUTE = 4; // First INP3 + ROUTE = DEST->ROUTE[0].ROUT_NEIGHBOUR; + } + + // if NetROM over VARA conection is made by the driver + + TNC = TNCInfo[ROUTE->NEIGHBOUR_PORT]; + + if (TNC && TNC->NetRomMode) + { + return TRUE; + } + + LINK = ROUTE->NEIGHBOUR_LINK; + + if (LINK == 0) + { + // Need to Activate Link + + // SET UP LINK TABLE ENTRY + + return L2SETUPCROSSLINK(ROUTE); + } + + // We mst be waiting for link to come up + + return TRUE; + +NOROUTETODEST: + + // CURRENT NEIGHBOUR NOT DEFINED - RESET TO USE FIRST + + if (DEST->DEST_ROUTE == 1) + return FALSE; // First not defined so give up + + DEST->DEST_ROUTE = 0; + return TRUE; +} + +char Call1[10]; +char Call2[10]; +char Call3[10]; + +VOID PROCESSNODEMESSAGE(MESSAGE * Msg, struct PORTCONTROL * PORT) +{ + // PROCESS A NET/ROM 'NODES' MESSAGE + + // UPDATE _NEIGHBOURS LIST WITH ORIGINATING CALL, AND + // DESTINATION LIST WITH ANY PRACTICAL ROUTES + + + struct DEST_LIST * DEST; + struct ROUTE * ROUTE; + int Portno = PORT->PORTNUMBER; + time_t Stamp; + int HH, MM; + int Msglen = Msg->LENGTH; + int n; + UCHAR * ptr1, * ptr2, * saveptr; + int Qual; + APPLCALLS * APPL; + int App; + + + if (PORT->PORTQUALITY == 0 || PORT->INP3ONLY) + return; + + // SEE IF OUR CALL - DONT WANT TO PUT IT IN LIST! + + if (CompareCalls(Msg->ORIGIN, NETROMCALL)) + return; + + if (CheckExcludeList(Msg->ORIGIN) == 0) + return; + + for (App = 0; App < NumberofAppls; App++) + { + APPL=&APPLCALLTABLE[App]; + + if (APPL->APPLHASALIAS == 0 && CompareCalls(Msg->ORIGIN, APPL->APPLCALL)) + return; + } + + Msg->ORIGIN[6] &= 0x1E; // MASK OFF LAST ADDR BIT + + // Trap Empty Call + + if (Msg->ORIGIN[0] == 0x40) + return; + +/* + // validate call ptr = &Buffer->ORIGIN[0]; + n = 6; + + while(n--) + { + // Try a bit harder to detect corruption + + c = *(ptr++); + + if (c & 1) + { + ReleaseBuffer(Buffer); + return; + } + + c = c >> 1; + + if (!isalnum(c) && !(c == '#') && !(c == ' ')) + { + ReleaseBuffer(Buffer); + return; + } + } + +*/ + // SEE IF ORIGINATING CALL IS IN NEIGHBOUR LIST - IF NOT ADD IT + + if (FindNeighbour(Msg->ORIGIN, Portno, &ROUTE) == 0) + { + // Not in list + + if (ROUTE == NULL) + return; // Table full + + // CREATE NEIGHBOUR RECORD + + memcpy(ROUTE->NEIGHBOUR_CALL, Msg->ORIGIN, 7); + + ROUTE->NEIGHBOUR_PORT = Portno; + ROUTE->NEIGHBOUR_QUAL = PORT->PORTQUALITY; + + ROUTE->NEIGHBOUR_LINK = 0; // CANT HAVE A LINK IF NEW _NODE + + ROUTE->NoKeepAlive = PORT->PortNoKeepAlive; + } + + // if locked route with quality zero ignore + + if ((ROUTE->NEIGHBOUR_FLAG)) // LOCKED ROUTE + if (ROUTE->NEIGHBOUR_QUAL == 0) + return; + + // If Ignoreunlocked set, ignore it not locked + + if ((ROUTE->NEIGHBOUR_FLAG) == 0) // LOCKED ROUTE + if (PORT->IgnoreUnlocked) + return; + + SendNETROMRoute(PORT, Msg->ORIGIN); + + // if not locked, update route quality from port quality (may have changed config and not cleared SAVENODES + + if (ROUTE->NEIGHBOUR_FLAG == 0) // Not LOCKED ROUTE + ROUTE->NEIGHBOUR_QUAL = PORT->PORTQUALITY; + + // GET TIME FROM BIOS DATA AREA OR RTC + + time((time_t *)&Stamp); + + Stamp = Stamp % 86400; // Secs into day + HH = (int)(Stamp / 3600); + + Stamp -= HH * 3600; + MM = (int)(Stamp / 60); + + ROUTE->NEIGHBOUR_TIME = 256 * HH + MM; + + // GET QUALITY + + Qual = ROUTEQUAL = ROUTE->NEIGHBOUR_QUAL; // FOR INITIAL ROUTE TABLE UPDATE + + // CHECK LINK IS IN DEST LIST + + if (FindDestination(Msg->ORIGIN, &DEST) == 0) + { + if (DEST == NULL) + return; // Tsble Full + + // CREATE DESTINATION RECORD + + memset(DEST, 0, sizeof(struct DEST_LIST)); + + memcpy(DEST->DEST_CALL, Msg->ORIGIN, 7); + + NUMBEROFNODES++; + } + + // ALWAYS UPDATE ALIAS IN CASE NOT PRESENT IN ORIGINAL TABLE + + ptr1 = &Msg->L2DATA[1]; + ptr2 = &DEST->DEST_ALIAS[0]; + + if (*ptr1 > ' ') // Only of present + { + // Validate Alias, mainly for DISABL KPC3 Problem + UCHAR c; + + n = 6; + while (n--) + { + c = *(ptr1++); + if (c < 0x20 || c > 0x7A) + c = ' '; + + *(ptr2++) = c; + } + } + else + ptr1 += 6; + + + // UPDATE QUALITY AND OBS COUNT + + PROCROUTES(DEST, ROUTE, Qual); + + Msglen -= (MSGHDDRLEN + 23); // LEVEL 2 HEADER FLAG and NODE MNEMONIC + + // PROCESS DESTINATIONS from message + + // ptr1 = start + + saveptr = ptr1 - 21; + + while (Msglen >= 21) // STILL AT LEAST 1 ENTRY LEFT + { + Msglen -= 21; + saveptr += 21; + ptr1 = saveptr; + + // SEE IF OUR CALL - DONT WANT TO PUT IT IN LIST! + + if (CompareCalls(ptr1, MYCALL)) + { + // But use it to get route quality setting from other end + + // As we now get qual ftom highest, only use this after a reload in + // case other end has changed. + if (ROUTE->FirstTimeFlag == 0) + { + // Check if route is via our node + + if (memcmp(ptr1, &ptr1[13], 7) == 0) + { + if (ROUTE->OtherendLocked == 0) // Dont update locked quality + ROUTE->OtherendsRouteQual = ptr1[20]; + + ROUTE->FirstTimeFlag = 1; // Only do it first time after load + } + } + continue; + } + + if (CheckExcludeList(ptr1) == 0) // Excluded + continue; + + for (n = 0; n < 32; n++) + { + if (CompareCalls(ptr1, APPLCALLTABLE[n].APPLCALL)) + continue; + } + + // MAKE SURE ITS NOT CORRUPTED + + n = 6; + + while (n--) + { + if (*(ptr1++) < 0x40) // Call + { + Call1[ConvFromAX25(Msg->ORIGIN, Call1)] = 0; + Call2[ConvFromAX25(saveptr, Call2)] = 0; + memcpy(Call3, saveptr + 7,6); + Call3[6] = 0; + Debugprintf("Corrupt Node Entry from %s for %s:%s", Call1, Call2, Call3); + goto IgnoreNode; + } + } + ptr1++; // skip ssid + + n = 6; + + while (n--) + { + if (*(ptr1) && ((*(ptr1) < 0x20 || *(ptr1) > 0x7A)) ) // Should we accept zeros or convert to spaces?? + { + Call1[ConvFromAX25(Msg->ORIGIN, Call1)] = 0; + Call2[ConvFromAX25(saveptr, Call2)] = 0; + memcpy(Call3, saveptr + 7,6); + Call3[6] = 0; + Debugprintf("Corrupt Node Entry from %s for %s:%s", Call1, Call2, Call3); + goto IgnoreNode; + } + ptr1++; + } + + ptr1 -= 13; // Back to start + + // CALCULATE ROUTE QUALITY + + // Experimantal Code to adjust received route qualities based on deduced quality + // settings at other end of link. + + // Don't mess with Application Qualities. There are almost always 255, and + // if not there is probably a good reason for the value chosen. + + if (CompareCalls(Msg->ORIGIN, &ptr1[13])) + { + // Application Node - Just do normal update + + Qual = (((ROUTEQUAL * ptr1[20]) + 128)) / 256; + } + else + { + // Try using the highest reported indirect route as remote qual + + if (ROUTE->OtherendLocked == 0) // Not locked + { + if (ptr1[20] > ROUTE->OtherendsRouteQual) + ROUTE->OtherendsRouteQual = ptr1[20]; + } + + // Treat 255 as 254, so 255 routes doen't get included with minquals + // designed to only include applcalls + + if (ptr1[20] == 255) + ptr1[20] = 254; + + Qual = (((ROUTEQUAL * ptr1[20]) + 128)) / 256; + + // I think we should normalize indirect routes twice + // as node not only sends differnet qual but adjusts incoming qual with it + + // or should we ............ + + // We don't touch appl callsigns so I think everything here is an indirect route + + if (ROUTE->OtherendsRouteQual && PORT->NormalizeQuality) + { + Qual = (Qual * ROUTEQUAL) / ROUTE->OtherendsRouteQual; +// not sure about this! Qual = (Qual * ROUTEQUAL) / ROUTE->OtherendsRouteQual; // Twice + + if (Qual > ROUTEQUAL) + Qual = ROUTEQUAL; + } + } + + // SEE IF BELOW MIN QUAL FOR AUTO UPDATES + + if (Qual < MINQUAL) + continue; + + // CHECK LINK IS IN DEST LIST + + if (FindDestination(ptr1, &DEST) == 0) + { + if (DEST == NULL) + continue; + + // CREATE DESTINATION RECORD + + memset(DEST, 0, sizeof(struct DEST_LIST)); + memcpy(DEST->DEST_CALL, ptr1, 7); + NUMBEROFNODES++; + } + + ptr1 += 7; + + // UPDATE ALIAS + + memcpy(DEST->DEST_ALIAS, ptr1, 6); + + ptr1 += 6; + + // NOW POINTING AT BEST NEIGHBOUR - IF THIS IS US, THEN ROUTE IS A LOOP + + if (CompareCalls(ptr1, NETROMCALL)) + Qual = 0; + + // DEST IS NOW IN TABLE - + + // 1. SEE IF THIS ROUTE IS IN TABLE - IF SO UPDATE QUALITY, + // IF NOT, ADD THIS ONE IF IT HAS HIGHER QUALITY THAN EXISTING ONES + + + PROCROUTES(DEST, ROUTE, Qual); + ptr1 += 8; +IgnoreNode:; + } +} + +VOID PROCROUTES(struct DEST_LIST * DEST, struct ROUTE * ROUTE, int Qual) +{ + // ADD NEIGHBOUR ADDRESS IN ROUTE TO NODE's ROUTE TABLE IF BETTER QUALITY THAN THOSE PRESENT + + int Index = 0; + struct NR_DEST_ROUTE_ENTRY Temp; + + if (DEST->DEST_STATE & 0x80) // BBS ENTRY + return; + + for (Index = 0; Index < 4; Index++) + { + if (DEST->NRROUTE[Index].ROUT_NEIGHBOUR == ROUTE) + { + if (Index == 0) + { + // THIS IS A REFRESH OF BEST - IF THIS ISNT ACTIVE ROUTE, MAKE IT ACTIVE + + if (DEST->DEST_ROUTE > 1) // LEAVE IT IF NOT SELECTED OR ALREADY USING BEST + DEST->DEST_ROUTE = 1; + } + + goto UpdatateThisEntry; + } + } + + // NOT IN ANY ROUTE + + Index = 0; + + if (DEST->NRROUTE[0].ROUT_NEIGHBOUR == 0) + goto UpdatateThisEntry; // SPARE ENTRY, SO USE IT + + if (DEST->NRROUTE[0].ROUT_QUALITY < Qual) + { + // New route is better than best, so move other two down and add here + + DEST->NRROUTE[2] = DEST->NRROUTE[1]; + DEST->NRROUTE[1] = DEST->NRROUTE[0]; + + DEST->DEST_ROUTE = 0; // Se we will switch to new one + + goto UpdatateThisEntry; + } + + Index = 1; + + if (DEST->NRROUTE[1].ROUT_NEIGHBOUR == 0) + goto UpdatateThisEntry; // SPARE ENTRY, SO USE IT + + if (DEST->NRROUTE[1].ROUT_QUALITY < Qual) + { + // New route is better than second, so move down and add here + + DEST->NRROUTE[2] = DEST->NRROUTE[1]; + goto UpdatateThisEntry; + } + + Index = 2; + + if (DEST->NRROUTE[2].ROUT_NEIGHBOUR == 0) + goto UpdatateThisEntry; + + if (DEST->NRROUTE[2].ROUT_QUALITY < Qual) + { + // New route is better than third, so add here + goto UpdatateThisEntry; + } + + // THIS ROUTE IS WORSE THAN ANY OF THE CURRENT 3 - IGNORE IT + + return; + + +UpdatateThisEntry: + + DEST->NRROUTE[Index].ROUT_NEIGHBOUR = ROUTE; + + // I DONT KNOW WHY I DID THIS, BUT IT CAUSES REFLECTED ROUTES + // TO BE SET UP WITH OBS = 0. THIS MAY PREVENT A VALID ALTERNATE + // VIA THE SAME NODE TO FAIL TO BE FOUND. SO I'LL TAKE OUT THE + // TEST AND SEE IF ANYTHING NASTY HAPPENS + // IT DID - THIS IS ALSO CALLED BY CHECKL3TABLES. TRY RESETING + // OBS, BUT NOT QUALITY + + if ((DEST->NRROUTE[Index].ROUT_OBSCOUNT & 0x80) == 0) + DEST->NRROUTE[Index].ROUT_OBSCOUNT = OBSINIT; // SET OBSOLESCENCE COUNT + + if (Qual) + DEST->NRROUTE[Index].ROUT_QUALITY = Qual; // IF ZERO, SKIP UPDATE + + // IT IS POSSIBLE ROUTES ARE NOW OUT OF ORDER + +SORTROUTES: + + if (DEST->NRROUTE[1].ROUT_QUALITY > DEST->NRROUTE[0].ROUT_QUALITY) + { + // SWAP 1 AND 2 + + Temp = DEST->NRROUTE[0]; + + DEST->NRROUTE[0] = DEST->NRROUTE[1]; + DEST->NRROUTE[1] = Temp; + + DEST->DEST_ROUTE = 0; // FORCE A RE-ASSESSMENT + } + + if (DEST->NRROUTE[2].ROUT_QUALITY > DEST->NRROUTE[1].ROUT_QUALITY) + { + // SWAP 2 AND 3 + + Temp = DEST->NRROUTE[1]; + + DEST->NRROUTE[1] = DEST->NRROUTE[2]; + DEST->NRROUTE[2] = Temp; + + goto SORTROUTES; // 1 AND 2 MAY NOW BE WRONG! + } +} + +int COUNTNODES(struct ROUTE * ROUTE) +{ + // COUNT NODES WITH ROUTE VIA NEIGHBOUR + + int count = 0; + int n = MAXDESTS; + struct DEST_LIST * DEST = DESTS; // NODE LIST + + while (n--) + { + if (DEST->NRROUTE[0].ROUT_NEIGHBOUR == ROUTE) + count++; + else if (DEST->NRROUTE[1].ROUT_NEIGHBOUR == ROUTE) + count++; + else if (DEST->NRROUTE[2].ROUT_NEIGHBOUR == ROUTE) + count++; + else if (DEST->ROUTE[0].ROUT_NEIGHBOUR == ROUTE) + count++; + else if (DEST->ROUTE[1].ROUT_NEIGHBOUR == ROUTE) + count++; + else if (DEST->ROUTE[2].ROUT_NEIGHBOUR == ROUTE) + count++; + + DEST++; + } + return count; +} + +VOID SENDNODE00(); +VOID L3BG(); + + + +VOID SENDNEXTNODESFRAGMENT(); + +VOID SENDNODESMSG() +{ + if (NODESINPROGRESS) + return; + + L3CURRENTPORT = PORTTABLE; + SENDNEXTNODESFRAGMENT(); +} + +int fragmentCount = 0; // Node broadcast packets sent to current port + +VOID SENDNEXTNODESFRAGMENT() +{ + // SEND NEXT FRAGMENT OF A NODES BROADCAST + + // FRAGMENTS ARE SENT AT 10 SECONDS INTERVALS - PARTLY TO REDUCE + // QRM, PARTLY TO REDUCE LOAD ON BUFFERS (AND TX POWER SUPPLIES!) + + // SEND TO PORT IN CURRENTPORT, STARTING AT CURRENTNODE + + struct PORTCONTROL * PORT = L3CURRENTPORT; + dest_list * DEST = CURRENTNODE; + MESSAGE * Buffer; + int Count; + int Qual; + int CURRENTPORTNO, TXMINQUAL; + UCHAR * ptr1, * ptr2; + + if (DEST == 0) + { + // FIRST FRAGMENT TO A PORT + + fragmentCount = 0; + + // Don't send NODES to Shared TX or INP3 Port + + while (PORT->PORTQUALITY == 0 || PORT->TXPORT || PORT->INP3ONLY) + { + // No NODES to this port, so go to next + + PORT = PORT->PORTPOINTER; + if (PORT == NULL) + { + // Finished + + NODESINPROGRESS = 0; + return; + } + } + + L3CURRENTPORT = PORT; + + DEST = CURRENTNODE = DESTS; // START OF LIST + NODESINPROGRESS = 1; + } + + CURRENTPORTNO = PORT->PORTNUMBER; + + TXMINQUAL = PORT->PORTMINQUAL; + + if (TXMINQUAL == 0) + TXMINQUAL = 1; // Dont send zero + + Buffer = GetBuff(); + + if (Buffer == 0) + return; + + Buffer->PORT = CURRENTPORTNO; + + 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++) = 0xff; // Nodes Flag + + memcpy(ptr1, MYALIASTEXT, 6); + + ptr1+= 6; + + // ADD DESTINATION INFO (UNLESS BBS ONLY) + + // If NODE = 0 just send application nodes + + Count = PORT->NODESPACLEN; + + if (Count == 0) + Count = 256; + + if (Count < 50) // STUPIDLY SMALL? + Count = 50; // EVEN THIS IS RATHER SILLY + + Count -= 22; // Fixed Part + + Count /= 21; // 21 Bytres per entry + + while (Count) + { + if (DEST >= ENDDESTLIST) + { + CURRENTNODE = 0; // Finished on this port + L3CURRENTPORT = PORT->PORTPOINTER; + if (L3CURRENTPORT == NULL) + { + // Finished + + NODESINPROGRESS = 0; + } + goto Sendit; + } + + if (DEST->DEST_CALL[0] != 0x40 && DEST->NRROUTE[0].ROUT_QUALITY >= TXMINQUAL && + DEST->NRROUTE[0].ROUT_OBSCOUNT >= OBSMIN && + (NODE == 1 || DEST->DEST_STATE & 0x80)) // Only send appl nodes if DEST = 0; + { + // Send it + + ptr2 = &DEST->DEST_CALL[0]; + memcpy(ptr1, ptr2, 13); // Dest and Alias + ptr1 += 13; + + ptr2 = (UCHAR *)DEST->NRROUTE[0].ROUT_NEIGHBOUR; + + if (ptr2 == 0) + + // DUMMY POINTER IN BBS ENTRY - PUT IN OUR CALL + + ptr2 = MYCALL; + + memcpy(ptr1, ptr2, 7); // Neighbour Call + ptr1 += 7; + + Qual = 100; + + if (DEST->NRROUTE[0].ROUT_NEIGHBOUR && DEST->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_PORT == CURRENTPORTNO) + { + // BEST NEIGHBOUR IS ON CURRENT PORT - REDUCE QUALITY BY QUAL_ADJUST + + Qual -= PORT->QUAL_ADJUST; + } + + Qual *= DEST->NRROUTE[0].ROUT_QUALITY; + Qual /= 100; + + *(ptr1++) = (UCHAR)Qual; + + Count--; + } + DEST++; + } + + CURRENTNODE = DEST; + +Sendit: + + Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer); + + if (Buffer->LENGTH > 35 || fragmentCount == 0) // Always send first even if no other nodes + { + if (PORT->TNC && PORT->TNC->Hardware == H_VARA) + SendVARANetromNodes(PORT->TNC, Buffer); + else + PUT_ON_PORT_Q(PORT, Buffer); + + fragmentCount++; + } + else + ReleaseBuffer(Buffer); + +} + +VOID L3LINKCLOSED(struct _LINKTABLE * LINK, int Reason) +{ + // L2 SESSION HAS SHUT DOWN (PROBABLY DUE TO INACTIVITY) + + struct ROUTE * ROUTE; + + // CLEAR NEIGHBOUR + + ROUTE = LINK->NEIGHBOUR; // TO NEIGHBOUR + + if (ROUTE) + { + LINK->NEIGHBOUR = NULL; // Clear links + ROUTE->NEIGHBOUR_LINK = NULL; + + CLEARACTIVEROUTE(ROUTE, Reason); // CLEAR ASSOCIATED DEST ENTRIES + } +} + +VOID CLEARACTIVEROUTE(struct ROUTE * ROUTE, int Reason) +{ + // FIND ANY DESINATIONS WITH [ESI] AS ACTIVE NEIGHBOUR, AND + // SET INACTIVE + + dest_list * DEST; + int n; + + if (Reason != NORMALCLOSE || ROUTE->INP3Node) + TellINP3LinkGone(ROUTE); + + DEST = DESTS; + n = MAXDESTS; + + DEST--; // So we can increment at start of loop + + while (n--) + { + DEST++; + + if (DEST->DEST_ROUTE == 0) + continue; + + if (DEST->ROUTE[DEST->DEST_ROUTE].ROUT_NEIGHBOUR == ROUTE) // Is this the active route + { + // Yes, so clear + + DEST->DEST_ROUTE = 0; // SET NO ACTIVE ROUTE + } + } +} + + +VOID L3TimerProc() +{ + int i; + struct PORTCONTROL * PORT = PORTTABLE; + struct ROUTE * ROUTE; + + struct _LINKTABLE * LINK; + + // CHECK FOR EXCESSIVE BUFFERS QUEUED AT LINK LEVEL + + if (QCOUNT < 100) + { + LINK = LINKS; + i = MAXLINKS; + + while (i--); + { + if (LINK->LINKTYPE == 3) // Only if Internode + { + if (COUNT_AT_L2(LINK) > 50) + { + Debugprintf("Excessive L3 Queue"); + L3LINKCLOSED(LINK, LINKSTUCK); // REPORT TO LEVEL 3 + CLEAROUTLINK(LINK); + } + } + LINK++; + } + } + + STATSTIME++; + + if (IDTIMER) // Not if Disabled + { + IDTIMER--; + + if (IDTIMER == 0) + { + IDTIMER = IDINTERVAL; + SENDIDMSG(); + } + } + + // CHECK FOR BEACON + + if (BTTIMER) // Not if Disabled + { + BTTIMER--; + + if (BTTIMER == 0) + { + BTTIMER = BTINTERVAL; + SENDBTMSG(); + } + } + + // CHECK FOR NODES BROADCAST + + if (L3TIMER) // Not if Disabled + { + L3TIMER--; + + if (L3TIMER == 0) + { + // UPDATE DEST LIST AND SEND 'NODES' MESSAGE + + L3TIMER = L3INTERVAL; + UPDATEDESTLIST(); + SENDNODESMSG(); + } + } + + // TIDY ROUTES + + ROUTE = NEIGHBOURS; + i = MAXNEIGHBOURS; + + ROUTE--; + + while (i--) + { + ROUTE++; + + if (ROUTE->NEIGHBOUR_FLAG) // Locked? + continue; + + if (ROUTE->NEIGHBOUR_LINK) // Has an active Session + continue; + + if (COUNTNODES(ROUTE) == 0) // NODES USING THIS DESTINATION + { + // IF NUMBER USING ROUTE IS ZERO, DELETE IT + + memset(ROUTE, 0, sizeof (struct ROUTE)); + } + } +} + +VOID L3FastTimer() +{ + // CALLED ONCE PER SECOND - USED ONLY TO SEND NEXT PART OF A NODES OR + // ID MESSAGE SEQUENCE + + MESSAGE * Msg; + struct PORTCONTROL * PORT = PORTTABLE; + + INP3TIMER(); + + // Send Node faster if VARA + + if (NODESINPROGRESS && L3CURRENTPORT->TNC && L3CURRENTPORT->TNC->NetRomMode) + SENDNEXTNODESFRAGMENT(); + + L3_10SECS--; + + if (L3_10SECS == 0) + { + L3_10SECS = 10; + + if (IDMSG_Q) // ID/BEACON TO SEND + { + Msg = Q_REM(&IDMSG_Q); + + PORT = GetPortTableEntryFromPortNum(Msg->PORT); + + if (PORT && PORT->TXPORT == 0) // DONT SEND IF SHARED TX + PUT_ON_PORT_Q(PORT, Msg); + else + ReleaseBuffer(Msg); + } + + if (NODESINPROGRESS) + SENDNEXTNODESFRAGMENT(); + } +} + + +VOID UPDATEDESTLIST() +{ + // DECREMENT OBS COUNTERS ON EACH ROUTE, AND REMOVE 'DEAD' ENTRIES + + dest_list * DEST; + int n; + + DEST = DESTS; + n = MAXDESTS; + + DEST--; // So we can increment at start of loop + + while (n--) + { + DEST++; + + if (DEST->DEST_CALL[0] == 0) // SPARE ENTRY + continue; + + if (DEST->DEST_STATE & 0x80) // LOCKED DESTINATION + continue; + +UPDEST000: + + if (DEST->NRROUTE[0].ROUT_NEIGHBOUR == 0) // NO DESTINATIONS - DELETE ENTRY unless inp3 routes + { + // Any INP3 Routes? + + if (DEST->ROUTE[0].ROUT_NEIGHBOUR == 0) + { + // NO ROUTES LEFT TO DEST - REMOVE IT + + REMOVENODE(DEST); // Unchain, Clear queue and zap + } + continue; + } + + if (DEST->NRROUTE[0].ROUT_OBSCOUNT == 0) + { + // FAILED IN USE - DELETE + + MOVEALL(DEST); + goto UPDEST000; //LOOP BACK TO PROCESS MOVED ENTRIES + } + + if ((DEST->NRROUTE[0].ROUT_OBSCOUNT & 0x80) == 0) // Locked? + { + DEST->NRROUTE[0].ROUT_OBSCOUNT--; + + if (DEST->NRROUTE[0].ROUT_OBSCOUNT == 0) // Timed out + { + MOVEALL(DEST); + goto UPDEST000; //LOOP BACK TO PROCESS MOVED ENTRIES + } + } + + // Process Next Neighbour + +UPDEST010: + + if (DEST->NRROUTE[1].ROUT_NEIGHBOUR == 0) + continue; // NO MORE DESTINATIONS + + if (DEST->NRROUTE[1].ROUT_OBSCOUNT == 0) + { + // FAILED IN USE - DELETE + + MOVE3TO2(DEST); + goto UPDEST010; //LOOP BACK TO PROCESS MOVED ENTRIES + } + + if ((DEST->NRROUTE[1].ROUT_OBSCOUNT & 0x80) == 0) // Locked? + { + DEST->NRROUTE[1].ROUT_OBSCOUNT--; + + if (DEST->NRROUTE[1].ROUT_OBSCOUNT == 0) // Timed out + { + MOVE3TO2(DEST); + goto UPDEST010; //LOOP BACK TO PROCESS MOVED ENTRIES + } + } + + // Process Next Neighbour + + if (DEST->NRROUTE[2].ROUT_NEIGHBOUR == 0) + continue; // NO MORE DESTINATIONS + + if (DEST->NRROUTE[2].ROUT_OBSCOUNT == 0) + { + // FAILED IN USE - DELETE + + CLEARTHIRD(DEST); + continue; + } + + if ((DEST->NRROUTE[2].ROUT_OBSCOUNT & 0x80) == 0) // Locked? + { + DEST->NRROUTE[2].ROUT_OBSCOUNT--; + + if (DEST->NRROUTE[2].ROUT_OBSCOUNT == 0) // Timed out + { + CLEARTHIRD(DEST); + continue; + } + } + } +} + + +VOID MOVEALL(dest_list * DEST) +{ + DEST->NRROUTE[0] = DEST->NRROUTE[1]; + MOVE3TO2(DEST); +} + +VOID MOVE3TO2(dest_list * DEST) +{ + DEST->NRROUTE[1] = DEST->NRROUTE[2]; + CLEARTHIRD(DEST); +} + +VOID CLEARTHIRD(dest_list * DEST) +{ + memset(&DEST->NRROUTE[2], 0, sizeof (struct NR_DEST_ROUTE_ENTRY)); + DEST->DEST_ROUTE = 0; // CANCEL ACTIVE ROUTE, SO WILL RE-ASSESS BEST +} + +// L4 Flags Values + +#define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED + +VOID REMOVENODE(dest_list * DEST) +{ + TRANSPORTENTRY * L4 = L4TABLE; + int n = MAXCIRCUITS; + + // Remove a node, either because routes have gone, or APPL API has invalidated it + + while (DEST->DEST_Q) + ReleaseBuffer(Q_REM(&DEST->DEST_Q)); + + // MAY NEED TO CHECK FOR L4 CIRCUITS USING DEST, BUT PROBABLY NOT, + // AS THEY SHOULD HAVE TIMED OUT LONG AGO + + // Not necessarily true with INP3, so had better check + + while (n--) + { + if (L4->L4USER[0]) + { + if (L4->L4TARGET.DEST == DEST) + { + // Session to/from this Dest + + TRANSPORTENTRY * Partner = L4->L4CROSSLINK; + char Nodename[20]; + + Nodename[DecodeNodeName(DEST->DEST_CALL, Nodename)] = 0; // null terminate + + Debugprintf("Delete Node for %s Called with active L4 Session - State %d", + Nodename, L4->L4STATE); + + if (Partner) + { + // if connnecting, send error message and drop back to command level + + if (L4->L4STATE == 2) + { + struct DATAMESSAGE * Msg = GetBuff(); + Partner->L4CROSSLINK = 0; // Back to command lewel + + if (Msg) + { + UCHAR * ptr1; + + Msg->PID = 0xf0; + ptr1 = SetupNodeHeader(Msg); + ptr1 += sprintf(ptr1, "Error - Node %s has disappeared\r", Nodename); + + Msg->LENGTH = (int)(ptr1 - (UCHAR *)Msg); + C_Q_ADD(&Partner->L4TX_Q, Msg); + PostDataAvailable(Partner); + } + } + else + { + // Failed in session - treat as if a L4DREQ received + + CLOSECURRENTSESSION(Partner); + } + } + CLEARSESSIONENTRY(L4); + } + } + L4++; + } + memset(DEST, 0, sizeof(struct DEST_LIST)); + NUMBEROFNODES--; +} + +VOID L3CONNECTFAILED(struct _LINKTABLE * LINK) +{ + // L2 LINK SETUP HAS FAILED - SEE IF ANOTHER NEIGHBOUR CAN BE USED + + struct PORTCONTROL * PORT = PORTTABLE; + struct ROUTE * ROUTE; + + + ROUTE = LINK->NEIGHBOUR; // TO NEIGHBOUR + + if (ROUTE == NULL) + return; // NOTHING ??? + + TellINP3LinkSetupFailed(ROUTE); + + ROUTE->NEIGHBOUR_LINK = 0; // CLEAR IT + + L3TRYNEXTDEST(ROUTE); // RESET ASSOCIATED DEST ENTRIES +} + + +VOID L3TRYNEXTDEST(struct ROUTE * ROUTE) +{ + // FIND ANY DESINATIONS WITH [ESI] AS ACTIVE NEIGHBOUR, AND + // SET NEXT BEST NEIGHBOUR (IF ANY) ACTIVE + + int n = MAXDESTS; + struct DEST_LIST * DEST = DESTS; // NODE LIST + int ActiveRoute; + + while (n--) + { + ActiveRoute = DEST->DEST_ROUTE; + + if (ActiveRoute) + { + ActiveRoute --; // Routes numbered 1 - 6, idex from 0 + + if (DEST->NRROUTE[ActiveRoute].ROUT_NEIGHBOUR == ROUTE) + { + // We were best + + // NEIGHBOUR HAS FAILED - DECREMENT OBSCOUNT + // AND TRY TO ACTIVATE ANOTHER (IF ANY) + + if (DEST->NRROUTE[ActiveRoute].ROUT_OBSCOUNT) + { + // IF ALREADY ZERO - WILL BE DELETED BY NEXT NODES UPDATE + + if ((DEST->NRROUTE[ActiveRoute].ROUT_OBSCOUNT & 0x80) == 0) + { + // not Locked + + DEST->NRROUTE[ActiveRoute].ROUT_OBSCOUNT--; + + // if ROUTE HAS EXPIRED - WE SHOULD CLEAR IT, AND MOVE OTHERS (IF ANY) UP + } + } + + // REMOVE FIRST MESSAGE FROM DEST_Q. L4 WILL RETRY - IF IT IS LEFT HERE + // WE WILL TRY TO ACTIVATE THE DESTINATION FOR EVER + + if (DEST->DEST_Q) + ReleaseBuffer(Q_REM(&DEST->DEST_Q)); + + DEST->DEST_ROUTE++; // TO NEXT + + if (DEST->DEST_ROUTE = 7) + DEST->DEST_ROUTE = 1; // TRY TO ACTIVATE FIRST + } + } + DEST++; + } +} + +VOID CHECKNEIGHBOUR(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * Msg) +{ + // MESSAGE RECEIVED ON LINK WITH NO ROUTE - SET ONE UP + + struct ROUTE * ROUTE; + struct PORTCONTROL * PORT = LINK->LINKPORT; + int Portno = PORT->PORTNUMBER; + + if (FindNeighbour(LINK->LINKCALL, Portno, &ROUTE) == 0) + { + if (ROUTE == 0) + goto L3CONN08; // TABLE FULL?? + + // FIRST MAKE SURE WE ARE ALLOWING NETWORK ACTIVITY ON THIS PORT + + if (PORT->PORTQUALITY == 0 && PORT->INP3ONLY == 0) + return; + + ROUTE->NEIGHBOUR_QUAL = PORT->PORTQUALITY; + ROUTE->INP3Node = PORT->INP3ONLY; + + memcpy(ROUTE->NEIGHBOUR_CALL, LINK->LINKCALL, 7); + + ROUTE->NEIGHBOUR_PORT = Portno; + ROUTE->NoKeepAlive = PORT->PortNoKeepAlive; + } + + // SET THIS AS ACTIVE LINK IF NONE PRESENT + + if (ROUTE->NEIGHBOUR_LINK == 0) + { + ROUTE->NEIGHBOUR_LINK = LINK; + ROUTE->NEIGHBOUR_PORT = Portno; + } + +L3CONN08: + + LINK->NEIGHBOUR = ROUTE; // SET LINK - NEIGHBOUR +} + +struct DEST_LIST * CHECKL3TABLES(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * Msg) +{ + // CHECK THAT FAR NODE IS IN 'NODES'. + // RETURNS POINTER TO NEIGHBOUR ENTRY + + struct ROUTE * ROUTE; + struct DEST_LIST * DEST; + int Qual; + + ROUTE = LINK->NEIGHBOUR; + + if (FindDestination(Msg->L3SRCE, &DEST)) + return DEST; // Ok + + if (DEST == NULL) + return NULL; // Tsble Full + + // ADD DESTINATION VIA NEIGHBOUR, UNLESS ON BLACK LIST + +#ifdef EXCLUDEBITS + + if (CheckExcludeList(Msg->L3SRCE) == 0) + return 0; + +#endif + + memcpy(DEST->DEST_CALL, Msg->L3SRCE, 7); + + NUMBEROFNODES++; + + // MAKE SURE NEIGHBOUR IS DEFINED FOR DESTINATION + + // IF NODE is NEIGHBOUR, THEN CAN USE NEIGHBOUR QUALITY, + // OTHERWISE WE DONT KNOW ROUTE, SO MUST SET QUAL TO 0 + + + Qual = 0; // DONT KNOW ROUTING, SO SET QUALITY TO ZERO + + PROCROUTES(DEST, ROUTE, Qual); // ADD NEIGHBOUR IF NOT PRESENT + + if (DEST->DEST_ROUTE == 0) + { + // MAKE CURRENT NEIGHBOUR ACTIVE + + int n = 0; + + DEST->DEST_ROUTE = 1; + + while (n < 3) + { + if (DEST->NRROUTE[n].ROUT_NEIGHBOUR == ROUTE) + break; + + DEST->DEST_ROUTE++; + n++; + } + + if (DEST->DEST_ROUTE > 3) + { + DEST->DEST_ROUTE = 1; // CURRENT NEIGHBOUR ISNT IN DEST LIST - SET TO USE BEST + return DEST; // Can't update OBS + } + } + + // REFRESH OBS COUNT + + if (DEST->DEST_ROUTE) + { + int Index = DEST->DEST_ROUTE -1; + + if (DEST->NRROUTE[Index].ROUT_OBSCOUNT & 0x80) // Locked: + return DEST; + + DEST->NRROUTE[Index].ROUT_OBSCOUNT = OBSINIT; + } + return DEST; +} + +VOID REFRESHROUTE(TRANSPORTENTRY * Session) +{ + // RESET OBS COUNT ON CURRENT ROUTE TO DEST FOR SESSION IN [EBX] + // CALLED WHEN INFO ACK RECEIVED, INDICATING ROUTE IS STILL OK + + struct DEST_LIST * DEST; + int Index; + + DEST = Session->L4TARGET.DEST; + + if (DEST == 0) + return; // No Dest ??? + + Index = DEST->DEST_ROUTE; + + if (Index == 0) + return; // NONE ACTIVE??? + + Index--; + + if (DEST->NRROUTE[Index].ROUT_OBSCOUNT & 0x80) + return; // Locked + + DEST->NRROUTE[Index].ROUT_OBSCOUNT = OBSINIT; +} + + diff --git a/.svn/pristine/16/1699d841a59761e7403099936a17146f4e764172.svn-base b/.svn/pristine/16/1699d841a59761e7403099936a17146f4e764172.svn-base new file mode 100644 index 0000000..1de771a --- /dev/null +++ b/.svn/pristine/16/1699d841a59761e7403099936a17146f4e764172.svn-base @@ -0,0 +1,1610 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// Interface to allow G8BPQ switch to use a KISS over TCP TNC for HF style use (ATTACH and single channel operation) + + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#include "cheaders.h" + + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + +#include "bpq32.h" + +#include "tncinfo.h" + +static int Socket_Data(int sock, int error, int eventcode); + +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); +BOOL SerialWriteCommBlock(struct TNCINFO * TNC); +void SerialCheckRX(struct TNCINFO * TNC); +int SerialSendData(struct TNCINFO * TNC, UCHAR * data, int txlen); +int SerialSendCommand(struct TNCINFO * TNC, UCHAR * data); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int KISSHFGetLine(char * buf); +int ProcessEscape(UCHAR * TXMsg); +VOID KISSHFProcessReceivedPacket(struct TNCINFO * TNC, int Channel); +static int KissEncode(struct TNCINFO * TNC, UCHAR * inbuff, UCHAR * outbuff, int len, int Channel); +int ConnecttoKISS(int port); +TRANSPORTENTRY * SetupNewSession(TRANSPORTENTRY * Session, char * Bufferptr); +BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls); +BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK); +VOID RESET2(struct _LINKTABLE * LINK); +VOID L2SENDXID(struct _LINKTABLE * LINK); +VOID SENDSABM(struct _LINKTABLE * LINK); + +static char ClassName[]="KISSSTATUS"; +static char WindowTitle[] = "KISSHF"; +static int RigControlRow = 165; + +#ifndef LINBPQ +#include +#endif + +extern int SemHeldByAPI; + +static RECT Rect; + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->DefaultMode = TNC->WL2KMode = 0; // Packet 1200 + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while (TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "UPDATEMAP", 9) == 0) + TNC->PktUpdateMap = TRUE; + else if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + } + return (TRUE); +} + +char * Config; +static char * ptr1, * ptr2; + +int KISSHFGetLine(char * buf) +{ +loop: + + if (ptr2 == NULL) + return 0; + + memcpy(buf, ptr1, ptr2 - ptr1 + 2); + buf[ptr2 - ptr1 + 2] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + if (buf[0] < 0x20) goto loop; + if (buf[0] == '#') goto loop; + if (buf[0] == ';') goto loop; + + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + buf[strlen(buf)] = 13; + + return 1; +} + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static time_t ltime; + +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // Serial mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int bytes,txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + int Channel = ((TNC->PortRecord->PORTCONTROL.CHANNELNUM - 1) & 15) << 4; + + if (TNC == NULL) + return 0; // Port not defined + + if (TNC->CONNECTED == 0 && TNC->CONNECTING == 0) + { + // Try to reopen every 30 secs + + if (fn > 3 && fn < 7) + goto ok; + + TNC->ReopenTimer++; + + if (TNC->ReopenTimer < 150) + return 0; + + TNC->ReopenTimer = 0; + + ConnecttoKISS(TNC->Port); + + return 0; + + SendInitScript(TNC); + + } +ok: + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + SerialCheckRX(TNC); + return 0; + + case 1: // poll + + STREAM = &TNC->Streams[0]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + SerialSendCommand(TNC, "DISCONNECT\r"); + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + // See if any frames for this port + + + STREAM = &TNC->Streams[0]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + STREAM->bytesTXed += txlen; + + bytes=SerialSendData(TNC, data, txlen); + WritetoTrace(TNC, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; + return -1; + } + } + + return (0); + + case 2: // send + + Stream = 0; + + if (!TNC->CONNECTED) + return 0; // Don't try if not connected + + STREAM = &TNC->Streams[0]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + // We may get KISS packets (UI or session related) or text commands such as RADIO, CONNECT + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN); + + TXMsg = &buff->L2DATA[0]; + + if (buff->PID != 240) // ax.25 address + { + txlen = KissEncode(TNC, &buff->PID, txbuff, txlen, Channel); + + // We need to che check for ackmode + + txlen = send(TNC->TCPSock, txbuff, txlen, 0); + return 1; + } + + TXMsg[txlen - 1] = 0; + strcpy(txbuff, TXMsg); + + if (STREAM->Attached == 0) + return 0; + + if (STREAM->Connected) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = txlen; + memcpy((UCHAR *)&buffptr->Data[0], &buff->L2DATA[0], txlen); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + // connected data + + return 1; + } + + // Process as Text Command + + if (_memicmp(txbuff, "D\r", 2) == 0 || _memicmp(txbuff, "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + if (_memicmp(txbuff, "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &txbuff[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + + if (_memicmp(txbuff, "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "KISSHF} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0) + { + if (buff->L2DATA[16] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "KISSHF} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + } + + if ((toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + || (toupper(buff->L2DATA[0]) == 'N' && buff->L2DATA[1] == ' ' && txlen > 2)) // Connect + { + // Connect Command. Pass to L2 code to start session + + char * ptr = strchr(&buff->L2DATA[2], 13); + TRANSPORTENTRY * NewSess = L4TABLE; + struct _LINKTABLE * LINK; + TRANSPORTENTRY * Session = TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK; + struct PORTCONTROL * PORT = &TNC->PortRecord->PORTCONTROL; + + UCHAR axcalls[64]; + UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted + int Stay = 0, Spy = 0, CQFLAG = 0, n; + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + if (DecodeCallString(&buff->L2DATA[2], &Stay, &Spy, &axcalls[0]) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "KISSHF} Invalid Call\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + + // Code copied from cmdc00 + + // Get Session Entry for Downlink + + NewSess = SetupNewSession(Session, NULL); + + if (NewSess == NULL) + return 0; + + NewSess->L4CIRCUITTYPE = L2LINK + DOWNLINK; + + // FORMAT LINK TABLE ENTRY FOR THIS CONNECTION + + memcpy(Session->L4USER, NewSess->L4USER, 7); + memcpy(ourcall, NewSess->L4USER, 7); + + // SSID SWAP TEST - LEAVE ALONE FOR HOST or Pactor like (unless UZ7HO) + + if ((Session->L4CIRCUITTYPE & BPQHOST))// host + goto noFlip3; + + if ((Session->L4CIRCUITTYPE & PACTOR)) + { + // incoming is Pactorlike - see if UZ7HO + + if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0) + goto noFlip3; + + if (Session->L4TARGET.EXTPORT->MAXHOSTMODESESSIONS < 2) // Not multisession + goto noFlip3; + + ourcall[6] ^= 0x1e; // UZ7HO Uplink - flip + } + else + + // Must be L2 uplink - flip + + ourcall[6] ^= 0x1e; // Flip SSID + +noFlip3: + + // SET UP NEW SESSION (OR RESET EXISTING ONE) + + FindLink(axcalls, ourcall, TNC->Port, &LINK); + + if (LINK == NULL) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "KISSHF} Sorry - System Tables Full\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + + memcpy(LINK->LINKCALL, axcalls, 7); + memcpy(LINK->OURCALL, ourcall, 7); + + LINK->LINKPORT = PORT; + + LINK->L2TIME = PORT->PORTT1; + + // Copy Digis + + n = 7; + ptr = &LINK->DIGIS[0]; + + while (axcalls[n]) + { + memcpy(ptr, &axcalls[n], 7); + n += 7; + ptr += 7; + + LINK->L2TIME += 2 * PORT->PORTT1; // ADJUST TIMER VALUE FOR 1 DIGI + } + + LINK->LINKTYPE = 2; // DOWNLINK + LINK->LINKWINDOW = PORT->PORTWINDOW; + + RESET2(LINK); // RESET ALL FLAGS + + if (toupper(buff->L2DATA[0]) == 'N' && SUPPORT2point2) + LINK->L2STATE = 1; // New (2.2) send XID + else + LINK->L2STATE = 2; // Send SABM + + LINK->CIRCUITPOINTER = NewSess; + + NewSess->L4TARGET.LINK = LINK; + + if (PORT->PORTPACLEN) + NewSess->SESSPACLEN = Session->SESSPACLEN = PORT->PORTPACLEN; + + STREAM->Connecting = TRUE; + + if (CQFLAG == 0) // if a CQ CALL DONT SEND SABM + { + if (LINK->L2STATE == 1) + L2SENDXID(LINK); + else + SENDSABM(LINK); + } + return 0; + } + + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + return ((TNC->CONNECTED) << 8 | TNC->Streams[0].Disconnecting << 15); // OK + + case 4: // reinit7 + + return 0; + + case 5: // Close + + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on KISSHF"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + SerialSendCommand(TNC, "CONOK OFF"); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + SerialSendCommand(TNC, "CONOK ON"); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID KISSHFReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[64]; + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + ReleaseOtherPorts(TNC); +} + +VOID KISSHFSuspendPort(struct TNCINFO * TNC, struct TNCINFO * THISTNC) +{ + TNC->PortRecord->PORTCONTROL.PortSuspended = 1; + strcpy(TNC->WEB_TNCSTATE, "Interlocked"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); +} + +VOID KISSHFReleasePort(struct TNCINFO * TNC) +{ + TNC->PortRecord->PORTCONTROL.PortSuspended = 0; + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "KISSHF Status" + "

KISSHF Status" + "

", + TNC->Port); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); +// Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); +// Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); +// Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); +// Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + + +VOID * KISSHFExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + struct PORTCONTROL * PORT = &PortEntry->PORTCONTROL; + + port = PORT->PORTNUMBER; + + if (TNCInfo[port]) // If restarting, free old config + free(TNCInfo[port]); + + TNC = TNCInfo[port] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (PortConfig[port]) // May not have config + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->PortRecord = PortEntry; + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_KISSHF; + TNC->ARDOPBuffer = malloc(8192); + + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->PORTCONTROL.USERS = 1; // Max 1 Session + + TNC->PacketChannels = 0; + + PortEntry->MAXHOSTMODESESSIONS = 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = KISSHFSuspendPort; + TNC->ReleasePortProc = KISSHFReleasePort; + +// PortEntry->PORTCONTROL.PORTSTARTCODE = KISSStartPort; +// PortEntry->PORTCONTROL.PORTSTOPCODE = KISSStopPort; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 700, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,40,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + Consoleprintf("KISSHF Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port)); + + ConnecttoKISS(port); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[Stream].BytesOutstanding == 0) + SerialSendCommand(TNC, "DISCONNECT\r"); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + SerialSendCommand(TNC, "DISCONNECT\r"); +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + KISSHFReleaseTNC(TNC); + } +} + + +VOID KISSThread(void * portptr); + +int ConnecttoKISS(int port) +{ + _beginthread(KISSThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID KISSThread(void * portptr) +{ + // Opens socket and looks for data on control and data sockets. + + int port = (int)(size_t)portptr; + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + char * ptr1; + char * ptr2; + UINT * buffptr; + int Channel = ((TNC->PortRecord->PORTCONTROL.CHANNELNUM - 1) & 15) << 4; + + if (TNC->HostName == NULL) + return; + + TNC->BusyFlags = 0; + + TNC->CONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +#ifdef WIN32 + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + if (TNC->PID == 0) + { + TNC->CONNECTING = FALSE; + return; // Not listening so no point trying to connect + } + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + HANDLE hProc; + char ExeName[256] = ""; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + } +#endif + +// // If we started the TNC make sure it is still running. + +// if (!IsProcess(TNC->PID)) +// { +// RestartTNC(TNC); +// Sleep(3000); +// } + + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + +// closesocket(TNC->TCPSock); +// closesocket(TNC->TCPDataSock); + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for KISSHF socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for KISSHF socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; // so V4 display will be updated + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + + // Send INIT script + + // VARA needs each command in a separate send + + ptr1 = &TNC->InitScript[0]; + + // We should wait for first RDY. Cheat by queueing a null command + + GetSemaphore(&Semaphore, 52); + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + + + while (ptr1 && ptr1[0]) + { + unsigned char c; + + ptr2 = strchr(ptr1, 13); + + if (ptr2) + { + c = *(ptr2 + 1); // Save next char + *(ptr2 + 1) = 0; // Terminate string + } +// VARASendCommand(TNC, ptr1, TRUE); + + if (ptr2) + *(1 + ptr2++) = c; // Put char back + + ptr1 = ptr2; + } + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to KISS TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + FreeSemaphore(&Semaphore); + + sprintf(Msg, "Connected to KISS TNC Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + + #ifndef LINBPQ +// FreeSemaphore(&Semaphore); + Sleep(1000); // Give VARA time to update Window title +// EnumWindows(EnumVARAWindowsProc, (LPARAM)TNC); +// GetSemaphore(&Semaphore, 52); +#endif + + + while (TNC->CONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TNC->TCPSock,&readfs); + FD_SET(TNC->TCPSock,&errorfs); + + timeout.tv_sec = 600; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TNC->TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("KISSHF Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + KISSHFProcessReceivedPacket(TNC, Channel); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { +Lost: + sprintf(Msg, "KISSHF Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + TNC->TCPSock = 0; + return; + } + } + } + sprintf(Msg, "KISSHF Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + +static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i,txptr=0; + UCHAR c; + + for (i=0;iPortRecord->PORTCONTROL.KISSFLAGS & ACKMODE) + { + UCHAR * ptr = inbuff - MSGHDDRLEN; + PMESSAGE Buffer = (PMESSAGE)ptr; + + if (Buffer->Linkptr) // Frame Needs ACK + { + UINT ACKWORD = (UINT)(Buffer->Linkptr - LINKS); + outbuff[1] |= 0x0c; // ACK OPCODE + outbuff[2] = ACKWORD & 0xff; + outbuff[3] = (ACKWORD >> 8) &0xff; + + Buffer->Linkptr->L2TIMER = ONEMINUTE; // Extend timeout + + txptr = 4; + + // have to reset flag so trace doesnt clear it + + Buffer->Linkptr = 0; + } + } + + for (i=0;iInputLen > 8000) // Shouldnt have packets longer than this + TNC->InputLen=0; + + InputLen = recv(TNC->TCPSock, &TNC->ARDOPBuffer[TNC->InputLen], 8192 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + int err = GetLastError(); + + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->InputLen += InputLen; + + // Extract and decode KISS frames + + ptr = memchr(TNC->ARDOPBuffer + 1, FEND, TNC->InputLen - 1); // Ignore leading FEND + + while (ptr) // FEND in buffer + { + ptr++; + + MsgLen = ptr - TNC->ARDOPBuffer; + + if (MsgLen > 360) + { + TNC->InputLen = 0; + return; + } + + TNC->InputLen -= MsgLen; + + if (MsgLen > 1) + { + PMESSAGE Buff; + + if ((TNC->ARDOPBuffer[1] & 0xf0) != Channel) + goto ignoreit; + + Buff = GetBuff(); + + MsgLen = KissDecode(TNC->ARDOPBuffer, Buffer, MsgLen); + + // See if ACKMODE ACK + + if ((Buffer[1] & 0xf) == 12) + { + //Ackmode + + struct _LINKTABLE * LINK; + int ACKWORD = Buffer[2] | Buffer[3] << 8; + + if (ACKWORD < MAXLINKS) + { + LINK = LINKS + ACKWORD; + + if (LINK->L2TIMER) + LINK->L2TIMER = LINK->L2TIME; + } + + ReleaseBuffer(Buff); + goto ignoreit; + } + + if ((Buffer[1] & 0xf) != 0) + { + ReleaseBuffer(Buff); + goto ignoreit; // Not data + } + + // we dont need the FENDS or control byte + + MsgLen -= 3; + + if (Buff) + { + memcpy(&Buff->DEST, &Buffer[2], MsgLen); + MsgLen += (3 + sizeof(void *)); + + PutLengthinBuffer((PDATAMESSAGE)Buff, MsgLen); // Needed for arm5 portability + + C_Q_ADD(&TNC->PortRecord->PORTCONTROL.PORTRX_Q, (UINT *)Buff); + } + } + +ignoreit: + + if (TNC->InputLen == 0) + return; + + memmove(TNC->ARDOPBuffer, ptr, TNC->InputLen); + ptr = memchr(TNC->ARDOPBuffer + 1, FEND, TNC->InputLen - 1); // Ignore leading FEND + } + +} + + + +/* + +// we dont need the control byte + +len --; + +if (Buffer) +{ +memcpy(&Buffer->DEST, &Port->RXMSG[1], len); +len += (3 + sizeof(void *)); + +PutLengthinBuffer((PDATAMESSAGE)Buffer, len); // Needed for arm5 portability + +C_Q_ADD(TNC->PortRecord->PORTCONTROL.PORTRX_Q, (UINT *)Buffer); +} + +*/ + +// TNC->InputLen -= MsgLen; +// goto loop; + + + + +void AttachKISSHF(struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + // SABM on HFKISS port. L2 code will accepr call and connect to appl if necessary, but + // need to attach the port + + char Call[16] = ""; + char OrigCall[16] = ""; + struct TNCINFO * TNC = PORT->TNC; + struct WL2KInfo * WL2K = TNC->WL2K; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + TRANSPORTENTRY * SESS; + struct TNCINFO * TNC = PORT->TNC; + + // Incoming Connect + + Call[ConvFromAX25(Buffer->DEST, Call)] = 0; + OrigCall[ConvFromAX25(Buffer->ORIGIN, OrigCall)] = 0; + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + ProcessIncommingConnectEx(TNC, Call, 0, FALSE, FALSE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + SESS->Mode = TNC->WL2KMode; + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", OrigCall, Call, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + if (SESS->Frequency == 1500) + { + // try to get from WL2K record + + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", OrigCall, Call); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } +} + +void DetachKISSHF(struct PORTCONTROL * PORT) +{ + // L2 Link Closed. Detach. + + struct TNCINFO * TNC = PORT->TNC; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + if (STREAM->Attached) + STREAM->ReportDISC = 10; // Tell Node but give time for error message to display + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; + +} + +void KISSHFConnected(struct PORTCONTROL * PORT, struct _LINKTABLE * LINK) +{ + // UA received when connecting + + struct TNCINFO * TNC = PORT->TNC; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct WL2KInfo * WL2K = TNC->WL2K; + + TRANSPORTENTRY * SESS; + char Call[16] = ""; + char OrigCall[16] = ""; + + + if (STREAM->Connecting) + { + STREAM->Connecting = FALSE; + STREAM->Connected = TRUE; + + Call[ConvFromAX25(LINK->LINKCALL, Call)] = 0; + OrigCall[ConvFromAX25(LINK->OURCALL, OrigCall)] = 0; + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + SESS->Mode = TNC->WL2KMode; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", OrigCall, Call, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + + if (SESS->Frequency == 1500) + { + // try to get from WL2K record + + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", OrigCall, Call); + + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } +} + + + + + diff --git a/.svn/pristine/16/16ba689d0fad7f42f44a09f68afaa938d90fbc93.svn-base b/.svn/pristine/16/16ba689d0fad7f42f44a09f68afaa938d90fbc93.svn-base new file mode 100644 index 0000000..a0babb3 --- /dev/null +++ b/.svn/pristine/16/16ba689d0fad7f42f44a09f68afaa938d90fbc93.svn-base @@ -0,0 +1,1287 @@ +/* +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 +*/ + +// +// Module to provide HDLC Card (DRSI, Baycom etc) support for +// G8BPQ switch in a 32bit environment + +// +// Win95 - Uses BPQHDLC.VXD to drive card +// NT -Uses BPQHDLC.DRV to drive card +// +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + +#include "cheaders.h" +#include "bpq32.h" + + +extern VOID * TRACE_Q; + +#ifdef WIN32 + +_CRT_OBSOLETE(GetVersionEx) errno_t __cdecl _get_winmajor(__out unsigned int * _Value); +_CRT_OBSOLETE(GetVersionEx) errno_t __cdecl _get_winminor(__out unsigned int * _Value); + + +#define FILE_DEVICE_BPQHDLC 0x00008421 + +#define IOCTL_BPQHDLC_SEND CTL_CODE(FILE_DEVICE_BPQHDLC,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQHDLC_POLL CTL_CODE(FILE_DEVICE_BPQHDLC,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQHDLC_TIMER CTL_CODE(FILE_DEVICE_BPQHDLC,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQHDLC_ADDCHANNEL CTL_CODE(FILE_DEVICE_BPQHDLC,0x803,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQHDLC_CHECKTX CTL_CODE(FILE_DEVICE_BPQHDLC,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQHDLC_IOREAD CTL_CODE(FILE_DEVICE_BPQHDLC,0x805,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQHDLC_IOWRITE CTL_CODE(FILE_DEVICE_BPQHDLC,0x806,METHOD_BUFFERED,FILE_ANY_ACCESS) + + +VOID __cdecl Debugprintf(const char * format, ...); + + +// Info to pass to Kernel HDLC Driver to define an SCC Subchannel + +typedef struct _BPQHDLC_ADDCHANNEL_INPUT { + + ULONG IOBASE; // IO Base Address + ULONG IOLEN; // Number of Addresses + UCHAR Interrupt; // Interrupt + UCHAR Channel; + + ULONG ASIOC; // A CHAN ADDRESSES + ULONG SIO; // OUR ADDRESSES (COULD BE A OR B) + ULONG SIOC; // Our Control Channel + ULONG BSIOC; // B CHAN CONTROL + + VOID * OtherChannel; // Kernel Channel record for first channel if this is 2nd channel + + UCHAR SOFTDCDFLAG; // Use SoftDCD flag + + int TXBRG; // FOR CARDS WITHOUT /32 DIVIDER + int RXBRG; + + UCHAR WR10; // NRZ/NRZI FLAG + + USHORT TXDELAY; //TX KEYUP DELAY TIMER + UCHAR PERSISTANCE; + +} BPQHDLC_ADDCHANNEL_INPUT, *PBPQHDLC_ADDCHANNEL_INPUT; + + +DWORD n; + +HANDLE hDevice=0; +BYTE bOutput[4]=" "; +DWORD cb=0; +int fResult=0; + +BOOL Win98 = FALSE; + + +extern int QCOUNT; +int Init98(HDLCDATA * PORTVEC); +int Init2K(HDLCDATA * PORTVEC); +int INITPORT(PHDLCDATA PORTVEC); + + + +int HDLCRX2K(PHDLCDATA PORTVEC, UCHAR * buff) +{ + ULONG Param; + DWORD len=0; + + if (hDevice == 0) + return (0); + + + if (PORTVEC->DRIVERPORTTABLE == 0) return 0; + + memcpy(&Param, &PORTVEC->DRIVERPORTTABLE,4); + + fResult = DeviceIoControl( + hDevice, // device handle + (Win98) ? 'G' : IOCTL_BPQHDLC_POLL, // control code + &Param, (Win98) ? (rand() & 0xff) : 4, //Input Params + buff,360,&len, // output parameters + 0); + + return (len); +} + +int HDLCTIMER2K(PHDLCDATA PORTVEC) +{ + DWORD len=0; + + if (hDevice == 0) + return (0); + + if (PORTVEC->DRIVERPORTTABLE == 0) return 0; + + fResult = DeviceIoControl( + hDevice, // device handle + (Win98) ? 'T' : IOCTL_BPQHDLC_TIMER, // control code + &PORTVEC->DRIVERPORTTABLE,4, //Input Params + 0,0,&len, // output parameters + 0); + + return (0); +} + int HDLCTXCHECK2K(PHDLCDATA PORTVEC) + { + DWORD Buff; + DWORD len=0; + + if (hDevice == 0) + return (0); + + if (Win98) + return 0; + + if (PORTVEC->DRIVERPORTTABLE == 0) return 0; + + fResult = DeviceIoControl( + hDevice, // device handle + IOCTL_BPQHDLC_CHECKTX, // control code + &PORTVEC->DRIVERPORTTABLE,4, //Input Params + &Buff,4,&len, // output parameters + 0); + + return (Buff); +} + + + +int HDLCTX2K(PHDLCDATA PORTVEC,UCHAR * buff) +{ + DWORD txlen=0; + + if (hDevice == 0) + return (0); + + txlen=(buff[6]<<8) + buff[5]; + + memcpy(buff,&PORTVEC->DRIVERPORTTABLE,4); + + fResult = DeviceIoControl( + hDevice, // device handle + (Win98) ? 'S' : IOCTL_BPQHDLC_SEND, // control code + // control code + buff,txlen, // input parameters + NULL,0,&cb, // output parameters + 0); + + return (0); +} + + +int HDLCCLOSE(PHDLCDATA PORTVEC) +{ + if (hDevice) + { + CloseHandle(hDevice); + hDevice = 0; + } + + return 0; +} + +int HDLCRX98(PHDLCDATA PORTVEC, UCHAR * buff) +{ + DWORD len=0; + + if (hDevice == 0) + return (0); + + fResult = DeviceIoControl( + hDevice, // device handle + 'G', // control code + PORTVEC->DRIVERPORTTABLE,rand() & 0xff, //Input Params + buff,360,&len, // output parameters + 0); + + return (len); +} + +int HDLCTIMER98(PHDLCDATA PORTVEC) +{ + DWORD len=0; + + if (hDevice == 0) + return (0); + + fResult = DeviceIoControl( + hDevice, // device handle + 'T', // control code + PORTVEC->DRIVERPORTTABLE,4, //Input Params + 0,0,&len, // output parameters + 0); + + return (0); +} + + int HDLCTXCHECK98(PHDLCDATA PORTVEC) + { + return 0; + } + +int HDLCTX98(PHDLCDATA PORTVEC,UCHAR * buff) +{ + DWORD txlen=0; + + if (hDevice == 0) + return (0); + + txlen=(buff[6]<<8) + buff[5]; + + memcpy(buff,&PORTVEC->DRIVERPORTTABLE,4); + + fResult = DeviceIoControl( + hDevice, // device handle + 'S', // control code + buff,txlen,// input parameters + NULL,0,&cb, // output parameters + 0); + + return (0); +} + +int IntHDLCRX(PHDLCDATA PORTVEC, UCHAR * buff) +{ + if (Win98) + return HDLCRX98(PORTVEC, buff); + else + return HDLCRX2K(PORTVEC, buff); +} + +VOID HDLCRX(PHDLCDATA PORTVEC) +{ + struct _MESSAGE * Message; + int Len; + struct PORTCONTROL * PORT = (struct PORTCONTROL *)PORTVEC; + + if (QCOUNT < 10) + return; + + Message = GetBuff(); + + if (Message == NULL) + return; + + Len = IntHDLCRX(PORTVEC, (UCHAR *)Message); + + if (Len == 0) + { + ReleaseBuffer((UINT *)Message); + return; + } + + C_Q_ADD(&PORT->PORTRX_Q, (UINT *)Message); + + return; +} + +int HDLCTIMER(PHDLCDATA PORTVEC) +{ + if (Win98) + return HDLCTIMER98(PORTVEC); + else + return HDLCTIMER2K(PORTVEC); +} + +int HDLCTXCHECK(PHDLCDATA PORTVEC) +{ + if (Win98) + return HDLCTXCHECK98(PORTVEC); + else + return HDLCTXCHECK2K(PORTVEC); +} + + + +VOID HDLCTX(PHDLCDATA PORTVEC, PMESSAGE Buffer) +{ + struct _LINKTABLE * LINK; + + LINK = Buffer->Linkptr; + + if (LINK) + { + if (LINK->L2TIMER) + LINK->L2TIMER = LINK->L2TIME; + + Buffer->Linkptr = 0; // CLEAR FLAG FROM BUFFER + } + + + if (Win98) + HDLCTX98(PORTVEC, (UCHAR *)Buffer); + else + HDLCTX2K(PORTVEC, (UCHAR *)Buffer); + + C_Q_ADD(&TRACE_Q, (UINT *)Buffer); + +} + +int HDLCINIT(HDLCDATA * PORTVEC) +{ + int WinVer = 0x0602, WinMinor = 0x02; + + WritetoConsole("HDLC\n"); + +#pragma warning(push) +#pragma warning(disable : 4996) + +#ifndef _winver + +#define _winmajor 6 +#define _winminor 0 + +#endif + +#pragma warning(pop) + + if (WinVer >= 5) // Win 2000 or above + return Init2K(PORTVEC); + else + { + Init98(PORTVEC); + OutputDebugString("HDLC Win98 Return from Init98\n"); + return 0; + } + +} + +int Init98(HDLCDATA * PORTVEC) +{ + char msg[255]; + int err; + + Win98 = TRUE; + + OutputDebugString("Init HDLC 98\n"); + + // + // Open HDLC Driver, send send config params + // + + if (hDevice == 0) // Not already loaded + { + // + // Load VXD + // + + hDevice = CreateFile("\\\\.\\BPQHDLC.VXD", + 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL); + + if (hDevice == INVALID_HANDLE_VALUE) + { + hDevice=0; + + err=GetLastError(); + + sprintf(msg,"Error loading Driver \\\\.\\BPQHDLC.VXD - Error code %d\n",err); + OutputDebugString(msg); + + MessageBox(NULL,msg,"BPQ32",MB_ICONSTOP); + + WritetoConsole("Initialisation Failed"); + + return (FALSE); + } + +// OutputDebugString("Calling GetVersion\n"); + +// fResult = DeviceIoControl( +// hDevice, // device handle +// 10,//DIOC_GETVERSION, // control code +// NULL,0,// input parameters +// bOutput, 4, &cb, // output parameters +// 0); + + srand( (unsigned)time( NULL ) ); //Prime random no generator + } + + OutputDebugString("Calling Initialize\n"); + + // + // Initialize Driver for this card and channel + // + + fResult = DeviceIoControl( + hDevice, // device handle + 'I', // control code + PORTVEC, sizeof (struct PORTCONTROL), // input parameters + bOutput, 4, &cb, // output parameters + 0); + + + memcpy(&PORTVEC->DRIVERPORTTABLE,bOutput,4); + + Debugprintf("BPQ32 HDLC Driver Table ADDR %X", PORTVEC->DRIVERPORTTABLE); + + OutputDebugString("Initialize Returned\n"); + + return (TRUE); + +} + +int PC120INIT(PHDLCDATA PORTVEC) +{ + return (HDLCINIT(PORTVEC)); +} + +int DRSIINIT(PHDLCDATA PORTVEC) +{ + return (HDLCINIT(PORTVEC)); +} + +int TOSHINIT(PHDLCDATA PORTVEC) +{ + return (HDLCINIT(PORTVEC)); +} + +int RLC100INIT(PHDLCDATA PORTVEC) +{ + return (HDLCINIT(PORTVEC)); +} + +int BAYCOMINIT(PHDLCDATA PORTVEC) +{ + return (HDLCINIT(PORTVEC)); +} + +int PA0INIT(PHDLCDATA PORTVEC) // 14 PA0HZP OPTO-SCC +{ + return (HDLCINIT(PORTVEC)); +} + +// W2K/XP Routines + +#define IOTXCA VECTOR[0] +#define IOTXEA VECTOR[1] +#define IORXCA VECTOR[2] +#define IORXEA VECTOR[3] + +#define SIOR READ_PORT_UCHAR(PORTVEC->SIO) +#define SIOW(A) WRITE_PORT_UCHAR(PORTVEC->SIO,A) + +#define SIOCR READ_PORT_UCHAR(PORTVEC->SIOC) +#define SIOCW(A) WRITE_PORT_UCHAR(PORTVEC->SIOC, A) + +//#define SETRVEC PORTVEC->IORXCA = +//#define SETTVEC PORTVEC->IOTXCA = + + +int CLOCKFREQ = 76800; // 4,915,200 HZ /(32*2) + +int TOSHCLOCKFREQ = 57600; + +UCHAR SDLCCMD[] = { + 0,0, + 2,0, // BASE VECTOR + 4,0x20, // SDLC MODE + 3,0xc8, // 8BIT, CRC ENABLE, RX DISABLED + + 7,0x7e, // STANDARD FLAGS + 1,0x13, // INT ON ALL RX, TX INT EN, EXT INT EN + 5,0xe1, // DTR, 8BIT, SDLC CRC,TX CRC EN + + 10,0xa0, // CRC PRESET TO 1 + + 9,0x09, // ENABLE INTS + + 11,0x66, // NO XTAL, RXC = DPLL, TXC = RTXC, TRXC = BRG (NEEDS /32 BETWEEN TRXC AND RTXC) + + 14,0x83, + 14,0x23, + 15,0xc0 // EXT INT ONLY ON TX UND AND ABORT RX +}; + +#define SDLCLEN 26 + +UCHAR TOSHR11 = 0x68; // NO XTAL, RXC = DPLL, TXC = DPLL, NO CLK OUTPUT + +UCHAR CIOPARAMS[] = { + + 0x2B,0xFF, // B DIRECTION - ALL IN + 0x23,0xFF, // A DIRECTION - ALL IN + + 0x1D,0x0E2, // C/T 2 MODE - CONT, EXT IN, EXT O, SQUARE + 0x1C,0x0E2, // C/T 1 MODE "" + + 0x19,0x10, // C/T 2 LSB - 16 = /32 FOR SQUARE WAVE + 0x18,0, // MSB + + 0x17,0x10, // C/T 1 LSB + 0x16,0, // MSB + + 0x0B,0x04, // CT2 "" - GATE + 0x0A,0x04, // CT1 "" - GATE + + 0x06,0x0F, // PORT C DIRECTION - INPUTS + + 1,0x84, // ENABLE PORTS A AND B + + 0,0 // INTERRUPT CONTROL +}; + +#define CIOLEN 26 + + +VOID WRITE_PORT_UCHAR(UINT Port, UINT Value) +{ + ULONG buff[3]; + + buff[0] = Port; + buff[1] = Value; + + fResult = DeviceIoControl( + hDevice, // device handle + IOCTL_BPQHDLC_IOWRITE, // control code + buff, 8, // input parameters + NULL,0,&cb, // output parameters + 0); +} + +UCHAR READ_PORT_UCHAR(ULONG Port) +{ + ULONG buff[3]; + + buff[0] = Port; + + fResult = DeviceIoControl( + hDevice, // device handle + IOCTL_BPQHDLC_IOREAD, // control code + buff, 4, // input parameters + buff, 4,&cb, // output parameters + 0); + + Debugprintf("BPQ32 HDLC READ_PORT_UCHAR Returned %X", LOBYTE(buff[0])); + + return LOBYTE(buff[0]); + +} + +int Init2K(HDLCDATA * PORTVEC) +{ + char msg[255]; + int err; + + if (hDevice == 0) // Not already loaded + { + // + // Open HDLC Driver + // + + hDevice = CreateFile( + "\\\\.\\BPQHDLC", // Open the Device "file" + GENERIC_WRITE, + FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + + + if (hDevice == INVALID_HANDLE_VALUE) + { + hDevice=0; + + err=GetLastError(); + + sprintf(msg,"Error Opening Driver \\device\\BPQHDLC - Error code %d\n", err); + OutputDebugString(msg); + + WritetoConsole(msg); + + return (FALSE); + } + } + + INITPORT(PORTVEC); + + return 0; +} + +PHDLCDATA See_if_First_On_Card(PHDLCDATA PORTVEC) +{ +// SEE IF ANOTHER PORT IS ALREADY USING THE OTHER CHANNEL ON THIS CARD + int i; + + PHDLCDATA PreviousPort = (PHDLCDATA)PORTTABLE; + + for (i = 0; i < NUMBEROFPORTS; i++) + { + if (PORTVEC == PreviousPort) + { + // NONE BEFORE OURS + + return NULL; + } + + if (PORTVEC->PORTCONTROL.IOBASE == PreviousPort->PORTCONTROL.IOBASE) + { + // ENSURE ENTRIES ARE FOR DIFFERENT CHANNELS + + if (PORTVEC->PORTCONTROL.CHANNELNUM == PreviousPort->PORTCONTROL.CHANNELNUM) + + // CHANNEL DEFINITION ERROR + + return (PHDLCDATA) -1; + else + return PreviousPort; + } + + PreviousPort = (PHDLCDATA)PreviousPort->PORTCONTROL.PORTPOINTER; + } + + return NULL; // FLAG NOT FOUND + + + + +} + +VOID INITPART2(PHDLCDATA PORTVEC, USHORT SCCOffset, PHDLCDATA PreviousPort) +{ +// SCCOffset is address of SCC relative to Card Base Address + + int i; + USHORT SCCBase=PORTVEC->PORTCONTROL.IOBASE + SCCOffset; + int BRG; + +// SET UP ADDRESS LIST - THIS PATH FOR CARDS WITH 'NORMAL' +// ADDRESSING - C/D=A0, A/B=A1, SO ORDER IS BCTRL BDATA ACTRL ADATA +// OR DE, WHICH USES WORD ADDRESSES C/D=A1, A/B=A2 + + PORTVEC->BSIOC = SCCBase; // B CHAN ADDR + PORTVEC->ASIOC = SCCBase+2; // A CHAN ADDR + +// SEE WHICH CHANNEL TO USE + + if (PORTVEC->PORTCONTROL.CHANNELNUM == 'A') + { + PORTVEC->A_PTR = PORTVEC; // POINT TO OUR ENTRY + PORTVEC->SIOC = SCCBase+2; + PORTVEC->SIO = SCCBase+3; // DATA 1 ABOVE CONTROL + + if (PreviousPort) // Another Channel is first on Card + PORTVEC->B_PTR = PreviousPort; // CROSSLINK CHANNELS + } + else + { + // MUST BE B - CHECKED EARLIER + + PORTVEC->B_PTR = PORTVEC; // POINT TO OUR ENTRY + PORTVEC->SIOC = SCCBase; + PORTVEC->SIO = SCCBase+1; // DATA 1 ABOVE CONTROL + + if (PreviousPort) // Another Channel is first on Card + PORTVEC->A_PTR = PreviousPort; // CROSSLINK CHANNELS + + } + +// INITIALISE COMMS CHIP + + if (PreviousPort == 0) // OTHER CHAN ALREADY SET UP? + { + // DO A HARD RESET OF THE SCC + + WRITE_PORT_UCHAR(PORTVEC->ASIOC, 0); // Make Sure WR0 + WRITE_PORT_UCHAR(PORTVEC->ASIOC, 0); + + WRITE_PORT_UCHAR(PORTVEC->ASIOC, 9); // WR9 + + WRITE_PORT_UCHAR(PORTVEC->ASIOC, 0xC0); // Hard Reset + + Sleep(2); + + } + + for (i=0; i< SDLCLEN; i++) + { + WRITE_PORT_UCHAR(PORTVEC->SIOC, SDLCCMD[i]); + } + + PORTVEC->WR10 = 0x20; // NRZI + +// SET UP BRG FOR REQUIRED SPEED + + if (PORTVEC->PORTCONTROL.BAUDRATE == 0) + { + // SET EXTERNAL CLOCK + + SIOCW(11); // WR11 + SIOCW(0x20); // RX = TRXC TX = RTXC + + return; + } + + if (PORTVEC->PORTCONTROL.PORTTYPE == 12) // RLC 400 USES SAME CLOCK AS TOSH + + BRG = TOSHCLOCKFREQ; + else + BRG = CLOCKFREQ; + + BRG=(BRG/PORTVEC->PORTCONTROL.BAUDRATE)-2; + + SIOCW(12); // Select WR12 + SIOCW(BRG & 0xff); // SET LSB + SIOCW(13); // Select WR13 + SIOCW(BRG >> 8); // SET MSB + + return; +} + +VOID INITCIO(PHDLCDATA PORTVEC) +{ +// INITIALISE CIO - DRSI ONLY + int i; + ULONG CIOAddr = PORTVEC->PORTCONTROL.IOBASE + 7; // TO CIO PORT + + READ_PORT_UCHAR(CIOAddr); + WRITE_PORT_UCHAR(CIOAddr, 0); + + READ_PORT_UCHAR(CIOAddr); + WRITE_PORT_UCHAR(CIOAddr, 0); + + WRITE_PORT_UCHAR(CIOAddr, 1); // FORCE RESET + WRITE_PORT_UCHAR(CIOAddr, 0); // CLEAR RESET + + for (i=0; i< CIOLEN; i++) + { + WRITE_PORT_UCHAR(CIOAddr, CIOPARAMS[i] ); + } + + return; + +} +VOID STARTCIO(PHDLCDATA PORTVEC) +{ + USHORT CIOAddr = PORTVEC->PORTCONTROL.IOBASE + 7; // TO CIO PORT + UCHAR Reg; + +// B CHANNEL + +// SET COUNTER OUTPUT BIT ACTIVE + + WRITE_PORT_UCHAR(CIOAddr, 0x2B); // PORT B DIRECTION + Reg = READ_PORT_UCHAR(CIOAddr); + + if (PORTVEC->PORTCONTROL.CHANNELNUM == 'B') + { + + Reg &= 0xEF; // SET BIT 4 AS OUTPUT + + WRITE_PORT_UCHAR(CIOAddr, 0x2B); // PORT B DIRECTION + WRITE_PORT_UCHAR(CIOAddr, Reg); // UPDATE PORT B DIRECTION + + // ENABLE COUNTER + + WRITE_PORT_UCHAR(CIOAddr,1); // MASTER CONFIG + Reg = READ_PORT_UCHAR(CIOAddr); // GET IT + + Reg |= 0x40; // ENABLE CT1 + + WRITE_PORT_UCHAR(CIOAddr,1); // MASTER CONFIG + WRITE_PORT_UCHAR(CIOAddr, Reg); // Set it + + // START COUNTER + + WRITE_PORT_UCHAR(CIOAddr,0x0A); // CT1 CONTROL + WRITE_PORT_UCHAR(CIOAddr,6); // START CT1 + + return; + } + + Reg &= 0xFE; // SET BIT 0 AS OUTPUT + + WRITE_PORT_UCHAR(CIOAddr, 0x2B); // PORT B DIRECTION + WRITE_PORT_UCHAR(CIOAddr, Reg); // UPDATE PORT B DIRECTION + + // ENABLE COUNTER + + WRITE_PORT_UCHAR(CIOAddr,1); // MASTER CONFIG + Reg = READ_PORT_UCHAR(CIOAddr); // GET IT + + Reg |= 0x20; // ENABLE CT2 + + WRITE_PORT_UCHAR(CIOAddr,1); // MASTER CONFIG + WRITE_PORT_UCHAR(CIOAddr, Reg); // Set it + +// START COUNTER + + WRITE_PORT_UCHAR(CIOAddr,0x0B); // CT2 CONTROL + WRITE_PORT_UCHAR(CIOAddr,6); // START CT2 + + return; +} + +VOID INITMODEM(PHDLCDATA PORTVEC) +{ + // SETUP MODEM - PC120 ONLY + + WRITE_PORT_UCHAR(PORTVEC->PORTCONTROL.IOBASE, 0x0a); +} + + +VOID CHECKCHAN(PHDLCDATA PORTVEC, USHORT CDOffset) +{ + // CDoffset contains offset to Second SCC + + // IF CHANNEL = C OR D SET TO SECOND SCC ADDRESS, AND CHANGE TO A OR B + + if (PORTVEC->PORTCONTROL.CHANNELNUM > 'B') + { + // SECOND SCC + + PORTVEC->PORTCONTROL.CHANNELNUM -=2; + PORTVEC->PORTCONTROL.IOBASE+=CDOffset; + } +} + +// BAYCOM CARD + +VOID BINITPART2(PHDLCDATA PORTVEC, USHORT SCCOffset, PHDLCDATA PreviousPort) +{ + // ORDER IS 0 1 2 3 4 5 6 7 + // ADATA BDATA CDATA DDATA ACTRL BCTRL CCTRL DCTRL + + // Before entering here IOBASE and Chan have been updated if Chan were C or D + + // SET UP ADDRESS LIST + + int i; + USHORT SCCBase=PORTVEC->PORTCONTROL.IOBASE + SCCOffset; + int BRG; + +// SET UP ADDRESS LIST - THIS PATH FOR CARDS WITH 'NORMAL' +// ADDRESSING - C/D=A0, A/B=A1, SO ORDER IS BCTRL BDATA ACTRL ADATA +// OR DE, WHICH USES WORD ADDRESSES C/D=A1, A/B=A2 + + PORTVEC->ASIOC = SCCBase+4; // A CHAN ADDR + PORTVEC->BSIOC = SCCBase+5; // B CHAN ADDR + +// SEE WHICH CHANNEL TO USE + + if (PORTVEC->PORTCONTROL.CHANNELNUM == 'A') + { + PORTVEC->A_PTR = PORTVEC; // POINT TO OUR ENTRY + PORTVEC->SIOC = SCCBase+4; + PORTVEC->SIO = SCCBase; + + if (PreviousPort) // Another Channel is first on Card + PORTVEC->B_PTR = PreviousPort; // CROSSLINK CHANNELS + } + else + { + // MUST BE B - CHECKED EARLIER + + PORTVEC->B_PTR = PORTVEC; // POINT TO OUR ENTRY + PORTVEC->SIOC = SCCBase+5; + PORTVEC->SIO = SCCBase+1; + + if (PreviousPort) // Another Channel is first on Card + PORTVEC->A_PTR = PreviousPort; // CROSSLINK CHANNELS + + } + + +// INITIALISE COMMS CHIP + + if (PreviousPort == 0) // OTHER CHAN ALREADY SET UP? + { + // DO A HARD RESET OF THE SCC + + WRITE_PORT_UCHAR(PORTVEC->ASIOC, 0); // Make Sure WR0 + WRITE_PORT_UCHAR(PORTVEC->ASIOC, 0); + + WRITE_PORT_UCHAR(PORTVEC->ASIOC, 9); // WR9 + + WRITE_PORT_UCHAR(PORTVEC->ASIOC, 0xC0); // Hard Reset + + Sleep(2); + + } + + for (i=0; i< SDLCLEN; i++) + { + WRITE_PORT_UCHAR(PORTVEC->SIOC, SDLCCMD[i]); + } + +// SET UP BRG FOR REQUIRED SPEED + + if (PORTVEC->PORTCONTROL.BAUDRATE == 0) + { + // SET EXTERNAL CLOCK + + SIOCW(11); // WR11 + SIOCW(0x20); // RX = TRXC TX = RTXC + + // BAYCOM RUH PORT USES NRZ + + PORTVEC->WR10 = 0x0; // NRZ + + return; + } + + PORTVEC->WR10 = 0x20; // NRZI + +// THERE IS NO /32 ON THE BAYCOM BOARD, SO FOR THE MOMENT WILL USE BRG +// FOR TRANSMIT. THIS REQUIRES IT TO BE REPROGRAMMED BETWEEN TX AND RX, +// AND SO PREVENTS LOOPBACK OR FULLDUP OPERATION + + BRG=(CLOCKFREQ/PORTVEC->PORTCONTROL.BAUDRATE)-2; + + SIOCW(12); // Select WR12 + SIOCW(BRG & 0xff); // SET LSB + SIOCW(13); // Select WR13 + SIOCW(BRG >> 8); // SET MSB + + SIOCW(11); // WR11 + SIOCW(0x70); // RXC=DPLL, TXC=BRG + + PORTVEC->RXBRG = BRG; + +// CALC TX RATE + + PORTVEC->TXBRG = ((BRG+2)/32)-2; + + SIOCW(12); // Select WR12 + SIOCW(BRG & 0xff); // SET LSB + SIOCW(13); // Select WR13 + SIOCW(BRG >> 8); // SET MSB + +// IF 7910/3105 PORTS, SET TXC=BRG, RXC=DPLL + +// IT SEEMS THE 3RD PORT IS MORE LIKELY TO BE USED WITH A SIMPLE +// MODEM WITHOUT CLOCK GERERATION (EG BAYCOM MODEM), SO SET ALL +// PORTS THE SAME + + SIOCW(11); // WR11 + SIOCW(0x70); // RXC=DPLL, TXC=BRG +} + +BOOLEAN INITREST(PHDLCDATA PORTVEC, PHDLCDATA PrevPort) +{ + BPQHDLC_ADDCHANNEL_INPUT AddParams; + VOID * Return = NULL; + int cb; + +/* mov IRQHand[EBX],0 ; in case already hooked by another port + + MOV PORTINTERRUPT[EBX],OFFSET32 SIOINT + + CMP EDI,0 + JNE SHORT INTDONE ; ALREADY SET UP + + CALL HOOKINT ; INTERRUPT + +INTDONE: + + CALL RXAINIT +; +*/ + + +// Pass Params to the driver + + AddParams.IOBASE = PORTVEC->PORTCONTROL.IOBASE; + AddParams.IOLEN = PORTVEC->IOLEN; + AddParams.Interrupt = PORTVEC->PORTCONTROL.INTLEVEL; + AddParams.ASIOC = PORTVEC->ASIOC; + AddParams.BSIOC = PORTVEC->BSIOC; + AddParams.SIOC = PORTVEC->SIOC; + AddParams.SIO = PORTVEC->SIO; + AddParams.TXBRG = PORTVEC->TXBRG; + AddParams.RXBRG = PORTVEC->RXBRG; + AddParams.WR10 = PORTVEC->WR10; + AddParams.Channel = PORTVEC->PORTCONTROL.CHANNELNUM; + AddParams.SOFTDCDFLAG = PORTVEC->PORTCONTROL.SOFTDCDFLAG; + AddParams.TXDELAY = PORTVEC->PORTCONTROL.PORTTXDELAY; + AddParams.PERSISTANCE = PORTVEC->PORTCONTROL.PORTPERSISTANCE; + if (PrevPort) + AddParams.OtherChannel = PrevPort->DRIVERPORTTABLE; + else + AddParams.OtherChannel = 0; + + fResult = DeviceIoControl( + hDevice, // device handle + IOCTL_BPQHDLC_ADDCHANNEL, // control code + &AddParams,sizeof(BPQHDLC_ADDCHANNEL_INPUT), // input parameters + &Return,4,&cb, // output parameters + 0); + + PORTVEC->DRIVERPORTTABLE = Return; + + if (Return == NULL) + { + // Init Failed, probably because resources were not alllocated to driver + + WritetoConsole("Kernel Driver Init Failed - Check Resource Allocation"); + return (FALSE); + } + + PORTVEC->RR0 = SIOCR; //GET INITIAL RR0 + + + return TRUE; +} + + +int INITPORT(PHDLCDATA PORTVEC) +{ + PHDLCDATA PreviousPort; + + // SEE IF C OR D. If so, Adjust IOBASE and change to A/B + + switch (PORTVEC->PORTCONTROL.PORTTYPE) + { + case 10: // RLC100 + case 12: // RLC400 + case 20: // PA0HZP OPTO-SCC + + CHECKCHAN(PORTVEC, 4); // Channels are 4 apart + break; + + case 18: // Baycom + + CHECKCHAN(PORTVEC, 2); //Channels are 2 apart + break; + } + + // By now Channel Should be only A or B + + if ((PORTVEC->PORTCONTROL.CHANNELNUM != 'A') && (PORTVEC->PORTCONTROL.CHANNELNUM != 'B')) + { + WritetoConsole("Invalid Channel\n"); + return FALSE; + } + + PreviousPort = See_if_First_On_Card(PORTVEC); + + if (PreviousPort == (PHDLCDATA)-1) + { + // Two ports on same card have same Channel + + WritetoConsole("Duplicate Channels\n"); + return FALSE; + } + + switch (PORTVEC->PORTCONTROL.PORTTYPE) + { + case 2: // PC120 + + PORTVEC->IOLEN = 8; // I think! - Modem is at offfset 0, SCC at 4 + INITPART2(PORTVEC, 4, PreviousPort); // SCC ADDRESS 4 Above Base Address + if (PreviousPort == NULL) INITMODEM(PORTVEC); + INITREST(PORTVEC, PreviousPort); + + return 0; + + case 4: // DRSIINIT + + PORTVEC->IOLEN = 8; // SCC at 0, CIO at 7 + if (PreviousPort == NULL) INITCIO(PORTVEC); // SET UP CIO FOR /32 UNLESS Already Done + INITPART2(PORTVEC, 0, PreviousPort); + if (PORTVEC->PORTCONTROL.BAUDRATE) STARTCIO(PORTVEC); + INITREST(PORTVEC, PreviousPort); + + return TRUE; + + case 6: + + WritetoConsole("TYPE=TOSH Not Supported\n"); + return FALSE; + + case 10: // RLC100 + case 12: // RLC400 + + PORTVEC->IOLEN = 4; // 2 * SCC, but each SCC can be on it's own + INITPART2(PORTVEC, 0, PreviousPort); + INITREST(PORTVEC, PreviousPort); + + return TRUE; + + case 18: // Baycom + + PORTVEC->IOLEN = 8; // 2 * SCC, but Need at least 6 Addresses + BINITPART2(PORTVEC, 0, PreviousPort); + INITREST(PORTVEC, PreviousPort); + + return 0; + + case 20: // PA0HZP OPTO-SCC + + PORTVEC->IOLEN = 4; // 2 * SCC, but each SCC can be on it's own + INITPART2(PORTVEC, 0, PreviousPort); + INITREST(PORTVEC, PreviousPort); + return TRUE; + } + + return FALSE; +} +#endif + +#include +#include +#include + + +#ifdef WIN32 +#include +#define read _read +#define write _write +#define close _close +#define open _open +#else +#endif + + +// Linux HDLC Kernel Module Support + +#define TIOCMGET 0x5415 + +PHDLCDATA FIRSTHDLCPORT = 0; + +int KHDLCINIT(PHDLCDATA PORTVEC) +{ + int ret, status = 65; + char Msg[64] = "HDLC Params"; + + // Only open device for first port - all ports share a kernel driver + + if (FIRSTHDLCPORT == 0) + { + // for now just open /dev/bpqhdlc. Needs to be a param somewhere + + PORTVEC->fd = open("/dev/bpqhdlc", O_RDWR); // Open the device with read/write access + + FIRSTHDLCPORT = PORTVEC; + + if (PORTVEC->fd < 0) + { + WritetoConsole("HDLC - Failed to open /dev/bpqhdlc\n"); + return errno; + } + + } + + ret = ioctl(FIRSTHDLCPORT->fd, 1, Msg); + + Consoleprintf("HDLC Channel %c", PORTVEC->PORTCONTROL.CHANNELNUM); + + return 0; +} + +void KHDLCTX(PHDLCDATA PORTVEC, PMESSAGE Buffer) +{ + int fd = FIRSTHDLCPORT->fd; + struct _LINKTABLE * LINK; + unsigned char Message[512]; + + if (fd != -1) + { + int len = GetLengthfromBuffer((PDATAMESSAGE)Buffer) - (3 + sizeof(void *)); + int ret; + + Message[0] = (PORTVEC->PORTCONTROL.CHANNELNUM - ('A' << 4)); // KISS Control + memcpy(&Message[1], Buffer->DEST, len); + + ret = write(fd, Message, len + 1); + + if (ret < 0) + { + Debugprintf("Failed to write the message to the device."); + return; + } + } + + LINK = Buffer->Linkptr; + + if (LINK) + { + if (LINK->L2TIMER) + LINK->L2TIMER = LINK->L2TIME; + + Buffer->Linkptr = 0; // CLEAR FLAG FROM BUFFER + } + + + // Pass buffer to trace routines + + C_Q_ADD(&TRACE_Q, Buffer); +} + +int KHDLCRX(PHDLCDATA PORTVEC) +{ + int len; + PMESSAGE Buffer; + unsigned char packet[512] = ""; + int fd = FIRSTHDLCPORT->fd; + + if (fd == -1) + return 0; + + packet[0] = (PORTVEC->PORTCONTROL.CHANNELNUM - ('A' << 4)); // KISS Control + + len = read(fd, packet, 512); // Read the response from the LKM + + while (len) + { + if (len < 0) + { + Debugprintf("bpqhdlc read failed"); + return errno; + } + Buffer = GetBuff(); + + if (Buffer) + { + memcpy(&Buffer->DEST, packet + 1, len - 1); // Has KISS control on front + len += (3 + sizeof(void *)); + + PutLengthinBuffer((PDATAMESSAGE)Buffer, len - 1); // Needed for arm5 portability + + C_Q_ADD(&PORTVEC->PORTCONTROL.PORTRX_Q, Buffer); + } + len = read(fd, packet, 512); // Read the response from the LKM + } + return 0; +} + +void KHDLCTIMER(PHDLCDATA PORTVEC) +{} + +void KHDLCCLOSE(PHDLCDATA PORTVEC) +{ + int fd = PORTVEC->fd; + + if (fd != -1) + close(fd); +} + +BOOL KHDLCTXCHECK(PHDLCDATA PORTVEC) +{ + return 0; +} + + diff --git a/.svn/pristine/17/1716b747f9da54ae568c4610eb9d64a2dca18075.svn-base b/.svn/pristine/17/1716b747f9da54ae568c4610eb9d64a2dca18075.svn-base new file mode 100644 index 0000000..81d2d61 --- /dev/null +++ b/.svn/pristine/17/1716b747f9da54ae568c4610eb9d64a2dca18075.svn-base @@ -0,0 +1,190 @@ +/* +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 +*/ + +/* + +Stuff to make compiling on WINDOWS and LINUX easier + +*/ + +#ifdef WIN32 + +typedef unsigned int uint32_t; + +#define pthread_t uint32_t + +int pthread_equal(pthread_t T1, pthread_t T2) +{ + return (T1 == T2); +} + +#else + +#include +#include +#include +#include +#include + +#define BOOL int + +#define VOID void +#define UCHAR unsigned char +#define USHORT unsigned short +#define ULONG uint32_t +#define UINT unsigned int +#define SHORT short +#define DWORD int32_t + +#define APIENTRY + +#define TRUE 1 +#define FALSE 0 +#define FAR + +#define HWND unsigned int +#define HINSTANCE unsigned int + +#define strtok_s strtok_r + +VOID Debugprintf(const char * format, ...); + +int memicmp(unsigned char *a, unsigned char *b, int n) +{ + if (n) + { + while (n && (toupper(*a) == toupper(*b))) + n--, a++, b++; + + if (n) + return toupper(*a) - toupper(*b); + } + return 0; +} +int stricmp(const unsigned char * pStr1, const unsigned char *pStr2) +{ + unsigned char c1, c2; + int v; + + if (pStr1 == NULL) + { + if (pStr2) + Debugprintf("stricmp called with NULL 1st param - 2nd %s ", pStr2); + else + Debugprintf("stricmp called with two NULL params"); + + return 1; + } + + + do { + c1 = *pStr1++; + c2 = *pStr2++; + /* The casts are necessary when pStr1 is shorter & char is signed */ + v = tolower(c1) - tolower(c2); + } while ((v == 0) && (c1 != '\0') && (c2 != '\0') ); + + return v; +} +char * strupr(char* s) +{ + char* p = s; + + if (s == 0) + return 0; + + while (*p = toupper( *p )) p++; + return s; +} + +char * strlwr(char* s) +{ + char* p = s; + while (*p = tolower( *p )) p++; + return s; +} + +int sprintf_s(char * string, int plen, const char * format, ...) +{ + va_list(arglist); + int Len; + + va_start(arglist, format); + Len = vsprintf(string, format, arglist); + va_end(arglist); + return Len; +} + + + +#include + +pthread_t _beginthread(void(*start_address)(), unsigned stack_size, VOID * arglist) +{ + pthread_t thread; + + // Need to set stack size for Mac + + int s, tnum, opt, num_threads; + struct thread_info *tinfo; + pthread_attr_t attr; + void *res; + + s = pthread_attr_init(&attr); + if (s != 0) + { + perror("pthread_attr_init"); + return 0; + } + if (stack_size > 0) + { + s = pthread_attr_setstacksize(&attr, stack_size); + if (s != 0) + { + perror("pthread_attr_setstacksize"); + return 0; + } + } + + if (pthread_create(&thread, &attr, (void * (*)(void *))start_address, (void*) arglist) != 0) + perror("New Thread"); + else + pthread_detach(thread); + + return thread; +} + +int Sleep(int ms) +{ + usleep(ms * 1000); + return 0; +} + +VOID OutputDebugString(char * string) +{ + syslog(LOG_DEBUG, "%s", string); +} + +void closesocket(int sock) +{ + if (sock) + close(sock); +} + +#endif diff --git a/.svn/pristine/17/172e0c32974a7cafbcfdcf70890fae584e4def61.svn-base b/.svn/pristine/17/172e0c32974a7cafbcfdcf70890fae584e4def61.svn-base new file mode 100644 index 0000000..c4b68a3 --- /dev/null +++ b/.svn/pristine/17/172e0c32974a7cafbcfdcf70890fae584e4def61.svn-base @@ -0,0 +1,399 @@ +/* +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 +*/ + +// +// C replacement for TNCCode.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "cheaders.h" +#include "tncinfo.h" + +int C_Q_COUNT(VOID *PQ); +VOID SENDUIMESSAGE(struct DATAMESSAGE * Msg); + +VOID TNCTimerProc() +{ + // CALLED AT 10 HZ + + int n = BPQHOSTSTREAMS; + PBPQVECSTRUC HOSTSESS = BPQHOSTVECTOR; + TRANSPORTENTRY * Session; + UCHAR DISCFLAG = 0; + + while (n--) + { + // Action any DISC Requests (must be done in timer owning process) + + if (HOSTSESS->HOSTFLAGS & 0x40) // DISC REQUEST + { + if (HOSTSESS->HOSTFLAGS & 0x20) // Stay? + DISCFLAG = 'S'; + + HOSTSESS->HOSTFLAGS &= 0x9F; // Clear Flags + + Session = HOSTSESS->HOSTSESSION; + + if (Session == 0) // Gone?? + { + HOSTSESS->HOSTFLAGS |= 3; // STATE CHANGE +#ifndef LINBPQ + if (HOSTSESS->HOSTHANDLE); + { + PostMessage(HOSTSESS->HOSTHANDLE, BPQMsg, HOSTSESS->HOSTSTREAM, 4); + } +#endif + continue; + } + + if (Session->L4CROSSLINK) + Session->L4CROSSLINK->STAYFLAG = DISCFLAG; + + HOSTSESS->HOSTSESSION = 0; + HOSTSESS->HOSTFLAGS |= 3; // STATE CHANGE + + PostStateChange(Session); + + CloseSessionPartner(Session); // SEND CLOSE TO PARTNER (IF PRESENT) + } + + // Check Trace Q + + if (HOSTSESS->HOSTAPPLFLAGS & 0x80) + { + if (HOSTSESS->HOSTTRACEQ) + { + int Count = C_Q_COUNT(&HOSTSESS->HOSTTRACEQ); + + if (Count > 100) + ReleaseBuffer((void *)Q_REM((void *)&HOSTSESS->HOSTTRACEQ)); + } + } + HOSTSESS++; + } +} + +VOID SendSmartID(struct PORTCONTROL * PORT) +{ + struct _MESSAGE * ID = IDMSG; + struct _MESSAGE * Buffer; + + PORT->SmartIDNeeded = 0; + + Buffer = GetBuff(); + + if (Buffer) + { + memcpy(Buffer, ID, ID->LENGTH); + + Buffer->PORT = PORT->PORTNUMBER; + + // IF PORT HAS A CALLSIGN DEFINED, SEND THAT INSTEAD + + if (PORT->PORTCALL[0] > 0x40) + { + memcpy(Buffer->ORIGIN, PORT->PORTCALL, 7); + Buffer->ORIGIN[6] |= 1; // SET END OF CALL BIT + } + + // If Pactor Style add to UI_Q + + if (PORT->PROTOCOL == 10 && PORT->TNC && PORT->TNC->Hardware != H_KISSHF && PORT->UICAPABLE) + { + EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; + + C_Q_ADD(&EXTPORT->UI_Q, Buffer); + return; + } + + PUT_ON_PORT_Q(PORT, Buffer); + } +} + + +VOID SENDIDMSG() +{ + struct PORTCONTROL * PORT = PORTTABLE; + struct _MESSAGE * ID = IDMSG; + struct _MESSAGE * Buffer; + + while (PORT) + { + if (PORT->PROTOCOL < 10) // Not Pactor-style + { + Buffer = GetBuff(); + + if (Buffer) + { + memcpy(Buffer, ID, ID->LENGTH); + + Buffer->PORT = PORT->PORTNUMBER; + + // IF PORT HAS A CALLSIGN DEFINED, SEND THAT INSTEAD + + if (PORT->PORTCALL[0] > 0x40) + { + memcpy(Buffer->ORIGIN, PORT->PORTCALL, 7); + Buffer->ORIGIN[6] |= 1; // SET END OF CALL BIT + } + C_Q_ADD(&IDMSG_Q, Buffer); + } + } + PORT = PORT->PORTPOINTER; + } +} + + + +VOID SENDBTMSG() +{ + struct PORTCONTROL * PORT = PORTTABLE; + struct _MESSAGE * Buffer; + char * ptr1, * ptr2; + + while (PORT) + { + if (PORT->PROTOCOL >= 10 || PORT->PORTUNPROTO == 0) // Pactor-style or no UNPROTO ADDR? + { + PORT = PORT->PORTPOINTER; + continue; + } + + Buffer = GetBuff(); + + if (Buffer) + { + memcpy(Buffer->DEST, PORT->PORTUNPROTO, 7); + Buffer->DEST[6] |= 0xC0; // Set Command bits + + // Send from BBSCALL unless PORTBCALL defined + + if (PORT->PORTBCALL[0] > 32) + memcpy(Buffer->ORIGIN, PORT->PORTBCALL, 7); + else if (APPLCALLTABLE->APPLCALL[0] > 32) + memcpy(Buffer->ORIGIN, APPLCALLTABLE->APPLCALL, 7); + else + memcpy(Buffer->ORIGIN, MYCALL, 7); + + ptr1 = &PORT->PORTUNPROTO[7]; // First Digi + ptr2 = &Buffer->CTL; // Digi field in buffer + + // Copy any digis + + while (*(ptr1)) + { + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + } + + *(ptr2 - 1) |= 1; // Set End of Address + *(ptr2++) = UI; + + memcpy(ptr2, &BTHDDR.PID, BTHDDR.LENGTH); + ptr2 += BTHDDR.LENGTH; + Buffer->LENGTH = (int)(ptr2 - (char *)Buffer); + Buffer->PORT = PORT->PORTNUMBER; + + C_Q_ADD(&IDMSG_Q, Buffer); + } + PORT = PORT->PORTPOINTER; + } +} + +VOID SENDUIMESSAGE(struct DATAMESSAGE * Msg) +{ + struct PORTCONTROL * PORT = PORTTABLE; + struct _MESSAGE * Buffer; + char * ptr1, * ptr2; + + Msg->LENGTH -= MSGHDDRLEN; // Remove Header + + while (PORT) + { + if ((PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0) || PORT->PORTUNPROTO == 0) // Pactor-style or no UNPROTO ADDR? + { + PORT = PORT->PORTPOINTER; + continue; + } + + Buffer = GetBuff(); + + if (Buffer) + { + memcpy(Buffer->DEST, PORT->PORTUNPROTO, 7); + Buffer->DEST[6] |= 0xC0; // Set Command bits + + // Send from BBSCALL unless PORTBCALL defined + + if (PORT->PORTBCALL[0] > 32) + memcpy(Buffer->ORIGIN, PORT->PORTBCALL, 7); + else if (APPLCALLTABLE->APPLCALL[0] > 32) + memcpy(Buffer->ORIGIN, APPLCALLTABLE->APPLCALL, 7); + else + memcpy(Buffer->ORIGIN, MYCALL, 7); + + ptr1 = &PORT->PORTUNPROTO[7]; // First Digi + ptr2 = &Buffer->CTL; // Digi field in buffer + + // Copy any digis + + while (*(ptr1)) + { + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + } + + *(ptr2 - 1) |= 1; // Set End of Address + *(ptr2++) = UI; + + memcpy(ptr2, &Msg->PID, Msg->LENGTH); + ptr2 += Msg->LENGTH; + Buffer->LENGTH = (int)(ptr2 - (char *)Buffer); + Buffer->PORT = PORT->PORTNUMBER; + + if (PORT->PROTOCOL == 10) + { + EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; + C_Q_ADD(&EXTPORT->UI_Q, Buffer); + } + else + C_Q_ADD(&IDMSG_Q, Buffer); + } + PORT = PORT->PORTPOINTER; + } +} + +Dll VOID APIENTRY Send_AX(UCHAR * Block, DWORD Len, UCHAR Port) +{ + // Block included the 7/11 byte header, Len does not + + struct PORTCONTROL * PORT; + PMESSAGE Copy; + + if (Len > 360 - 15) + return; + + if (QCOUNT < 50) + return; // Running low + + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == 0) + return; + + Copy = GetBuff(); + + if (Copy == 0) + return; + + memcpy(&Copy->DEST[0], &Block[MSGHDDRLEN], Len); + + Copy->LENGTH = (USHORT)Len + MSGHDDRLEN; + + if (PORT->PROTOCOL == 10 && PORT->TNC && PORT->TNC->Hardware != H_KISSHF) + { + // Pactor Style. Probably will only be used for Tracker uneless we do APRS over V4 or WINMOR + + EXTPORTDATA * EXTPORT = (EXTPORTDATA *) PORT; + + C_Q_ADD(&EXTPORT->UI_Q, Copy); + return; + } + + Copy->PORT = Port; + + PUT_ON_PORT_Q(PORT, Copy); +} + + +TRANSPORTENTRY * SetupSessionFromHost(PBPQVECSTRUC HOST, UINT ApplMask) +{ + // Create a Transport (L4) session linked to an incoming HOST (API) Session + + TRANSPORTENTRY * NewSess = L4TABLE; + int Index = 0; + + + while (Index < MAXCIRCUITS) + { + if (NewSess->L4USER[0] == 0) + { + // Got One + + UCHAR * ourcall = &MYCALL[0]; + + // IF APPL PORT USE APPL CALL, ELSE NODE CALL + + 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, ourcall, 7); + + NewSess->CIRCUITINDEX = Index; //OUR INDEX + NewSess->CIRCUITID = NEXTID; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // Keep Non-Zero + + NewSess->L4TARGET.HOST = HOST; + NewSess->L4STATE = 5; + + + NewSess->SESSIONT1 = L4T1; + NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + NewSess->SESSPACLEN = PACLEN; // Default; + + return NewSess; + } + Index++; + NewSess++; + } + + // Table Full + + return NULL; +} + + + + diff --git a/.svn/pristine/17/179e11cd7f9e2d936d3d5483992d5bb249537f77.svn-base b/.svn/pristine/17/179e11cd7f9e2d936d3d5483992d5bb249537f77.svn-base new file mode 100644 index 0000000..9003a21 --- /dev/null +++ b/.svn/pristine/17/179e11cd7f9e2d936d3d5483992d5bb249537f77.svn-base @@ -0,0 +1,650 @@ + +/* pngtrans.c - transforms the data in a row (used by both readers and writers) + * + * libpng 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* turn on BGR-to-RGB mapping */ +void PNGAPI +png_set_bgr(png_structp png_ptr) +{ + png_debug(1, "in png_set_bgr\n"); + png_ptr->transformations |= PNG_BGR; +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* turn on 16 bit byte swapping */ +void PNGAPI +png_set_swap(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap\n"); + if (png_ptr->bit_depth == 16) + png_ptr->transformations |= PNG_SWAP_BYTES; +} +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* turn on pixel packing */ +void PNGAPI +png_set_packing(png_structp png_ptr) +{ + png_debug(1, "in png_set_packing\n"); + if (png_ptr->bit_depth < 8) + { + png_ptr->transformations |= PNG_PACK; + png_ptr->usr_bit_depth = 8; + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* turn on packed pixel swapping */ +void PNGAPI +png_set_packswap(png_structp png_ptr) +{ + png_debug(1, "in png_set_packswap\n"); + if (png_ptr->bit_depth < 8) + png_ptr->transformations |= PNG_PACKSWAP; +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +void PNGAPI +png_set_shift(png_structp png_ptr, png_color_8p true_bits) +{ + png_debug(1, "in png_set_shift\n"); + png_ptr->transformations |= PNG_SHIFT; + png_ptr->shift = *true_bits; +} +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +int PNGAPI +png_set_interlace_handling(png_structp png_ptr) +{ + png_debug(1, "in png_set_interlace handling\n"); + if (png_ptr->interlaced) + { + png_ptr->transformations |= PNG_INTERLACE; + return (7); + } + + return (1); +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte on read, or remove a filler or alpha byte on write. + * The filler type has changed in v0.95 to allow future 2-byte fillers + * for 48-bit input data, as well as to avoid problems with some compilers + * that don't like bytes as parameters. + */ +void PNGAPI +png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_filler\n"); + png_ptr->transformations |= PNG_FILLER; + png_ptr->filler = (png_byte)filler; + if (filler_loc == PNG_FILLER_AFTER) + png_ptr->flags |= PNG_FLAG_FILLER_AFTER; + else + png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; + + /* This should probably go in the "do_read_filler" routine. + * I attempted to do that in libpng-1.0.1a but that caused problems + * so I restored it in libpng-1.0.2a + */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_ptr->usr_channels = 4; + } + + /* Also I added this in libpng-1.0.2a (what happens when we expand + * a less-than-8-bit grayscale to GA? */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8) + { + png_ptr->usr_channels = 2; + } +} + +#if !defined(PNG_1_0_X) +/* Added to libpng-1.2.7 */ +void PNGAPI +png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_add_alpha\n"); + png_set_filler(png_ptr, filler, filler_loc); + png_ptr->transformations |= PNG_ADD_ALPHA; +} +#endif + +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void PNGAPI +png_set_swap_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap_alpha\n"); + png_ptr->transformations |= PNG_SWAP_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void PNGAPI +png_set_invert_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_alpha\n"); + png_ptr->transformations |= PNG_INVERT_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +void PNGAPI +png_set_invert_mono(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_mono\n"); + png_ptr->transformations |= PNG_INVERT_MONO; +} + +/* invert monochrome grayscale data */ +void /* PRIVATE */ +png_do_invert(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_invert\n"); + /* This test removed from libpng version 1.0.13 and 1.2.0: + * if (row_info->bit_depth == 1 && + */ +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row == NULL || row_info == NULL) + return; +#endif + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(~(*rp)); + rp++; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 8) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=2) + { + *rp = (png_byte)(~(*rp)); + rp+=2; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=4) + { + *rp = (png_byte)(~(*rp)); + *(rp+1) = (png_byte)(~(*(rp+1))); + rp+=4; + } + } +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* swaps byte order on 16 bit depth images */ +void /* PRIVATE */ +png_do_swap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_swap\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop= row_info->width * row_info->channels; + + for (i = 0; i < istop; i++, rp += 2) + { + png_byte t = *rp; + *rp = *(rp + 1); + *(rp + 1) = t; + } + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +static png_byte onebppswaptable[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + +static png_byte twobppswaptable[256] = { + 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, + 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, + 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, + 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, + 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, + 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, + 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, + 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, + 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, + 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, + 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, + 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, + 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, + 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, + 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, + 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, + 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, + 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, + 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, + 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, + 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, + 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, + 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, + 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, + 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, + 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, + 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, + 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, + 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, + 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, + 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, + 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF +}; + +static png_byte fourbppswaptable[256] = { + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, + 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, + 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, + 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, + 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, + 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, + 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, + 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, + 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, + 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, + 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, + 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, + 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, + 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, + 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, + 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, + 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, + 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, + 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, + 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, + 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, + 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, + 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, + 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, + 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, + 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, + 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, + 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, + 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, + 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, + 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, + 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF +}; + +/* swaps pixel packing order within bytes */ +void /* PRIVATE */ +png_do_packswap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_packswap\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->bit_depth < 8) + { + png_bytep rp, end, table; + + end = row + row_info->rowbytes; + + if (row_info->bit_depth == 1) + table = onebppswaptable; + else if (row_info->bit_depth == 2) + table = twobppswaptable; + else if (row_info->bit_depth == 4) + table = fourbppswaptable; + else + return; + + for (rp = row; rp < end; rp++) + *rp = table[*rp]; + } +} +#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */ + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +/* remove filler or alpha byte(s) */ +void /* PRIVATE */ +png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) +{ + png_debug(1, "in png_do_strip_filler\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_bytep sp=row; + png_bytep dp=row; + png_uint_32 row_width=row_info->width; + png_uint_32 i; + + if ((row_info->color_type == PNG_COLOR_TYPE_RGB || + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 4) + { + if (row_info->bit_depth == 8) + { + /* This converts from RGBX or RGBA to RGB */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + dp+=3; sp+=4; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp++; + } + } + /* This converts from XRGB or ARGB to RGB */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */ + sp += 8; dp += 6; + for (i = 1; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */ + for (i = 0; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + sp+=2; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 48; + row_info->rowbytes = row_width * 6; + } + row_info->channels = 3; + } + else if ((row_info->color_type == PNG_COLOR_TYPE_GRAY || + (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 2) + { + if (row_info->bit_depth == 8) + { + /* This converts from GX or GA to G */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + for (i = 0; i < row_width; i++) + { + *dp++ = *sp++; + sp++; + } + } + /* This converts from XG or AG to G */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from GGXX or GGAA to GG */ + sp += 4; dp += 2; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXGG or AAGG to GG */ + for (i = 0; i < row_width; i++) + { + sp += 2; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + row_info->channels = 1; + } + if (flags & PNG_FLAG_STRIP_ALPHA) + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + } +} +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* swaps red and blue bytes within a pixel */ +void /* PRIVATE */ +png_do_bgr(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_bgr\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 3) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 4) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + } + else if (row_info->bit_depth == 16) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 6) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 8) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + } + } +} +#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +void PNGAPI +png_set_user_transform_info(png_structp png_ptr, png_voidp + user_transform_ptr, int user_transform_depth, int user_transform_channels) +{ + png_debug(1, "in png_set_user_transform_info\n"); +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + png_ptr->user_transform_ptr = user_transform_ptr; + png_ptr->user_transform_depth = (png_byte)user_transform_depth; + png_ptr->user_transform_channels = (png_byte)user_transform_channels; +#else + if(user_transform_ptr || user_transform_depth || user_transform_channels) + png_warning(png_ptr, + "This version of libpng does not support user transform info"); +#endif +} +#endif + +/* This function returns a pointer to the user_transform_ptr associated with + * the user transform functions. The application should free any memory + * associated with this pointer before png_write_destroy and png_read_destroy + * are called. + */ +png_voidp PNGAPI +png_get_user_transform_ptr(png_structp png_ptr) +{ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + return ((png_voidp)png_ptr->user_transform_ptr); +#else + if(png_ptr) + return (NULL); + return (NULL); +#endif +} diff --git a/.svn/pristine/18/18c3f1aef566540674fb61ab096d58e5c4dcda69.svn-base b/.svn/pristine/18/18c3f1aef566540674fb61ab096d58e5c4dcda69.svn-base new file mode 100644 index 0000000..8d65795 --- /dev/null +++ b/.svn/pristine/18/18c3f1aef566540674fb61ab096d58e5c4dcda69.svn-base @@ -0,0 +1,602 @@ +/* + +Declarations of BPQ32 API funtions + +There are two sets of definitions, one for static linking against bpq32.lib, and one for +dynamic linking using LoadLibrary/GetProcAddress + +Define symbol DYNLOADBPQ before including this file to use the dynamic form. + +If you are writing an External Driver, rather than an application, +you must use Dynamic Linking, and you must also define symbol EXTDLL, which will +make the code use GetProcAddress rather than LoadLibrary. Without this the reference +count on BPQ32.dll gets messed up, and the code will not unload cleanly. + +*/ + + +#ifndef DYNLOADBPQ + +// Definitions for Statically Linked DLL + +struct PORTCONTROL * APIENTRY GetPortTableEntryFromPortNum(int portslot); +struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); + + +// Returns number of free buffers +// (BPQHOST function 7 (part)). + +int APIENTRY GetFreeBuffs(); + +// Returns count of packets waiting on stream +// (BPQHOST function 7 (part)). + +int APIENTRY RXCount(int Stream); + + +// Returns number of packets on TX queue for stream +// (BPQHOST function 7 (part)). + +int APIENTRY TXCount(int Stream); + + +// Returns number of monitor frames available +// (BPQHOST function 7 (part)). + +int APIENTRY MONCount(int Stream); + + +// Returns call connecten on stream (BPQHOST function 8 (part)). + +int APIENTRY GetCallsign(int stream, char * callsign); + + +// Returns connection info for stream (BPQHOST function 8). + +int APIENTRY GetConnectionInfo(int stream, char * callsign, + int * port, int * sesstype, int * paclen, + int * maxframe, int * l4window); + + +int APIENTRY GetStreamPID(int Stream); + +// Returns Path of BPQDirectroy + +char * APIENTRY GetBPQDirectory(); +char * APIENTRY GetProgramDirectory(); +UCHAR * APIENTRY GetLogDirectory(); + +HKEY APIENTRY GetRegistryKey(); +char * APIENTRY GetRegistryKeyText(); + +char * APIENTRY GetSignOnMsg(); + +char * APIENTRY GetVersionString(); + + +// Returns number of prcess attached to BPQ32 + +int APIENTRY GetAttachedProcesses(); + + +// Send Session Control command (BPQHOST function 6) +// Command = 0 Connect using APPL MASK IN param +// Command = 1 Connect +// Command = 2 Disconect +// Command = 3 Return to Node + +int APIENTRY SessionControl(int stream, int command, int param); + + + +// Sets Application Flags and Mask for stream. (BPQHOST function 1) +// Top bit of flags enables monitoring + +int APIENTRY SetAppl(int stream, int flags, int mask); + + +int APIENTRY GetApplMask(int Stream); + +int APIENTRY GetApplFlags(int Stream); + + +BOOL APIENTRY GetAllocationState(int Stream); + + +// Get current Session State. Any state changed is ACK'ed +// automatically. See BPQHOST functions 4 and 5. + +int APIENTRY SessionState(int stream, int * state, int * change); + +// Get current Session State. Dont Ack state change +// See BPQHOST function 4. + +int APIENTRY SessionStateNoAck(int stream, int * state); + + + +// Send message to stream (BPQHOST Function 2) + +int APIENTRY SendMsg(int stream, char * msg, int len); + + + +// Send Raw (KISS mode) frame to port (BPQHOST function 10) + +int APIENTRY SendRaw(int port, char * msg, int len); + + + +// Get message from stream. Returns length, and count of frames +// still waiting to be collected. (BPQHOST function 3) + +int APIENTRY GetMsg(int stream, char * msg, int * len, int * count ); + + +// Perl Version - I couldn't get bpq32.pm to call GetMsg. Returns Lenth + +int APIENTRY GetMsgPerl(int stream, char * msg); + + +// Get Raw (Trace) data (BPQHOST function 11) + +int APIENTRY GetRaw(int stream, char * msg, int * len, int * count ); + + + +// This is not an API function. It is a utility to decode a received +// monitor frame into ascii text. + +int APIENTRY DecodeFrame(char * msg, char * buffer, time_t Stamp); + + + +// Sets the tracing options for DecodeFrame. Mask is a bit +// mask of ports to monitor (ie 101 binary will monitor ports +// 1 and 3). MTX enables monitoring on transmitted frames. MCOM +// enables monitoring of protocol control frames (eg SABM, UA, RR), +// as well as info frames. + +int APIENTRY SetTraceOptions(long mask, int mtxparam, int mcomparam); +int APIENTRY SetTraceOptionsEx(uint64_t mask, int mtxparam, int mcomparam, int monUIOnly); +int APIENTRY SetTraceOptions64(uint64_t mask, int mtxparam, int mcomparam, int monUIOnly); + +// Returns number of first unused BPQHOST stream. If none available, +// returns 255. See API function 13. + +int APIENTRY FindFreeStream(); + + + +// Allocate stream. If stream is already allocated, return nonzero. +// Otherwise allocate stream, and return zero + +int APIENTRY AllocateStream(int stream); + + + +// Release stream. + +int APIENTRY DeallocateStream(int stream); + +// Get number of ports configured + +int APIENTRY GetNumberofPorts(); + + +// Get port number (ports aren't necessarily numbered 1 to n) + +int APIENTRY GetPortNumber(int portslot); + +UCHAR * APIENTRY GetPortDescription(int portslot, char * Desc); + +// Enable async operation - new to Win32 version of API + +int APIENTRY BPQSetHandle(int Stream, HWND hWnd); + +int ConvFromAX25(unsigned char * incall, unsigned char * outcall); +BOOL ConvToAX25(unsigned char * callsign, unsigned char * ax25call); +char * APIENTRY GetNodeCall(); + +int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall); +int APIENTRY ChangeSessionPaclen(int Stream, int Paclen); + +int APIENTRY GetApplNum(int Stream); + +char * APIENTRY GetApplCall(int Appl); + +char * APIENTRY GetApplAlias(int Appl); + +long APIENTRY GetApplQual(int Appl); + +char * APIENTRY GetApplNabe(int Appl); + +BOOL APIENTRY SetApplCall(int Appl, char * NewCall); + +BOOL APIENTRY SetApplAlias(int Appl, char * NewCall); + +BOOL APIENTRY SetApplQual(int Appl, int NewQual); + + +// Routines to support "Minimize to Tray" + +BOOL APIENTRY GetMinimizetoTrayFlag(); + +int APIENTRY AddTrayMenuItem(HWND hWnd, char * Label); + +int APIENTRY DeleteTrayMenuItem(HWND hWnd); + +BOOL APIENTRY StartMinimizedFlag(); + +// Log a message to the bpq32 console. + +int APIENTRY WritetoConsole(char * buff); + +// CheckTimer should be called regularly to ensure BPQ32 detects if an application crashes + +int APIENTRY CheckTimer(); + +// CloseBPQ32 is used if you want to close and restart BPQ32 while your application is +// running. This is only relevant of you have Dynamically linked BPQ32 + +int APIENTRY CloseBPQ32(); + +int APIENTRY GETBPQAPI(); + +UINT APIENTRY GETMONDECODE(); + +VOID APIENTRY RelBuff(VOID * Msg); +//VOID *APIENTRY GetBuff(); + +VOID APIENTRY CreateOneTimePassword(char * Password, char * KeyPhrase, int TimeOffset); + +BOOL APIENTRY CheckOneTimePassword(char * Password, char * KeyPhrase); + +VOID APIENTRY md5 (char *arg, unsigned char * checksum); + +int APIENTRY SetupTrayIcon(); + +BOOL APIENTRY SaveReg(char * KeyIn, HANDLE hFile); + +VOID APIENTRY SendChatReport(UINT_PTR ChatReportSocket, char * buff, int txlen); + +int APIENTRY CountFramesQueuedOnStream(int Stream); + +char * APIENTRY GetLOC(); + +DllExport uint64_t APIENTRY GetPortFrequency(int PortNo, char * FreqString); + +#else + +struct PORTCONTROL * (FAR WINAPI * GetPortTableEntryFromPortNum) (int portnum); +struct PORTCONTROL * (FAR WINAPI * GetPortTableEntryFromSlot) (int portslot); + +// API Definitions for Dynamic Load of BPQ32.dll + +ULONG (FAR WINAPI * GETBPQAPI)(); + +ULONG (FAR WINAPI * GETMONDECODE)(); + +UCHAR * (FAR WINAPI * GetBPQDirectory)(); +UCHAR * (FAR WINAPI * GetProgramDirectory)(); +UCHAR * (FAR WINAPI * GetRegistryKeyText)(); +HKEY (FAR WINAPI * GetRegistryKey)(); + +UCHAR * (FAR WINAPI * GetSignOnMsg)(); +UCHAR * (FAR WINAPI * GetVersionString)(); + +// Returns number of free buffers +// (BPQHOST function 7 (part)). + +int (FAR WINAPI * GetFreeBuffs)(); + + +// Returns count of packets waiting on stream +// (BPQHOST function 7 (part)). + +int (FAR WINAPI * RXCount) (int Stream); + +// Returns number of packets on TX queue for stream +// (BPQHOST function 7 (part)). + +int (FAR WINAPI * TXCount)(int Stream); + + +// Returns number of monitor frames available +// (BPQHOST function 7 (part)). + +int (FAR WINAPI * MONCount) (int Stream); + +// Returns call connecten on stream (BPQHOST function 8 (part)). + +int (FAR WINAPI * GetCallsign)(int stream, char * callsign); + +// Returns connection info for stream (BPQHOST function 8). + +int (FAR WINAPI * GetConnectionInfo) (int stream, char * callsign, + int * port, int * sesstype, int * paclen, + int * maxframe, int * l4window); + + +int (FAR WINAPI * GetStreamPID) (int Stream); + +// Returns Path of BPQDirectroy + +UCHAR * (FAR WINAPI * GetBPQDirectory)(); + +// Returns number of prcess attached to BPQ32 + +int (FAR WINAPI * GetAttachedProcesses) (); + + +// Send Session Control command (BPQHOST function 6) +// Command = 0 Connect using APPL MASK IN param +// Command = 1 Connect +// Command = 2 Disconect +// Command = 3 Return to Node + +int (FAR WINAPI * SessionControl) (int stream, int command, int param); + + +// Sets Application Flags and Mask for stream. (BPQHOST function 1) +// Top bit of flags enables monitoring + +int (FAR WINAPI * SetAppl) (int stream, int flags, int mask); + +int (FAR WINAPI * GetApplMask) (int Stream); + +int (FAR WINAPI * GetApplFlags)(int Stream); + +BOOL (FAR WINAPI * GetAllocationState) (int Stream); + + + +// Get current Session State. Any state changed is ACK'ed +// automatically. See BPQHOST functions 4 and 5. + +int (FAR WINAPI * SessionState) (int stream, int * state, int * change); + +// Get current Session State. Dont Ack state change +// See BPQHOST function 4. + +int (FAR WINAPI * SessionStateNoAck) (int stream, int * state); + + +// Send message to stream (BPQHOST Function 2) + +int (FAR WINAPI * SendMsg) (int stream, char * msg, int len); + + +// Send Raw (KISS mode) frame to port (BPQHOST function 10) + +int (FAR WINAPI * SendRaw) (int port, char * msg, int len); + + +// Get message from stream. Returns length, and count of frames +// still waiting to be collected. (BPQHOST function 3) + +int (FAR WINAPI * GetMsg) (int stream, char * msg, int * len, int * count ); + + +// Perl Version - I couldn't get bpq32.pm to call GetMsg. Returns Lenth + +int (FAR WINAPI * GetMsgPerl) (int stream, char * msg); + + +// Get Raw (Trace) data (BPQHOST function 11) + +int (FAR WINAPI * GetRaw) (int stream, char * msg, int * len, int * count ); + + + +// This is not an API function. It is a utility to decode a received +// monitor frame into ascii text. + +int (FAR WINAPI * DecodeFrame) (char * msg, char * buffer, int Stamp); + + +// Sets the tracing options for DecodeFrame. Mask is a bit +// mask of ports to monitor (ie 101 binary will monitor ports +// 1 and 3). MTX enables monitoring on transmitted frames. MCOM +// enables monitoring of protocol control frames (eg SABM, UA, RR), +// as well as info frames. + +int (FAR WINAPI * SetTraceOptions) (long mask, int mtxparam, int mcomparam); + + +// Returns number of first unused BPQHOST stream. If none available, +// returns 255. See API function 13. + +int (FAR WINAPI * FindFreeStream) (); + + +// Allocate stream. If stream is already allocated, return nonzero. +// Otherwise allocate stream, and return zero + +int (FAR WINAPI * AllocateStream) (int stream); + + +// Release stream. + +int (FAR WINAPI * DeallocateStream) (int stream); + +// Get number of ports configured + +int (FAR WINAPI * GetNumberofPorts) (); + + +// Get port number (ports aren't necessarily numbered 1 to n) + +int (FAR WINAPI * GetPortNumber) (int portslot); + + +// Enable async operation - new to Win32 version of API + +int (FAR WINAPI * BPQSetHandle) (int Stream, HWND hWnd); + +int (FAR * ConvFromAX25) (unsigned char * incall, unsigned char * outcall); +BOOL (FAR * ConvToAX25) (unsigned char * callsign, unsigned char * ax25call); + +char * (FAR WINAPI * GetNodeCall) (); +int (FAR WINAPI * ChangeSessionCallsign) (int Stream, unsigned char * AXCall); + +int (FAR WINAPI * GetApplNum) (int Stream); + +char * (FAR WINAPI * GetApplCall) (int Appl); + +char * (FAR WINAPI * GetApplAlias) (int Appl); + +char * (FAR WINAPI * GetApplName) (int Appl); + +long (FAR WINAPI * GetApplQual) (int Appl); + +BOOL (FAR WINAPI * SetApplCall) (int Appl, char * NewCall); + +BOOL (FAR WINAPI * SetApplAlias) (int Appl, char * NewCall); + +BOOL (FAR WINAPI * SetApplQual) (int Appl, int NewQual); + + +// Routines to support "Minimize to Tray" + +BOOL (FAR WINAPI * GetMinimizetoTrayFlag)(); + +//int APIENTRY AddTrayMenuItem(); +int (FAR WINAPI * AddTrayMenuItem)(HWND hWnd, char * Label); + +//int APIENTRY DeleteTrayMenuItem(); +int (FAR WINAPI * DeleteTrayMenuItem)(HWND hWnd); + +int (FAR WINAPI * SetupTrayIcon)(); + +BOOL (FAR WINAPI * GetStartMinimizedFlag)(); + +// Log a message to the bpq32 console. + +//int APIENTRY WritetoConsole(); +int (FAR WINAPI * WritetoConsole)(char * buff); + +int (FAR WINAPI * CheckTimer)(); + +int (FAR WINAPI * CloseBPQ32)(); + +char (FAR WINAPI * GetLOC)(); + +HMODULE ExtDriver; + +BOOL GetAPI() +{ + // This procedure must be called if you are using Dynamic Linking. It loads BPQ32 + // if necessary, and get the addresses of the API routines + + int err; + char Msg[256]; + +#ifdef EXTDLL + ExtDriver=GetModuleHandle("bpq32.dll"); +#else + ExtDriver=LoadLibrary("bpq32.dll"); +#endif + + if (ExtDriver == NULL) + { + err=GetLastError(); + wsprintf(Msg,"Error loading bpq32.dll - Error code %d",err); + + MessageBox(NULL,Msg,"BPQDEMO",MB_ICONSTOP); + + return(FALSE); + } + + GetPortTableEntryFromPortNum = (struct PORTCONTROL * (__stdcall *)(int PortSlot))GetProcAddress(ExtDriver,"_GetPortTableEntryFromPortNum@4"); + GetPortTableEntryFromSlot = (struct PORTCONTROL * (__stdcall *)(int PortSlot))GetProcAddress(ExtDriver,"_GetPortTableEntryFromSlot@4"); + + GETBPQAPI = (ULONG(__stdcall *)())GetProcAddress(ExtDriver,"_GETBPQAPI@0"); + GETMONDECODE = (ULONG(__stdcall *)())GetProcAddress(ExtDriver,"_GETMONDECODE@0"); + + GetFreeBuffs = (int (__stdcall *)(int stream))GetProcAddress(ExtDriver,"_GetFreeBuffs@0"); + TXCount = (int (__stdcall *)(int stream))GetProcAddress(ExtDriver,"_RXCount@4"); + RXCount = (int (__stdcall *)(int stream))GetProcAddress(ExtDriver,"_RXCount@4"); + MONCount = (int (__stdcall *)(int stream))GetProcAddress(ExtDriver,"_MONCount@4"); + GetCallsign = (int (__stdcall *)(int stream, char * callsign))GetProcAddress(ExtDriver,"_GetCallsign@8"); + GetBPQDirectory = (UCHAR *(__stdcall *)())GetProcAddress(ExtDriver,"_GetBPQDirectory@0"); + GetProgramDirectory = (UCHAR *(__stdcall *)())GetProcAddress(ExtDriver,"_GetProgramDirectory@0"); + + GetRegistryKey = (HKEY(__stdcall *)())GetProcAddress(ExtDriver,"_GetRegistryKey@0"); + GetRegistryKeyText = (UCHAR *(__stdcall *)())GetProcAddress(ExtDriver,"_GetRegistryKeyText@0"); + + GetSignOnMsg = (UCHAR *(__stdcall *)())GetProcAddress(ExtDriver,"_GetSignOnMsg@0"); + GetVersionString = (UCHAR *(__stdcall *)())GetProcAddress(ExtDriver,"_GetVersionString@0"); + GetConnectionInfo = (int (__stdcall *)(int, char *,int *, int *, int *,int *, int *))GetProcAddress(ExtDriver,"_GetConnectionInfo@28"); + GetStreamPID = (int (__stdcall *)(int Stream))GetProcAddress(ExtDriver,"_GetStreamPID@4"); + GetAttachedProcesses = (int (__stdcall *)())GetProcAddress(ExtDriver,"_GetAttachedProcesses@0"); + SessionControl = (int (__stdcall *)(int stream, int command, int param))GetProcAddress(ExtDriver,"_SessionControl@12"); + SetAppl = (int (__stdcall *)(int stream, int flags, int mask))GetProcAddress(ExtDriver,"_SetAppl@12"); + GetApplMask = (int (__stdcall *)(int Stream))GetProcAddress(ExtDriver,"_GetApplMask@4"); + GetApplFlags = (int (__stdcall *)(int Stream))GetProcAddress(ExtDriver,"_GetApplFlags@4"); + GetAllocationState = (int (__stdcall *)(int Stream))GetProcAddress(ExtDriver,"_GetAllocationState@4"); + + SessionState = (int (__stdcall *)(int, int *, int *))GetProcAddress(ExtDriver,"_SessionState@12"); + SessionStateNoAck = (int (__stdcall *)(int, int *))GetProcAddress(ExtDriver,"_SessionStateNoAck@8"); + SendMsg = (int (__stdcall *)(int stream, char * msg, int len))GetProcAddress(ExtDriver,"_SendMsg@12"); + SendRaw = (int (__stdcall *)(int port, char * msg, int len))GetProcAddress(ExtDriver,"_SendRaw@12"); + GetMsg = (int (__stdcall *)(int stream, char * msg, int * len, int * count ))GetProcAddress(ExtDriver,"_GetMsg@16"); + GetMsgPerl = (int (__stdcall *)(int stream, char * msg))GetProcAddress(ExtDriver,"_GetMsgPerl@8"); + GetRaw = (int (__stdcall *)(int stream, char * msg, int * len, int * count))GetProcAddress(ExtDriver,"_GetRaw@16"); + DecodeFrame = (int (__stdcall *)(char * msg, char * buffer, time_t Stamp))GetProcAddress(ExtDriver,"_DecodeFrame@12"); + SetTraceOptions = (int (__stdcall *)(long mask, int mtxparam, int mcomparam))GetProcAddress(ExtDriver,"_SetTraceOptions@12"); + FindFreeStream = (int (__stdcall *)())GetProcAddress(ExtDriver,"_FindFreeStream@0"); + AllocateStream= (int (__stdcall *)(int Stream))GetProcAddress(ExtDriver,"_AllocateStream@4"); + DeallocateStream = (int (__stdcall *)(int Stream))GetProcAddress(ExtDriver,"_DeallocateStream@4"); + + GetNumberofPorts = (int (__stdcall *)())GetProcAddress(ExtDriver,"_GetNumberofPorts@0"); + GetPortNumber = (int (__stdcall *)(int))GetProcAddress(ExtDriver,"_GetPortNumber@4"); + BPQSetHandle = (int (__stdcall *)(int Stream, HWND hWnd))GetProcAddress(ExtDriver,"_BPQSetHandle@8"); + + ConvFromAX25 = (int ( *)(unsigned char * incall, unsigned char * outcall))GetProcAddress(ExtDriver,"ConvFromAX25"); + ConvToAX25 = (BOOL ( *)(unsigned char * callsign, unsigned char * ax25call))GetProcAddress(ExtDriver,"ConvToAX25"); + GetNodeCall = (char * (__stdcall *) ())GetProcAddress(ExtDriver,"_GetNodeCall@0"); + ChangeSessionCallsign = (int (__stdcall *) (int Stream, unsigned char * AXCall))GetProcAddress(ExtDriver,"_ChangeSessionCallsign@8"); + GetApplNum = (int (__stdcall *)(int Stream))GetProcAddress(ExtDriver,"_GetApplNum@4"); + GetApplCall = (char * (__stdcall *) (int Appl))GetProcAddress(ExtDriver,"_GetApplCall@4"); + GetApplAlias = (char * (__stdcall *) (int Appl))GetProcAddress(ExtDriver,"_GetApplAlias@4"); + GetApplQual = (long (__stdcall *)(int Appl))GetProcAddress(ExtDriver,"_GetApplQual@4"); + GetApplName = (char * (__stdcall *) (int Appl))GetProcAddress(ExtDriver,"_GetApplName@4"); + SetApplCall = (BOOL (__stdcall *)(int Appl, char * NewCall))GetProcAddress(ExtDriver,"_SetApplCall@8"); + SetApplAlias = (BOOL (__stdcall *)(int Appl, char * NewCall))GetProcAddress(ExtDriver,"_SetApplAlias@8"); + SetApplQual = (BOOL (__stdcall *)(int Appl, int NewQual))GetProcAddress(ExtDriver,"_SetApplQual@8"); + + GetMinimizetoTrayFlag = (BOOL (__stdcall *)())GetProcAddress(ExtDriver,"_GetMinimizetoTrayFlag@0"); + AddTrayMenuItem = (int (__stdcall *)(HWND hWnd, char * Label))GetProcAddress(ExtDriver,"_AddTrayMenuItem@8"); + DeleteTrayMenuItem = (int (__stdcall *)(HWND hWnd))GetProcAddress(ExtDriver,"_DeleteTrayMenuItem@4"); + SetupTrayIcon = (BOOL (__stdcall *)())GetProcAddress(ExtDriver,"_SetupTrayIcon@0"); + + GetStartMinimizedFlag = (BOOL (__stdcall *)())GetProcAddress(ExtDriver,"_GetStartMinimizedFlag@0"); + WritetoConsole = (int (__stdcall *)(char *))GetProcAddress(ExtDriver,"_WritetoConsole@4"); + CheckTimer = (int (__stdcall *)(char *))GetProcAddress(ExtDriver,"_CheckTimer@0"); + CloseBPQ32 = (int (__stdcall *)(char *))GetProcAddress(ExtDriver,"_CloseBPQ32@0"); + GetLOC = (char * (__stdcall *)(char *))GetProcAddress(ExtDriver,"_GetLOC@0"); + return TRUE; +} + +#endif + +// +// Constants and equates for async operation +// + +#ifndef BPQWINMSG +#define BPQWINMSG + +static char BPQWinMsg[] = "BPQWindowMessage"; +extern UINT BPQMsg; + +// +// Values returned in lParam of Windows Message +// +#define BPQMonitorAvail 1 +#define BPQDataAvail 2 +#define BPQStateChange 4 + +#endif + diff --git a/.svn/pristine/19/198e636958d95c251edd4e02577dcfaa6e219619.svn-base b/.svn/pristine/19/198e636958d95c251edd4e02577dcfaa6e219619.svn-base new file mode 100644 index 0000000..ee6d7e0 --- /dev/null +++ b/.svn/pristine/19/198e636958d95c251edd4e02577dcfaa6e219619.svn-base @@ -0,0 +1,125 @@ + +#ifdef Kernel + +#define Vers 5,2,9,2 +#define Verstring "5.2.9.2\0" +#define Datestring "September 2012" +#define VerComments "G8BPQ Packet Switch V5.2.9.2\0" +#define VerCopyright "Copyright © 2001-2012 John Wiseman G8BPQ\0" +#define VerDesc "BPQ32 Switch\0" + +#endif + +#define KVers 6,0,24,65 +#define KVerstring "6.0.24.65\0" + +#ifdef CKernel + +#define Vers KVers +#define Verstring KVerstring +#define Datestring "February 2025" +#define VerComments "G8BPQ Packet Switch (C Version)" KVerstring +#define VerCopyright "Copyright © 2001-2025 John Wiseman G8BPQ\0" +#define VerDesc "BPQ32 Switch\0" +#define VerProduct "BPQ32" + +#endif + +#ifdef TermTCP + +#define Vers 1,0,16,2 +#define Verstring "1.0.16.2\0" +#define VerComments "Internet Terminal for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2011-2025 John Wiseman G8BPQ\0" +#define VerDesc "Simple TCP Terminal Program for G8BPQ Switch\0" +#define VerProduct "BPQTermTCP" + +#endif + +#ifdef BPQTerm + +#define Vers 2,2,5,2 +#define Verstring "2.2.5.2\0" +#define VerComments "Simple Terminal for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 1999-2025 John Wiseman G8BPQ\0" +#define VerDesc "Simple Terminal Program for G8BPQ Switch\0" +#define VerProduct "BPQTerminal" + +#endif + +#ifdef BPQTermMDI + +#define Vers 2,2,0,3 +#define Verstring "2.2.0.3\0" +#define VerComments "MDI Terminal for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 1999-2025 John Wiseman G8BPQ\0" +#define VerDesc "MDI Terminal Program for G8BPQ Switch\0" + +#endif + +#ifdef MAIL + +#define Vers KVers +#define Verstring KVerstring +#define VerComments "Mail server for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2009-2025 John Wiseman G8BPQ\0" +#define VerDesc "Mail server for G8BPQ's 32 Bit Switch\0" +#define VerProduct "BPQMail" + +#endif + +#ifdef HOSTMODES + +#define Vers 1,1,8,1 +#define Verstring "1.1.8.1\0" +//#define SPECIALVERSION "Test 3" +#define VerComments "Host Modes Emulator for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2009-2019 John Wiseman G8BPQ\0" +#define VerDesc "Host Modes Emulator for G8BPQ's 32 Bit Switch\0" +#define VerProduct "BPQHostModes" + +#endif + + +#ifdef UIUTIL + +#define Vers 0,1,3,1 +#define Verstring "0.1.3.1\0" +#define VerComments "Beacon Utility for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2011-2019 John Wiseman G8BPQ\0" +#define VerDesc "Beacon Utility for G8BPQ Switch\0" +#define VerProduct "BPQUIUtil" + +#endif + +#ifdef AUTH + +#define Vers 0,1,0,0 +#define Verstring "0.1.0.0\0" +#define VerComments "Password Generation Utility for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2011-2025 John Wiseman G8BPQ\0" +#define VerDesc "Password Generation Utility for G8BPQ Switch\0" + +#endif + +#ifdef APRS + +#define Vers KVers +#define Verstring KVerstring +#define VerComments "APRS Client for G8BPQ Switch\0" +#define VerCopyright "Copyright © 2012-2025 John Wiseman G8BPQ\0" +#define VerDesc "APRS Client for G8BPQ Switch\0" +#define VerProduct "BPQAPRS" + +#endif + +#ifdef CHAT + +#define Vers KVers +#define Verstring KVerstring +#define VerComments "Chat server for G8BPQ Packet Switch\0" +#define VerCopyright "Copyright © 2009-2025 John Wiseman G8BPQ\0" +#define VerDesc "Chat server for G8BPQ's 32 Bit Switch\0" +#define VerProduct "BPQChat" + +#endif diff --git a/.svn/pristine/19/19d07ae246cafe227e02586bf3fd98dda0a476eb.svn-base b/.svn/pristine/19/19d07ae246cafe227e02586bf3fd98dda0a476eb.svn-base new file mode 100644 index 0000000..102eb2e --- /dev/null +++ b/.svn/pristine/19/19d07ae246cafe227e02586bf3fd98dda0a476eb.svn-base @@ -0,0 +1,1720 @@ +/* +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 +{ + unsigned int Port; + 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(char * 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 < 7 || RawLen > 350) + { + ReleaseBuffer(monbuff); + FreeSemaphore(&Semaphore); + return 0; + } + + Stamp = monbuff->Timestamp; + + memcpy(Buffer, monbuff, RawLen); + + ReleaseBuffer(monbuff); + + FreeSemaphore(&Semaphore); + +//' 4 byte chain +//' 1 byte port - top bit = transmit +//' 2 byte length (LO-HI) + + Port = Buffer[4]; + + if (Port > 127) + { + RXFlag = FALSE; + Port = Port - 128; + } + else + { + RXFlag = TRUE; + } + + // 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(Buffer, 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 - 6; + + if (RXFlag || Loopflag) // Send transmitted frames if requested + { + + // + // Send raw data to any sockets that have requested Raw frames + // + + Buffer[6]=0; + + 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[6], 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}; + 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; + + // 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]-48]); + + 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--) + { + sprintf(ConnectMsg, "%s, %s", ConnectMsg, 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 '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'; + + 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; +} + diff --git a/.svn/pristine/1a/1a61fc04c88e7e28f322dfadaad63b36d53bbdf5.svn-base b/.svn/pristine/1a/1a61fc04c88e7e28f322dfadaad63b36d53bbdf5.svn-base new file mode 100644 index 0000000..32fd397 --- /dev/null +++ b/.svn/pristine/1a/1a61fc04c88e7e28f322dfadaad63b36d53bbdf5.svn-base @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual C++ Express 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UDPtoTCP", "UDPtoTCP\UDPtoTCP.vcproj", "{879D4FD3-82E0-429E-8A54-7AF4CA9FEF7D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {879D4FD3-82E0-429E-8A54-7AF4CA9FEF7D}.Debug|Win32.ActiveCfg = Debug|Win32 + {879D4FD3-82E0-429E-8A54-7AF4CA9FEF7D}.Debug|Win32.Build.0 = Debug|Win32 + {879D4FD3-82E0-429E-8A54-7AF4CA9FEF7D}.Release|Win32.ActiveCfg = Release|Win32 + {879D4FD3-82E0-429E-8A54-7AF4CA9FEF7D}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/.svn/pristine/1a/1ab747510373273fcceb1edcbff19b7f253dbcb3.svn-base b/.svn/pristine/1a/1ab747510373273fcceb1edcbff19b7f253dbcb3.svn-base new file mode 100644 index 0000000..015c433 --- /dev/null +++ b/.svn/pristine/1a/1ab747510373273fcceb1edcbff19b7f253dbcb3.svn-base @@ -0,0 +1,1490 @@ +//Microsoft Developer Studio generated resource script. +// +#include "BPQMailrc.h" + +// Generated Help ID header file +#define APSTUDIO_HIDDEN_SYMBOLS +#include "BPQMailrc.hm" +#undef APSTUDIO_HIDDEN_SYMBOLS + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "AFXRES.h" +#undef APSTUDIO_HIDDEN_SYMBOLS +#define MAILCHAT +#include "..\CommonSource\Versions.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PROPPAGE_LARGE DIALOG DISCARDABLE 0, 0, 235, 156 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Property Page" +FONT 8, "MS Sans Serif" +BEGIN +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO MOVEABLE PURE +BEGIN + IDD_PROPPAGE_LARGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 149 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +BPQMAIL DIALOG DISCARDABLE 120, 50, 294, 165 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "G8BPQ Mail and Chat Server 1.0.0.10 Beta July 2009" +CLASS "BPQMail" +FONT 8, "FixedSys" +BEGIN + LTEXT " User Callsign Stream Conf Queue",101,3,4,184, + 10 + LTEXT "UTC",IDC_STATIC,197,3,15,10 + LTEXT "Local",IDC_STATIC,241,3,21,10 + LTEXT "",IDC_UTC,215,3,25,10 + LTEXT "",IDC_LOCAL,269,3,25,10 + LISTBOX 100,2,16,190,130,WS_VSCROLL + LTEXT "Msgs",IDC_STATIC,197,21,40,10 + LTEXT "",IDC_MSGS,243,21,20,8 + LTEXT "Sysop Msgs",IDC_STATIC,197,32,40,10 + LTEXT "Held Msgs",IDC_STATIC,197,43,40,10 + LTEXT "",IDC_SYSOPMSGS,243,32,20,8 + LTEXT "",IDC_HELD,243,43,20,8 + LTEXT "SMTP Msgs",IDC_STATIC,197,54,40,10 + LTEXT "",IDC_SMTP,243,54,20,8 + LTEXT "Msg SEM Clashes",IDC_STATIC,197,70,63,10 + LTEXT "Alloc SEM Clashes",IDC_STATIC,197,81,68,10 + LTEXT "0",IDC_MSGSEM,274,70,20,10 + LTEXT "0",IDC_ALLOCSEM,274,81,20,10 + LTEXT "Con SEM Clashes",IDC_STATIC,197,92,68,10 + LTEXT "0",IDC_CONSEM,274,92,20,10 +END + +CONSOLEWINDOW DIALOG DISCARDABLE 17, 25, 400, 301 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Mail Console" +MENU CONSOLEMENU +CLASS "CONSOLEWINDOW" +FONT 8, "FixedSys" +BEGIN + EDITTEXT 118,24,228,348,15,ES_AUTOHSCROLL | ES_NOHIDESEL +END + +BPQMONWINDOW DIALOG DISCARDABLE 17, 25, 400, 300 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Mail Monitor" +MENU MENU_2 +CLASS "BPQMONWINDOW" +FONT 8, "FixedSys" +BEGIN + LISTBOX 121,6,25,290,109,LBS_MULTIPLESEL | LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_HSCROLL +END + +BPQMCWINDOW DIALOG DISCARDABLE 17, 25, 500, 300 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Multicast Monitor" +MENU MENU_1 +CLASS "BPQMCWINDOW" +FONT 8, "FixedSys" +BEGIN +END + +BPQDEBUGWINDOW DIALOG DISCARDABLE 17, 25, 400, 300 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Mail Debug Window" +MENU MENU_3 +CLASS "BPQDEBUGWINDOW" +FONT 8, "FixedSys" +BEGIN + LISTBOX 122,5,156,290,109,LBS_MULTIPLESEL | LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_HSCROLL +END + +102 DIALOG DISCARDABLE 0, 0, 385, 301 +STYLE WS_POPUP | WS_CAPTION | WS_VSCROLL | WS_HSCROLL | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Configuration" +FONT 8, "System" +BEGIN +END + +BBS_CONFIG DIALOG DISCARDABLE 0, 0, 381, 306 +STYLE WS_CHILD +FONT 8, "System" +BEGIN + LTEXT "BBS Call is the base callsign, without SSID. This is not necessarily the same as the Application Callsign defined in the BPQ32 configuration. SYSOP Call is the callsign used by the local console.", + IDC_STATIC,10,4,354,23 + LTEXT "The Application Number defines which BPQ32 Application gives access to the BBS. Note this is the APPLNumber (1-32) not an Application Mask, as used in many BPQ32 programs.", + IDC_STATIC,10,27,353,18 + LTEXT "The eMail Server Params configure the NNTP, SMTP and POP3 Servers, which allow normal Internet email and News clients to get messages from, and post mesages to the BBS. If you don't want to use this, set them to zero.. ", + IDC_STATIC,8,52,357,27 + LTEXT "Enable UI System activates FBB compatible UI broadcasting of Message Headers", + IDC_STATIC,8,80,357,15 + LTEXT "BBS Call",IDC_STATIC,7,105,37,12 + EDITTEXT IDC_BBSCall,83,103,47,14,ES_UPPERCASE + LTEXT "SYSOP Call",IDC_STATIC,138,105,42,12 + EDITTEXT IDC_SYSOPCALL,184,103,42,14,ES_UPPERCASE + CONTROL "Send SYSTEM Msgs to SYSOP call",IDC_SYSTOSYSOPCALL, + "Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,231, + 104,129,12 + LTEXT "H Route",IDC_STATIC,7,122,37,12 + EDITTEXT IDC_HRoute,83,120,119,13,ES_UPPERCASE + LTEXT "BBS Appl Number",IDC_STATIC,7,138,61,10 + EDITTEXT IDC_BBSAppl,83,136,29,12 + LTEXT "Streams",IDC_STATIC,130,138,32,10 + EDITTEXT IDC_BBSStreams,168,136,29,12 + CONTROL "Refuse Bulls",IDC_REFUSEBULLS,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,211,138,53,10 + CONTROL "Enable FBB UI System",IDC_ENABLEUI,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,6,157,87,10 + LTEXT "Send Mail For Beacons Every",IDC_STATIC,102,157,100,12 + EDITTEXT MAILFOR_MINS,205,156,22,12,ES_UPPERCASE + LTEXT "Minutes",IDC_STATIC,232,157,30,12 + PUSHBUTTON "Config UI Ports and Digis",IDC_UICONFIG,273,155,91,13 + LTEXT "eMail Server Params:",IDC_STATIC,7,227,135,10 + LTEXT "NNTP Port",IDC_STATIC,7,243,39,12 + EDITTEXT IDC_NNTPPort,47,241,29,14 + LTEXT "POP3 Port",IDC_STATIC,90,243,37,12 + EDITTEXT IDC_POP3Port,130,241,29,14 + LTEXT "SMTP Port",IDC_STATIC,170,243,37,12 + EDITTEXT IDC_SMTPPort,210,241,29,14 + CONTROL "Enable Remote Access",1009,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,249,240,91,14 + PUSHBUTTON "Save",IDC_BBSSAVE,159,281,50,14,BS_CENTER | BS_VCENTER + CONTROL "Don't hold messages from new users",IDC_DONTHOLDNEW, + "Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,5, + 189,134,12 + CONTROL "Forward Messages to BBS Call",IDC_FORWARDTOBBS,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,145,189,114, + 12 + CONTROL "Redirect msgs to BBS Call to SYSOP Call", + IDC_BBSTOSYSOPCALL,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,213,120,146,12 + CONTROL "Don't Request Home BBS",IDC_NOHOMEBBS,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,260,174,99,12 + CONTROL "Don't Request Name",IDC_NONAME,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,176,174,79,12 + EDITTEXT IDC_AMPR,72,258,119,13 + LTEXT "AMPR Address",IDC_STATIC,6,260,60,12 + CONTROL "Send ampr.org mail to AMPR host",IDC_FORWARDAMPR, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,201,258,124,14 + CONTROL "Allow users to kill T msgs",IDC_USERRKILLT,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,264,189,95,12 + CONTROL "Set Don't add WINLINK.ORG flag on new users", + IDC_DEFAULTNOWINLINK,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,5,174,162,12 + CONTROL "Don't allow unknown users",IDC_KNOWNUSERS,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,277,138,98,10 + CONTROL "Don't Check From Calls",IDC_DONTCHECKFROM,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,5,204,95,12 +END + +CHAT_CONFIG DIALOG DISCARDABLE 0, 0, 381, 266 +STYLE WS_CHILD +FONT 8, "System" +BEGIN + LTEXT "Chat Appl Number",-1,115,109,61,8 + EDITTEXT 2001,185,106,29,14 + LTEXT "Nodes to link to",-1,115,129,53,8 + EDITTEXT 2002,185,125,87,88,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "Save",2100,172,226,50,14,BS_CENTER | BS_VCENTER + LTEXT "The Application Number defines which BPQ32 Application gives access to the Chat Server. Note this is the APPLNumber (1-32) not an Application Mask, as uses in many BPQ32 programs.", + -1,10,10,353,18 + LTEXT "The Nodes to link to box defines which other Chat Nodes should be connected to, or from which connections may be accepted. The format is ALIAS:CALL, eg BPQCHT:G8BPQ-4. Note these must be directly connectable - ie in your NODES table.", + -1,10,30,355,25 + LTEXT "The Callsign of the Chat Node is not defined here - It is obtained from the BPQ32 APPLCALL parameter corresponding to the Chat Appl Number.", + -1,10,60,360,25 +END + +IDD_USEREDIT DIALOGEX 20, 20, 293, 281 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Edit User" +FONT 8, "System", 0, 0, 0x1 +BEGIN + COMBOBOX 5000,7,10,57,123,CBS_SIMPLE | CBS_SORT | CBS_UPPERCASE | + WS_VSCROLL | WS_TABSTOP + CONTROL "BBS",IDC_BBSFLAG,"Button",BS_AUTOCHECKBOX | BS_LEFT | + WS_TABSTOP,72,9,57,10 + CONTROL "PMS",IDC_PMSFLAG,"Button",BS_AUTOCHECKBOX | BS_LEFT | + WS_TABSTOP,72,22,57,10 + CONTROL "Sysop",IDC_SYSOP,"Button",BS_AUTOCHECKBOX | BS_LEFT | + WS_TABSTOP,72,35,57,10 + CONTROL "Expert",IDC_EXPERT,"Button",BS_AUTOCHECKBOX | BS_LEFT | + WS_TABSTOP,72,48,57,10 + CONTROL "Excluded",IDC_EXCLUDED,"Button",BS_AUTOCHECKBOX | + BS_LEFT | WS_TABSTOP,72,61,57,10 + CONTROL "Hold Messages",IDC_HOLDMAIL,"Button",BS_AUTOCHECKBOX | + BS_LEFT | WS_TABSTOP,72,74,65,10 + CONTROL "NTS MPS",IDC_NTSMPS,"Button",BS_AUTOCHECKBOX | BS_LEFT | + WS_TABSTOP,72,87,69,10 + CONTROL "Permit Email",IDC_EMAIL,"Button",BS_AUTOCHECKBOX | + BS_LEFT | WS_TABSTOP,163,9,57,10 + CONTROL "Poll RMS for SSID's",IDC_POLLRMS,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,163,23,78,8,0, + HIDC_POLLRMS + EDITTEXT RMS_SSID1,244,21,13,12,ES_AUTOHSCROLL + EDITTEXT RMS_SSID2,258,21,13,12,ES_AUTOHSCROLL + EDITTEXT RMS_SSID3,272,21,13,12,ES_AUTOHSCROLL + CONTROL "RMS Express User",RMS_EXPRESS_USER,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,163,35,77,10 + LTEXT "Last Listed",IDC_STATIC,163,87,38,12 + EDITTEXT IDC_LASTLISTED,248,85,29,12,ES_AUTOHSCROLL + LTEXT "Name",IDC_STATIC,7,184,35,14 + EDITTEXT IDC_NAME,55,182,96,14,ES_AUTOHSCROLL + LTEXT "Password",IDC_STATIC,7,202,35,14 + EDITTEXT IDC_PASSWORD,55,200,89,14,ES_AUTOHSCROLL + LTEXT "QTH",IDC_STATIC,7,220,35,14 + EDITTEXT IDC_QTH,55,218,130,14,ES_AUTOHSCROLL + LTEXT "Home BBS",IDC_STATIC,7,238,40,14 + EDITTEXT IDC_HOMEBBS,55,236,162,14,ES_UPPERCASE | ES_AUTOHSCROLL + PUSHBUTTON "Add user",IDC_ADDUSER,79,257,42,14,BS_CENTER | + BS_VCENTER + PUSHBUTTON "Delete user",5101,124,257,42,14,BS_CENTER | BS_VCENTER + PUSHBUTTON "Save",5102,169,257,42,14,BS_CENTER | BS_VCENTER + LTEXT "Connects in",IDC_STATIC,10,131,45,10 + LTEXT "Connects out",IDC_STATIC,10,142,45,10 + LTEXT "0",CONN_IN,57,131,26,10,0,WS_EX_RIGHT + LTEXT "0",CONN_OUT,57,142,26,10,0,WS_EX_RIGHT + LTEXT "Msgs in",IDC_STATIC,87,131,35,10 + LTEXT "Msgs out",IDC_STATIC,87,142,35,10 + LTEXT "0",MSGS_IN,125,131,26,10,0,WS_EX_RIGHT + LTEXT "0",MSGS_OUT,125,142,26,10,0,WS_EX_RIGHT + LTEXT "Rejects in",IDC_STATIC,159,131,35,10 + LTEXT "Rejects out",IDC_STATIC,159,142,40,10 + LTEXT "0",REJECTS_IN,201,131,26,10,0,WS_EX_RIGHT + LTEXT "0",REJECTS_OUT,201,142,26,10,0,WS_EX_RIGHT + LTEXT "Bytes out",IDC_STATIC,10,164,35,10 + LTEXT "",BYTES_OUT,45,164,38,10,0,WS_EX_RIGHT + LTEXT "Bytes in",IDC_STATIC,10,153,35,10 + LTEXT "0",BYTES_IN,45,153,38,10,0,WS_EX_RIGHT + LTEXT "Last Connect",IDC_STATIC,87,153,45,10 + LTEXT "Never",LASTCONNECT,134,153,93,10 + CONTROL "Don't add @winlink.org",NO_WINLINKdotORG,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,163,47,100,10 + LTEXT "ZIP",IDC_STATIC,189,221,12,11 + EDITTEXT IDC_UZIP,207,219,38,12,ES_AUTOHSCROLL + CONTROL "Allow sending Bulls",ALLOW_BULLS,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,163,61,100,10 + CONTROL "Include SYSOP msgs in LM",IDC_SYSOP_IN_LM,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_DISABLED | WS_TABSTOP,163, + 74,100,10 + EDITTEXT IDC_CMSPASS,187,201,88,14,ES_PASSWORD + CONTROL "CMS Pass",IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | + WS_GROUP,149,203,34,14 + CONTROL "Send Mail For to APRS ssid",IDC_APRSMFOR,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,72,113,108,10 + EDITTEXT IDC_APRSSSID,182,112,16,12,ES_AUTOHSCROLL + CONTROL "Redirect to RMS",IDC_RMSREDIRECT,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,72,100,69,10 +END + +ISP_CONFIG DIALOG DISCARDABLE 26, 5, 381, 284 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "System" +BEGIN + EDITTEXT 3001,95,110,100,15 + LTEXT "SMTP Server",-1,10,130,53,13 + LTEXT "The system relies on having an email domain which supports forwarding of all email addresses to a fixed mailbox. For example, I could register domain mycall.org.uk, and have any mail sent to anyone@mycall.org.uk forwarded to mymailbox@myisp.com", + 1,10,34,349,25 + LTEXT "POP3 Server",-1,10,146,61,13 + LTEXT "ISP Account Name",-1,10,163,63,13 + EDITTEXT IDC_ISPSMTPName,95,127,148,15 + EDITTEXT 3004,95,144,147,15 + EDITTEXT 3006,95,161,147,15 + LTEXT "Port",-1,250,147,18,13 + LTEXT "Port",-1,250,130,19,13 + EDITTEXT 3005,275,144,29,15 + EDITTEXT 3003,275,127,29,15 + LTEXT "This page configures the BBS <> Internet Mail Gateway. The Gateway allows local users to send messages to Internet email addresses, and get replies from those messages", + -1,10,10,353,20 + LTEXT "My Domain",-1,10,112,51,8 + LTEXT "ISP Account Password",-1,10,180,80,13 + EDITTEXT 3007,95,178,147,15,ES_PASSWORD + LTEXT "POP3 Poll Interval (Seconds)",-1,10,216,80,18 + EDITTEXT 3008,95,219,29,12 + LTEXT "WARNING This feature my be illegal in some administrations. Make sure your authorities permit forwarding mail from the Amateur Service before enabling it", + -1,10,64,348,15 + CONTROL "Enable Internet Gateway",3000,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_LEFT | WS_TABSTOP,10,90,90,15 + DEFPUSHBUTTON "Save",3100,165,261,57,17,BS_CENTER | BS_VCENTER + CONTROL "SMTP Server Requires Authentication",ISP_SMTP_AUTH, + "Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_LEFT | + BS_MULTILINE | WS_TABSTOP,250,159,69,32 + EDITTEXT SMTP_EHELO,95,196,100,15 + LTEXT "SMTP ""EHELO"" Domain",-1,10,198,80,8 +END + +IDD_FORWARDING DIALOG DISCARDABLE 20, 20, 457, 327 +STYLE WS_POPUP | WS_CAPTION | WS_VSCROLL | WS_HSCROLL | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Manage Forwarding" +FONT 8, "System" +BEGIN + LTEXT "Forwarding Rules are define here. BBS Records are created by setting the BBS flag on a User Record.", + IDC_STATIC,5,7,368,10 + LTEXT "The Enable Forwarding flag allows you to disable forwarding without losing the messages to be forwarded. ", + IDC_STATIC,5,20,373,8 + GROUPBOX "Global Params",IDC_STATIC,2,33,114,263 + LTEXT "Max Size to Send",IDC_STATIC,5,50,58,12 + EDITTEXT IDC_MAXSEND,66,49,26,12,ES_AUTOHSCROLL + LTEXT "Max Size to Recv",IDC_STATIC,5,68,58,12 + EDITTEXT IDC_MAXRECV,66,66,26,12,ES_AUTOHSCROLL + CONTROL "Warn if no route for P or T",IDC_WARNNOROUTE,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,5,101,103,8 + LTEXT "Aliases",IDC_STATIC,5,165,57,13 + EDITTEXT IDC_ALIAS,4,183,99,81,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + CONTROL "Readdress Locally Input",IDC_READDRESSLOCAL,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_DISABLED | WS_TABSTOP,4,267,97,8 + CONTROL "Readdress Received",IDC_READDRESSRXED,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_DISABLED | WS_TABSTOP,4,281,97,8 + GROUPBOX "Per-BBS Params",IDC_STATIC,121,33,326,263 + LTEXT "BBS",IDC_STATIC,128,46,57,10 + COMBOBOX IDC_BBS,122,59,50,60,CBS_SIMPLE | CBS_OEMCONVERT | + CBS_SORT | CBS_NOINTEGRALHEIGHT | WS_VSCROLL + LTEXT "To Calls:",IDC_STATIC,186,46,40,10 + EDITTEXT IDC_TOCALLS,177,59,50,60,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + LTEXT "AT Calls:",IDC_STATIC,242,46,43,10 + EDITTEXT IDC_ATCALLS,233,59,50,60,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + LTEXT "Times",IDC_STATIC,299,46,30,13 + EDITTEXT IDC_FWDTIMES,288,59,50,60,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + LTEXT "Connect Script",IDC_STATIC,348,46,57,13 + EDITTEXT IDC_CALL,343,59,92,60,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL + LTEXT "Hierarchical Routes (Flood Bulls)",IDC_STATIC,141,126, + 114,12 + EDITTEXT IDC_HROUTES,122,140,148,60,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL | WS_HSCROLL + LTEXT "HR (Personals and Directed Bulls)",IDC_STATIC,281,126, + 125,12 + EDITTEXT IDC_HROUTESP,280,141,151,60,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL | WS_HSCROLL + LTEXT "Interval (Secs)",IDC_STATIC,359,205,48,8 + EDITTEXT IDC_FWDINT,253,204,22,12,ES_AUTOHSCROLL + CONTROL "Enable Forwarding",4000,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_LEFT | WS_TABSTOP,122,206,72,8 + CONTROL "Request Reverse",4006,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_LEFT | WS_TABSTOP,286,206,67,8 + CONTROL "Send new messages without waiting for poll timer", + IDC_SENDNEW,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | + BS_MULTILINE | WS_TABSTOP,122,221,182,8 + LTEXT "BBS HA",IDC_STATIC,125,242,30,12 + EDITTEXT IDC_BBSHA,162,240,134,12,ES_UPPERCASE | ES_AUTOHSCROLL + LTEXT "Max Block",IDC_STATIC,368,241,37,12 + EDITTEXT IDC_MAXBLOCK,407,240,26,12,ES_AUTOHSCROLL + CONTROL "Send Personal Mail Only",IDC_PERSONALONLY,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,321,221,108,8 + CONTROL "Allow Binary",IDC_ALLOWCOMP,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,125,260,58,8 + CONTROL "Use B1 Protocol",IDC_USEB1,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,185,260,72,8 + CONTROL "Use B2 Protocol",IDC_USEB2,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,261,260,67,8 + DEFPUSHBUTTON "? Help",IDC_HRHELP,381,6,33,11,BS_CENTER | BS_VCENTER | + NOT WS_TABSTOP + DEFPUSHBUTTON "Save",4100,352,303,57,17,BS_CENTER | BS_VCENTER + LTEXT "Interval (Secs)",IDC_STATIC,198,206,48,8 + EDITTEXT IDC_REVFWDINT,407,205,23,12,ES_AUTOHSCROLL + CONTROL "Use Local Time ",IDC_USELOCALTIME,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,5,116,103,8 + CONTROL "Send ctrl/Z instead of /ex",IDC_CTRLZ,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,333,260,100,8 + LTEXT "Max Bull Age",IDC_STATIC,5,86,58,12 + EDITTEXT IDC_MAXAGE,66,84,26,12,ES_AUTOHSCROLL + CONTROL "FBB Blocked",IDC_BLOCKED,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,301,241,61,8 + PUSHBUTTON "Copy From BBS",COPYCONFIG,160,303,57,17,BS_CENTER | + BS_VCENTER + EDITTEXT COPYFROMCALL,227,303,35,17,ES_UPPERCASE | ES_AUTOHSCROLL + CONTROL "Send P to more than 1 BBS",IDC_MULTIP,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,5,130,103,8 + LTEXT "Incoming Connect Timeout",IDC_STATIC,125,278,95,12 + EDITTEXT IDC_CONTIMEOUT,219,276,22,12,ES_AUTOHSCROLL + CONTROL "Use 4 Char Continent Codes",IDC_FOURCHARCONTINENT, + "Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,6,146,103,8 +END + +IDD_USERADDED_BOX DIALOG DISCARDABLE 176, 132, 129, 68 +STYLE DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_THICKFRAME +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "Ok",0,47,48,36,17,BS_CENTER | BS_VCENTER + LTEXT "Label0",5050,5,10,117,32 +END + +IDD_MSGEDIT DIALOGEX 20, 20, 421, 298 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Edit Message" +FONT 8, "System", 0, 0, 0x1 +BEGIN + RTEXT "Msg",IDC_STATIC,7,7,19,8 + LISTBOX 0,33,7,65,81,LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_MSGTYPE,125,9,25,55,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_MSGSTATUS,185,9,25,55,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + RTEXT "From",IDC_STATIC,7,98,19,8 + EDITTEXT 6001,33,95,65,14,ES_UPPERCASE | ES_AUTOHSCROLL + RTEXT "BID",IDC_STATIC,7,114,19,8 + EDITTEXT 6002,33,111,65,14,ES_AUTOHSCROLL + RTEXT "To",IDC_STATIC,7,132,19,8 + EDITTEXT 6003,33,128,65,14,ES_UPPERCASE | ES_AUTOHSCROLL + RTEXT "Via",IDC_STATIC,4,190,25,8 + EDITTEXT 6004,33,186,177,14,ES_UPPERCASE | ES_AUTOHSCROLL + RTEXT "Subject",IDC_STATIC,4,206,25,8 + EDITTEXT 6005,33,203,177,14,ES_AUTOHSCROLL + LTEXT "Status",IDC_STATIC,160,10,20,8 + RTEXT "Sent",IDC_STATIC,116,96,32,8 + EDITTEXT 6018,153,95,57,14,ES_AUTOHSCROLL + RTEXT "Received",IDC_STATIC,116,113,32,8 + EDITTEXT 6019,153,111,57,14,ES_AUTOHSCROLL + RTEXT "Last Changed",IDC_STATIC,103,130,45,8 + EDITTEXT 6021,153,128,57,14,ES_AUTOHSCROLL + RTEXT "Size",IDC_STATIC,116,147,32,8 + EDITTEXT 6020,153,145,55,14,ES_AUTOHSCROLL + LTEXT "Type",IDC_STATIC,105,10,17,8 + CONTROL "",25,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,7,35,10 + CONTROL "",26,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,17,35,10 + CONTROL "",27,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,27,35,10 + CONTROL "",28,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,37,35,10 + CONTROL "",29,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,47,35,10 + CONTROL "",30,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,57,35,10 + CONTROL "",31,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,67,35,10 + CONTROL "",32,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,77,35,10 + CONTROL "",33,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,87,35,10 + CONTROL "",34,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,97,35,10 + CONTROL "",35,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,107,35,10 + CONTROL "",36,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,117,35,10 + CONTROL "",37,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,127,35,10 + CONTROL "",38,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,137,35,10 + CONTROL "",39,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,147,35,10 + CONTROL "",40,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,157,35,10 + CONTROL "",41,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,167,35,10 + CONTROL "",42,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,177,35,10 + CONTROL "",43,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,187,35,10 + CONTROL "",44,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,197,35,10 + CONTROL "",45,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,208,35,10 + CONTROL "",46,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,218,35,10 + CONTROL "",47,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,228,35,10 + CONTROL "",48,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,238,35,10 + CONTROL "",49,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,221,248,35,10 + CONTROL "",50,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,7,35,10 + CONTROL "",51,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,17,35,10 + CONTROL "",52,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,27,35,10 + CONTROL "",53,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,37,35,10 + CONTROL "",54,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,47,35,10 + CONTROL "",55,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,57,35,10 + CONTROL "",56,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,67,35,10 + CONTROL "",57,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,77,35,10 + CONTROL "",58,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,87,35,10 + CONTROL "",59,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,97,35,10 + CONTROL "",60,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,107,35,10 + CONTROL "",61,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,117,35,10 + CONTROL "",62,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,127,35,10 + CONTROL "",63,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,137,35,10 + CONTROL "",64,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,147,35,10 + CONTROL "",65,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,157,35,10 + CONTROL "",66,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,167,35,10 + CONTROL "",67,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,177,35,10 + CONTROL "",68,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,187,35,10 + CONTROL "",69,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,197,35,10 + CONTROL "",70,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,207,35,10 + CONTROL "",71,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,217,35,10 + CONTROL "",72,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,227,35,10 + CONTROL "",73,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,237,35,10 + CONTROL "",74,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,260,247,35,10 + CONTROL "",75,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,7,35,10 + CONTROL "",76,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,17,35,10 + CONTROL "",77,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,27,35,10 + CONTROL "",78,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,37,35,10 + CONTROL "",79,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,47,35,10 + CONTROL "",80,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,57,35,10 + CONTROL "",81,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,67,35,10 + CONTROL "",82,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,77,35,10 + CONTROL "",83,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,87,35,10 + CONTROL "",84,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,97,35,10 + CONTROL "",85,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,107,35,10 + CONTROL "",86,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,117,35,10 + CONTROL "",87,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,127,35,10 + CONTROL "",88,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,137,35,10 + CONTROL "",89,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,147,35,10 + CONTROL "",90,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,157,35,10 + CONTROL "",91,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,167,35,10 + CONTROL "",92,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,177,35,10 + CONTROL "",93,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,187,35,10 + CONTROL "",94,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,197,35,10 + CONTROL "",95,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,207,35,10 + CONTROL "",96,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,217,35,10 + CONTROL "",97,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,227,35,10 + CONTROL "",98,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,237,35,10 + CONTROL "",99,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,299,247,35,10 + CONTROL "",100,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,7,35,10 + CONTROL "",101,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,18,35,10 + CONTROL "",102,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,27,35,10 + CONTROL "",103,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,37,35,10 + CONTROL "",104,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,47,35,10 + LTEXT "Key",IDC_STATIC,317,267,35,9 + CONTROL "Dont Send",205,"Button",BS_3STATE | BS_CENTER | + BS_PUSHLIKE,262,281,43,10 + CONTROL "Send",206,"Button",BS_3STATE | BS_CENTER | BS_PUSHLIKE, + 311,280,35,10 + CONTROL "Sent",207,"Button",BS_3STATE | BS_CENTER | BS_PUSHLIKE, + 353,280,35,10 + DEFPUSHBUTTON "Edit Text",IDC_EDITTEXT,24,275,38,15,BS_CENTER | + BS_VCENTER + DEFPUSHBUTTON "Print",IDC_PRINTMSG,150,275,24,15,BS_CENTER | + BS_VCENTER + RTEXT "Filter From",IDC_STATIC,104,30,38,8 + EDITTEXT FILTER_FROM,147,28,62,14,ES_UPPERCASE | ES_AUTOHSCROLL + EDITTEXT FILTER_TO,147,44,62,14,ES_UPPERCASE | ES_AUTOHSCROLL + EDITTEXT FILTER_VIA,147,60,62,14,ES_UPPERCASE | ES_AUTOHSCROLL,0, + HFILTER_VIA + RTEXT "Filter To",IDC_STATIC,104,46,38,8 + RTEXT "Filter Via",IDC_STATIC,104,62,38,8 + DEFPUSHBUTTON "Save",IDC_SAVEMSG,67,275,24,15,BS_CENTER | BS_VCENTER + DEFPUSHBUTTON "Save to File",IDC_SAVETOFILE,96,275,49,15,BS_CENTER | + BS_VCENTER + DEFPUSHBUTTON "Export",IDC_EXPORT,179,275,32,15,BS_CENTER | BS_VCENTER + EDITTEXT EMAILFROM,33,169,177,14,ES_UPPERCASE | ES_AUTOHSCROLL + RTEXT "Email From",IDC_STATIC,4,168,25,17 + CONTROL "",105,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,57,35,10 + CONTROL "",106,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,67,35,10 + CONTROL "",107,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,77,35,10 + CONTROL "",108,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,87,35,10 + CONTROL "",109,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,97,35,10 + CONTROL "",110,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,107,35,10 + CONTROL "",111,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,118,35,10 + CONTROL "",112,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,127,35,10 + CONTROL "",113,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,137,35,10 + CONTROL "",114,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,147,35,10 + CONTROL "",115,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,157,35,10 + CONTROL "",116,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,167,35,10 + CONTROL "",117,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,177,35,10 + CONTROL "",118,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,187,35,10 + CONTROL "",119,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,197,35,10 + CONTROL "",120,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,207,35,10 + CONTROL "",121,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,217,35,10 + CONTROL "",122,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,227,35,10 + CONTROL "",123,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,237,35,10 + CONTROL "",124,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,337,247,35,10 + CONTROL "",148,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,237,35,10 + CONTROL "",125,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,8,35,10 + CONTROL "",126,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,17,35,10 + CONTROL "",127,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,27,35,10 + CONTROL "",128,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,37,35,10 + CONTROL "",129,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,47,35,10 + CONTROL "",130,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,57,35,10 + CONTROL "",131,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,67,35,10 + CONTROL "",132,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,77,35,10 + CONTROL "",133,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,87,35,10 + CONTROL "",134,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,97,35,10 + CONTROL "",135,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,108,35,10 + CONTROL "",136,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,117,35,10 + CONTROL "",137,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,127,35,10 + CONTROL "",138,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,137,35,10 + CONTROL "",139,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,147,35,10 + CONTROL "",140,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,157,35,10 + CONTROL "",141,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,167,35,10 + CONTROL "",150,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,247,35,10 + CONTROL "",142,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,177,35,10 + CONTROL "",143,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,187,35,10 + CONTROL "",144,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,197,35,10 + CONTROL "",145,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,207,35,10 + CONTROL "",146,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,217,35,10 + CONTROL "",147,"Button",BS_AUTO3STATE | BS_LEFT | BS_PUSHLIKE | + WS_TABSTOP,377,227,35,10 + EDITTEXT FILTER_BID,147,76,62,14,ES_UPPERCASE | ES_AUTOHSCROLL,0, + HFILTER_BID + RTEXT "Filter BID",IDC_STATIC,104,78,38,8 +END + +WELCOMEMSG DIALOG DISCARDABLE 26, 5, 365, 281 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "System" +BEGIN + LTEXT "Normal User Welcome Message",IDC_STATIC,5,7,130,8 + EDITTEXT IDM_USERMSG,5,20,340,50,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "New User Welcome Message",IDC_STATIC,5,75,130,8 + EDITTEXT IDM_NEWUSERMSG,5,89,340,65,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "Expert User Welcome Message",IDC_STATIC,5,159,130,8 + EDITTEXT IDM_EXPERTUSERMSG,5,172,340,12,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "Save",IDM_MSGSAVE,166,264,50,14,BS_CENTER | BS_VCENTER + LTEXT "$U : Callsign of the user $I : First name of the user $X Messages for user $x Unread messages", + IDC_STATIC,0,222,365,8 + LTEXT "$L : Number of the latest message $N : Number of active messages. $Z : Last message read by user", + IDC_STATIC,0,234,365,10 + LTEXT "Signoff Message",IDC_STATIC,5,190,130,8 + EDITTEXT IDM_SIGNOFF,5,202,340,12,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "$F : Number of mesages to be forwarded to user (if a BBS)", + IDC_STATIC,0,246,365,10 +END + +BBSPROMPTS DIALOG DISCARDABLE 26, 5, 381, 266 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "System" +BEGIN + LTEXT "Normal User Prompt",IDC_STATIC,5,7,130,8 + EDITTEXT IDM_USERMSG,5,20,340,35,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "New User Prompt",IDC_STATIC,5,59,130,8 + EDITTEXT IDM_NEWUSERMSG,5,70,340,35,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "Expert User Prompt",IDC_STATIC,5,110,130,8 + EDITTEXT IDM_EXPERTUSERMSG,5,120,340,35,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "Save",IDM_PROMPTSAVE,166,250,50,14,BS_CENTER | + BS_VCENTER + LTEXT "$U : Callsign of the user $I : First name of the user $X Messages for user $x Unread messages", + IDC_STATIC,2,165,353,8 + LTEXT "$L : Number of the latest message $N : Number of active messages. $Z : Last message read by user", + IDC_STATIC,3,175,347,10 +END + +IDD_MAINTRESULTS DIALOG DISCARDABLE 0, 0, 181, 114 +STYLE DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_THICKFRAME +CAPTION "Maintenance Results" +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "OK",IDOK,66,91,50,14 + LTEXT "Killed Messsages Removed",IDC_STATIC,11,10,103,10 + LTEXT "Messages Killed",IDC_STATIC,11,21,103,10 + LTEXT "Live Messages",IDC_STATIC,11,32,104,10 + LTEXT "Total Messages",IDC_STATIC,11,43,103,10 + LTEXT "BIDs Removed",IDC_STATIC,11,58,103,10 + LTEXT "BIDs Left",IDC_STATIC,11,70,103,10 + LTEXT "Static",IDC_REMOVED,146,10,30,10 + LTEXT "Static",IDC_KILLED,146,21,30,10 + LTEXT "Static",IDC_LIVE,146,32,30,10 + LTEXT "Static",IDC_TOTAL,146,43,30,10 + LTEXT "Static",IDC_BIDSREMOVED,146,58,30,10 + LTEXT "Static",IDC_BIDSLEFT,146,69,30,10 +END + +IDD_EDITWP DIALOG DISCARDABLE 0, 0, 398, 265 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "EDIT WP Record" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_WP,9,9,76,190,CBS_SIMPLE | CBS_SORT | CBS_UPPERCASE | + WS_VSCROLL | WS_TABSTOP + LTEXT "Name",-1,100,11,47,10 + EDITTEXT IDC_WPNAME,165,10,128,12,ES_AUTOHSCROLL + LTEXT "Home BBS 1",-1,100,26,53,10 + EDITTEXT IDC_HOMEBBS1,165,25,216,12,ES_UPPERCASE | ES_AUTOHSCROLL + LTEXT "Home BBS 2",-1,100,41,53,10 + EDITTEXT IDC_HOMEBBS2,165,40,216,12,ES_UPPERCASE | ES_AUTOHSCROLL + LTEXT "QTH 1",-1,100,56,47,10 + EDITTEXT IDC_QTH1,165,55,215,12,ES_AUTOHSCROLL + LTEXT "QTH 2",-1,100,71,47,10 + EDITTEXT IDC_QTH2,165,70,215,12,ES_AUTOHSCROLL + LTEXT "ZIP 1",-1,100,86,47,12 + EDITTEXT IDC_ZIP1,165,85,128,12,ES_AUTOHSCROLL + LTEXT "ZIP 2",-1,100,101,47,12 + EDITTEXT IDC_ZIP2,165,100,128,12,ES_AUTOHSCROLL + LTEXT "Last Seen",-1,100,121,47,12 + EDITTEXT IDC_LASTSEEN,165,120,128,12,ES_AUTOHSCROLL + LTEXT "Last Modified",-1,100,136,47,12 + EDITTEXT IDC_LASTMODIFIED,165,135,128,12,ES_AUTOHSCROLL + LTEXT "Type",-1,100,156,47,12 + EDITTEXT IDC_TYPE,165,155,59,12,ES_AUTOHSCROLL + LTEXT "Changed",-1,100,171,47,12 + LTEXT "Seen",-1,100,186,47,12 + EDITTEXT IDC_CHANGED,165,170,59,12,ES_AUTOHSCROLL + EDITTEXT IDC_SEEN,165,185,59,12,ES_AUTOHSCROLL + PUSHBUTTON "Delete",IDC_DELETEWP,106,215,56,17,BS_CENTER | + BS_VCENTER + PUSHBUTTON "Save",IDC_SAVEWP,171,216,56,17,BS_CENTER | BS_VCENTER +END + +IDD_UICONFIG DIALOG DISCARDABLE 0, 0, 400, 151 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "UI Configuration" +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "Save",IDOK,7,98,50,14 + PUSHBUTTON "Cancel",IDCANCEL,342,98,50,14 + LTEXT "Mailfor Header",IDC_STATIC,5,5,50,10 + EDITTEXT IDC_MAILFOR,61,3,331,12,ES_AUTOHSCROLL + LTEXT "Send: Mail For Headers Null Mailfor",IDC_STATIC,242, + 25,127,10 + LTEXT "(use \\r to insert newline in message)",IDC_STATIC,62, + 16,125,10 +END + +IDD_CHATCOLCONFIG DIALOG DISCARDABLE 0, 0, 224, 120 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Chat Colour Configuration" +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "Save",IDOK,50,95,50,14 + PUSHBUTTON "Close",IDCANCEL,120,95,50,14 + COMBOBOX IDC_CHATCALLS,10,5,100,60,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_CHATCOLOURS,115,5,100,60,CBS_DROPDOWNLIST | + CBS_OWNERDRAWFIXED | WS_VSCROLL | WS_TABSTOP +END + +IDD_UPDATECHATMAP DIALOG DISCARDABLE 0, 0, 274, 146 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Update Chat Map" +FONT 8, "System" +BEGIN + LTEXT "Click Help for full information about the Chat Network Map", + IDC_STATIC,5,10,195,15 + DEFPUSHBUTTON "? Help",IDC_MAPHELP,220,10,33,11,BS_CENTER | BS_VCENTER + LTEXT "Position",IDC_STATIC,5,30,35,10 + EDITTEXT IDC_MAPPOSITION,5,45,145,15,ES_AUTOHSCROLL + LTEXT "Popup Box Text. ",IDC_STATIC,5,65,70,9 + EDITTEXT IDC_POPUPTEXT,5,80,200,35,ES_MULTILINE | ES_AUTOHSCROLL | + WS_HSCROLL + LTEXT "Popup Mode",IDC_STATIC,215,80,49,10 + CONTROL "Hover",IDC_HOVER,"Button",BS_AUTORADIOBUTTON,219,92,45, + 10 + CONTROL "Click",IDC_CLICK,"Button",BS_AUTORADIOBUTTON,219,104,45, + 10 + DEFPUSHBUTTON "Send to Map System",IDSENDTOMAP,5,125,90,14 + DEFPUSHBUTTON "Save",IDOK,100,125,35,14 + PUSHBUTTON "Cancel",IDCANCEL,140,125,40,14 +END + +IDD_MSGFROMCLIPBOARD DIALOGEX 0, 0, 442, 306 +STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Send Message" +FONT 8, "System", 0, 0, 0x1 +BEGIN + PUSHBUTTON "Send",IDSEND,333,5,50,14 + PUSHBUTTON "Cancel",IDCANCEL,333,25,50,14 + EDITTEXT IDC_EDIT1,4,57,431,245,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL, + WS_EX_ACCEPTFILES + LTEXT "To:",IDC_STATIC,66,8,12,8 + EDITTEXT IDC_MSGTO,83,7,147,12,ES_UPPERCASE | ES_AUTOHSCROLL + LTEXT "Title:",IDC_STATIC,8,26,22,8 + EDITTEXT IDC_MSGTITLE,50,24,277,12,ES_AUTOHSCROLL + LTEXT "BID:",IDC_STATIC,240,8,20,8 + EDITTEXT IDC_MSGBID,262,7,65,12,ES_UPPERCASE | ES_AUTOHSCROLL + LTEXT "Type",IDC_STATIC,8,8,20,8 + COMBOBOX IDC_MSGTYPE,34,7,25,55,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "Select",IDSelectFiles,333,42,50,14 + LTEXT "Attachments",IDC_STATIC,8,43,42,8 + EDITTEXT IDC_ATTACHMENTS,50,41,277,12,ES_AUTOHSCROLL +END + +IDD_HRHELP DIALOG DISCARDABLE 0, 0, 415, 182 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Hierarchical Forwarding Help" +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "OK",IDOK,182,158,50,14 + EDITTEXT IDC_HRTEXT,4,4,405,150,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL +END + +IDD_EDITMSGTEXT DIALOGEX 0, 0, 492, 356 +STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Edit Message" +FONT 8, "Fixedsys", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_MESSAGE,4,25,480,327,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL, + WS_EX_ACCEPTFILES + PUSHBUTTON "Save",IDSAVE,5,5,40,17 + PUSHBUTTON "Cancel",IDCANCEL,125,5,40,17 + PUSHBUTTON "Save Attachments",IDC_SAVEATTACHMENTS,50,5,70,17, + WS_DISABLED +END + +MAINT DIALOG DISCARDABLE 26, 5, 382, 298 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "System" +BEGIN + LTEXT "This sets the parameters for deleting old messages. Specify length of time (in days) for messages to be kept before being deleted", + IDC_STATIC,149,13,226,22 + LTEXT "Read",IDC_STATIC,159,46,46,10 + EDITTEXT IDM_PR,224,46,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Unread",IDC_STATIC,159,61,46,10 + EDITTEXT IDM_PUR,224,61,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Forwarded",IDC_STATIC,159,76,46,10 + EDITTEXT IDM_PF,224,76,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Not Forwarded",IDC_STATIC,159,91,49,10 + EDITTEXT IDM_PNF,224,91,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Forwarded",IDC_STATIC,264,46,49,10 + EDITTEXT IDM_BF,324,46,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Not Forwarded",IDC_STATIC,264,61,49,10 + EDITTEXT IDM_BNF,324,61,20,12,ES_CENTER | ES_AUTOHSCROLL + GROUPBOX "Personals",IDC_STATIC,149,36,108,107 + GROUPBOX "Bulletins",IDC_STATIC,259,36,103,44 + LTEXT "The following boxes allow you to specify different values for different Bulletin origins and destinations. Normally these apply to Sent Messages, to apply to unsent, check box below", + IDC_STATIC,151,148,214,27 + LTEXT "Specify Call, Lifetime, eg ALL, 10",IDC_STATIC,151,176, + 154,8 + LTEXT "From",IDC_STATIC,176,189,28,9 + EDITTEXT IDM_LTFROM,151,202,64,55,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "To",IDC_STATIC,247,189,27,10 + EDITTEXT IDM_LTTO,221,202,64,55,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "At",IDC_STATIC,316,189,15,8 + EDITTEXT IDM_LTAT,289,202,64,55,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "Save",IDM_MAINTSAVE,166,279,50,14,BS_CENTER | + BS_VCENTER + GROUPBOX "Lifetimes",IDC_STATIC,144,0,231,275 + GROUPBOX "Parameters",IDC_STATIC,3,3,133,275 + LTEXT "Max Message Number",IDC_STATIC,7,68,77,10 + LTEXT "BID Lifetime (Days)",IDC_STATIC,7,87,77,10 + LTEXT "Maintenance Time (UTC) Enter as HHMM",IDC_STATIC,7,21, + 83,25 + EDITTEXT IDC_MAXMSG,102,67,25,12,ES_CENTER + EDITTEXT IDC_BIDLIFETIME,102,86,25,12,ES_CENTER | ES_AUTOHSCROLL + EDITTEXT IDC_MAINTTIME,102,23,25,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Log File Lifetime (Days)",IDC_STATIC,7,107,80,10 + EDITTEXT IDC_LOGLIFETIME,102,105,25,12,ES_CENTER | ES_AUTOHSCROLL + CONTROL "Delete Log and Message Files to Recycle Bin", + IDC_DELETETORECYCLE,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,5,142,115,20 + CONTROL "Suppress Mailing of Housekeeping Results", + IDC_MAINTNOMAIL,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | + BS_MULTILINE | WS_TABSTOP,5,182,115,20 + CONTROL "Generate Traffic Report",IDC_MAINTTRAFFIC,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,5,204,115,10 + CONTROL "Send Non-delivery Notifications for P and T messages", + IDC_MAINTNONDELIVERY,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | BS_MULTILINE | WS_TABSTOP,5,162,115,20 + CONTROL "Apply Overrides to Unsent Bulls",IDC_OVERRIDEUNSENT, + "Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,153,261,195,10 + LTEXT "Delete Inactive Users (Days)",IDC_STATIC,7,127,93,10 + EDITTEXT IDC_USERLIFETIME,102,125,25,12,ES_CENTER | + ES_AUTOHSCROLL + GROUPBOX "NTS",IDC_STATIC,259,80,102,62 + LTEXT "Forwarded",IDC_STATIC,264,109,49,10 + EDITTEXT IDM_NTSF,324,108,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Not Forwarded",IDC_STATIC,264,124,49,10 + EDITTEXT IDM_NTSU,324,123,20,12,ES_CENTER | ES_AUTOHSCROLL + LTEXT "Delivered",IDC_STATIC,264,94,49,10 + EDITTEXT IDM_NTSD,324,94,20,12,ES_CENTER | ES_AUTOHSCROLL + CONTROL "Save Registry",IDC_MAINTSAVEREG2,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | BS_MULTILINE | + WS_TABSTOP,5,222,115,10 + LTEXT "Maintenance Interval (Hrs)",IDC_STATIC,7,47,93,10 + EDITTEXT IDC_MAINTINTERVAL,102,45,25,12,ES_CENTER | + ES_AUTOHSCROLL +END + +FILTERS DIALOG DISCARDABLE 26, 5, 382, 371 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "System" +BEGIN + LTEXT "Reject Messages:",IDC_STATIC,162,26,70,10 + LTEXT "From",IDC_STATIC,83,137,28,10 + EDITTEXT IDC_HOLDFROM,58,149,64,83,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "To",IDC_STATIC,152,137,27,10 + EDITTEXT IDC_HOLDTO,126,149,64,83,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "At",IDC_STATIC,223,137,15,10 + EDITTEXT IDC_HOLDAT,194,149,64,83,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "Save",IDC_FILTERSAVE,171,341,50,14,BS_CENTER | + BS_VCENTER + LTEXT "From",IDC_STATIC,83,40,28,10 + EDITTEXT IDC_REJFROM,58,52,64,67,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "To",IDC_STATIC,154,40,27,10 + EDITTEXT IDC_REJTO,126,52,64,68,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "At",IDC_STATIC,223,40,15,10 + EDITTEXT IDC_REJAT,194,52,64,68,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "Message Filtering Setup.",IDC_STATIC,152,10,95,15 + LTEXT "Hold Messages:",IDC_STATIC,166,128,60,9 + EDITTEXT IDC_REJBID,262,52,64,68,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + EDITTEXT IDC_HOLDBID,262,149,64,83,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "BID",IDC_STATIC,289,137,15,10 + LTEXT "BID",IDC_STATIC,289,41,15,10 + EDITTEXT IDC_REJSYS,58,265,270,66,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "Composite Rules (like fbb reject.sys)",IDC_STATIC,152, + 236,134,9 + LTEXT "Action, Type, from, @BBS, to, BID, maximum size", + IDC_STATIC,59,251,247,9 +END + +WPUPDATE DIALOG DISCARDABLE 26, 5, 382, 287 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "System" +BEGIN + CONTROL "Send WP Updates",IDC_SENDWP,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,90,18,71,10 + EDITTEXT IDC_WPTO,112,82,154,113,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + COMBOBOX IDC_WPTYPE,193,17,19,55,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Type",IDC_STATIC,172,18,21,10 + DEFPUSHBUTTON "Save",IDC_WPSAVE,165,204,50,14,BS_CENTER | BS_VCENTER + CONTROL "Reject WP Bulls",IDC_FILTERWPB,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,222,18,71,10 + LTEXT "Send To.\rBPQMail only automatically processes messages with TO of WP, so for P messages format should be WP@BBSCALL. ", + IDC_STATIC,98,38,176,35 +END + +IDD_RMSBULLDLG DIALOG DISCARDABLE 0, 0, 277, 258 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "RMS Express Bull Configuration" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,66,237,50,14 + PUSHBUTTON "Cancel",IDCANCEL,164,237,50,14 + EDITTEXT IDC_TOCALLS,82,76,54,112,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + EDITTEXT IDC_TOCALLS2,144,76,54,112,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + EDITTEXT IDC_TOCALLS3,208,76,54,112,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + EDITTEXT IDC_TOCALLS4,18,76,54,112,ES_MULTILINE | ES_UPPERCASE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + LTEXT "Common",IDC_STATIC,63,47,48,8 + LTEXT "User Specific",IDC_STATIC,174,47,48,8 + LTEXT "TO AT",IDC_STATIC,39,62,79,8 + LTEXT "TO AT",IDC_STATIC,163,62,79, + 8 + LTEXT "This defines Bulls that will be downloaded to RMS Express Users. There are two lists, one for all RMS Express users and one per User. The lists will be combined, Updating the Common list will affect all users.", + IDC_STATIC,7,7,242,37 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO MOVEABLE PURE +BEGIN + 102, DIALOG + BEGIN + TOPMARGIN, 47 + END + + "BBS_CONFIG", DIALOG + BEGIN + BOTTOMMARGIN, 303 + HORZGUIDE, 87 + END + + IDD_USEREDIT, DIALOG + BEGIN + RIGHTMARGIN, 292 + BOTTOMMARGIN, 280 + END + + IDD_FORWARDING, DIALOG + BEGIN + RIGHTMARGIN, 444 + BOTTOMMARGIN, 314 + END + + IDD_USERADDED_BOX, DIALOG + BEGIN + BOTTOMMARGIN, 65 + END + + IDD_MSGEDIT, DIALOG + BEGIN + RIGHTMARGIN, 415 + BOTTOMMARGIN, 266 + END + + "WELCOMEMSG", DIALOG + BEGIN + BOTTOMMARGIN, 266 + END + + IDD_MAINTRESULTS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 176 + TOPMARGIN, 7 + BOTTOMMARGIN, 106 + END + + IDD_EDITWP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 391 + TOPMARGIN, 9 + BOTTOMMARGIN, 258 + END + + IDD_UICONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 392 + TOPMARGIN, 2 + BOTTOMMARGIN, 144 + END + + IDD_MSGFROMCLIPBOARD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 435 + TOPMARGIN, 7 + END + + IDD_HRHELP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 408 + TOPMARGIN, 7 + BOTTOMMARGIN, 175 + END + + IDD_EDITMSGTEXT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 485 + TOPMARGIN, 7 + BOTTOMMARGIN, 349 + END + + "MAINT", DIALOG + BEGIN + RIGHTMARGIN, 381 + BOTTOMMARGIN, 266 + END + + "FILTERS", DIALOG + BEGIN + RIGHTMARGIN, 377 + BOTTOMMARGIN, 355 + END + + IDD_RMSBULLDLG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 267 + TOPMARGIN, 7 + BOTTOMMARGIN, 251 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDC_BPQMailChat MENU DISCARDABLE +BEGIN + POPUP "Actions" + BEGIN + POPUP "Start Forwarding" + BEGIN + MENUITEM "All", IDM_FORWARD_ALL + END + POPUP "Logging Options" + BEGIN + MENUITEM "Log BBS Traffic", IDM_LOGBBS + MENUITEM "Log TCP Traffic", IDM_LOGTCP + END + POPUP "Disconnect User" + BEGIN + MENUITEM ".", IDM_DISCONNECT + END + MENUITEM "Housekeeping", IDM_HOUSEKEEPING + MENUITEM "Send Message", ID_ACTIONS_SENDMESSAGE + MENUITEM "Send Msg from Clipboard", ID_ACTIONS_SENDMSGFROMCLIPBOARD + + MENUITEM "Rerun Message Routing", RESCANMSGS + MENUITEM "Import Messages", IDM_IMPORT + END + POPUP "&Configuration" + BEGIN + MENUITEM "Main &Configuration", IDM_CONFIG + MENUITEM "Manage &Users", IDM_USERS + MENUITEM "Manage &Forwarding", IDM_FWD + MENUITEM "Manage &Messages", IDM_MESSAGES + MENUITEM "Manage &White Pages", IDM_WP + END + POPUP "Windows" + BEGIN + MENUITEM "BBS Console (F2)", IDM_CONSOLE + MENUITEM "Multicast Monitor (F3)", IDM_MCMONITOR + MENUITEM "Monitor (F4)", IDM_MONITOR + END + POPUP "&Help" + BEGIN + MENUITEM "&About ...", IDM_ABOUT + MENUITEM "Online Documentation", ID_HELP_ONLINEHELP + END +END + +CONSOLEMENU MENU DISCARDABLE +BEGIN + POPUP "Options" + BEGIN + MENUITEM "Enable Bells", BPQBELLS + MENUITEM "Flash instead of Beep on Bell", BPQFLASHONBELL + MENUITEM "Strip Linefeeds", BPQStripLF + MENUITEM "Wrap Input ", IDM_WRAPTEXT + MENUITEM "Beep if Input too long", IDM_WARNINPUT + MENUITEM "Close Window on exit", IDM_CLOSEWINDOW + END + POPUP "Edit" + BEGIN + MENUITEM "Copy Output Window", BPQCOPYOUT + MENUITEM "Clear Output Window", BPQCLEAROUT + END + POPUP "Actions" + BEGIN + POPUP "Chat to user" + BEGIN + MENUITEM ".", BBSUSERCHAT + END + MENUITEM "End user chat", ENDUSERCHAT + END +END + +MENU_2 MENU DISCARDABLE +BEGIN + POPUP "Monitor" + BEGIN + MENUITEM "Monitor BBS", MONBBS + MENUITEM "Monitor TCP", MONTCP + END + POPUP "Edit" + BEGIN + MENUITEM "Copy Monitor Window", BPQCOPYOUT + MENUITEM "Clear Monitor Window", BPQCLEAROUT + END +END + +MENU_3 MENU DISCARDABLE +BEGIN + POPUP "Edit" + BEGIN + MENUITEM "Copy Monitor Window", BPQCOPYOUT + MENUITEM "Clear Monitor Window", BPQCLEAROUT + END +END + +MENU_1 MENU DISCARDABLE +BEGIN + POPUP "Edit" + BEGIN + MENUITEM "Copy", ID_EDIT_COPY + MENUITEM "Clear", ID_EDIT_CLEAR + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDC_TELNETSERVER ACCELERATORS MOVEABLE PURE +BEGIN + "?", IDM_ABOUT, ASCII, ALT + "/", IDM_ABOUT, ASCII, ALT +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE MOVEABLE PURE +BEGIN + "BPQMailrc.h\0" +END + +2 TEXTINCLUDE MOVEABLE PURE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""AFXRES.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#define MAILCHAT\r\n" + "#include ""..\\CommonSource\\Versions.h""\r\n" + "\0" +END + +3 TEXTINCLUDE MOVEABLE PURE +BEGIN + "#define MAIL\r\n" + "#include ""Versions.h""\r\n" + "#include ""StdVer.inc""\r\n" + "\0" +END + +1 TEXTINCLUDE MOVEABLE PURE +BEGIN + "resource.h\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_USEREDIT AFX_DIALOG_LAYOUT MOVEABLE PURE +BEGIN + 0x0000 +END + +IDD_FORWARDING AFX_DIALOG_LAYOUT MOVEABLE PURE +BEGIN + 0x0000 +END + +FILTERS AFX_DIALOG_LAYOUT MOVEABLE PURE +BEGIN + 0x0000 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_APP_TITLE "BPQMail" + IDC_BPQMailChat "BPQMail" +END + +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define MAIL +#include "Versions.h" +#include "StdVer.inc" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/.svn/pristine/1c/1c5eb1c1defb7927e51be30ee9cfb4231368e7aa.svn-base b/.svn/pristine/1c/1c5eb1c1defb7927e51be30ee9cfb4231368e7aa.svn-base new file mode 100644 index 0000000..8453eb5 --- /dev/null +++ b/.svn/pristine/1c/1c5eb1c1defb7927e51be30ee9cfb4231368e7aa.svn-base @@ -0,0 +1,224 @@ +/* +Copyright 2001-2018 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 +*/ + + +#include "bpqmail.h" + +int LastVer[4] = {0, 0, 0, 0}; // In case we need to do somthing the first time a version is run + +HWND MainWnd; +HWND hWndSess; +RECT MainRect; +HMENU hActionMenu; +static HMENU hMenu; +HMENU hDisMenu; // Disconnect Menu Handle +HMENU hFWDMenu; // Forward Menu Handle + +int SessX, SessY, SessWidth; // Params for Session Window + +char szBuff[80]; + +#define MaxSockets 64 + +ConnectionInfo Connections[MaxSockets+1]; + +struct SEM ChatSemaphore = {0, 0}; +struct SEM AllocSemaphore = {0, 0}; +struct SEM ConSemaphore = {0, 0}; +struct SEM Semaphore = {0, 0}; +struct SEM OutputSEM = {0, 0}; +struct SEM ConfigSEM = {0, 0}; + +struct UserInfo ** UserRecPtr=NULL; +int NumberofUsers=0; + +struct UserInfo * BBSChain = NULL; // Chain of users that are BBSes + +struct MsgInfo ** MsgHddrPtr=NULL; +int NumberofMessages=0; + +int FirstMessageIndextoForward = 0; // Lowest Message with a forward bit set - limits search + +BIDRec ** BIDRecPtr=NULL; +int NumberofBIDs=0; + +BIDRec ** TempBIDRecPtr=NULL; +int NumberofTempBIDs=0; + +WPRec ** WPRecPtr=NULL; +int NumberofWPrecs=0; + +char ** BadWords=NULL; +int NumberofBadWords=0; +char * BadFile = NULL; + +int LatestMsg = 0; +struct SEM MsgNoSemaphore = {0, 0}; // For locking updates to LatestMsg +int HighestBBSNumber = 0; + +int MaxMsgno = 60000; +int BidLifetime = 60; +int MaxAge = 30; +int MaintInterval = 24; +int MaintTime = 0; + +int UserLifetime = 0; + +BOOL cfgMinToTray; + +BOOL DisconnectOnClose=FALSE; + +char PasswordMsg[100]="Password:"; + +char cfgHOSTPROMPT[100]; + +char cfgCTEXT[100]; + +char cfgLOCALECHO[100]; + +char AttemptsMsg[] = "Too many attempts - Disconnected\r\r"; +char disMsg[] = "Disconnected by SYSOP\r\r"; + +char LoginMsg[]="user:"; + +char BlankCall[]=" "; + + +ULONG BBSApplMask; +ULONG ChatApplMask; + +int BBSApplNum=0; +int ChatApplNum=0; + +//int StartStream=0; +int NumberofStreams=0; +int MaxStreams=0; + +char BBSSID[]="[%s%d.%d.%d.%d-%s%s%s%sIH%sM$]\r"; + +char ChatSID[]="[BPQChatServer-%d.%d.%d.%d]\r"; + +char NewUserPrompt[100]="Please enter your Name\r>\r"; + +char * WelcomeMsg = NULL; +char * NewWelcomeMsg = NULL; +char * ExpertWelcomeMsg = NULL; + +char * Prompt = NULL; +char * NewPrompt = NULL; +char * ExpertPrompt = NULL; + +char BBSName[100] = "NOCALL"; +char SYSOPCall[50]; + +char MailForText[100]; + +char HRoute[100]; +char AMPRDomain[100]; +BOOL SendAMPRDirect = 0; + +char SignoffMsg[120]; + +char AbortedMsg[100]="\rOutput aborted\r"; + +char UserDatabaseName[MAX_PATH] = "BPQBBSUsers.dat"; +char UserDatabasePath[MAX_PATH]; + +char MsgDatabasePath[MAX_PATH]; +char MsgDatabaseName[MAX_PATH] = "DIRMES.SYS"; + +char BIDDatabasePath[MAX_PATH]; +char BIDDatabaseName[MAX_PATH] = "WFBID.SYS"; + +char WPDatabasePath[MAX_PATH]; +char WPDatabaseName[MAX_PATH] = "WP.SYS"; + +char BadWordsPath[MAX_PATH]; +char BadWordsName[MAX_PATH] = "BADWORDS.SYS"; + +char NTSAliasesPath[MAX_PATH]; +char NTSAliasesName[MAX_PATH] = "INTRCPT.APS"; + +char ConfigName[250]; +char ChatConfigName[250]; + +BOOL UsingingRegConfig = FALSE; + +BOOL MulticastRX = FALSE; + +char BaseDir[MAX_PATH]; +char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% +char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat + + +char MailDir[MAX_PATH]; + +char RlineVer[50]; + +BOOL KISSOnly = FALSE; + +BOOL EnableUI = FALSE; +BOOL RefuseBulls = FALSE; +BOOL SendSYStoSYSOPCall = FALSE; +BOOL SendBBStoSYSOPCall = FALSE; +BOOL DontHoldNewUsers = FALSE; +BOOL DefaultNoWINLINK = FALSE; +BOOL ForwardToMe = FALSE; +BOOL OnlyKnown = FALSE; + +BOOL DontNeedHomeBBS = FALSE; +BOOL DontCheckFromCall = FALSE; + +// Send WP Params + +BOOL SendWP; +BOOL FilterWPBulls; +BOOL NoWPGuesses; + +char SendWPVIA[81]; +char SendWPTO[11]; + +char ** SendWPAddrs; // Replaces WP To and VIA + +int SendWPType; + +int SMTPMsgs; + +int MailForInterval = 0; + +char zeros[NBMASK]; // For forward bitmask tests + +time_t MaintClock; // Time to run housekeeping +time_t APIClock; // Time to sent to MOLTE's Database + +struct MsgInfo * MsgnotoMsg[100000]; // Message Number to Message Slot List. + +// Filter Params + +char ** RejFrom; // Reject on FROM Call +char ** RejTo; // Reject on TO Call +char ** RejAt; // Reject on AT Call +char ** RejBID; // Reject on BID + +char ** HoldFrom; // Hold on FROM Call +char ** HoldTo; // Hold on TO Call +char ** HoldAt; // Hold on AT Call +char ** HoldBID; // Hold on BID + +struct ConsoleInfo * ConsHeader[2]; \ No newline at end of file diff --git a/.svn/pristine/1d/1d04846540580b708716a839d48baabc097f4284.svn-base b/.svn/pristine/1d/1d04846540580b708716a839d48baabc097f4284.svn-base new file mode 100644 index 0000000..d2507fc --- /dev/null +++ b/.svn/pristine/1d/1d04846540580b708716a839d48baabc097f4284.svn-base @@ -0,0 +1,205 @@ +/* Types.h -- Basic types + 2008-11-23 : Igor Pavlov : Public domain */ + +#ifndef __7Z_TYPES_H +#define __7Z_TYPES_H + +#include + +#ifdef WIN32 +#include +#endif + +#define SZ_OK 0 + +#define SZ_ERROR_DATA 1 +#define SZ_ERROR_MEM 2 +#define SZ_ERROR_CRC 3 +#define SZ_ERROR_UNSUPPORTED 4 +#define SZ_ERROR_PARAM 5 +#define SZ_ERROR_INPUT_EOF 6 +#define SZ_ERROR_OUTPUT_EOF 7 +#define SZ_ERROR_READ 8 +#define SZ_ERROR_WRITE 9 +#define SZ_ERROR_PROGRESS 10 +#define SZ_ERROR_FAIL 11 +#define SZ_ERROR_THREAD 12 + +#define SZ_ERROR_ARCHIVE 16 +#define SZ_ERROR_NO_ARCHIVE 17 + +extern const char *LZMA_ERRORS[]; + +typedef int SRes; + +#ifdef _WIN32 +typedef DWORD WRes; +#else +typedef int WRes; +#endif + +#ifndef RINOK +#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } +#endif + +typedef unsigned char Byte; +typedef short Int16; +typedef unsigned short UInt16; + +#ifdef _LZMA_LZ_UInt32_IS_ULONG +typedef long LZ_Int32; +typedef unsigned long LZ_UInt32; +#else +typedef int LZ_Int32; +typedef unsigned int LZ_UInt32; +#endif + +#ifdef _SZ_NO_INT_64 + +/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. + NOTES: Some code will work incorrectly in that case! */ + +typedef long LZ_Int64; +typedef unsigned long LZ_UInt64; + +#else + +typedef long long int LZ_Int64; +typedef unsigned long long int LZ_UInt64; + +#endif + +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef LZ_UInt32 SizeT; +#else +typedef size_t SizeT; +#endif + +//typedef int Bool; +#define True 1 +#define False 0 + + +#ifdef _MSC_VER + +#if _MSC_VER >= 1300 +#define MY_NO_INLINE __declspec(noinline) +#else +#define MY_NO_INLINE +#endif + +#define MY_CDECL __cdecl +#define MY_STD_CALL __stdcall +#define MY_FAST_CALL MY_NO_INLINE __fastcall + +#else + +#define MY_CDECL +#define MY_STD_CALL +#define MY_FAST_CALL + +#endif + + +/* The following interfaces use first parameter as pointer to structure */ + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) < input(*size)) is allowed */ +} ISeqInStream; + +/* it can return SZ_ERROR_INPUT_EOF */ +SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); +SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); +SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); + +typedef struct +{ + size_t (*Write)(void *p, const void *buf, size_t size); + /* Returns: result - the number of actually written bytes. + (result < size) means error */ +} ISeqOutStream; + +typedef enum +{ + SZ_SEEK_SET = 0, + SZ_SEEK_CUR = 1, + SZ_SEEK_END = 2 +} ESzSeek; + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ + SRes (*Seek)(void *p, LZ_Int64 *pos, ESzSeek origin); +} ISeekInStream; + +typedef struct +{ + SRes (*Look)(void *p, void **buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) > input(*size)) is not allowed + (output(*size) < input(*size)) is allowed */ + SRes (*Skip)(void *p, size_t offset); + /* offset must be <= output(*size) of Look */ + + SRes (*Read)(void *p, void *buf, size_t *size); + /* reads directly (without buffer). It's same as ISeqInStream::Read */ + SRes (*Seek)(void *p, LZ_Int64 *pos, ESzSeek origin); +} ILookInStream; + +SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); +SRes LookInStream_SeekTo(ILookInStream *stream, LZ_UInt64 offset); + +/* reads via ILookInStream::Read */ +SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); +SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); + +#define LookToRead_BUF_SIZE (1 << 14) + +typedef struct +{ + ILookInStream s; + ISeekInStream *realStream; + size_t pos; + size_t size; + Byte buf[LookToRead_BUF_SIZE]; +} CLookToRead; + +void LookToRead_CreateVTable(CLookToRead *p, int lookahead); +void LookToRead_Init(CLookToRead *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToLook; + +void SecToLook_CreateVTable(CSecToLook *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToRead; + +void SecToRead_CreateVTable(CSecToRead *p); + +typedef struct +{ + SRes (*Progress)(void *p, LZ_UInt64 inSize, LZ_UInt64 outSize); + /* Returns: result. (result != SZ_OK) means break. + Value (LZ_UInt64)(LZ_Int64)-1 for size means unknown value. */ +} ICompressProgress; + +typedef struct +{ + void *(*Alloc)(void *p, size_t size); + void (*Free)(void *p, void *address); /* address can be 0 */ +} ISzAlloc; + +#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) +#define IAlloc_Free(p, a) (p)->Free((p), a) + +#endif diff --git a/.svn/pristine/1d/1d64f44a865c47352c66912fe2cfc8c6e1c46e72.svn-base b/.svn/pristine/1d/1d64f44a865c47352c66912fe2cfc8c6e1c46e72.svn-base new file mode 100644 index 0000000..a024e62 --- /dev/null +++ b/.svn/pristine/1d/1d64f44a865c47352c66912fe2cfc8c6e1c46e72.svn-base @@ -0,0 +1,1456 @@ + +/* pngread.c - read a PNG file + * + * libpng 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains routines that an application calls directly to + * read a PNG file or stream. + */ + +#define PNG_INTERNAL +#include "png.h" + +/* Create a PNG structure for reading, and allocate any memory needed. */ +png_structp PNGAPI +png_create_read_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ + +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_read_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +} + +/* Alternate create PNG structure for reading, and allocate any memory needed. */ +png_structp PNGAPI +png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + + png_structp png_ptr; + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + + int i; + + png_debug(1, "in png_create_read_struct\n"); +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif + if (png_ptr == NULL) + return (NULL); + +#if !defined(PNG_1_0_X) +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED + png_init_mmx_flags(png_ptr); /* 1.2.0 addition */ +#endif +#endif /* PNG_1_0_X */ + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + { + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf=NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, + (png_free_ptr)free_fn, (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + return (NULL); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#endif +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif + + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + i=0; + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[80]; + if (user_png_ver) + { + sprintf(msg, "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + sprintf(msg, "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "Incompatible libpng version in application and library"); + } + } + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + + switch (inflateInit(&png_ptr->zstream)) + { + case Z_OK: /* Do nothing */ break; + case Z_MEM_ERROR: + case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory error"); break; + case Z_VERSION_ERROR: png_error(png_ptr, "zlib version error"); break; + default: png_error(png_ptr, "Unknown zlib error"); + } + + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); + +#ifdef PNG_SETJMP_SUPPORTED +/* Applications that neglect to set up their own setjmp() and then encounter + a png_error() will longjmp here. Since the jmpbuf is then meaningless we + abort instead of returning. */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) + PNG_ABORT(); + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#else + if (setjmp(png_ptr->jmpbuf)) + PNG_ABORT(); +#endif +#endif + return (png_ptr); +} + +/* Initialize PNG structure for reading, and allocate any memory needed. + This interface is deprecated in favour of the png_create_read_struct(), + and it will eventually disappear. */ +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +#undef png_read_init +void PNGAPI +png_read_init(png_structp png_ptr) +{ + /* We only come here via pre-1.0.7-compiled applications */ + png_read_init_2(png_ptr, "1.0.6 or earlier", 0, 0); +} +#endif + +void PNGAPI +png_read_init_2(png_structp png_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size, png_size_t png_info_size) +{ + /* We only come here via pre-1.0.12-compiled applications */ +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + if(png_sizeof(png_struct) > png_struct_size || + png_sizeof(png_info) > png_info_size) + { + char msg[80]; + png_ptr->warning_fn=NULL; + if (user_png_ver) + { + sprintf(msg, "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + sprintf(msg, "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); + } +#endif + if(png_sizeof(png_struct) > png_struct_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The png struct allocated by the application for reading is too small."); + } + if(png_sizeof(png_info) > png_info_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The info struct allocated by application for reading is too small."); + } + png_read_init_3(&png_ptr, user_png_ver, png_struct_size); +} + +void PNGAPI +png_read_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* to save current jump buffer */ +#endif + + int i=0; + + png_structp png_ptr=*ptr_ptr; + + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + { +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; +#else + png_ptr->warning_fn=NULL; + png_warning(png_ptr, + "Application uses deprecated png_read_init() and should be recompiled."); + break; +#endif + } + } while (png_libpng_ver[i++]); + + png_debug(1, "in png_read_init_3\n"); + +#ifdef PNG_SETJMP_SUPPORTED + /* save jump buffer and error functions */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + if(png_sizeof(png_struct) > png_struct_size) + { + png_destroy_struct(png_ptr); + *ptr_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); + png_ptr = *ptr_ptr; + } + + /* reset all variables to 0 */ + png_memset(png_ptr, 0, png_sizeof (png_struct)); + +#ifdef PNG_SETJMP_SUPPORTED + /* restore jump buffer */ + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + + switch (inflateInit(&png_ptr->zstream)) + { + case Z_OK: /* Do nothing */ break; + case Z_MEM_ERROR: + case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory"); break; + case Z_VERSION_ERROR: png_error(png_ptr, "zlib version"); break; + default: png_error(png_ptr, "Unknown zlib error"); + } + + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); +} + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. This has been + * changed in v0.90 to allow reading a file that already has the magic + * bytes read from the stream. You can tell libpng how many bytes have + * been read from the beginning of the stream (up to the maximum of 8) + * via png_set_sig_bytes(), and we will only check the remaining bytes + * here. The application can then have access to the signature bytes we + * read if it is determined that this isn't a valid PNG file. + */ +void PNGAPI +png_read_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_info\n"); + /* If we haven't checked all of the PNG signature bytes, do so now. */ + if (png_ptr->sig_bytes < 8) + { + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check); + png_ptr->sig_bytes = 8; + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + if (num_checked < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; + } + + for(;;) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; + PNG_IDAT; + PNG_IEND; + PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_sCAL; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_sPLT; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_sRGB; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + png_byte chunk_length[4]; + png_uint_32 length; + + png_read_data(png_ptr, chunk_length, 4); + length = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + + png_debug2(0, "Reading %s chunk, length=%lu.\n", png_ptr->chunk_name, + length); + + /* This should be a binary subdivision search or a hash for + * matching the chunk name rather than a linear search. + */ + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + break; + } + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + png_ptr->idat_size = length; + png_ptr->mode |= PNG_HAVE_IDAT; + break; + } +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(c, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +/* optional call to update the users info_ptr structure */ +void PNGAPI +png_read_update_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_update_info\n"); + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + else + png_warning(png_ptr, + "Ignoring extra png_read_update_info() call; row buffer not reallocated"); + png_read_transform_info(png_ptr, info_ptr); +} + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Initialize palette, background, etc, after transformations + * are set, but before any reading takes place. This allows + * the user to obtain a gamma-corrected palette, for example. + * If the user doesn't call this, we will do it ourselves. + */ +void PNGAPI +png_start_read_image(png_structp png_ptr) +{ + png_debug(1, "in png_start_read_image\n"); + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +void PNGAPI +png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; + const int png_pass_dsp_mask[7] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; + const int png_pass_mask[7] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; +#endif + int ret; + png_debug2(1, "in png_read_row (row %lu, pass %d)\n", + png_ptr->row_number, png_ptr->pass); + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* check for transforms that have been set but were defined out */ +#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && !defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined."); +#endif + } + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* if interlaced and we do not need a new row, combine row and return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + if (dsp_row != NULL && (png_ptr->row_number & 4)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 3) || png_ptr->width < 3) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 3) != 2) + { + if (dsp_row != NULL && (png_ptr->row_number & 2)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 1) || png_ptr->width < 2) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 1)) + { + png_read_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "Invalid attempt to read row data"); + + png_ptr->zstream.next_out = png_ptr->row_buf; + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + do + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_byte chunk_length[4]; + + png_crc_finish(png_ptr, 0); + + png_read_data(png_ptr, chunk_length, 4); + png_ptr->idat_size = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, + (png_size_t)png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_error(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression error"); + + } while (png_ptr->zstream.avail_out); + + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + if(png_ptr->row_buf[0]) + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, + png_ptr->rowbytes + 1); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_read_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* blow up interlaced rows to full size */ + if (png_ptr->interlaced && + (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) +/* old interface (pre-1.0.9): + png_do_read_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + if (row != NULL) + png_combine_row(png_ptr, row, + png_pass_mask[png_ptr->pass]); + } + else +#endif + { + if (row != NULL) + png_combine_row(png_ptr, row, 0xff); + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 0xff); + } + png_read_finish_row(png_ptr); + + if (png_ptr->read_row_fn != NULL) + (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. If the image is interlaced, + * and png_set_interlace_handling() has been called, the rows need to + * contain the contents of the rows from the previous pass. If the + * image has alpha or transparency, and png_handle_alpha()[*] has been + * called, the rows contents must be initialized to the contents of the + * screen. + * + * "row" holds the actual image, and pixels are placed in it + * as they arrive. If the image is displayed after each pass, it will + * appear to "sparkle" in. "display_row" can be used to display a + * "chunky" progressive image, with finer detail added as it becomes + * available. If you do not want this "chunky" display, you may pass + * NULL for display_row. If you do not want the sparkle display, and + * you have not called png_handle_alpha(), you may pass NULL for rows. + * If you have called png_handle_alpha(), and the image has either an + * alpha channel or a transparency chunk, you must provide a buffer for + * rows. In this case, you do not have to provide a display_row buffer + * also, but you may. If the image is not interlaced, or if you have + * not called png_set_interlace_handling(), the display_row buffer will + * be ignored, so pass NULL to it. + * + * [*] png_handle_alpha() does not exist yet, as of libpng version 1.2.8 + */ + +void PNGAPI +png_read_rows(png_structp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows) +{ + png_uint_32 i; + png_bytepp rp; + png_bytepp dp; + + png_debug(1, "in png_read_rows\n"); + rp = row; + dp = display_row; + if (rp != NULL && dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp++; + png_bytep dptr = *dp++; + + png_read_row(png_ptr, rptr, dptr); + } + else if(rp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp; + png_read_row(png_ptr, rptr, png_bytep_NULL); + rp++; + } + else if(dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep dptr = *dp; + png_read_row(png_ptr, png_bytep_NULL, dptr); + dp++; + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the entire image. If the image has an alpha channel or a tRNS + * chunk, and you have called png_handle_alpha()[*], you will need to + * initialize the image to the current image that PNG will be overlaying. + * We set the num_rows again here, in case it was incorrectly set in + * png_read_start_row() by a call to png_read_update_info() or + * png_start_read_image() if png_set_interlace_handling() wasn't called + * prior to either of these functions like it should have been. You can + * only call this function once. If you desire to have an image for + * each pass of a interlaced image, use png_read_rows() instead. + * + * [*] png_handle_alpha() does not exist yet, as of libpng version 1.2.8 + */ +void PNGAPI +png_read_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i,image_height; + int pass, j; + png_bytepp rp; + + png_debug(1, "in png_read_image\n"); + +#ifdef PNG_READ_INTERLACING_SUPPORTED + pass = png_set_interlace_handling(png_ptr); +#else + if (png_ptr->interlaced) + png_error(png_ptr, + "Cannot read interlaced image -- interlace handler disabled."); + pass = 1; +#endif + + + image_height=png_ptr->height; + png_ptr->num_rows = image_height; /* Make sure this is set correctly */ + + for (j = 0; j < pass; j++) + { + rp = image; + for (i = 0; i < image_height; i++) + { + png_read_row(png_ptr, *rp, png_bytep_NULL); + rp++; + } + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. Will not read past the end of the + * file, will verify the end is accurate, and will read any comments + * or time information at the end of the file, if info is not NULL. + */ +void PNGAPI +png_read_end(png_structp png_ptr, png_infop info_ptr) +{ + png_byte chunk_length[4]; + png_uint_32 length; + + png_debug(1, "in png_read_end\n"); + png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */ + + do + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; + PNG_IDAT; + PNG_IEND; + PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_sCAL; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_sPLT; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_sRGB; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + + png_read_data(png_ptr, chunk_length, 4); + length = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + + png_debug1(0, "Reading %s chunk.\n", png_ptr->chunk_name); + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (length > 0 || png_ptr->mode & PNG_AFTER_IDAT) + png_error(png_ptr, "Too many IDAT's found"); + } + else + png_ptr->mode |= PNG_AFTER_IDAT; + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + /* Zero length IDATs are legal after the last IDAT has been + * read, but not after other chunks have been read. + */ + if (length > 0 || png_ptr->mode & PNG_AFTER_IDAT) + png_error(png_ptr, "Too many IDAT's found"); + png_crc_finish(png_ptr, length); + } + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } while (!(png_ptr->mode & PNG_HAVE_IEND)); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +/* free all memory used by the read */ +void PNGAPI +png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, + png_infopp end_info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL, end_info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; + png_voidp mem_ptr; +#endif + + png_debug(1, "in png_destroy_read_struct\n"); + if (png_ptr_ptr != NULL) + png_ptr = *png_ptr_ptr; + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (end_info_ptr_ptr != NULL) + end_info_ptr = *end_info_ptr_ptr; + +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + + png_read_destroy(png_ptr, info_ptr, end_info_ptr); + + if (info_ptr != NULL) + { +#if defined(PNG_TEXT_SUPPORTED) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, -1); +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (end_info_ptr != NULL) + { +#if defined(PNG_READ_TEXT_SUPPORTED) + png_free_data(png_ptr, end_info_ptr, PNG_FREE_TEXT, -1); +#endif +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)end_info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)end_info_ptr); +#endif + *end_info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + +/* free all memory used by the read (old method) */ +void /* PRIVATE */ +png_read_destroy(png_structp png_ptr, png_infop info_ptr, png_infop end_info_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_read_destroy\n"); + if (info_ptr != NULL) + png_info_destroy(png_ptr, info_ptr); + + if (end_info_ptr != NULL) + png_info_destroy(png_ptr, end_info_ptr); + + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->big_row_buf); + png_free(png_ptr, png_ptr->prev_row); +#if defined(PNG_READ_DITHER_SUPPORTED) + png_free(png_ptr, png_ptr->palette_lookup); + png_free(png_ptr, png_ptr->dither_index); +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + png_free(png_ptr, png_ptr->gamma_table); +#endif +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + png_free(png_ptr, png_ptr->gamma_from_1); + png_free(png_ptr, png_ptr->gamma_to_1); +#endif +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->free_me &= ~PNG_FREE_PLTE; +#else + if (png_ptr->flags & PNG_FLAG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; +#endif +#if defined(PNG_tRNS_SUPPORTED) || \ + defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans); + png_ptr->free_me &= ~PNG_FREE_TRNS; +#else + if (png_ptr->flags & PNG_FLAG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans); + png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; +#endif +#endif +#if defined(PNG_READ_hIST_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_HIST) + png_free(png_ptr, png_ptr->hist); + png_ptr->free_me &= ~PNG_FREE_HIST; +#else + if (png_ptr->flags & PNG_FLAG_FREE_HIST) + png_free(png_ptr, png_ptr->hist); + png_ptr->flags &= ~PNG_FLAG_FREE_HIST; +#endif +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->gamma_16_table != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_table[i]); + } + png_free(png_ptr, png_ptr->gamma_16_table); + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_from_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_from_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_from_1); + } + if (png_ptr->gamma_16_to_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_to_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_to_1); + } +#endif +#endif +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_free(png_ptr, png_ptr->time_buffer); +#endif + + inflateEnd(&png_ptr->zstream); +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_free(png_ptr, png_ptr->save_buffer); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +#ifdef PNG_TEXT_SUPPORTED + png_free(png_ptr, png_ptr->current_text); +#endif /* PNG_TEXT_SUPPORTED */ +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + + /* Save the important info out of the png_struct, in case it is + * being used again. + */ +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof (png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + +} + +void PNGAPI +png_set_read_status_fn(png_structp png_ptr, png_read_status_ptr read_row_fn) +{ + png_ptr->read_row_fn = read_row_fn; +} + + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_read_png(png_structp png_ptr, png_infop info_ptr, + int transforms, + voidp params) +{ + int row; + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel from opacity to transparency + */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* png_read_info() gives us all of the information from the + * PNG file before the first IDAT (image data chunk). + */ + png_read_info(png_ptr, info_ptr); + if (info_ptr->height > PNG_UINT_32_MAX/png_sizeof(png_bytep)) + png_error(png_ptr,"Image is too high to process with png_read_png()"); + + /* -------------- image transformations start here ------------------- */ + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + /* tell libpng to strip 16 bit/color files down to 8 bits per color + */ + if (transforms & PNG_TRANSFORM_STRIP_16) + png_set_strip_16(png_ptr); +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + /* Strip alpha bytes from the input data without combining with + * the background (not recommended). + */ + if (transforms & PNG_TRANSFORM_STRIP_ALPHA) + png_set_strip_alpha(png_ptr); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED) + /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single + * byte into separate bytes (useful for paletted and grayscale images). + */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + /* Change the order of packed pixels to least significant bit first + * (not useful if you are using png_set_packing). + */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + /* Expand paletted colors into true RGB triplets + * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel + * Expand paletted or RGB images with transparency to full alpha + * channels so the data will be available as RGBA quartets. + */ + if (transforms & PNG_TRANSFORM_EXPAND) + if ((png_ptr->bit_depth < 8) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) || + (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) + png_set_expand(png_ptr); +#endif + + /* We don't handle background color or gamma transformation or dithering. + */ + +#if defined(PNG_READ_INVERT_SUPPORTED) + /* invert monochrome files to have 0 as white and 1 as black + */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) + /* If you want to shift the pixel values from the range [0,255] or + * [0,65535] to the original [0,7] or [0,31], or whatever range the + * colors were originally in: + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) + { + png_color_8p sig_bit; + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + png_set_shift(png_ptr, sig_bit); + } +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) + /* flip the RGB pixels to BGR (or RGBA to BGRA) + */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) + /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) + */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) + /* swap bytes of 16 bit files to least significant byte first + */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + + /* We don't handle adding filler bytes */ + + /* Optional call to gamma correct and add the background to the palette + * and update info structure. REQUIRED if you are expecting libpng to + * update the palette for you (i.e., you selected such a transform above). + */ + png_read_update_info(png_ptr, info_ptr); + + /* -------------- image transformations end here ------------------- */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); +#endif + if(info_ptr->row_pointers == NULL) + { + info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr, + info_ptr->height * png_sizeof(png_bytep)); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_ROWS; +#endif + for (row = 0; row < (int)info_ptr->height; row++) + { + info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr, + png_get_rowbytes(png_ptr, info_ptr)); + } + } + + png_read_image(png_ptr, info_ptr->row_pointers); + info_ptr->valid |= PNG_INFO_IDAT; + + /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end(png_ptr, info_ptr); + + if(transforms == 0 || params == NULL) + /* quiet compiler warnings */ return; + +} +#endif +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ diff --git a/.svn/pristine/1d/1da1b06e9d6a2a3cfdf8eb22dcc7d5ecb7d02723.svn-base b/.svn/pristine/1d/1da1b06e9d6a2a3cfdf8eb22dcc7d5ecb7d02723.svn-base new file mode 100644 index 0000000..25e3c41 --- /dev/null +++ b/.svn/pristine/1d/1da1b06e9d6a2a3cfdf8eb22dcc7d5ecb7d02723.svn-base @@ -0,0 +1,5850 @@ +/* +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 +*/ + +// +// C replacement for cmd.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE +#pragma data_seg("_BPQDATA") + +//#include "windows.h" +//#include "winerror.h" + + +#include "time.h" +#include "stdio.h" +#include +//#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); + +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; + + +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 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; + + + +struct CMDX COMMANDS[]; + +int CMDXLEN = sizeof (struct CMDX); + +VOID SENDNODESMSG(); +VOID KISSCMD(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 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); + + + +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 SENDNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + SENDNODESMSG(); + + 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 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; + + 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) +{ + 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)); +} + +VOID CMDL00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + // PROCESS 'LINKS' MESSAGE + + struct _LINKTABLE * LINK = LINKS; + int n = MAXLINKS; + int len; + char Normcall[11] = ""; + + Bufferptr = Cmdprintf(Session, Bufferptr, "Links\r"); + + while (n--) + { + if (LINK->LINKCALL[0]) + { + len = ConvFromAX25(LINK->LINKCALL, Normcall); + + 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\r", + LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE); + else + Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=%d\r", + LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE, 2 - LINK->VER1FLAG); + } + 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/1000.0; + double nsrtt = Routes->NeighbourSRTT/1000.0; + + Bufferptr = Cmdprintf(Session, Bufferptr, " %4.2fs %4.2fs", srtt, nsrtt); + } + + 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; + + 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"); + + while (MaxRoutes--) + { + if (Routes->NEIGHBOUR_CALL[0] != 0) + if (Port == 0 || Port == Routes->NEIGHBOUR_PORT) + Bufferptr = DisplayRoute(Session, Bufferptr, Routes, Verbose); + + Routes++; + } + 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] = ""; + + 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; + } + } + + sprintf(ListenPortList, "%s %d", ListenPortList, 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) +{ + TRANSPORTENTRY * NewSess; + + NewSess = SetupNewSession(Session, Bufferptr); + + if (NewSess == NULL) + return; // Tables Full + + NewSess->L4CIRCUITTYPE = SESSION + DOWNLINK; + + NewSess->L4TARGET.DEST = Dest; + NewSess->L4STATE = 2; // CONNECTING + + NewSess->SPYFLAG = Spy; + + ReleaseBuffer((UINT *)REPLYBUFFER); + + SENDL4CONNECT(NewSess); + + L4CONNECTSOUT++; + + return; +} + +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; + + +#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; + } + + 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 + + // 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); + return; + } + Dest++; + } + } + + Dest = DESTS; + n = MAXDESTS; + + while (n--) + { + if (CompareCalls(Dest->DEST_CALL, axcalls)) + { + DoNetromConnect(Session, Bufferptr, Dest, Spy); + 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"); + + // Send Port List + + 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)); +} + +int CompareNode(const void *a, const void *b); +int CompareAlias(const void *a, const void *b); + +char * DoOneNode(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest) +{ + char Normcall[10]; + char Alias[10]; + struct NR_DEST_ROUTE_ENTRY * NRRoute; + struct 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->ROUTE[0]; + + Active = Dest->DEST_ROUTE; + + for (i = 1; i < 4; i++) + { + Neighbour = Route->ROUT_NEIGHBOUR; + + if (Neighbour) + { + double srtt = Route->SRTT/1000.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 && Dest->NRROUTE[n].ROUT_NEIGHBOUR->INP3Node == 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->ROUTE[n].ROUT_NEIGHBOUR != 0) + { + srtt = Dest->ROUTE[n].SRTT/1000.0; + + len=ConvFromAX25(Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall); + Portcall[len]=0; + + len=sprintf(&line[cursor],"%s %d %d %4.2fs ", + Portcall, + Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, + Dest->ROUTE[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; countNRROUTE[0].ROUT_NEIGHBOUR == 0 && Dest->ROUTE[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->ROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + || (Dest->ROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + || (Dest->ROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[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; + + + // 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') // 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, + "SENDNODES ",8, &SENDNODES,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), + "VALIDCALLS ",5,VALNODES,0, + "WL2KSYSOP ",5,WL2KSYSOP,0, + "STOPPORT ",4,STOPPORT,0, + "STARTPORT ",5,STARTPORT,0, + "STOPCMS ",7,STOPCMS,0, + "STARTCMS ",8,STARTCMS,0, + + "FINDBUFFS ",4,FINDBUFFS,0, + "KISS ",4,KISSCMD,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, + "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 + "*** 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, + "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, + "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; + + 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++; + + } + 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; +} + + + +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; + + if (KISS->FIRSTPORT != KISS) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Send Command + + KissLen = KissEncode(KissString, ENCBUFF, KissLen); + + PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; // ALL FRAMES GO ON SAME Q + + 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); + + // 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; +} + +VOID QTSMCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + int port; + struct PORTCONTROL * PORT; + struct KISSINFO * KISS; + + CmdTail = CmdTail + (OrigCmdBuffer - COMMANDBUFFER); // Replace with original case version + + port = atoi(CmdTail); + + 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; + + 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; +} + + + + + + + + + + + + + diff --git a/.svn/pristine/1f/1fdcc1fa5f533062ed56afdfcd1b4fd9b1540a1c.svn-base b/.svn/pristine/1f/1fdcc1fa5f533062ed56afdfcd1b4fd9b1540a1c.svn-base new file mode 100644 index 0000000..40adb92 --- /dev/null +++ b/.svn/pristine/1f/1fdcc1fa5f533062ed56afdfcd1b4fd9b1540a1c.svn-base @@ -0,0 +1,21 @@ +#ifndef MINIUPNPC_DECLSPEC_H_INCLUDED +#define MINIUPNPC_DECLSPEC_H_INCLUDED + +#if defined(_WIN32) && !defined(MINIUPNP_STATICLIB) + /* for windows dll */ + #ifdef MINIUPNP_EXPORTS + #define MINIUPNP_LIBSPEC __declspec(dllexport) + #else + #define MINIUPNP_LIBSPEC __declspec(dllimport) + #endif +#else + #if defined(__GNUC__) && __GNUC__ >= 4 + /* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */ + #define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default"))) + #else + #define MINIUPNP_LIBSPEC + #endif +#endif + +#endif /* MINIUPNPC_DECLSPEC_H_INCLUDED */ + diff --git a/.svn/pristine/21/211b32037817283a36474ae9dd374541334687ef.svn-base b/.svn/pristine/21/211b32037817283a36474ae9dd374541334687ef.svn-base new file mode 100644 index 0000000..63147d2 --- /dev/null +++ b/.svn/pristine/21/211b32037817283a36474ae9dd374541334687ef.svn-base @@ -0,0 +1,10 @@ +int MQTTConnect(char* host, int port, char* user, char* pass); +int MQTTPublish(void * msg, char *topic); + +void MQTTKISSTX(void *message); +void MQTTKISSTX_RAW(char* buffer, int bufferLength, void* PORT); +void MQTTKISSRX(void *message); +void MQTTKISSRX_RAW(char* buffer, int bufferLength, void* PORT); + +void MQTTMessageEvent(void *message); + diff --git a/.svn/pristine/21/21d41eb5e8af7548d8963b9fbca1744118169e60.svn-base b/.svn/pristine/21/21d41eb5e8af7548d8963b9fbca1744118169e60.svn-base new file mode 100644 index 0000000..46a6efe --- /dev/null +++ b/.svn/pristine/21/21d41eb5e8af7548d8963b9fbca1744118169e60.svn-base @@ -0,0 +1,68 @@ +// CmdLineAuth.cpp : Defines the entry point for the console application. +// + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE + +#define _USE_32BIT_TIME_T + +#include + +#include +#include + +#include "MD5.c" + +int LastNow; +int PassCode; + +VOID CreateOneTimePassword(char * KeyPhrase) +{ + // Create a time dependent One Time Password from the KeyPhrase + + time_t NOW = time(NULL); + unsigned char Hash[16]; + char Password[20]; + char Key[1000]; + int i, chr; + long long Val; + + NOW = NOW/30; // Only Change every 30 secs + + if (NOW == LastNow) + return; + + LastNow = NOW; + + sprintf(Key, "%s%x", KeyPhrase, NOW); + + md5(Key, Hash); + + for (i=0; i<16; i++) + { + chr = (Hash[i] & 31); + if (chr > 9) chr += 7; + + Password[i] = chr + 48; + } + + Password[16] = 0; + + memcpy(&Val, Password, 8); + PassCode = Val %= 1000000; + printf("Passcode is %d\n", PassCode); + + return; +} + +int main(int argc, char * argv[]) +{ + if (argc < 2) + { + printf ("Need to supply KeyPhrase\n"); + return 0; + } + CreateOneTimePassword(argv[1]); + return 0; +} + diff --git a/.svn/pristine/22/226bee19b44371b4549b7942ae8eb8c94fbf6754.svn-base b/.svn/pristine/22/226bee19b44371b4549b7942ae8eb8c94fbf6754.svn-base new file mode 100644 index 0000000..95eefc1 --- /dev/null +++ b/.svn/pristine/22/226bee19b44371b4549b7942ae8eb8c94fbf6754.svn-base @@ -0,0 +1,71 @@ + +//Microsoft Developer Studio generated resource script. +// +//#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "windows.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + + +#define BPQICON 400 + +BPQICON ICON DISCARDABLE "..\BPQIcon.ICO" + +// +// Version +// +#define TEXTVER "1. 0. 3. 4\0" +#define BINVER 1, 0, 3, 4 + +VS_VERSION_INFO VERSIONINFO + FILEVERSION BINVER + PRODUCTVERSION BINVER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "080904b0" + BEGIN + VALUE "Comments", "Program to control WINMOR TNC when running remotely\0" + VALUE "CompanyName", " \0" + VALUE "FileDescription", "WinmorControl\0" + VALUE "FileVersion", TEXTVER + VALUE "InternalName", "bpq32\0" + VALUE "LegalCopyright", "Copyright © 2021 G8BPQ\0" + VALUE "OriginalFilename", "\WinmorControl.exe\0" + VALUE "ProductName", "WinmorControl\0" + VALUE "ProductVersion", TEXTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x809, 1200 + END +END + + +#endif // not APSTUDIO_INVOKED diff --git a/.svn/pristine/22/2285f096dcda9b9b4a0602d14ab046e62d27818b.svn-base b/.svn/pristine/22/2285f096dcda9b9b4a0602d14ab046e62d27818b.svn-base new file mode 100644 index 0000000..67447f8 --- /dev/null +++ b/.svn/pristine/22/2285f096dcda9b9b4a0602d14ab046e62d27818b.svn-base @@ -0,0 +1,968 @@ +/* +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 +*/ + + + +// Monitor Code - from moncode.asm + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + +#pragma data_seg("_BPQDATA") + +#include "cheaders.h" +#include "tncinfo.h" + +// MSGFLAG contains CMD/RESPONSE BITS + +#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND +#define RESP 2 // CURRENT MSG IS RESPONSE +#define VER1 1 // CURRENT MSG IS VERSION 1 + + +#define UI 3 +#define SABM 0x2F +#define DISC 0x43 +#define DM 0x0F +#define UA 0x63 +#define FRMR 0x87 +#define RR 1 +#define RNR 5 +#define REJ 9 + +#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE + +#define NETROM_PID 0xCF +#define IP_PID 0xCC +#define ARP_PID 0xCD + +#define NODES_SIG 0xFF + +UCHAR * DisplayINP3RIF(UCHAR * ptr1, UCHAR * ptr2, unsigned int msglen); + +char * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen); +UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen); +char * DISPLAYARPDATAGRAM(UCHAR * Datagram, UCHAR * Output); + + +DllExport int APIENTRY SetTraceOptions(int mask, int mtxparam, int mcomparam) +{ + +// Sets the tracing options for DecodeFrame. Mask is a bit +// mask of ports to monitor (ie 101 binary will monitor ports +// 1 and 3). MTX enables monitoring on transmitted frames. MCOM +// enables monitoring of protocol control frames (eg SABM, UA, RR), +// as well as info frames. + +// *** For external use only, supports portnum up to 31 *** + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + + return (0); +} + +DllExport int APIENTRY SetTraceOptions64(uint64_t mask, int mtxparam, int mcomparam, int monUIOnly) +{ + +// Sets the tracing options for DecodeFrame. Mask is a bit +// mask of ports to monitor (ie 101 binary will monitor ports +// 1 and 3). MTX enables monitoring on transmitted frames. MCOM +// enables monitoring of protocol control frames (eg SABM, UA, RR), +// as well as info frames. + +// *** For external use only, supports portnum up to 63 *** + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + MUIONLY = monUIOnly; + + return (0); +} +DllExport int APIENTRY SetTraceOptionsEx(int mask, int mtxparam, int mcomparam, int monUIOnly) +{ + +// *** For external use only, supports portnum up to 31 *** + +// Sets the tracing options for DecodeFrame. Mask is a bit +// mask of ports to monitor (ie 101 binary will monitor ports +// 1 and 3). MTX enables monitoring on transmitted frames. MCOM +// enables monitoring of protocol control frames (eg SABM, UA, RR), +// as well as info frames. + + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + MUIONLY = monUIOnly; + + return 0; +} + +int IntSetTraceOptionsEx(uint64_t mask, int mtxparam, int mcomparam, int monUIOnly) +{ + +// Sets the tracing options for DecodeFrame. Mask is a bit +// mask of ports to monitor (ie 101 binary will monitor ports +// 1 and 3). MTX enables monitoring on transmitted frames. MCOM +// enables monitoring of protocol control frames (eg SABM, UA, RR), +// as well as info frames. + + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + MUIONLY = monUIOnly; + + return 0; +} + +int APRSDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, uint64_t Mask) +{ + return IntDecodeFrame(msg, buffer, Stamp, Mask, TRUE, FALSE); +} +DllExport int APIENTRY DecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp) +{ + return IntDecodeFrame(msg, buffer, Stamp, MMASK, FALSE, FALSE); +} + +int IntDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, uint64_t Mask, BOOL APRS, BOOL MINI) +{ + UCHAR * ptr; + int n; + MESSAGE * ADJBUFFER; + ptrdiff_t Work; + UCHAR CTL; + BOOL PF = 0; + char CRCHAR[3] = " "; + char PFCHAR[3] = " "; + int Port; + int MSGFLAG = 0; //CR and V1 flags + char * Output = buffer; + char TR = 'R'; + char From[10], To[10]; + BOOL Info = 0; + BOOL FRMRFLAG = 0; + BOOL XIDFLAG = 0; + BOOL TESTFLAG = 0; + + size_t MsgLen = msg->LENGTH; + + // Use gmtime so we can also display in local time + + struct tm * TM; + + if (MTX & 0x80) + TM = localtime(&Stamp); + else + TM = gmtime(&Stamp); + + // MINI mode is for Node Listen (remote monitor) Mode. Keep info to minimum +/* +KO6IZ*>K7TMG-1: +/ex +KO6IZ*>K7TMG-1: +b +KO6IZ*>K7TMG-1 (UA) +W0TX*>KC6OAR>KB9KC>ID: +W0TX/R DRC/D W0TX-2/G W0TX-1/B W0TX-7/N +KC6OAR*>ID: +*/ + // Check Port + + Port = msg->PORT; + + if (Port == 40) + Port = Port; + + if (Port & 0x80) + { + if ((MTX & 1) == 0) + return 0; // TRANSMITTED FRAME - SEE IF MTX ON + + TR = 'T'; + } + + Port &= 0x7F; + + if ((((uint64_t)1 << (Port - 1)) & Mask) == 0) // Check MMASK + { + if (msg->Padding[0] == '[') + msg->Padding[0] = 0; + + return 0; + } + + + // We now pass Text format monitoring from non-ax25 drivers through this code + // As a valid ax.25 address must have bottom bit set flag plain text messages + // with hex 01. + + // GET THE CONTROL BYTE, TO SEE IF THIS FRAME IS TO BE DISPLAYED + + if (msg->DEST[0] == 1) + { + // Just copy text (Null Terminated) to output + + // Need Timestamp and T/R + + // Add Port: unless Mail Mon (port 64) + + Output += sprintf((char *)Output, "%02d:%02d:%02d%c ", TM->tm_hour, TM->tm_min, TM->tm_sec, TR); + + strcpy(Output, &msg->DEST[1]); + Output += strlen(Output); + + if (buffer[strlen(buffer) -1] == '\r') + Output--; + + if (Port == 64) + Output += sprintf((char *)Output, " \r"); + else + Output += sprintf((char *)Output, " Port=%d\r", Port); + + return (int)strlen(buffer); + } + + n = 8; // MAX DIGIS + ptr = &msg->ORIGIN[6]; // End of Address bit + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + ptr += 7; + n--; + + if (n < 0) + return 0; // Corrupt - no end of address bit + } + + // Reached End of digis + + Work = ptr - &msg->ORIGIN[6]; // Work is length of digis + + MsgLen -= Work; + + ADJBUFFER = (MESSAGE *)((UCHAR *)msg + Work); // ADJBUFFER points to CTL, etc. allowing for digis + + CTL = ADJBUFFER->CTL; + + if (CTL & PFBIT) + PF = TRUE; + + CTL &= ~PFBIT; + + if (MUIONLY) + if (CTL != 3) + return 0; + + if ((CTL & 1) == 0 || CTL == FRMR || CTL == 3) + { + } + else + { + if (((CTL & 2) && MINI) == 0) // Want Control (but not super unless MCOM + if (MCOM == 0) + return 0; // Dont do control + } + + + // Add Port: if MINI mode and monitoring more than one port + + if (MINI == 0) + Output += sprintf((char *)Output, "%02d:%02d:%02d%c ", TM->tm_hour, TM->tm_min, TM->tm_sec, TR); + else + if (CountBits64(Mask) > 1) + Output += sprintf((char *)Output, "%d:", Port); + + From[ConvFromAX25(msg->ORIGIN, From)] = 0; + To[ConvFromAX25(msg->DEST, To)] = 0; + + Output += sprintf((char *)Output, "%s>%s", From, To); + + // Display any Digi-Peaters + + n = 8; // Max number of digi-peaters + ptr = &msg->ORIGIN[6]; // End of Address bit + + 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++) = '*'; + else + if ((ptr[7] & 0x80) == 0) // Repeased by next? + *(Output++) = '*'; // No, so need * + } + } + + if (MINI == 0) + Output += sprintf((char *)Output, " Port=%d ", Port); + + // Set up CR and PF + + CRCHAR[0] = 0; + PFCHAR[0] = 0; + + if (msg->DEST[6] & 0x80) + { + if (msg->ORIGIN[6] & 0x80) // Both set, assume V1 + MSGFLAG |= VER1; + else + { + MSGFLAG |= CMDBIT; + CRCHAR[0] = ' '; + CRCHAR[1] = 'C'; + if (PF) // If FP set + { + PFCHAR[0] = ' '; + PFCHAR[1] = 'P'; + } + } + } + else + { + if (msg->ORIGIN[6] & 0x80) // Only Origin Set + { + MSGFLAG |= RESP; + CRCHAR[0] = ' '; + CRCHAR[1] = 'R'; + if (PF) // If FP set + { + PFCHAR[0] = ' '; + PFCHAR[1] = 'F'; + } + } + else + MSGFLAG |= VER1; // Neither, assume V1 + } + + if ((CTL & 1) == 0) // I frame + { + int NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S) + int NR = (CTL >> 5) & 7; + + Info = 1; + + if (MINI == 0) + Output += sprintf((char *)Output, "", CRCHAR, PFCHAR, NS, NR); + } + else if (CTL == 3) + { + // Un-numbered Information Frame + + Output += sprintf((char *)Output, "", CRCHAR); + Info = 1; + } + else if (CTL & 2) + { + // UN Numbered + + char SUP[6] = "??"; + + switch (CTL) + { + case SABM: + + strcpy(SUP, "C"); + break; + + case SABME: + + strcpy(SUP, "SABME"); + break; + + case XID: + + strcpy(SUP, "XID"); + XIDFLAG = 1; + break; + + case TEST: + + strcpy(SUP, "TEST"); + TESTFLAG = 1; + break; + + case DISC: + + strcpy(SUP, "D"); + break; + + case DM: + + strcpy(SUP, "DM"); + break; + + case UA: + + strcpy(SUP, "UA"); + break; + + + case FRMR: + + strcpy(SUP, "FRMR"); + FRMRFLAG = 1; + break; + } + + if (MINI) + Output += sprintf((char *)Output, "<%s>", SUP); + else + Output += sprintf((char *)Output, "<%s%s%s>", SUP, CRCHAR, PFCHAR); + } + else + { + // Super + + int NR = (CTL >> 5) & 7; + char SUP[5] = "??"; + + switch (CTL & 0x0F) + { + case RR: + + strcpy(SUP, "RR"); + break; + + case RNR: + + strcpy(SUP, "RNR"); + break; + + case REJ: + + strcpy(SUP, "REJ"); + break; + case SREJ: + + strcpy(SUP, "SREJ"); + break; + } + + Output += sprintf((char *)Output, "<%s%s%s R%d>", SUP, CRCHAR, PFCHAR, NR); + + } + + if (FRMRFLAG) + Output += sprintf((char *)Output, " %02X %02X %02X", ADJBUFFER->PID, ADJBUFFER->L2DATA[0], ADJBUFFER->L2DATA[1]); + + if (XIDFLAG) + { + // Decode and display XID + + UCHAR * ptr = &ADJBUFFER->PID; + + if (*ptr++ == 0x82 && *ptr++ == 0x80) + { + int Type; + int Len; + unsigned int value; + int xidlen = *(ptr++) << 8; + xidlen += *ptr++; + + // XID is set of Type, Len, Value n-tuples + +// G8BPQ-2>G8BPQ:(XID cmd, p=1) Half-Duplex SREJ modulo-128 I-Field-Length-Rx=256 Window-Size-Rx=32 Ack-Timer=3000 Retries=10 + + + while (xidlen > 0) + { + Type = *ptr++; + Len = *ptr++; + + value = 0; + xidlen -= (Len + 2); + + while (Len--) + { + value <<=8; + value += *ptr++; + } + switch(Type) + { + case 2: //Bin fields + case 3: + + Output += sprintf((char *)Output, " %d=%x", Type, value); + break; + + case 6: //RX Size + + Output += sprintf((char *)Output, " RX Paclen=%d", value / 8); + break; + + case 8: //RX Window + + Output += sprintf((char *)Output, " RX Window=%d", value); + break; + } + } + } + } + + if (msg->Padding[0] == '[') + Output += sprintf((char *)Output, " %s", msg->Padding); + + msg->Padding[0] = 0; + + if (Info) + { + // We have an info frame + + switch (ADJBUFFER->PID) + { + case 0xF0: // Normal Data + { + char Infofield[257]; + char * ptr1 = Infofield; + char * ptr2 = ADJBUFFER->L2DATA; + UCHAR C; + size_t len; + + MsgLen = MsgLen - (19 + sizeof(void *)); + + if (MsgLen < 0 || MsgLen > 257) + return 0; // Duff + + while (MsgLen--) + { + C = *(ptr2++); + + if (APRS) + *(ptr1++) = C; // MIC-E needs all chars + else + { + // Convert to printable + + C &= 0x7F; + + if (C == 13 || C == 10 || C > 31) + *(ptr1++) = C; + } + } + + len = ptr1 - Infofield; + + Output[0] = ':'; + Output[1] = 13; + memcpy(&Output[2], Infofield, len); + Output += (len + 2); + + break; + } + case NETROM_PID: + + Output = DISPLAY_NETROM(ADJBUFFER, Output, (int)MsgLen); + break; + + case IP_PID: + + Output += sprintf((char *)Output, " \r"); + Output = DISPLAYIPDATAGRAM((IPMSG *)&ADJBUFFER->L2DATA[0], Output, (int)MsgLen); + break; + + case ARP_PID: + + Output = DISPLAYARPDATAGRAM(&ADJBUFFER->L2DATA[0], Output); + break; + + case 8: // Fragmented IP + + n = ADJBUFFER->L2DATA[0]; // Frag Count + + Output += sprintf((char *)Output, "\r", n); + + if (ADJBUFFER->L2DATA[0] & 0x80) // First Frag - Display Header + { + Output = DISPLAYIPDATAGRAM((IPMSG *)&ADJBUFFER->L2DATA[2], Output, (int)MsgLen - 1); + } + + break; + } + } + + if (Output[-1] != 13) + Output += sprintf((char *)Output, "\r"); + + return (int)(Output - buffer); + +} +// Display NET/ROM data + +char * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen) +{ + char Alias[7]= ""; + char Dest[10]; + char Node[10]; + UCHAR TTL, Index, ID, TXNO, RXNO, OpCode, Flags, Window; + UCHAR * ptr = &ADJBUFFER->L2DATA[0]; + + if (ADJBUFFER->L2DATA[0] == NODES_SIG) + { + // Display NODES + + + // If an INP3 RIF (type <> UI) decode as such + + if (ADJBUFFER->CTL != 3) // UI + return DisplayINP3RIF(&ADJBUFFER->L2DATA[1], Output, MsgLen - (MSGHDDRLEN + 14 + 3)); + + memcpy(Alias, ++ptr, 6); + + ptr += 6; + + Output += sprintf((char *)Output, " NODES broadcast from %s\r", Alias); + + MsgLen -= 30; //Header, mnemonic and signature length + + while(MsgLen > 20) // Entries are 21 bytes + { + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + memcpy(Alias, ptr, 6); + ptr +=6; + strlop(Alias, ' '); + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + Output += sprintf((char *)Output, " %s:%s via %s qlty=%d\r", Alias, Dest, Node, ptr[0]); + ptr++; + MsgLen -= 21; + } + return Output; + } + + // Display normal NET/ROM transmissions + + Output += sprintf((char *)Output, " NET/ROM\r "); + + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + TTL = *(ptr++); + Index = *(ptr++); + ID = *(ptr++); + TXNO = *(ptr++); + RXNO = *(ptr++); + OpCode = Flags = *(ptr++); + + OpCode &= 15; // Remove Flags + + Output += sprintf((char *)Output, "%s to %s ttl %d cct=%02X%02X ", Dest, Node, TTL, Index, ID ); + MsgLen -= 20; + + switch (OpCode) + { + case L4CREQ: + + Window = *(ptr++); + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + Output += sprintf((char *)Output, " w=%d %s at %s", Window, Dest, Node); + + if (MsgLen > 38) // BPQ Extended Params + { + short Timeout = (SHORT)*ptr; + Output += sprintf((char *)Output, " t/o %d", Timeout); + } + + return Output; + + case L4CACK: + + if (Flags & L4BUSY) // BUSY RETURNED + return Output + sprintf((char *)Output, " - BUSY"); + + return Output + sprintf((char *)Output, " w=%d my cct=%02X%02X", ptr[1], TXNO, RXNO); + + case L4DREQ: + + return Output + sprintf((char *)Output, " "); + + case L4DACK: + + return Output + sprintf((char *)Output, " "); + + case L4RESET: + + return Output + sprintf((char *)Output, " "); + + case L4INFO: + { + char Infofield[257]; + char * ptr1 = Infofield; + UCHAR C; + size_t len; + + Output += sprintf((char *)Output, " ", TXNO, RXNO); + + if (Flags & L4BUSY) + *(Output++) = 'B'; + + if (Flags & L4NAK) + *(Output++) = 'N'; + + if (Flags & L4MORE) + *(Output++) = 'M'; + + if (Flags & L4COMP) + *(Output++) = 'C'; + + MsgLen = MsgLen - (19 + sizeof(void *)); + + if (MsgLen < 0 || MsgLen > 257) + return Output; // Duff + + while (MsgLen--) + { + C = *(ptr++); + + // Convert to printable + + C &= 0x7F; + + if (C == 13 || C == 10 || C > 31) + *(ptr1++) = C; + } + + len = ptr1 - Infofield; + + Output[0] = ':'; + Output[1] = 13; + memcpy(&Output[2], Infofield, len); + Output += (len + 2); + } + + return Output; + + case L4IACK: + + Output += sprintf((char *)Output, " ", RXNO); + + if (Flags & L4BUSY) + *(Output++) = 'B'; + + if (Flags & L4NAK) + *(Output++) = 'N'; + + if (Flags & L4MORE) + *(Output++) = 'M'; + + return Output; + + + case 0: + + // OPcode zero is used for several things + + if (Index == 0x0c && ID == 0x0c) // IP + { + *(Output++) = 13; + *(Output++) = ' '; + Output = DISPLAYIPDATAGRAM((IPMSG *)ptr, Output, MsgLen); + return Output; + } + + if (Index == 0 && ID == 1) // NRR + { + Output += sprintf((char *)Output, " \r"); + + MsgLen -= 23; + + while (MsgLen > 6) + { + Dest[ConvFromAX25(ptr, Dest)] = 0; + + if (ptr[7] & 0x80) + Output += sprintf((char *)Output, "%s* ", Dest); + else + Output += sprintf((char *)Output, "%s ", Dest); + + ptr +=8; + MsgLen -= 8; + } + + return Output; + } + } + + Output += sprintf((char *)Output, " "); + return Output; +} + +/* + + PUBLIC L3IP +L3IP: +; +; TCP/IP DATAGRAM +; + mov EBX,OFFSET IP_MSG + call NORMSTR +; + INC ESI ; NOW POINTING TO IP HEADER + +*/ +UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen) +{ + UCHAR * ptr; + USHORT FRAGWORD; + + ptr = (UCHAR *)&IP->IPSOURCE; + Output += sprintf((char *)Output, "%d.%d.%d.%d>", ptr[0], ptr[1], ptr[2], ptr[3]); + + ptr = (UCHAR *)&IP->IPDEST; + Output += sprintf((char *)Output, "%d.%d.%d.%d LEN:%d ", ptr[0], ptr[1], ptr[2], ptr[3], htons(IP->IPLENGTH)); + + FRAGWORD = ntohs(IP->FRAGWORD); + + if (FRAGWORD) + { + // If nonzero, check which bits are set + + //Bit 0: reserved, must be zero + //Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment. + //Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments. + //Fragment Offset: 13 bits + + if (FRAGWORD & (1 << 14)) + Output += sprintf((char *)Output, "DF "); + + if (FRAGWORD & (1 << 13)) + Output += sprintf((char *)Output, "MF "); + + FRAGWORD &= 0xfff; + + if (FRAGWORD) + { + Output += sprintf((char *)Output, "Offset %d ", FRAGWORD * 8); + return Output; // Cant display proto fields + } + } + + if (IP->IPPROTOCOL == 6) + { + PTCPMSG TCP = (PTCPMSG)&IP->Data; + + Output += sprintf((char *)Output, "TCP Src %d Dest %d ", ntohs(TCP->SOURCEPORT), ntohs(TCP->DESTPORT)); + return Output; + } + + if (IP->IPPROTOCOL == 1) + { + PICMPMSG ICMPptr = (PICMPMSG)&IP->Data; + + Output += sprintf((char *)Output, "ICMP "); + + if (ICMPptr->ICMPTYPE == 8) + Output += sprintf((char *)Output, "Echo Request "); + else + if (ICMPptr->ICMPTYPE == 0) + Output += sprintf((char *)Output, "Echo Reply "); + else + Output += sprintf((char *)Output, "Code %d", ICMPptr->ICMPTYPE); + + return Output; + } + +/* + MOV AL,IPPROTOCOL[ESI] + CMP AL,6 + JNE @F + + MOV EBX, OFFSET TCP + CALL NORMSTR + JMP ADD_CR +@@: + + CMP AL,1 + JNE @F + + MOV EBX, OFFSET ICMP + CALL NORMSTR + JMP ADD_CR +@@: + + CALL DISPLAY_BYTE_1 ; DISPLAY PROTOCOL TYPE + +; mov AL,CR +; call PUTCHAR +; +; MOV ECX,39 ; TESTING +;IPLOOP: +; lodsb +; CALL BYTE_TO_HEX +; +; LOOP IPLOOP + + JMP ADD_CR + + +*/ + return Output; +} + + + +char * DISPLAYARPDATAGRAM(UCHAR * Datagram, UCHAR * Output) +{ + UCHAR * ptr = Datagram; + char Dest[10]; + + if (ptr[7] == 1) // Request + return Output + sprintf((char *)Output, " ARP Request who has %d.%d.%d.%d? Tell %d.%d.%d.%d", + ptr[26], ptr[27], ptr[28], ptr[29], ptr[15], ptr[16], ptr[17], ptr[18]); + + // Response + + Dest[ConvFromAX25(&ptr[8], Dest)] = 0; + + return Output + sprintf((char *)Output, " ARP Reply %d.%d.%d.%d is at %s Tell %d.%d.%d.%d", + ptr[15], ptr[16], ptr[17], ptr[18], Dest, ptr[26], ptr[27], ptr[28], ptr[29]); + +} diff --git a/.svn/pristine/23/233e93fb0d99b917501d60345425a3cdb5f8b794.svn-base b/.svn/pristine/23/233e93fb0d99b917501d60345425a3cdb5f8b794.svn-base new file mode 100644 index 0000000..a23b13c --- /dev/null +++ b/.svn/pristine/23/233e93fb0d99b917501d60345425a3cdb5f8b794.svn-base @@ -0,0 +1,578 @@ +// +// DLL to inteface the BPQ Virtual COM emulator to BPQ32 switch +// +// Uses BPQ EXTERNAL interface +// + +// Version 1.0 November 2005 +// + +// Version 1.1 October 2006 + +// Write diagmnostics to BPQ console window instead of STDOUT + +// Version 1.2 February 2008 + +// Changes for dynamic unload of bpq32.dll + +// Version 1.2.1 May 2008 + +// Correct RX length (was 1 byte too long) + +// Version 1.3.1 Jan 2009 + +// Support Win98 VirtualCOM Driver + +#define WIN32_LEAN_AND_MEAN +#define _CRT_SECURE_NO_DEPRECATE + +//#include +//#include + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 0 + +typedef unsigned char byte; + +#include "cheaders.h" +#include "bpqvkiss.h" + +#include + +//#define DYNLOADBPQ // Dynamically Load BPQ32.dll +//#define EXTDLL // Use GetMuduleHandle instead of LoadLibrary +#include "bpq32.h" + + +static int ASYINIT(int comport, int speed, int bpqport, BOOL Report); +int kissencode(UCHAR * inbuff, UCHAR * outbuff, int len); +int GetRXMessage(int port, PMESSAGE buff); +void CheckReceivedData(PVCOMINFO pVCOMInfo); +static int ReadCommBlock(PVCOMINFO pVCOMInfo, LPSTR lpszBlock, DWORD nMaxLength ); +static BOOL WriteCommBlock(int port, UCHAR * lpByte , DWORD dwBytesToWrite); + +PVCOMINFO CreateInfo( int port,int speed, int bpqport ) ; + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +static BOOL Win98 = FALSE; + +struct PORTCONTROL * PORTVEC[MaxBPQPortNo + 1]; + +static size_t ExtProc(int fn, int port, PMESSAGE buff) +{ + int len,txlen=0; + char txbuff[1000]; + + if (VCOMInfo[port]->ComDev == (HANDLE) -1) + { + // Try to reopen every 30 secs + + VCOMInfo[port]->ReopenTimer++; + + if (VCOMInfo[port]->ReopenTimer < 300) + return 0; + + VCOMInfo[port]->ReopenTimer = 0; + + ASYINIT(PORTVEC[port]->IOBASE, 9600, port, FALSE); + + if (VCOMInfo[port]->ComDev == (HANDLE) -1) + return 0; + } + + + switch (fn) + { + case 1: // poll + + len = GetRXMessage(port,buff); + +// if (len > 0) +// { +// // Randomly drop packets + +// if ((rand() % 7) > 5) +// { +// Debugprintf("VKISS Test Drop packet"); +// return 0; +// } +// } + + return len; + + case 2: // send + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff); + + txlen = kissencode(&buff->DEST[0], (char *) &txbuff, txlen - MSGHDDRLEN); + + WriteCommBlock(port, txbuff, txlen); + + return (0); + + + case 3: // CHECK IF OK TO SEND + + return (0); // OK + + break; + + case 4: // reinit + + CloseHandle(VCOMInfo[port]->ComDev); + VCOMInfo[port]->ComDev =(HANDLE) -1; + VCOMInfo[port]->ReopenTimer = 250; + + return (0); + + case 5: // Close + + CloseHandle(VCOMInfo[port]->ComDev); + + return (0); + + } + + return 0; + +} + +VOID * VCOMExtInit(struct PORTCONTROL * PortEntry) +{ + char msg[80]; + + // + // Will be called once for each port to be mapped to a BPQ Virtual COM Port + // The VCOM port number is in IOBASE + // + + sprintf(msg,"VKISS COM%d", PortEntry->IOBASE); + WritetoConsole(msg); + + PORTVEC[PortEntry->PORTNUMBER] = PortEntry; + + CreateInfo(PortEntry->IOBASE, 9600, PortEntry->PORTNUMBER); + + // Open File + + ASYINIT(PortEntry->IOBASE, 9600, PortEntry->PORTNUMBER, TRUE); + + WritetoConsole("\n"); + + return ExtProc; +} + +static int kissencode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + + outbuff[0] = FEND; + outbuff[1] = 0; + txptr = 2; + + for (i = 0; i < len; i++) + { + c = inbuff[i]; + + switch (c) + { + case FEND: + outbuff[txptr++] = FESC; + outbuff[txptr++] = TFEND; + break; + + case FESC: + + outbuff[txptr++] = FESC; + outbuff[txptr++] = TFESC; + break; + + default: + + outbuff[txptr++] = c; + } + } + + outbuff[txptr++] = FEND; + + return txptr; + +} + +int ASYINIT(int comport, int speed, int bpqport, BOOL Report) +{ + char szPort[ 30 ]; + char buf[256]; + int n, Err; + +#pragma warning( push ) +#pragma warning( disable : 4996 ) + +#ifndef _winver + +#define _winver 0x0600 + +#endif + + + if (HIBYTE(_winver) < 5) + Win98 = TRUE; + +#pragma warning( pop ) + + if (Win98) + { + VCOMInfo[bpqport]->ComDev = CreateFile( "\\\\.\\BPQVCOMM.VXD", GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + } + else{ + sprintf( szPort, "\\\\.\\pipe\\BPQCOM%d", comport ) ; + + VCOMInfo[bpqport]->ComDev = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + //Handle = CreateFile(Value, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + + Err = GetLastError(); + + if (VCOMInfo[bpqport]->ComDev != (HANDLE) -1) + { + VCOMInfo[bpqport]->NewVCOM = TRUE; + Err = GetFileType(VCOMInfo[bpqport]->ComDev); + } + else + { + // Try old style + + sprintf( szPort, "\\\\.\\BPQ%d", comport ) ; + + VCOMInfo[bpqport]->ComDev = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + } + } + if (VCOMInfo[bpqport]->ComDev == (HANDLE) -1 && Report) + { + n=sprintf(buf,"Virtual COM Port %d could not be opened ",comport); + WritetoConsole(buf); + + return (FALSE) ; + } + + return (TRUE) ; +} + +static int GetRXMessage(int port, PMESSAGE buff) +{ + int len; + PVCOMINFO pVCOMInfo ; + + if (NULL == (pVCOMInfo = VCOMInfo[port])) + return 0; + + if (!pVCOMInfo->MSGREADY) + CheckReceivedData(pVCOMInfo); // Look for data in RXBUFFER and COM port + + if (pVCOMInfo->MSGREADY) + { + len = (int)(pVCOMInfo->RXMPTR- &pVCOMInfo->RXMSG[1]); // Don't need KISS Control Byte + + if (pVCOMInfo->RXMSG[0] != 0 && pVCOMInfo->RXMSG[0] != 12) + { + pVCOMInfo->MSGREADY=FALSE; + pVCOMInfo->RXMPTR=(UCHAR *)&pVCOMInfo->RXMSG; + return 0; // Not KISS Data + } + + // + // Remove KISS control byte + // + + if (pVCOMInfo->RXMSG[0] == 12) + { + // AckMode Frame. Return the next 2 bytes, but don't pass them to Host + + UCHAR AckResp[8]; + + AckResp[0] = FEND; + memcpy(&AckResp[1], &pVCOMInfo->RXMSG[0], 3); //Copy Opcode and Ack Bytes + AckResp[4] = FEND; + WriteCommBlock(port, AckResp, 5); + + len -= 2; + memcpy(&buff->DEST[0], &pVCOMInfo->RXMSG[3], len); +// Debugprintf("VKISS Ackmode Frame"); + } + else + + memcpy(&buff->DEST[0], &pVCOMInfo->RXMSG[1], len); + + len += MSGHDDRLEN; + + PutLengthinBuffer((PDATAMESSAGE)buff, len); + + // + // reset pointers + // + + pVCOMInfo->MSGREADY=FALSE; + pVCOMInfo->RXMPTR=(UCHAR *)&pVCOMInfo->RXMSG; + + return len; + } + else + + return 0; // nothing doing +} + +static void CheckReceivedData(PVCOMINFO pVCOMInfo) +{ + UCHAR c; + + if (pVCOMInfo->RXBCOUNT == 0) + { + // + // Check com buffer + // + + pVCOMInfo->RXBCOUNT = ReadCommBlock(pVCOMInfo, (LPSTR) &pVCOMInfo->RXBUFFER, MAXBLOCK-1 ); + pVCOMInfo->RXBPTR=(UCHAR *)&pVCOMInfo->RXBUFFER; + } + + if (pVCOMInfo->RXBCOUNT == 0) + return; + + while (pVCOMInfo->RXBCOUNT != 0) + { + pVCOMInfo->RXBCOUNT--; + + c = *(pVCOMInfo->RXBPTR++); + + if (pVCOMInfo->ESCFLAG) + { + // + // FESC received - next should be TFESC or TFEND + + pVCOMInfo->ESCFLAG = FALSE; + + if (c == TFESC) + c=FESC; + + if (c == TFEND) + c=FEND; + + } + else + { + switch (c) + { + case FEND: + + // + // Either start of message or message complete + // + + if (pVCOMInfo->RXMPTR == (UCHAR *)&pVCOMInfo->RXMSG) + continue; + + pVCOMInfo->MSGREADY=TRUE; + return; + + case FESC: + + pVCOMInfo->ESCFLAG = TRUE; + continue; + + } + } + + // + // Ok, a normal char + // + + *(pVCOMInfo->RXMPTR++) = c; + + } + + if (pVCOMInfo->RXMPTR - (UCHAR *)&pVCOMInfo->RXMSG > 500) + pVCOMInfo->RXMPTR=(UCHAR *)&pVCOMInfo->RXMSG; + + return; +} + +static PVCOMINFO CreateInfo( int port,int speed, int bpqport ) +{ + PVCOMINFO pVCOMInfo ; + + if (NULL == (pVCOMInfo = + (PVCOMINFO) LocalAlloc( LPTR, sizeof( VCOMINFO ) ))) + return ( (PVCOMINFO) -1 ) ; + + pVCOMInfo->RXBCOUNT=0; + pVCOMInfo->MSGREADY=FALSE; + pVCOMInfo->RXBPTR=(UCHAR *)&pVCOMInfo->RXBUFFER; + pVCOMInfo->RXMPTR=(UCHAR *)&pVCOMInfo->RXMSG; + + pVCOMInfo->ComDev = 0 ; + pVCOMInfo->Connected = FALSE ; + pVCOMInfo->Port = port; + + VCOMInfo[bpqport]=pVCOMInfo; + + return (pVCOMInfo); +} + +static BOOL NEAR DestroyTTYInfo( int port ) +{ + PVCOMINFO pVCOMInfo ; + + if (NULL == (pVCOMInfo = VCOMInfo[port])) + return ( FALSE ) ; + + LocalFree( pVCOMInfo ) ; + + VCOMInfo[port] = 0; + + return ( TRUE ) ; + +} + +static int ReadCommBlock(PVCOMINFO pVCOMInfo, LPSTR lpszBlock, DWORD nMaxLength) +{ + DWORD dwLength = 0; + DWORD Available = 0; + + if (Win98) + DeviceIoControl(pVCOMInfo->ComDev, (pVCOMInfo->Port << 16) | W98_SERIAL_GETDATA, + NULL,0,lpszBlock,nMaxLength, &dwLength,NULL); + + else if (pVCOMInfo->NewVCOM) + { + int ret = PeekNamedPipe(pVCOMInfo->ComDev, NULL, 0, NULL, &Available, NULL); + + if (ret == 0) + { + ret = GetLastError(); + + if (ret == ERROR_BROKEN_PIPE) + { + CloseHandle(pVCOMInfo->ComDev); + pVCOMInfo->ComDev = INVALID_HANDLE_VALUE; + return 0; + } + } + + if (Available > nMaxLength) + Available = nMaxLength; + + if (Available) + { + UCHAR * ptr1 = lpszBlock; + UCHAR * ptr2 = lpszBlock; + UCHAR c; + int Length; + + ReadFile(pVCOMInfo->ComDev, lpszBlock, Available, &dwLength, NULL); + + // Have to look foro FF escape chars + + Length = dwLength; + + while (Length != 0) + { + c = *(ptr1++); + Length--; + + if (c == 0xff) + { + c = c = *(ptr1++); + Length--; + + if (c == 0xff) // ff ff means ff + { + dwLength--; + } + else + { + // This is connection statua from other end + + dwLength -= 2; + pVCOMInfo->NewVCOMConnected = c; + continue; + } + } + *(ptr2++) = c; + } + } + } + + else + DeviceIoControl( + pVCOMInfo->ComDev,IOCTL_SERIAL_GETDATA,NULL,0,lpszBlock,nMaxLength, &dwLength,NULL); + + return (dwLength); + +} + +static BOOL WriteCommBlock(int port, UCHAR * Message, DWORD MsgLen) +{ + ULONG bytesReturned; + +// if ((rand() % 100) > 80) +// return 0; + + if (Win98) + return DeviceIoControl( + VCOMInfo[port]->ComDev,(VCOMInfo[port]->Port << 16) | W98_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL); + + else if (VCOMInfo[port]->NewVCOM) + { + // Have to escape all oxff chars, as these are used to get status info + + UCHAR NewMessage[1000]; + UCHAR * ptr1 = Message; + UCHAR * ptr2 = NewMessage; + UCHAR c; + + int Length = MsgLen; + + while (Length != 0) + { + c = *(ptr1++); + *(ptr2++) = c; + + if (c == 0xff) + { + *(ptr2++) = c; + MsgLen++; + } + Length--; + } + + return WriteFile(VCOMInfo[port]->ComDev, NewMessage, MsgLen, &bytesReturned, NULL); + } + else + return DeviceIoControl( + VCOMInfo[port]->ComDev,IOCTL_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL); + +} + + + diff --git a/.svn/pristine/23/23fcfba01636a643d41ed32ea57b94ee15224e86.svn-base b/.svn/pristine/23/23fcfba01636a643d41ed32ea57b94ee15224e86.svn-base new file mode 100644 index 0000000..6627749 --- /dev/null +++ b/.svn/pristine/23/23fcfba01636a643d41ed32ea57b94ee15224e86.svn-base @@ -0,0 +1,654 @@ +/* +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 +*/ + + + +// Monitor Code - from moncode.asm + +// Modified for AGW form monitor + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + +#pragma data_seg("_BPQDATA") + +#include "cheaders.h" +#include "tncinfo.h" + +// MSGFLAG contains CMD/RESPONSE BITS + +#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND +#define RESP 2 // CURRENT MSG IS RESPONSE +#define VER1 1 // CURRENT MSG IS VERSION 1 + + +#define UI 3 +#define SABM 0x2F +#define DISC 0x43 +#define DM 0x0F +#define UA 0x63 +#define FRMR 0x87 +#define RR 1 +#define RNR 5 +#define REJ 9 + +#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE + +#define NETROM_PID 0xCF +#define IP_PID 0xCC +#define ARP_PID 0xCD + +#define NODES_SIG 0xFF + +UCHAR * DisplayINP3RIF(UCHAR * ptr1, UCHAR * ptr2, int msglen); + +static UCHAR * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen, int DoNodes); +static UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen); +static UCHAR * DISPLAYARPDATAGRAM(UCHAR * Datagram, UCHAR * Output); + + +int InternalAGWDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, int * FrameType, int useLocalTime, int DoNodes) +{ + UCHAR * ptr; + int n; + MESSAGE * ADJBUFFER; + ptrdiff_t Work; + UCHAR CTL; + BOOL PF = 0; + char CRCHAR[3] = " "; + char PFCHAR[3] = " "; + int MSGFLAG = 0; //CR and V1 flags + char * Output = buffer; + char From[10], To[10]; + BOOL Info = 0; + BOOL FRMRFLAG = 0; + BOOL XIDFLAG = 0; + BOOL TESTFLAG = 0; + size_t MsgLen = msg->LENGTH; + + struct tm * TM; + + if (useLocalTime) + TM = localtime(&Stamp); + else + TM = gmtime(&Stamp); + + // GET THE CONTROL BYTE, TO SEE IF THIS FRAME IS TO BE DISPLAYED + + n = 8; // MAX DIGIS + ptr = &msg->ORIGIN[6]; // End of Address bit + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + ptr += 7; + n--; + + if (n == 0) + { + return 0; // Corrupt - no end of address bit + } + } + + // Reached End of digis + + Work = ptr - &msg->ORIGIN[6]; // Work is length of digis + + MsgLen -= Work; + + ADJBUFFER = (MESSAGE *)((UCHAR *)msg + Work); // ADJBUFFER points to CTL, etc. allowing for digis + + CTL = ADJBUFFER->CTL; + + if (CTL & PFBIT) + PF = TRUE; + + CTL &= ~PFBIT; + + *FrameType = CTL; + + Output += sprintf((char *)Output, " %d:Fm ", msg->PORT & 0x7f); // Mask TX bit + + From[ConvFromAX25(msg->ORIGIN, From)] = 0; + To[ConvFromAX25(msg->DEST, To)] = 0; + + Output += sprintf((char *)Output, "%s To %s", From, To); + + // Display any Digi-Peaters + + n = 8; // Max number of digi-peaters + ptr = &msg->ORIGIN[6]; // End of Address bit + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + From[ConvFromAX25(ptr + 1, From)] = 0; + + if (n == 8) + Output += sprintf((char *)Output, " Via %s", From); // Send via on first + else + 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++) = '*'; + else + if ((ptr[7] & 0x80) == 0) // Repeased by next? + *(Output++) = '*'; // No, so need * + } + } + + *(Output++) = ' '; + + // Set up CR and PF + + CRCHAR[0] = 0; + PFCHAR[0] = 0; + + if (msg->DEST[6] & 0x80) + { + if (msg->ORIGIN[6] & 0x80) // Both set, assume V1 + MSGFLAG |= VER1; + else + { + MSGFLAG |= CMDBIT; + CRCHAR[0] = ' '; + CRCHAR[1] = 'C'; + if (PF) // If FP set + { + PFCHAR[0] = ' '; + PFCHAR[1] = 'P'; + } + } + } + else + { + if (msg->ORIGIN[6] & 0x80) // Only Origin Set + { + MSGFLAG |= RESP; + CRCHAR[0] = ' '; + CRCHAR[1] = 'R'; + if (PF) // If FP set + { + PFCHAR[0] = ' '; + PFCHAR[1] = 'F'; + } + } + else + MSGFLAG |= VER1; // Neither, assume V1 + } + + if ((CTL & 1) == 0) // I frame + { + int NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S) + int NR = (CTL >> 5) & 7; + + Info = 1; + + Output += sprintf((char *)Output, "", CRCHAR, PFCHAR, NS, NR); + } + else if (CTL == 3) + { + // Un-numbered Information Frame + //UI pid=F0 Len=20 > + + Output += sprintf((char *)Output, "", ADJBUFFER->PID, (int)MsgLen - 23); + Info = 1; + } + else if (CTL & 2) + { + // UN Numbered + + char SUP[5] = "??"; + + switch (CTL) + { + case SABM: + + strcpy(SUP, "C"); + break; + + case DISC: + + strcpy(SUP, "D"); + break; + + case DM: + + strcpy(SUP, "DM"); + break; + + case UA: + + strcpy(SUP, "UA"); + break; + + + case FRMR: + + strcpy(SUP, "FRMR"); + FRMRFLAG = 1; + break; + } + + Output += sprintf((char *)Output, "<%s%s%s>", SUP, CRCHAR, PFCHAR); + } + else + { + // Super + + int NR = (CTL >> 5) & 7; + char SUP[4] = "??"; + + switch (CTL & 0x0F) + { + case RR: + + strcpy(SUP, "RR"); + break; + + case RNR: + + strcpy(SUP, "RNR"); + break; + + case REJ: + + strcpy(SUP, "REJ"); + break; + } + + Output += sprintf((char *)Output, "<%s%s%s R%d>", SUP, CRCHAR, PFCHAR, NR); + + } + + Output += sprintf((char *)Output, "[%02d:%02d:%02d]", TM->tm_hour, TM->tm_min, TM->tm_sec); + + + if (FRMRFLAG) + Output += sprintf((char *)Output, "%02X %02X %02X", ADJBUFFER->PID, ADJBUFFER->L2DATA[0], ADJBUFFER->L2DATA[1]); + + if (Info) + { + // We have an info frame + + switch (ADJBUFFER->PID) + { + case 0xF0: // Normal Data + { + char Infofield[257]; + char * ptr1 = Infofield; + char * ptr2 = ADJBUFFER->L2DATA; + UCHAR C; + size_t len; + + MsgLen = MsgLen - 23; + + if (MsgLen < 0 || MsgLen > 257) + return 0; // Duff + + while (MsgLen--) + { + C = *(ptr2++); + + // Convert to printable + + C &= 0x7F; + + if (C == 13 || C == 10 || C > 31) + *(ptr1++) = C; + } + + len = ptr1 - Infofield; + +// Output[0] = ':'; + Output[0] = 13; + memcpy(&Output[1], Infofield, len); + Output += (len + 1); + + break; + } + case NETROM_PID: + + Output = DISPLAY_NETROM(ADJBUFFER, Output,(int) MsgLen, DoNodes); + break; + + case IP_PID: + + Output += sprintf((char *)Output, " \r"); + Output = DISPLAYIPDATAGRAM((IPMSG *)&ADJBUFFER->L2DATA[0], Output, (int)MsgLen); + break; + + case ARP_PID: + + Output = DISPLAYARPDATAGRAM(&ADJBUFFER->L2DATA[0], Output); + break; + + case 8: // Fragmented IP + + Output += sprintf((char *)Output, ""); + break; + } + } + + if (Output == NULL) + return 0; + + if (Output[-1] != 13) + Output += sprintf((char *)Output, "\r"); + + return (int)(Output - buffer); + +} +// Display NET/ROM data + +UCHAR * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen, int DoNodes) +{ + char Alias[7]= ""; + char Dest[10]; + char Node[10]; + UCHAR TTL, Index, ID, TXNO, RXNO, OpCode, Flags, Window; + UCHAR * ptr = &ADJBUFFER->L2DATA[0]; + + if (ADJBUFFER->L2DATA[0] == NODES_SIG) + { + // Display NODES + + if (DoNodes == 0) + return NULL; + + // If an INP3 RIF (type <> UI) decode as such + + if (ADJBUFFER->CTL != 3) // UI + return DisplayINP3RIF(&ADJBUFFER->L2DATA[1], Output, MsgLen - 24); + + memcpy(Alias, ++ptr, 6); + + ptr += 6; + + Output += sprintf((char *)Output, "\rFF %s (NetRom Routing)\r", Alias); + + MsgLen -= 30; //Header, mnemonic and signature length + + while(MsgLen > 20) // Entries are 21 bytes + { + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + memcpy(Alias, ptr, 6); + ptr +=6; + strlop(Alias, ' '); + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + Output += sprintf((char *)Output, " %s:%s via %s qlty=%d\r", Alias, Dest, Node, ptr[0]); + ptr++; + MsgLen -= 21; + } + return Output; + } + + // Display normal NET/ROM transmissions + + Output += sprintf((char *)Output, " NET/ROM\r "); + + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + TTL = *(ptr++); + Index = *(ptr++); + ID = *(ptr++); + TXNO = *(ptr++); + RXNO = *(ptr++); + OpCode = Flags = *(ptr++); + + OpCode &= 15; // Remove Flags + + Output += sprintf((char *)Output, "%s to %s ttl %d cct=%02X%02X ", Dest, Node, TTL, Index, ID ); + MsgLen -= 20; + + switch (OpCode) + { + case L4CREQ: + + Window = *(ptr++); + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + Output += sprintf((char *)Output, " w=%d %s at %s", Window, Dest, Node); + + if (MsgLen > 38) // BPQ Extended Params + { + short Timeout = (SHORT)*ptr; + Output += sprintf((char *)Output, " t/o %d", Timeout); + } + + return Output; + + case L4CACK: + + if (Flags & L4BUSY) // BUSY RETURNED + return Output + sprintf((char *)Output, " - BUSY"); + + return Output + sprintf((char *)Output, " w=%d my cct=%02X%02X", ptr[1], TXNO, RXNO); + + case L4DREQ: + + return Output + sprintf((char *)Output, " "); + + case L4DACK: + + return Output + sprintf((char *)Output, " "); + + case L4INFO: + { + char Infofield[257]; + char * ptr1 = Infofield; + UCHAR C; + size_t len; + + Output += sprintf((char *)Output, " ", TXNO, RXNO); + + if (Flags & L4BUSY) + *(Output++) = 'B'; + + if (Flags & L4NAK) + *(Output++) = 'N'; + + if (Flags & L4MORE) + *(Output++) = 'M'; + + MsgLen = MsgLen - 23; + + if (MsgLen < 0 || MsgLen > 257) + return Output; // Duff + + while (MsgLen--) + { + C = *(ptr++); + + // Convert to printable + + C &= 0x7F; + + if (C == 13 || C == 10 || C > 31) + *(ptr1++) = C; + } + + len = ptr1 - Infofield; + + Output[0] = ':'; + Output[1] = 13; + memcpy(&Output[2], Infofield, len); + Output += (len + 2); + } + + return Output; + + case L4IACK: + + Output += sprintf((char *)Output, " ", RXNO); + + if (Flags & L4BUSY) + *(Output++) = 'B'; + + if (Flags & L4NAK) + *(Output++) = 'N'; + + if (Flags & L4MORE) + *(Output++) = 'M'; + + return Output; + + + case 0: + + // OPcode zero is used for several things + + if (Index == 0x0c && ID == 0x0c) // IP + { +// Output = L3IP(Output); + return Output; + } + + if (Index == 0 && ID == 1) // NRR + { + Output += sprintf((char *)Output, " \r"); + + MsgLen -= 23; + + while (MsgLen > 6) + { + Dest[ConvFromAX25(ptr, Dest)] = 0; + + if (ptr[7] & 0x80) + Output += sprintf((char *)Output, "%s* ", Dest); + else + Output += sprintf((char *)Output, "%s ", Dest); + + ptr +=8; + MsgLen -= 8; + } + + return Output; + } + } + + Output += sprintf((char *)Output, " "); + return Output; +} + +/* + + PUBLIC L3IP +L3IP: +; +; TCP/IP DATAGRAM +; + mov EBX,OFFSET IP_MSG + call NORMSTR +; + INC ESI ; NOW POINTING TO IP HEADER + +*/ +UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen) +{ + UCHAR * ptr; + + ptr = (UCHAR *)&IP->IPSOURCE; + Output += sprintf((char *)Output, "%d.%d.%d.%d>", ptr[0], ptr[1], ptr[2], ptr[3]); + + ptr = (UCHAR *)&IP->IPDEST; + Output += sprintf((char *)Output, "%d.%d.%d.%d LEN:%d ", ptr[0], ptr[1], ptr[2], ptr[3], htons(IP->IPLENGTH)); + +/* + MOV AL,IPPROTOCOL[ESI] + CMP AL,6 + JNE @F + + MOV EBX, OFFSET TCP + CALL NORMSTR + JMP ADD_CR +@@: + + CMP AL,1 + JNE @F + + MOV EBX, OFFSET ICMP + CALL NORMSTR + JMP ADD_CR +@@: + + CALL DISPLAY_BYTE_1 ; DISPLAY PROTOCOL TYPE + +; mov AL,CR +; call PUTCHAR +; +; MOV ECX,39 ; TESTING +;IPLOOP: +; lodsb +; CALL BYTE_TO_HEX +; +; LOOP IPLOOP + + JMP ADD_CR + + +*/ + return Output; +} + + + +UCHAR * DISPLAYARPDATAGRAM(UCHAR * Datagram, UCHAR * Output) +{ + UCHAR * ptr = Datagram; + UCHAR Dest[10]; + + if (ptr[7] == 1) // Request + return Output + sprintf((char *)Output, " < ARP Request who has %d.%d.%d.%d? Tell %d.%d.%d.%d", + ptr[26], ptr[27], ptr[28], ptr[29], ptr[15], ptr[16], ptr[17], ptr[18]); + + // Response + + Dest[ConvFromAX25(&ptr[8], Dest)] = 0; + + return Output + sprintf((char *)Output, " < ARP Rreply %d.%d.%d.%d? is at %s", + ptr[15], ptr[16], ptr[17], ptr[18], "??"); + +} diff --git a/.svn/pristine/24/2421c6b032114a3cd15551100fce50097c1a52a3.svn-base b/.svn/pristine/24/2421c6b032114a3cd15551100fce50097c1a52a3.svn-base new file mode 100644 index 0000000..80c9fd8 --- /dev/null +++ b/.svn/pristine/24/2421c6b032114a3cd15551100fce50097c1a52a3.svn-base @@ -0,0 +1,572 @@ + +EXCLUDEBITS EQU 0 ; INCLUDE ExcludeList SUPPORT + +TNCBUFFLEN EQU 400h +BPQHOSTSTREAMS EQU 64 +;BUFFLEN EQU 400 ; ?? + +MAXAPPLS EQU 32 ; Max Application Commands +ApplOffset EQU 80000 ; Applications string in buffer +C_INFOMSG EQU 85000 ; Info Msg in buffer +ApplStringLen EQU 48 ; Length of each config entry + +ALIASLEN EQU 32 ; Max chars in command alias + +MHENTRIES EQU 30 ; Entries in MH List + +; +; BUFFLEN-4 = L2 POINTER (FOR CLEARING TIMEOUT WHEN ACKMODE USED) +; BUFFLEN-8 = TIMESTAMP +; BUFFLEN-12 = BUFFER ALLOCATED FLAG (ADDR OF ALLOCATING ROUTINE) + +;MAXDATA EQU BUFFLEN-16 + +MAXDIGIS EQU 8 +; +; BPQHOST MODE VECTOR STRUC +; +BPQVECSTRUC STRUC + + +HOSTSESSION DD 0 +HOSTFLAGS DB 0 ; ALLOCATED AND STATE CHANGE FLAGS +HOSTAPPLMASK DD 0 +HOSTAPPLFLAGS DB 0 +HOSTSTREAM DB 0 ; STREAM NUMBER +HOSTTRACEQ DD 0 + +HOSTHANDLE DD 0 ; HANDLE FOR POSTING MSGS TO + +HOSTAPPLNUM DD 0 ; Application Number + +STREAMOWNER DD 0 ; PID of Process owning stream +HOSTPGMNAME DB 32 dup (0); + +BPQVECSTRUC ENDS + +; HOSTFLAGS = Bit 80 = Allocated +; Bit 40 = Disc Request +; Bit 20 = Stay Flag +; Bit 02 and 01 State Change Bits + + +VECTORLENGTH EQU TYPE BPQVECSTRUC + +; +; BASIC LINK LEVEL MESSAGE BUFFER LAYOUT +; +MESSAGE STRUC + +MSGCHAIN DD ? ; CHAIN WORD +MSGPORT DB ? ; PORT +MSGLENGTH DW ? ; LENGTH + +MSGDEST DB 7 DUP (?) ; DESTINATION +MSGORIGIN DB 7 DUP (?) ; ORIGIN +; +; MAY BE UP TO 56 BYTES OF DIGIS +; +MSGCONTROL DB ? ; CONTROL BYTE +MSGPID DB ? ; PROTOCOL IDENTIFIER +MSGDATA DB ? ; START OF LEVEL 2 MESSAGE +; +MESSAGE ENDS +; + +L3MESSAGE STRUC +; +; NETROM LEVEL 3 MESSAGE - WITHOUT L2 INFO +; +L3HEADER DB 7 DUP (?) ; CHAIN, PORT, LENGTH +L3PID DB ? ; PID + +L3SRCE DB 7 DUP (?) ; ORIGIN NODE +L3DEST DB 7 DUP (?) ; DEST NODE +L3MONR DB ? ; TX MONITOR FIELD - TO PREVENT MESSAGE GOING + ; ROUND THE NETWORK FOR EVER DUE TO ROUTING LOOP +; +; NETROM LEVEL 4 DATA +; +L4INDEX DB ? ; TRANSPORT SESSION INDEX +L4ID DB ? ; TRANSPORT SESSION ID +L4TXNO DB ? ; TRANSMIT SEQUENCE NUMBER +L4RXNO DB ? ; RECEIVE (ACK) SEQ NUMBER +L4FLAGS DB ? ; FRAGMENTATION, ACK/NAK, FLOW CONTROL AND MSG TYPE BITS + +L4DATA DB ? ; DATA +L4CALLS DB 14 DUP (?) ; CALLS IN CONNECT REQUEST +L4_BPQ DB ? ; THENODE EXTENDED CONNECT PARAMS + +L3MESSAGE ENDS + +; +; L4FLAGS DEFINITION +; +L4BUSY EQU 80H ; BNA - DONT SEND ANY MORE +L4NAK EQU 40H ; NEGATIVE RESPONSE FLAG +L4MORE EQU 20H ; MORE DATA FOLLOWS - FRAGMENTATION FLAG + +L4CREQ EQU 1 ; CONNECT REQUEST +L4CACK EQU 2 ; CONNECT ACK +L4DREQ EQU 3 ; DISCONNECT REQUEST +L4DACK EQU 4 ; DISCONNECT ACK +L4INFO EQU 5 ; INFORMATION +L4IACK EQU 6 ; INFORMATION ACK +; +; PORT CONTROL TABLE +; +PORTCONTROL STRUC + +PORTCALL DB 7 DUP (0) +PORTALIAS DB 7 DUP (0) ; USED FOR UPLINKS ONLY +PORTNUMBER DB ? +PORTPOINTER DD ? ; NEXT IN CHAIN +PORTQUALITY DB ? ; 'STANDARD' QUALITY FOR THIS PORT +PORTRX_Q DD ? ; FRAMES RECEIVED ON THIS PORT +PORTTX_Q DD ? ; FRAMES TO BE SENT ON THIS PORT +PORTTXROUTINE DD ? ; POINTER TO TRANSMIT ROUTINE FOR THIS PORT +PORTRXROUTINE DD ? ; POINTER TO RECEIVE ROUTINE FOR THIS PORT +PORTINITCODE DD ? ; INITIALISATION ROUTINE +PORTTIMERCODE DD ? +PORTCLOSECODE DD ? +PORTTXCHECK DD ? ; OK to Send Check + +PORTDESCRIPTION DB 30 DUP (0) ; TEXT DESCRIPTION OF FREQ/SPEED ETC +PORTBBSFLAG DB ? ; NZ MEANS PORT CALL/ALIAS ARE FOR BBS +PORTL3FLAG DB ? ; NZ RESTRICTS OUTGOING L2 CONNECTS +; +; CWID FIELDS +; +CWID DW 9 DUP (0) ; 8 ELEMENTS + FLAG +ELEMENT DW ? ; REMAINING BITS OF CURRENT CHAR +CWPOINTER DD ? ; POINTER TO NEXT CHAR +CWIDTIMER DW ? ; TIME TO NEXT ID +CWSTATE DB ? ; STATE MACHINE FOR CWID +CWTYPE DB ? ; SET TO USE ON/OFF KEYING INSTEAD OF + ; FSK (FOR RUH MODEMS) +PORTMINQUAL DB ? ; MIN QUAL TO BRAOCAST ON THIS PORT + +; STATS COUNTERS +; +L2DIGIED DD ? +L2FRAMES DD ? +L2FRAMESFORUS DD ? +L2FRAMESSENT DD ? +L2TIMEOUTS DD ? +L2ORUNC DD ? ; OVERRUNS +L2URUNC DD ? ; UNDERRUNS +L1DISCARD DD ? ; FRAMES DISCARDED (UNABLE TO TX DUE TO DCD) +L2FRMRRX DD ? +L2FRMRTX DD ? +RXERRORS DD ? ; RECEIVE ERRORS +L2REJCOUNT DD ? ; REJ FRAMES RECEIVED +L2OUTOFSEQ DD ? ; FRAMES RECEIVED OUT OF SEQUENCE +L2RESEQ DD ? ; FRAMES RESEQUENCED +SENDING DW 0 ; LINK STATUS BITS +ACTIVE DW 0 + +AVSENDING DB 0 ; LAST MINUTE +AVACTIVE DB 0 + +PORTTYPE DB 0 ; H/W TYPE + ; 0 = ASYNC, 2 = PC120, 4 = DRSI + ; 6 = TOSH, 8 = QUAD, 10 = RLC100 + ; 12 = RLC400 14 = INTERNAL 16 = EXTERNAL ; 18 = i2c + +PROTOCOL DB 0 ; PORT PROTOCOL + ; 0 = KISS, 2 = NETROM, 4 = BPQKISS + ; 6 = HDLC, 8 = L2, 10 = PACTOR + +IOBASE DW ? ; CONFIG PARAMS FOR HARDWARE DRIVERS +INTLEVEL DB ? ; FIRST 4 SAME FOR ALL H/W TYPES +BAUDRATE DD ? ; SPEED +CHANNELNUM DB ? ; ON MULTICHANNEL H/W +INTCHAIN DD ? ; POINTER TO NEXT PORT USING THIS LEVEL +PORTWINDOW DB ? ; L2 WINDOW FOR THIS PORT +PORTTXDELAY DW ? ; TX DELAY FOR THIS PORT +PORTPERSISTANCE DB ? ; PERSISTANCE VALUE FOR THIS PORT +FULLDUPLEX DB ? ; FULL DUPLEX IF SET +SOFTDCDFLAG DB ? ; IF SET USE 'SOFT DCD' - IF MODEM CANT GIVE A REAL ONE +PORTSLOTTIME DB ? ; SLOT TIME +PORTTAILTIME DB ? ; TAIL TIME +BBSBANNED DB ? ; SET IF PORT CAN'T ACCEPT L2 CALLS TO BBS CALLSIGN +PORTT1 DB ? ; L2 TIMEOUT +PORTT2 DB ? ; L2 DELAYED ACK TIMER +PORTN2 DB ? ; RETRIES +PORTPACLEN DB ? ; DEFAULT PACLEN FOR INCOMING SESSIONS +PORTINTERRUPT DD ? ; ADDRESS OF INTERRUPT HANDLER + +QUAL_ADJUST DB ? ; % REDUCTION IN QUALITY IF ON SAME PORT + +PERMITTEDCALLS DD ? ; POINTER TO PERMITED CALLS LIST +PORTUNPROTO DD ? ; POINTER TO UI DEST AND DIGI LIST +PORTDISABLED DB 0 ; PORT TX DISABLE FLAG +DIGIFLAG DB 0 ; ENABLE/DISABLE/UI ONLY +DIGIPORT DB 0 ; CROSSBAND DIGI PORT +DIGIMASK DW 0 ; CROSSBAND DIGI MASK +USERS DB 0 ; MAX USERS ON PORT +KISSFLAGS DB 0 ; KISS SPECIAL MODE BITS +PORTINTERLOCK DB 0 ; TO DEFINE PORTS WHICH CANT TX AT SAME TIME +NODESPACLEN DB 0 ; MAX LENGTH OF 'NODES' MSG +TXPORT DB 0 ; PORT FOR SHARED TX OPERATION +PORTMHEARD DD 0 ; POINTER TO MH DATA + +PARAMTIMER DW 0 ; MOVED FROM HW DATA FOR SYSOPH +PORTMAXDIGIS DB 0 ; DIGIS ALLOWED ON THIS PORT +PORTALIAS2 DB 7 DUP (0) ; 2ND ALIAS FOR DIGIPEATING FOR APRS +PORTBCALL DB 7 DUP (0) ; Source for Beacons +PortNoKeepAlive DB 0; ; Default to No Keepalives +PortUIOnly DB 0; +UICAPABLE DB 0; ; Pactor-style port that can do UI +WL2K DB 189 DUP (0) ; WL2K Report Data +PORTIPADDR DD 0; +SerialPortName DD 0 ; Serial Port Name for Unix +XDIGIS DD 0; ; Cross Port Digi Definitions + +NormalizeQuality DD 0 ; Normalise Node Qualities +IgnoreUnlocked DD 0 ; Ignore Unlocked Routes + +HARDWAREDATA DB 200 DUP (?) ; WORK AREA FOR HARDWARE DRIVERS + +PORTCONTROL ENDS + +; +; DEFINE MAPPING FOR EXTERNAL DRIVER +; +EXTDATA STRUC + DB HARDWAREDATA DUP (0) ; REMAP HARDWARE INFO + +PORT_EXT_ADDR DD ? ; ADDR OF RESIDENT ROUTINE +PORT_DLL_NAME DB 16 DUP (0); +EXTRESTART DB ? ; FLAG FOR DRIVER REINIT +DLLHANDLE DD ? +MAXHOSTMODESESSIONS DD ? ; Max Host Sessions supported (Used for KAM Pactor + ax.25 support) +ATTACHEDSESSIONS DD 27 DUP (0); Owning Sessions for PACTOR, etc +PERMITGATEWAY DD 0 ; Set if ax.25 ports can change callsign (ie SCS, not KAM +SCANCAPABILITIES DD 0 ; Type of scan control Controller supports (None, Simple, Connect Lock) +UI_Q DD 0 ; Unproto Frames for Session Mode Drivers (TRK, etc) + +EXTDATA ENDS + + IF TYPE EXTDATA GT TYPE PORTCONTROL + .ERR2 TOO MUCH PORT DATA + ENDIF + +EXTERNAL EQU 16 +L2 EQU 8 +; +; CW STATE MACHINE EQUATES +; +dot equ 1b +dash equ 10b +dotspace equ 100b +letterspace equ 1000b +IDPENDING EQU 10000B +; +; LEVEL 2 LINK CONTROL TABLE +; +LINKTABLE STRUC + +LINKCALL DB 7 DUP (?) ; CALLSIGN OF STATION +OURCALL DB 7 DUP (?) ; CALLSIGN OF OUR END +DIGIS DB MAXDIGIS*7 DUP (?) ; LEVEL 2 DIGIS IN PATH + +LINKPORT DD ? ; PORT POINTER +LINKTYPE DB ? ; 1 = UP, 2= DOWN, 3 = INTERNODE + +LINKNR DB ? +LINKNS DB ? ; LEV 2 SEQUENCE COUNTS +LINKWS DB ? ; WINDOW START +LINKOWS DB ? ; OLD (LAST ACKED) WINDOW START +LINKWINDOW DB ? ; LEVEL 2 WINDOW SIZE + +L2FLAGS DB ? ; CONTROL BITS +VER1FLAG DB ? ; SET IF OTHER END RUNNING VERSION 1 + +RX_Q DD ? ; PACKETS RECEIVED ON THIS LINK + +TX_Q DD ? ; PACKETS TO SEND +FRAMES DD 8 DUP (?) ; FRAMES WAITING ACK +RXFRAMES DD 8 DUP (?) ; Frames received out of sequence + +L2STATE DB ? ; PROCESSING STATE +L2TIMER DW ? ; FRAME RETRY TIMER +L2TIME DB ? ; RETRY TIMER INITIAL VALUE +L2SLOTIM DW ? ; DELAY FOR LINK VALIDATION POLL +L2ACKREQ DB ? ; DELAYED TEXT ACK TIMER +REJTIMER DB ? ; TO TIME OUT REJ IN VERSION 1 +LAST_F_TIME DW ? ; TIME LAST R(F) SENT +SDREJF DB ? ; FLAGS FOR FRMR +SDRBYTE DB ? ; SAVED CONTROL BYTE FOR FRMR + +SDTSLOT DB ? ; POINTER TO NEXT TXSLOT TO USE + +L2RETRIES DB ? ; RETRY COUNTER + +SESSACTIVE DB ? ; SET WHEN WE ARE SURE SESSION IS UP + +KILLTIMER DW ? ; TIME TO KILL IDLE LINK + +CIRCUITPOINTER DD ? ; POINTER TO L4 CIRCUIT TABLE ENTRY + ; (IF UP/DOWN) +NEIGHBOUR DD ? ; POINTER TO NEIGHBOUR (IF CROSSLINK) + +L2FRAG_Q DD ? ; DEFRAGMENTATION QUEUE + +LINKTABLE ENDS +; +; L2FLAGS EQUATES +; +REJSENT EQU 1B ; SET WHEN FIRST REJ IS SENT IN REPLY + ; TO AN I(P) +RNRSET EQU 10B ; RNR RECEIVED FROM OTHER END +;DISCPENDING EQU 1000B ; SEND DISC WHEN ALL DATA ACK'ED +RNRSENT EQU 10000B ; WE HAVE SEND RNR +POLLSENT EQU 100000B ; POLL BIT OUTSTANDING +; +; FRMR REJECT FLAGS +; +SDINVC EQU 1B ; INVALID COMMAND +SDNRER EQU 1000B ; INVALID N(R) + +TRANSPORTENTRY STRUC + +L4USER DB 7 DUP (?) ; CALL OF ORIGINATING USER +L4TARGET DD ? ; POINTER TO TARGET LINK/DEST +L4MYCALL DB 7 DUP (0) ; CALL WE ARE USING + +CIRCUITINDEX DB ? ; OUR CIRCUIT INFO +CIRCUITID DB ? + +FARINDEX DB ? +FARID DB ? ; OTHER END'S INFO + +L4WINDOW DB ? ; WINDOW SIZE +L4WS DB ? ; WINDOW START - NEXT FRAME TO ACK +TXSEQNO DB ? +RXSEQNO DB ? ; TRANSPORT LEVEL SEQUENCE INFO +L4LASTACKED DB ? ; LAST SEQUENCE ACKED + +FLAGS DB ? ; TRANSPORT LEVEL FLAGS +NAKBITS DB ? ; NAK & CHOKE BITS TO BE SENT +L4CROSSLINK DD ? ; POINTER TO LINKED L4 SESSION ENTRY +L4CIRCUITTYPE DB ? ; BIT SIGNIFICANT - SEE BELOW +KAMSESSION DB ? ; Session Number on KAM Host Mode TNC +L4TX_Q DD ? +L4RX_Q DD ? +L4HOLD_Q DD ? ; FRAMES WAITING TO BE ACKED +L4RESEQ_Q DD ? ; FRAMES RECEIVED OUT OF SEQUENCE + +L4STATE DB ? +L4TIMER DW ? +L4ACKREQ DB ? ; DATA ACK NEEDED +L4RETRIES DB ? ; RETRY COUNTER +L4KILLTIMER DW 0 ; IDLE CIRCUIT TIMER +SESSIONT1 DW 0 ; TIMEOUT FOR SESSIONS FROM HERE +SESSPACLEN DB 0 ; PACLEN FOR THIS SESSION +BADCOMMANDS DB 0 ; SUCCESSIVE BAD COMMANDS +STAYFLAG DB 0 ; STAY CONNECTED FLAG +SPYFLAG DB 0 ; SPY - CONNECT TO NODE VIA BBS CALLSIGN + +RTT_SEQ DB 0 ; SEQUENCE NUMBER BEING TIMED +RTT_TIMER DD 0 ; TIME ABOVE SEQUENCE WAS SENT + +PASSWORD DW 0 ; AUTHORISATION CODE FOR REMOTE SYSOP +; +SESS_APPLFLAGS DB 0 ; APPL FLAGS FOR THIS SESSION + +Authorised_Session DB 0; // Set if Host session from BPQTerminal or BPQMailChat + +DUMPPTR DD 0 ; POINTER FOR REMOTE DUMP MODE +PARTCMDBUFFER DD 0 ; Save area for incomplete commmand + +Frequency DD 0 ; If known - for CMS Reporting Hz +RMSCall DB 10 DUP (0); +Mode DB 0 ; ditto + +UNPROTO DD 0 ; Unproto Mode flag - port number if in unproto mode +UAddrLen DD 0 ; +UADDRESS DB 64 DUP (0); Unproto Address String - Dest + Digis + +LISTEN DD 0 ; Listen Mode flag - port number of Listen Mode + +APPL DB 16 DUP (0); Set if session initiated by an APPL +L4LIMIT dd 0 ; Idle Timout for this session + +TRANSPORTENTRY ENDS +; +; CIRCUITTYPE EQUATES +; +L2LINK EQU 1 +SESSION EQU 10B +UPLINK EQU 100B +DOWNLINK EQU 1000B +BPQHOST EQU 100000B +PACTOR EQU 1000000B +; +; FLAGS EQUATES +; +DISCPENDING EQU 1000B ; SEND DISC WHEN ALL DATA ACK'ED +; +; TOP 4 BITS MATCH L4FLAGS BITS - ALSO USED FOR NAKBITS +; +;L4BUSY EQU 80H ; BNA - DONT SEND ANY MORE +;L4NAK EQU 40H ; NEGATIVE RESPONSE FLAG +;L4MORE EQU 20H ; MORE DATA FOLLOWS - FRAGMENTATION FLAG + + + +DEST_LIST STRUC + +DEST_CHAIN DD ? ; SORTED LIST CHAIN + +DEST_CALL DB 7 DUP (?) ; DESTINATION CALLSIGN (AX25 FORMAT) +DEST_ALIAS DB 6 DUP (?) + +DEST_STATE DB ? ; CONTROL BITS - SETTING UP, ACTIVE ETC + +DEST_ROUTE DB ? ; CURRENTY ACTIVE DESTINATION + +INP3FLAGS DB ? + +ROUT1_NEIGHBOUR DD ? ; POINTER TO NEXT NODE IN PATH +ROUT1_QUALITY DB ? ; QUALITY +ROUT1_OBSCOUNT DB ? + db 5 dup (?); Padding + +ROUT2_NEIGHBOUR DD ? +ROUT2_QUALITY DB ? +ROUT2_OBSCOUNT DB ? + db 5 dup (?); Padding + +ROUT3_NEIGHBOUR DD ? +ROUT3_QUALITY DB ? +ROUT3_OBSCOUNT DB ? + db 5 dup (?); Padding + +INPROUT1_NEIGHBOUR DD ? +LastRTT1 DW ?; // Last Value Reported +RTT1 DW ?; // Current +SRTT1 DW ?; // Smoothed RTT +Hops1 DB ?; + +INPROUT2_NEIGHBOUR DD ? +LastRTT2 DW ?; // Last Value Reported +RTT2 DW ?; // Current +SRTT2 DW ?; // Smoothed RTT +Hops2 DB ?; + +INPROUT3_NEIGHBOUR DD ? +LastRTT3 DW ?; // Last Value Reported +RTT3 DW ?; // Current +SRTT3 DW ?; // Smoothed RTT +Hops3 DB ?; +DEST_Q DD ? ; QUEUE OF FRAMES FOR THIS DESTINATION + +DEST_RTT DD ? ; SMOOTHED ROUND TRIP TIMER +DEST_COUNT DD ? ; FRAMES SENT + +DEST_LIST ENDS + + +; +; MODEFLAG DEFINITIONS +; +COMMAND EQU 1B +TRANS EQU 10B +CONV EQU 100B +; +; APPL DEFINITIONS +; +BBSAPPL EQU 1B +HOSTAPPL EQU 10B +SYSOPAPPL EQU 100B +; +; HOSTFLAG DEFINITIONS +; +;HOSTMODE EQU 1B ; PK232 HOSTMODE ENABLED +;HOSTESCBIT EQU 10B ; IN ESCAPE SEQUENCE +;UFQ EQU 100B ; UFQ MODE (IE NOT AA4RE) +;POLLED EQU 1000B ; POLL RECEIVED + +;KISSMODE EQU 10000B ; KISS MODE +;KISSESC EQU 100000B ; IN ESCAPE SEQUENCE +; +; APPLFLAGS BITS +; +CMD_TO_APPL EQU 1B ; PASS COMMAND TO APPLICATION +MSG_TO_USER EQU 10B ; SEND 'CONNECTED' TO USER +MSG_TO_APPL EQU 100B ; SEND 'CONECTED' TO APPL +; +; HDLC COMMANDS (WITHOUT P/F) +; +UI EQU 3 +SABM EQU 2FH +DISC EQU 43H +DM EQU 0FH +UA EQU 63H +FRMR EQU 87H +RR EQU 1 +RNR EQU 5 +REJ EQU 9 +; +PFBIT EQU 10H ; POLL/FINAL BIT IN CONTROL BYTE +; +; MH DATA AREA +; +;MHSTRUC STRUC + +;MHCALL DB 7 DUP (0) +;MHTIME DD 0 ; ? FROM BIOS +;MHDIGI DB 0 +;MHFreq DB 12 DUP (0) +;MHLocator DB 6 DUP (0) + +;MHSTRUC ENDS + +; Application Calls/Alias Supports multiple L4 application calls + +APPLCALLS STRUC + +APPLCALL DB 7 DUP (0) ; ax.25 +APPLALIAS_TEXT DB 10 DUP (0) ; TEXT, WITH APPENDED SPACE + +APPLCALL_TEXT DB 10 DUP (0) +APPLALIAS DB 6 DUP (0) +APFiller DB 0 +APPLQUAL DW 0 +APPNODEPOINTER DD 0 ; Pointer to "NODES" entry for this App (if L4) +APPLCMDNAME DB 13 DUP (0) ; Command to invoke this APPL +APPLHASALIAS DD 0 +APPLPORT DD 0 ; Port if Appl has an alias +APPLALIASPTR DD 0 ; Pointer to Alias if defined + +APPLCALLS ENDS + +; New Style APPL configuration + +APPLCONFIG STRUC + +ACCommand DB 12 DUP (0); +ACCommandAlias DB 48 DUP (0); +ACApplCall DB 10 DUP (0); +ACApplAlias DB 10 DUP (0); +ACApplQual DD 0 + +APPLCONFIG ENDS +; +; HARDWARE TYPE EQUATES +; +KISS EQU 0 +PC120 EQU 2 +DRSI EQU 4 +TOSH EQU 6 +QUADRAM EQU 8 +RLC100 EQU 0AH +RLC400 EQU 0CH +INTERNAL EQU 0EH +EXTERNAL EQU 10H +BAYCOM EQU 12H +PA0HZP EQU 14H + + diff --git a/.svn/pristine/24/24c7314c19911fc81af53c19f4fba3e074f7f0cc.svn-base b/.svn/pristine/24/24c7314c19911fc81af53c19f4fba3e074f7f0cc.svn-base new file mode 100644 index 0000000..bbece07 --- /dev/null +++ b/.svn/pristine/24/24c7314c19911fc81af53c19f4fba3e074f7f0cc.svn-base @@ -0,0 +1,65 @@ + + + + + + + + + + + diff --git a/.svn/pristine/26/266dc42f4ae824ccda85ec76fa6d085b8bf2a8f0.svn-base b/.svn/pristine/26/266dc42f4ae824ccda85ec76fa6d085b8bf2a8f0.svn-base new file mode 100644 index 0000000..cbf158e --- /dev/null +++ b/.svn/pristine/26/266dc42f4ae824ccda85ec76fa6d085b8bf2a8f0.svn-base @@ -0,0 +1,840 @@ + +// Program to start or stop a Software TNC on a remote host + +// Should work with ARDOP, WINMOR or VARA + +// Version 1. 0. 0. 1 June 2013 + +// Version 1. 0. 2. 1 April 2018 + +// Updated to support running programs with command line parameters +// Add option to KILL by program name + +// Version 1. 0. 3. 1 April 2019 + +// Add remote rigcontrol feature + +// Version 1. 0. 3. 2 Feb 2020 + +// Fix Rigcontol + +// Version 1. 0. 3. 3 Dec 2020 + +// Set working directory when starting TNC + +// Version 1. 0. 3. 4 Jan 2021 + +// Add trace window + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include + +#include +#include +#include +#include +#include + +#define BPQICON 400 + +#define WSA_READ WM_USER + 1 + +HINSTANCE hInst; +char AppName[] = "WinmorControl"; +char Title[80] = "WinmorControl"; + +// Foward declarations of functions included in this code module: + +ATOM MyRegisterClass(CONST WNDCLASS*); +BOOL InitApplication(HINSTANCE); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); +VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap); +VOID WINAPI txCompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap); +VOID CATThread(); + +int TimerHandle = 0; + +LOGFONT LFTTYFONT ; + +HFONT hFont; + +struct sockaddr_in sinx; +struct sockaddr rx; +SOCKET sock; +int udpport = 8500; +int addrlen = sizeof(struct sockaddr_in); + +BOOL MinimizetoTray=FALSE; + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[10000]; + va_list(arglist); + va_start(arglist, format); + vsprintf(Mess, format, arglist); + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + + return; +} + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr; + + if (buf == NULL) return NULL; // Protect + + ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++ = 0; + return ptr; +} + + +BOOL KillOldTNC(char * Path) +{ + HANDLE hProc; + char ExeName[256] = ""; + DWORD Pid = 0; + + DWORD Processes[1024], Needed, Count; + unsigned int i; + + if (!EnumProcesses(Processes, sizeof(Processes), &Needed)) + return FALSE; + + // Calculate how many process identifiers were returned. + + Count = Needed / sizeof(DWORD); + + for (i = 0; i < Count; i++) + { + if (Processes[i] != 0) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Processes[i]); + + if (hProc) + { + GetModuleFileNameEx(hProc, 0, ExeName, 255); + + if (_memicmp(ExeName, Path, strlen(ExeName)) == 0) + { + Debugprintf("Killing Pid %d %s", Processes[i], ExeName); + TerminateProcess(hProc, 0); + CloseHandle(hProc); + return TRUE; + } + CloseHandle(hProc); + } + } + } + + return FALSE; +} + +KillTNC(int PID) +{ + HANDLE hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID); + + Debugprintf("KillTNC Called Pid %d", PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + return 0; +} + +RestartTNC(char * Path) +{ + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + int i, n = 0; + char workingDirectory[256]; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + Debugprintf("RestartTNC Called for %s", Path); + + strcpy(workingDirectory, Path); + + i = strlen(Path); + + while (i--) + { + if (workingDirectory[i] == '\\' || workingDirectory[i] == '/') + { + workingDirectory[i] = 0; + break; + } + } + + + while (KillOldTNC(Path) && n++ < 100) + { + Sleep(100); + } + + if (CreateProcess(NULL, Path, NULL, NULL, FALSE,0 ,NULL ,workingDirectory, &SInfo, &PInfo)) + Debugprintf("Restart TNC OK"); + else + Debugprintf("Restart TNC Failed %d ", GetLastError()); +} +char * RigPort = NULL; +char * RigSpeed = NULL; +HANDLE RigHandle = 0; +int RigType = 0; // Flag for possible RTS/DTR + +#define RTS 1 +#define DTR 2 + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + MSG msg; + + if (lpCmdLine[0]) + { + // Port Name and Speed for Remote CAT + + RigPort = _strdup(lpCmdLine); + RigSpeed = strlop(RigPort, ':'); + } + + if (!InitApplication(hInstance)) + return (FALSE); + + if (!InitInstance(hInstance, nCmdShow)) + return (FALSE); + + + // Main message loop: + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + KillTimer(NULL, TimerHandle); + + return (msg.wParam); +} + +// + +// +// FUNCTION: InitApplication(HANDLE) +// +// PURPOSE: Initializes window data and registers window class +// +// COMMENTS: +// +// In this function, we initialize a window class by filling out a data +// structure of type WNDCLASS and calling either RegisterClass or +// the internal MyRegisterClass. +// +BOOL InitApplication(HINSTANCE hInstance) +{ + WNDCLASS wc; + + // Fill in window class structure with parameters that describe + // the main window. + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = (WNDPROC)WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + +// wc.lpszMenuName = MAKEINTRESOURCE(BPQMENU) ; + + wc.lpszClassName = AppName; + + // Register the window class and return success/failure code. + + return RegisterClass(&wc); + +} + +// +// FUNCTION: InitInstance(HANDLE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// + +HFONT FAR PASCAL MyCreateFont( void ) +{ + CHOOSEFONT cf; + LOGFONT lf; + HFONT hfont; + + // Initialize members of the CHOOSEFONT structure. + + cf.lStructSize = sizeof(CHOOSEFONT); + cf.hwndOwner = (HWND)NULL; + cf.hDC = (HDC)NULL; + cf.lpLogFont = &lf; + cf.iPointSize = 0; + cf.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY; + cf.rgbColors = RGB(0,0,0); + cf.lCustData = 0L; + cf.lpfnHook = (LPCFHOOKPROC)NULL; + cf.lpTemplateName = (LPSTR)NULL; + cf.hInstance = (HINSTANCE) NULL; + cf.lpszStyle = (LPSTR)NULL; + cf.nFontType = SCREEN_FONTTYPE; + cf.nSizeMin = 0; + cf.nSizeMax = 0; + + // Display the CHOOSEFONT common-dialog box. + + ChooseFont(&cf); + + // Create a logical font based on the user's + // selection and return a handle identifying + // that font. + + hfont = CreateFontIndirect(cf.lpLogFont); + return (hfont); +} + +VOID COMSetDTR(HANDLE fd) +{ + EscapeCommFunction(fd, SETDTR); +} + +VOID COMClearDTR(HANDLE fd) +{ + EscapeCommFunction(fd, CLRDTR); +} + +VOID COMSetRTS(HANDLE fd) +{ + EscapeCommFunction(fd, SETRTS); +} + +VOID COMClearRTS(HANDLE fd) +{ + EscapeCommFunction(fd, CLRRTS); +} + + + + + +OVERLAPPED txOverlapped; + +HANDLE txEvent; + +char RXBuffer[1024]; +int RXLen; + +int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength) +{ + BOOL fReadStat ; + COMSTAT ComStat ; + DWORD dwErrorFlags; + DWORD dwLength; + BOOL ret; + + // only try to read number of bytes in queue + + ret = ClearCommError(fd, &dwErrorFlags, &ComStat); + + if (ret == 0) + { + int Err = GetLastError(); + return 0; + } + + + dwLength = min((DWORD) MaxLength, ComStat.cbInQue); + + if (dwLength > 0) + { + fReadStat = ReadFile(fd, Block, dwLength, &dwLength, NULL) ; + + if (!fReadStat) + { + dwLength = 0 ; + ClearCommError(fd, &dwErrorFlags, &ComStat ) ; + } + } + + return dwLength; +} + + +BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite) +{ + BOOL fWriteStat; + DWORD BytesWritten; + DWORD ErrorFlags; + COMSTAT ComStat; + int Err, txLength; + + Err = WaitForSingleObject(txEvent, 1000); + + if (Err == WAIT_TIMEOUT) + { + Debugprintf("TX Event Wait Timout"); + } + else + { + ResetEvent(txEvent); + Err = GetOverlappedResult(RigHandle, &txOverlapped, &txLength, FALSE); + } + + memset(&txOverlapped, 0, sizeof(OVERLAPPED)); + txOverlapped.hEvent = txEvent; + + fWriteStat = WriteFile(fd, Block, BytesToWrite, + &BytesWritten, &txOverlapped); + + if (!fWriteStat) + { + Err = GetLastError(); + + if (Err != ERROR_IO_PENDING) + { + ClearCommError(fd, &ErrorFlags, &ComStat); + return FALSE; + } + } + return TRUE; +} + + +HANDLE OpenCOMPort(char * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits) +{ + char szPort[80]; + BOOL fRetVal ; + COMMTIMEOUTS CommTimeOuts ; + char buf[100]; + HANDLE fd; + DCB dcb; + + sprintf( szPort, "\\\\.\\%s", pPort); + + // open COMM device + + fd = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL ); + + + if (fd == (HANDLE) -1) + { + char Msg[80]; + + sprintf(Msg, "%s could not be opened - Error %d", pPort, GetLastError()); + MessageBox(NULL, Msg, "WinmorControl", MB_OK); + + return FALSE; + } + + // setup device buffers + + SetupComm(fd, 4096, 4096); + + // purge any information in the buffer + + PurgeComm(fd, PURGE_TXABORT | PURGE_RXABORT | + PURGE_TXCLEAR | PURGE_RXCLEAR ) ; + + // set up for overlapped I/O + + CommTimeOuts.ReadIntervalTimeout = 20; + CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ; + CommTimeOuts.ReadTotalTimeoutConstant = 0 ; + CommTimeOuts.WriteTotalTimeoutMultiplier = 0 ; +// CommTimeOuts.WriteTotalTimeoutConstant = 0 ; + CommTimeOuts.WriteTotalTimeoutConstant = 500 ; + + SetCommTimeouts(fd, &CommTimeOuts); + + dcb.DCBlength = sizeof(DCB); + + GetCommState(fd, &dcb); + + dcb.BaudRate = speed; + dcb.ByteSize = 8; + dcb.Parity = 0; + dcb.StopBits = TWOSTOPBITS; + dcb.StopBits = Stopbits; + + // setup hardware flow control + + dcb.fOutxDsrFlow = 0; + dcb.fDtrControl = DTR_CONTROL_DISABLE ; + + dcb.fOutxCtsFlow = 0; + dcb.fRtsControl = RTS_CONTROL_DISABLE ; + + // setup software flow control + + dcb.fInX = dcb.fOutX = 0; + dcb.XonChar = 0; + dcb.XoffChar = 0; + dcb.XonLim = 100 ; + dcb.XoffLim = 100 ; + + // other various settings + + dcb.fBinary = TRUE ; + dcb.fParity = FALSE; + + fRetVal = SetCommState(fd, &dcb); + + if (fRetVal) + { + if (SetDTR) + EscapeCommFunction(fd, SETDTR); + else + EscapeCommFunction(fd, CLRDTR); + + if (SetRTS) + EscapeCommFunction(fd, SETRTS); + else + EscapeCommFunction(fd, CLRRTS); + } + else + { + sprintf(buf,"%s Setup Failed %d ", pPort, GetLastError()); + OutputDebugString(buf); + CloseHandle(fd); + return 0; + } + + txEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + // Start Read Thread + + _beginthread(CATThread, 0, 0); + + return fd; +} + + + +VOID CloseCOMPort(HANDLE fd) +{ + SetCommMask(fd, 0); + + // drop DTR + + COMClearDTR(fd); + + // purge any outstanding reads/writes and close device handle + + PurgeComm(fd, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ) ; + + CloseHandle(fd); +} + +HWND hMonitor; + +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + HWND hWnd; + u_long param=1; + BOOL bcopt=TRUE; + char Msg[255]; + int ret, err; + WSADATA WsaData; // receives data from WSAStartup + + + hInst = hInstance; // Store instance handle in our global variable + + WSAStartup(MAKEWORD(2, 0), &WsaData); + + hWnd = CreateWindow(AppName, Title, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, 420,275, + NULL, NULL, hInstance, NULL); + + if (!hWnd) { + return (FALSE); + } + + // setup default font information + + LFTTYFONT.lfHeight = 12; + LFTTYFONT.lfWidth = 8 ; + LFTTYFONT.lfEscapement = 0 ; + LFTTYFONT.lfOrientation = 0 ; + LFTTYFONT.lfWeight = 0 ; + LFTTYFONT.lfItalic = 0 ; + LFTTYFONT.lfUnderline = 0 ; + LFTTYFONT.lfStrikeOut = 0 ; + LFTTYFONT.lfCharSet = OEM_CHARSET ; + LFTTYFONT.lfOutPrecision = OUT_DEFAULT_PRECIS ; + LFTTYFONT.lfClipPrecision = CLIP_DEFAULT_PRECIS ; + LFTTYFONT.lfQuality = DEFAULT_QUALITY ; + LFTTYFONT.lfPitchAndFamily = FIXED_PITCH | FF_MODERN ; + lstrcpy( LFTTYFONT.lfFaceName, "Fixedsys" ) ; + + hFont = CreateFontIndirect(&LFTTYFONT) ; +// hFont = MyCreateFont(); + + SetWindowText(hWnd,Title); + + + hMonitor = CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 2,2,400,230, hWnd, NULL, hInstance, NULL); + + + + ShowWindow(hWnd, nCmdShow); + + sock = socket(AF_INET,SOCK_DGRAM,0); + + if (sock == INVALID_SOCKET) + { + err = WSAGetLastError(); + sprintf(Msg, "Failed to create UDP socket - error code = %d", err); + MessageBox(NULL, Msg, "WinmorControl", MB_OK); + return FALSE; + } + + if (WSAAsyncSelect(sock, hWnd, WSA_READ, FD_READ) > 0) + { + wsprintf(Msg, TEXT("WSAAsyncSelect failed Error %d"), WSAGetLastError()); + + MessageBox(hWnd, Msg, TEXT("WinmorControl"), MB_OK); + + closesocket(sock); + + return FALSE; + + } + +// ioctlsocket (sock, FIONBIO, ¶m); + + setsockopt (sock,SOL_SOCKET,SO_BROADCAST,(const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + + sinx.sin_port = htons(udpport); + + ret = bind(sock, (struct sockaddr *) &sinx, sizeof(sinx)); + + if (ret != 0) + { + // Bind Failed + + err = WSAGetLastError(); + sprintf(Msg, "Bind Failed for UDP socket - error code = %d", err); + MessageBox(NULL, Msg, "WinmorControl", MB_OK); + + return FALSE; + } + + if (RigPort) + { + int Speed = 9600; + + if (RigSpeed) + Speed = atoi(RigSpeed); + + RigHandle = OpenCOMPort(RigPort, Speed, FALSE, FALSE, FALSE, TWOSTOPBITS); + + if (RigHandle) + { + if (RigType == RTS) + COMClearRTS(RigHandle); + else + COMSetRTS(RigHandle); + + if (RigType == DTR) + COMClearDTR(RigHandle); + else + COMSetDTR(RigHandle); + } + else + return FALSE; // Open Failed + } + + TimerHandle = SetTimer(hWnd, 1, 100, NULL); + + return TRUE; +} + +void Trace(char * Msg) +{ + int index = SendMessage(hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Msg); + + if (index > 1200) + do + index=index=SendMessage(hMonitor, LB_DELETESTRING, 0, 0); + while (index > 1000); + + if (index > -1) + index=SendMessage(hMonitor, LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + char Msg[256]; + int len; + + switch (message) + { + case WSA_READ: // Notification on data socket + + len = recvfrom(sock, Msg, 256, 0, &rx, &addrlen); + + if (len <= 0) + return 0; + + Msg[len] = 0; + + + if (_memicmp(Msg, "REMOTE:", 7) == 0) + { + Trace(Msg); + RestartTNC(&Msg[7]); + } + else + if (_memicmp(Msg, "KILL ", 5) == 0) + { + Trace(Msg); + KillTNC(atoi(&Msg[5])); + } + else + if (_memicmp(Msg, "KILLBYNAME ", 11) == 0) + { + Trace(Msg); + KillOldTNC(&Msg[11]); + } + else + { + // Anything else is Rig Control + + len = WriteCOMBlock(RigHandle, Msg, len); + } + + break; + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + + case SC_MINIMIZE: + + if (MinimizetoTray) + + return ShowWindow(hWnd, SW_HIDE); + else + return (DefWindowProc(hWnd, message, wParam, lParam)); + + + break; + + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + + case WM_CLOSE: + + PostQuitMessage(0); + + break; + + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + + return (0); +} + +BOOL EndCATThread = FALSE; + +VOID CATThread() +{ + DWORD dwLength = 0; + int Length, ret; + HANDLE Event; + OVERLAPPED Overlapped; + + EndCATThread = FALSE; + + Event = CreateEvent(NULL, TRUE, FALSE, NULL); + + memset(&Overlapped, 0, sizeof(OVERLAPPED)); + Overlapped.hEvent = Event; + + ReadFile(RigHandle, RXBuffer, 1024, &RXLen, &Overlapped); + + while (EndCATThread == FALSE) + { + ret = WaitForSingleObject(Event, 10000); + + if (ret == WAIT_TIMEOUT) + { + if (EndCATThread) + { + CancelIo(RigHandle); + CloseHandle(RigHandle); + RigHandle = INVALID_HANDLE_VALUE; + CloseHandle(Event); + EndCATThread = FALSE; + return; + } + continue; + } + ResetEvent(Event); + + ret = GetOverlappedResult(RigHandle, &Overlapped, &Length, FALSE); + + if (ret && Length) + { + // got something so send to BPQ + + sendto(sock, RXBuffer, Length, 0, &rx, sizeof(struct sockaddr)); + } + + memset(&Overlapped, 0, sizeof(OVERLAPPED)); + Overlapped.hEvent = Event; + + ReadFile(RigHandle, RXBuffer, 1024, &RXLen, &Overlapped); + } +} + diff --git a/.svn/pristine/26/26d170620b3dcb5480289ddc559e08196347c052.svn-base b/.svn/pristine/26/26d170620b3dcb5480289ddc559e08196347c052.svn-base new file mode 100644 index 0000000..fc2d1c2 --- /dev/null +++ b/.svn/pristine/26/26d170620b3dcb5480289ddc559e08196347c052.svn-base @@ -0,0 +1,62 @@ +// CmdLineAuth.cpp : Defines the entry point for the console application. +// + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE + +#define _USE_32BIT_TIME_T + +#include + +#include +#include + +#include "md5.c" + + +VOID CreateOneTimePassword(char * KeyPhrase) +{ + // Create a time dependent One Time Password from the KeyPhrase + + time_t NOW = time(NULL); + unsigned char Hash[16]; + char Password[20]; + char Key[1000]; + int i, chr; + long long Val; + int PassCode; + + NOW = NOW/30; // Only Change every 30 secs + + sprintf(Key, "%s%x", KeyPhrase, NOW); + + md5(Key, Hash); + + for (i=0; i<16; i++) + { + chr = (Hash[i] & 31); + if (chr > 9) chr += 7; + + Password[i] = chr + 48; + } + + Password[16] = 0; + + memcpy(&Val, Password, 8); + PassCode = Val % 1000000; + printf("Passcode is %06d\n", PassCode); + + return; +} + +int main(int argc, char * argv[]) +{ + if (argc < 2) + { + printf ("Need to supply KeyPhrase\n"); + return 0; + } + CreateOneTimePassword(argv[1]); + return 0; +} + diff --git a/.svn/pristine/29/29c5a8d64148305a76e411fb064479a2c2461872.svn-base b/.svn/pristine/29/29c5a8d64148305a76e411fb064479a2c2461872.svn-base new file mode 100644 index 0000000..4e601cd --- /dev/null +++ b/.svn/pristine/29/29c5a8d64148305a76e411fb064479a2c2461872.svn-base @@ -0,0 +1,41 @@ +/* +Copyright 2001-2015 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 +*/ + +#include "cheaders.h" +#include "tncinfo.h" + + + + +//int SENDNODES() {return 0;} +//int BYECMD() {return 0;} +//int CMDR00() {return 0;} + +//int DoNetromConnect() {return 0;} + + +//int CMDC00() {return 0;} + + + +//int CheckReceivedData() {return 0;} +//int GetLastError() {return 0;} + + + diff --git a/.svn/pristine/29/29da7fd3fe6e7d0580931894198b0468dbc0ff02.svn-base b/.svn/pristine/29/29da7fd3fe6e7d0580931894198b0468dbc0ff02.svn-base new file mode 100644 index 0000000..597d157 --- /dev/null +++ b/.svn/pristine/29/29da7fd3fe6e7d0580931894198b0468dbc0ff02.svn-base @@ -0,0 +1,434 @@ + +#define _CRT_SECURE_NO_DEPRECATE + +#ifndef NOMQTT + +#include "MQTTAsync.h" +#ifndef WIN32 +#include +#endif + +#include "cheaders.h" +#include "asmstrucs.h" +#include "mqtt.h" + +extern int MQTT_Connecting; +extern int MQTT_Connected; + + +DllExport int APIENTRY MQTTSend(char * topic, char * Msg, int MsgLen); + +MQTTAsync client = NULL; + +time_t MQTTLastStatus = 0; + +void MQTTSendStatus() +{ + char topic[256]; + char payload[128]; + + sprintf(topic, "PACKETNODE/%s", NODECALLLOPPED); + strcpy(payload,"{\"status\":\"online\"}"); + + MQTTSend(topic, payload, strlen(payload)); + MQTTLastStatus = time(NULL); +} + +void MQTTTimer() +{ + if (MQTT_Connecting == 0 && MQTT_Connected == 0) + MQTTConnect(MQTT_HOST, MQTT_PORT, MQTT_USER, MQTT_PASS); + + if ((time(NULL) - MQTTLastStatus) > 1800) + MQTTSendStatus(); + +} + + +void MQTTDisconnect() +{ + if (MQTT_Connected) + { + MQTTAsync_disconnectOptions disc_opts = MQTTAsync_disconnectOptions_initializer; + + MQTTAsync_disconnect(client, &disc_opts); + + MQTT_Connecting = MQTT_Connected = 0; + + // Try to recconect. If it fails system will rety every minute + + MQTTConnect(MQTT_HOST, MQTT_PORT, MQTT_USER, MQTT_PASS); + } +} + +DllExport int APIENTRY MQTTSend(char * topic, char * Msg, int MsgLen) +{ + int rc; + + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + MQTTAsync_message pubmsg = MQTTAsync_message_initializer; + + pubmsg.payload = Msg; + pubmsg.payloadlen = MsgLen; + rc = MQTTAsync_sendMessage(client, topic, &pubmsg, &opts); + + if (rc) + MQTTDisconnect(); + + return rc; +} + + + +void onConnect(void* context, MQTTAsync_successData* response) +{ + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + MQTTAsync_message pubmsg = MQTTAsync_message_initializer; + + MQTT_Connecting = 0; + MQTT_Connected = 1; + + printf("Successful MQTT connection\n"); + + // Send start up message + + MQTTSendStatus(); + +} + +void onConnectFailure(void* context, MQTTAsync_failureData* response) +{ + printf("MQTT connection failed, rc %d\n", response ? response->code : 0); + MQTT_Connecting = 0; +} + + + +char* jsonEncodeMessage(MESSAGE *msg) +{ + char From[10]; + char To[10]; + + char buffer[1024]; + unsigned long long SaveMMASK = MMASK; + BOOL SaveMTX = MTX; + BOOL SaveMCOM = MCOM; + BOOL SaveMUI = MUIONLY; + int len; + char *msg_str; + char payload_timestamp[16]; + + struct tm * TM = localtime(&msg->Timestamp); + sprintf(payload_timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + + IntSetTraceOptionsEx(MMASK, TRUE, TRUE, FALSE); + From[ConvFromAX25(msg->ORIGIN, From)] = 0; + To[ConvFromAX25(msg->DEST, To)] = 0; + + len = IntDecodeFrame(msg, buffer, msg->Timestamp, 0xffffffffffffffff, FALSE, FALSE); + IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); + + buffer[len] = 0; + +#ifdef WIN32 + + msg_str = zalloc(2048); + + sprintf(msg_str, "{\"from\": \"%s\", \"to\": \"%s\", \"payload\": \"%s\", \"port\": %d, \"timestamp\": \"%s\"}", + From, To, buffer, msg->PORT, payload_timestamp); + +#else + + json_t *root; + + root = json_object(); + + json_object_set_new(root, "from", json_string(From)); + json_object_set_new(root, "to", json_string(To)); + + + json_object_set_new(root, "payload", json_string(buffer)); + json_object_set_new(root, "port", json_integer(msg->PORT)); + sprintf(payload_timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec); + json_object_set_new(root, "timestamp", json_string(payload_timestamp)); + msg_str = json_dumps(root, 0); + json_decref(root); + +#endif + + return msg_str; +} + +void MQTTKISSTX(void *message) +{ + MESSAGE *msg = (MESSAGE *)message; + char topic[256]; + char *msg_str; + + sprintf(topic, "PACKETNODE/ax25/trace/bpqformat/%s/sent/%d", NODECALLLOPPED, msg->PORT); + + msg_str = jsonEncodeMessage(msg); + + MQTTSend(topic, msg_str, strlen(msg_str)); + + free(msg_str); +} + +void MQTTKISSTX_RAW(char* buffer, int bufferLength, void* PORT) +{ + PPORTCONTROL PPORT = (PPORTCONTROL)PORT; + char topic[256]; + + sprintf(topic, "PACKETNODE/kiss/%s/sent/%d", NODECALLLOPPED, PPORT->PORTNUMBER); + + MQTTSend(topic, buffer, bufferLength); +} + + +void MQTTKISSRX(void *message) +{ + MESSAGE *msg = (MESSAGE *)message; + char topic[256]; + char *msg_str; + + + sprintf(topic, "PACKETNODE/ax25/trace/bpqformat/%s/rcvd/%d", NODECALLLOPPED, msg->PORT); + msg_str = jsonEncodeMessage(msg); + + MQTTSend(topic, msg_str, strlen(msg_str)); + + free(msg_str); +} + +void MQTTKISSRX_RAW(char* buffer, int bufferLength, void* PORT) +{ + PPORTCONTROL PPORT = (PPORTCONTROL)PORT; + char topic[256]; + + sprintf(topic, "PACKETNODE/kiss/%s/rcvd/%d", NODECALLLOPPED, PPORT->PORTNUMBER); + + MQTTSend(topic, buffer, bufferLength); + +} + +void MQTTReportSession(char * Msg) +{ + char topic[256]; + sprintf(topic, "PACKETNODE/stats/session/%s", NODECALLLOPPED); + + MQTTSend(topic, Msg, strlen(Msg)); +} + + +char* replace(char* str, char* a, char* b) +{ + int len = strlen(str); + int lena = strlen(a), lenb = strlen(b); + char * p; + + for (p = str; p = strstr(p, a); p) { + if (lena != lenb) // shift end as needed + memmove(p + lenb, p + lena, + len - (p - str) + lenb); + memcpy(p, b, lenb); + } + return str; +} + +int MQTTPublish(void *message, char *topic) +{ + MESSAGE *msg = (MESSAGE *)message; + char From[10]; + char To[10]; + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + MQTTAsync_message pubmsg = MQTTAsync_message_initializer; + + unsigned long long SaveMMASK = MMASK; + BOOL SaveMTX = MTX; + BOOL SaveMCOM = MCOM; + BOOL SaveMUI = MUIONLY; + int len; + char* replaced_buffer; + char buffer[1024]; + + time_t timestamp = msg->Timestamp; + + + From[ConvFromAX25(msg->ORIGIN, From)] = 0; + To[ConvFromAX25(msg->DEST, To)] = 0; + + + IntSetTraceOptionsEx(8, TRUE, TRUE, FALSE); + len = IntDecodeFrame(msg, buffer, timestamp, 1, FALSE, FALSE); + IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); + + // MQTT _really_ doesn't like \r, so replace it with something + // that is at least human readable + + replaced_buffer = replace(buffer, "\r", "\r\n"); + + pubmsg.payload = replaced_buffer; + pubmsg.payloadlen = strlen(replaced_buffer); + + printf("%s\n", replaced_buffer); + + return MQTTAsync_sendMessage(client, topic, &pubmsg, &opts); +} + +int MQTTConnect(char* host, int port, char* user, char* pass) +{ + MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer; + int rc; + char hostString[256]; + + sprintf(hostString, "tcp://%s:%d", host, port); + + printf("MQTT Connect to %s\n", hostString); + + rc = MQTTAsync_create(&client, hostString, NODECALLLOPPED, MQTTCLIENT_PERSISTENCE_NONE, NULL); + + if (rc != MQTTASYNC_SUCCESS) + { + printf("Failed to create client, return code %d\n", rc); + return rc; + } + + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + conn_opts.username = user; + conn_opts.password = pass; + conn_opts.onSuccess = onConnect; + conn_opts.onFailure = onConnectFailure; +// conn_opts.automaticReconnect = 1; +// conn_opts.minRetryInterval = 30; +// conn_opts.maxRetryInterval = 300; + + rc = MQTTAsync_connect(client, &conn_opts); + + if (rc != MQTTASYNC_SUCCESS) + { + printf("Failed to start connect, return code %d\n", rc); + return rc; + } + + MQTT_Connecting = 1; + + return 0; +} + +// Message Database Entry. Designed to be compatible with FBB + +#define NBBBS 160 // Max BBSes we can forward to. Must be Multiple of 8, and must be 80 for FBB compatibliliy +#define NBMASK NBBBS/8 // Number of bytes in Forward bitlists. + +#pragma pack(1) + +struct MsgInfo +{ + char type; + char status; + int number; + int length; + int xdatereceived; + char bbsfrom[7]; // ? BBS we got it from ? + char via[41]; + char from[7]; + char to[7]; + char bid[13]; + char title[61]; + int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp + + UCHAR B2Flags; // Not all flags specific to B2 + + #define B2Msg 1 // Set if Message File is a formatted B2 message + #define Attachments 2 // Set if B2 message has attachments + #define FromPaclink 4 + #define FromCMS 8 + #define FromRMSExpress 16 + #define RadioOnlyMsg 32 // Received using call-T + #define RadioOnlyFwd 64 // Received using call-R + #define WarnNotForwardedSent 128 + + int xdatecreated; + int xdatechanged; + UCHAR fbbs[NBMASK]; + UCHAR forw[NBMASK]; + char emailfrom[41]; + char Locked; // Set if selected for sending (NTS Pickup) + char Defered; // FBB response '=' received + UCHAR UTF8; // Set if Message is in UTF8 (ie from POP/SMTP) + +// For 64 bit time_t compatibility define as long long +// (so struct is same with 32 or 64 bit time_t) + + int64_t datereceived; + int64_t datecreated; + int64_t datechanged; + + char Spare[61 - 24]; // For future use +} ; + +#pragma pack() + +void MQTTMessageEvent(void* message) +{ + struct MsgInfo* msg = (struct MsgInfo *)message; + char *msg_str; + char * ptr; + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + MQTTAsync_message pubmsg = MQTTAsync_message_initializer; + char topic[256]; + + json_t *root = json_object(); + json_object_set_new(root, "id", json_integer(msg->number)); + json_object_set_new(root, "size", json_integer(msg->length)); + json_object_set_new(root, "type", json_string(msg->type == 'P' ? "P" : "B")); + json_object_set_new(root, "to", json_string(msg->to)); + json_object_set_new(root, "from", json_string(msg->from)); + json_object_set_new(root, "subj", json_string(msg->title)); + + switch(msg->status) { + case 'N': + json_object_set_new(root, "event", json_string("newmsg")); + break; + case 'F': + json_object_set_new(root, "event", json_string("fwded")); + break; + case 'R': + json_object_set_new(root, "event", json_string("read")); + break; + case 'K': + json_object_set_new(root, "event", json_string("killed")); + break; + } + + msg_str = json_dumps(root, 0); + + pubmsg.payload = msg_str; + pubmsg.payloadlen = strlen(msg_str); + + + sprintf(topic, "PACKETNODE/event/%s/pmsg", NODECALLLOPPED); + + MQTTAsync_sendMessage(client, topic, &pubmsg, &opts); +} + +#else + +// Dummies ofr build without MQTT libraries + +int MQTTConnect(char* host, int port, char* user, char* pass) +{ + return 0; +} + +void MQTTKISSTX(void *message) {}; +void MQTTKISSTX_RAW(char* buffer, int bufferLength, void* PORT) {}; +void MQTTKISSRX(void *message) {}; +void MQTTKISSRX_RAW(char* buffer, int bufferLength, void* PORT) {}; +void MQTTTimer() {}; +void MQTTReportSession(char * Msg) {}; + +#endif + diff --git a/.svn/pristine/2b/2b5de92dd0ba153cdd0cdefd6afbf91f5c308631.svn-base b/.svn/pristine/2b/2b5de92dd0ba153cdd0cdefd6afbf91f5c308631.svn-base new file mode 100644 index 0000000..2064b47 --- /dev/null +++ b/.svn/pristine/2b/2b5de92dd0ba153cdd0cdefd6afbf91f5c308631.svn-base @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.svn/pristine/2b/2ba2b06ed54be8c1bdae4ab00a9a94641691b9c0.svn-base b/.svn/pristine/2b/2ba2b06ed54be8c1bdae4ab00a9a94641691b9c0.svn-base new file mode 100644 index 0000000..ec9ce91 --- /dev/null +++ b/.svn/pristine/2b/2ba2b06ed54be8c1bdae4ab00a9a94641691b9c0.svn-base @@ -0,0 +1,1620 @@ +// Inline definitions of stuff from HTMLPages + +// These save users having to update HTMLPages every time something changes + +// Doing it as functions instead of static text allows edit and continue debugging + +#define BTNCOLOUR "red" + +char * WebMailMsgtxt() +{ + char Msg[] = + + "" + " " + " " + " " + "" + "\r\n" + "\r\n" + "WebMail " + "\r\n" + "" + "

%s Webmail Interface - User %s - Message %d

" + "" + "" + "" + "%s" + "" + "" + "" + "
ReplyKill MessageNextPreviousBack to List
" + "
" + "
" + "<%s id=\"txt\" style=\"overflow:auto;\" align=left>%s" + "
"; + + return _strdup(Msg); +} + +char * FwdPagetxt() +{ + char Msg[] = + + "" + "" + "" + "Edit Forwarding" + + "\r\n" + "" + "

BPQ32 BBS %s

" + + "" + + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + " " + " " + " " + "
StatusConfigurationUsersMessagesForwardingWelcome Msgs & PromptsHousekeepingWP UpdateWebMailNode Menu
" + + "
" + + "
" + "
" + "
" + "
" + "Max size to Send
" + "
" + "Max Size to Receive
" + "
" + "Max age for Bulls
" + "
" + "Warn if no route
" + " for P or T
" + "
" + "Use Local Time 
" + "
" + "Send P Msgs to more than one BBS 
" + "
" + "Use 4 Char Continent Codes 
" + "
" + "Aliases
" + "
" + "

" + "
" + "
" + "
" + "
" + "
" + "
" + ""; + + return _strdup(Msg); +} + +char * FwdDetailtxt() +{ + char Msg[] = + + "\r\n" + "

Forwarding Config for %s - %d Messages Queued

\r\n" + "  TO            AT         \r\n" + " TIMES         Connect Script
\r\n" + " \r\n" + " \r\n" + " \r\n" + "
\r\n" + "Hierarchical Routes (Flood Bulls) HR (Personals and Directed Bulls)\r\n" + " \r\n" + "\r\n" + "

\r\n" + "BBS HA
\r\n" + "Enable Forwarding  Interval (Secs)
\r\n" + "Request Reverse    Interval (Secs)
\r\n" + "Send new messages without waiting for poll timer
\r\n" + "FBB Blocked Max Block \r\n" + "Send Personal Mail Only
\r\n" + "Allow Binary Use B1 Protocol   Use B2 Protocol
\r\n" + "Send ctrl/Z instead of /ex in text mode forwarding
\r\n" + "Incoming Connect Timeout (Secs)
\r\n" + "
\r\n" + "\r\n" + " \r\n" + "\r\n" + "\r\n" + "
\r\n"; + + + return _strdup(Msg); +} + +char * webscriptjs() +{ + char Msg[] = + "\r\n" + "var Main\r\n" + "var fromleft;\r\n" + "\r\n" + "function initialize(mainoffset)\r\n" + "{\r\n" + " var w=window,d=document,e=d.documentElement,g=d.getElementsByTagName('body')[0];\r\n" + " x=w.innerWidth; //||e.clientWidth||g.clientWidth;\r\n" + " y=w.innerHeight; //||e.clientHeight||g.clientHeight; \r\n" + " Main = document.getElementById(\"main\");\r\n" + " w = x; \r\n" + " if (w > 920) {w = 920;}\r\n" + " fromleft = (x / 2) - (x - 150)/2;\r\n" + " if (fromleft < 0) {fromleft = 0;}\r\n" + " Main.style.left = fromleft + \"px\";\r\n" + " Main.style.width = x - 150 + \"px\";\r\n" + " Main.style.height = y - mainoffset + \"px\";\r\n" + "}\r\n" + "function newmsg(Key)\r\n" + "{\r\n" + "var param = \"toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes,scrollbars=yes,resizable=yes,titlebar=yes,toobar=yes\";\r\n" + "window.open(\"/WebMail/NewMsg?\" + Key,\"_self\",param);\r\n" + "}\r\n" + "function Reply(Num, Key)\r\n" + "{\r\n" + "var param = \"toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes,scrollbars=yes,resizable=yes,titlebar=yes,toobar=yes\";\r\n" + "window.open(\"/WebMail/Reply/\" + Num + \"?\" + Key,\"_self\",param);\r\n" + "}\r\n"; + + return _strdup(Msg); +} + + + + + +char * WebMailPagetxt() +{ + char Msg[] = + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "" + "\r\n" + "\r\n" + "WebMail \r\n" + "\r\n" + "\r\n" + "\r\n" + "

%s Webmail Interface - User %s - Message List

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
BullsPersonalNTSAll TypesMineMy SentMy RxedAuto RefreshSend MessageLogoutMail MgmtNode Menu
\r\n" + "
\r\n" + "
\r\n" + "
%s
\r\n" + "
\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg); +} + + +char * MainConfigtxt() +{ + char Msg[] = + "\r\n" + "\r\n" + "Main Configuration\r\n" + "\r\n" + "" + "\r\n" + "\r\n" + "

BPQ32 BBS %s

\r\n" + "\r\n" + "\r\n" + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "" + " \r\n" + "
StatusConfigurationUsersMessagesForwardingWelcome Msgs & PromptsHousekeepingWP UpdateWebMailNode Menu
\r\n" + "
\r\n" + "
Main Configuration
\r\n" + "\r\n" + "
\r\n" + "
\r\n" + "

 BBS Params

\r\n" + " BBS Call   SYSOP\r\n" + "Call
\r\n" + " H Route   \r\n" + " Redirect msgs to BBS Call to SYSOP Call
\r\n" + "
\r\n" + " BBS APPL No   Streams\r\n" + "
\r\n" + "
\r\n" + " Send System Msgs to\r\n" + "SYSOP Call
\r\n" + " Refuse Bulls
\r\n" + " Enable FBB UI System
\r\n" + "    Send Mail For Beacons every  Minutes
\r\n" + " Don't Hold Messages From New Users
\r\n" + " Set Don't add WINLINK.ORG flag on new users
\r\n" + " Don't Request Name
\r\n" + " Don't Request Home BBS
\r\n" + " Dont Check From Call
\r\n" + " Allow users to kill T messages
\r\n" + " Forward Messages to BBS Call
\r\n" + " Don't allow unknown users
\r\n" + " Enable Event Reporting

\r\n" + + " POP3 Port   \r\n" + "SMTP Port NTPPort   Enable Remote Access
\r\n" + " AMPR Address Send AMPR Mail to AMPR host\r\n" + "\r\n" + "

 ISP Params

\r\n" + " Enable ISP\r\n" + "Interface
\r\n" + " My Domain               
\r\n" + " SMTP Server        Port     SMTP Domain  
\r\n" + " POP3 Server        Port    
\r\n" + " ISP Account Name   \r\n" + "Password
\r\n" + " POP3 Poll Interval  \r\n" + " SMTP Server Requires Authentication\r\n" + "

 WP Params

\r\n" + " Send WPUpdates    \r\n" + " Reject WP Bulls     \r\n" + " Type BType P

\r\n" + " WP Destinations
 
\r\n" + "

 Message Filters

\r\n" + " Reject From  Reject\r\n" + "To    Reject At   Reject BID  Hold " + "From    Hold\r\n" + "To     Hold At    Hold BID
 \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "\r\n" + " \r\n" + "\r\n" + "\r\n" + "

" + " FBB reject.sys type filters (all fields must match, wildcards allowed)\r\n" + "

" + "
%s
" + "
" + " " + "
" + "
\r\n" + "
\r\n"; + + return _strdup(Msg); +} + + + + +char * MsgPagetxt() +{ + char Msg[] = + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "\r\n" + "\r\n" + "Edit Messages \r\n" + "\r\n" + "\r\n" + "\r\n" + "

BPQ32 BBS %s

\r\n" + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "" + " \r\n" + " \r\n" + " \r\n" + "
StatusConfigurationUsersMessagesForwardingWelcome Msgs & PromptsHousekeepingWP UpdateWebMailNode Menu
\r\n" + "\r\n" + "
\r\n" + "
\r\n" + "
\r\n" + "
\r\n" + "
Filter
\r\n" + "From
\r\n" + "To   \r\n" + "Via 
\r\n" + "BID  \r\n" + "
\r\n" + "
\r\n" + "
\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg); +} + + + +char * UserDetailtxt() +{ + char Msg[] = + "\r\n" + "
\r\n" + "

Update User %s

\r\n" + "
\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " " + " " + " \r\n" + "\r\n" + " " + "
BBSPermit Email
PMSRMS Express User
SYSOPPoll RMS
Expert   For SSID's \r\n" + "\r\n" + "\r\n" + "\r\n" + "
ExcludedHold Messages
Include SYSOP msgs in LM Don't add @winlink.org
\r\n" + "
Allow Sending BullsNTS MPS
Redirect to RMS
Send APRS Mail For to SSID" + "
" + "
\r\n" + "
\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "
Connects In
\r\n" + "
%d
\r\n" + "
Msgs in
\r\n" + "
%d
\r\n" + "
Rejects In
\r\n" + "
%d
\r\n" + "
Connects Out
\r\n" + "
%d
\r\n" + "
Msgs Out
\r\n" + "
%d
\r\n" + "
Rejects Out
\r\n" + "
%d
\r\n" + "
Bytes In
\r\n" + "
%d
\r\n" + "
Last Connect%s
Bytes Out
\r\n" + "
%d
\r\n" + "

\r\n" + "
Last Listed
\r\n" + "

\r\n" + "
\r\n" + "
\r\n" + "
\r\n" + "Name    
\r\n" + "Password  \r\n" + "CMS Pass  \r\n" + "
\r\n" + "QTH      ZIP
\r\n" + "Home BBS
\r\n" + "
\r\n" + "
\r\n" + " \r\n" + " \r\n" + " \r\n" + "
\r\n" + "
\r\n" + "
\r\n" + "\r\n"; + + return _strdup(Msg); +} + + +char * UserPagetxt() +{ + char Msg[] = + "\r\n" + " \r\n" + " \r\n" + "" + " \r\n" + " \r\n" + "\r\n" + "\r\n" + "Edit Users \r\n" + "\r\n" + "\r\n" + "\r\n" + "

BPQ32 BBS %s

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "" + "
StatusConfigurationUsersMessagesForwardingWelcome Msgs & PromptsHousekeepingWP UpdateWebMailNode Menu
\r\n" + "
\r\n" + "
\r\n" + "
\r\n" + "
\r\n" + "
\r\n" + "\r\n" + "\r\n"; + + return _strdup(Msg); +} + + + +char * Housekeepingtxt() +{ + char Msg[] = + "\r\n" + "\r\n" + "\r\n" + " \r\n" + "\r\n" + "Housekeeping\r\n" + "" + "\r\n" + "\r\n" + "\r\n" + "

BPQ32 BBS %s

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "" + "
StatusConfigurationUsersMessagesForwardingWelcome Msgs & PromptsHousekeepingWP UpdateWebMailNode Menu
\r\n" + "
\r\n" + "
Housekeeping
\r\n" + "\r\n" + "
\r\n" + "
\r\n" + "
Parameters

\r\n" + "
\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "
Maintenance Time (UTC)
Maintenance Interval (Hrs)
Max Message Number
BID Lifetime (Days)
Log File Lifetime (days)
Delete Inactive Users (days)
\r\n" + "
\r\n" + "Delete Messages and logs
\r\n" + "to recycle bin
\r\n" + "
\r\n" + "Send Non-delivery Notifications
\r\n" + "for P and T messages
\r\n" + "
\r\n" + "Suppress Mailing of
\r\n" + "Housekeeping Result

\r\n" + "Generate Traffic Report

\r\n" + "
\r\n" + "
\r\n" + "
Lifetimes (Days)
\r\n" + "
\r\n" + "\r\n" + "
Personals\r\n" + "
\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "
Read
Unread
Forwarded
Unforwarded
\r\n" + "
\r\n" + "
Bulletins\r\n" + "
\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "
Forwarded
Unforwarded
\r\n" + "
\r\n" + " \r\n" + "
NTS\r\n" + "
\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "
Delivered
Forwarded
Unforwarded
\r\n" + "
\r\n" + "\r\n" + " \r\n" + " \r\n" + " \r\n" + "
The following boxes allow you to specify different\r\n" + "values for different Bulletin origins and destinations. Normally these apply to Sent Messages. To apply to unsent, check box below.

\r\n" + "Specify Call, Lifetime....eg ALL, 10

\r\n" + "     \r\n" + "From       \r\n" + "   \r\n" + "To             \r\n" + "At
\r\n" + "
\r\n" + "
\r\n" + " Apply Overrides to unsent Bulls
\r\n" + "
\r\n" + "
\r\n" + "
\r\n" + "\r\n" + "
\r\n" + "
\r\n" + "
\r\n"; + + return _strdup(Msg); +} + +char * WPtxt() +{ + char Msg[] = + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "Edit WP\r\n" + "\r\n" + "\r\n" + "\r\n" + "

BPQ32 BBS %s

\r\n" + "\r\n" + "\r\n" + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "" + " \r\n" + " \r\n" + " \r\n" + "
StatusConfigurationUsersMessagesForwardingWelcome Msgs & PromptsHousekeepingWP UpdateWebMailNode Menu
\r\n" + "
\r\n" + "
White Pages
\r\n" + "
\r\n" + "
\r\n" + "
\r\n" + "
\r\n" + "\r\n"; + + return _strdup(Msg); +} + + +char * ChatConfigtxt() +{ + char Msg[] = + "\r\n" + "\r\n" + "" + "\r\n" + "Chat Configuration\r\n" + "\r\n" + "\r\n" + "ChatP\r\n" + "\r\n" + "\r\n" + "

BPQ32 Chat Node %s

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
StatusConfigurationNode Menu
\r\n" + "
\r\n" + "
Chat Configuration
\r\n" + "
\r\n" + "
\r\n" + "

 Chat Server Params

\r\n" + "Chat APPL No  \r\n" + "
\r\n" + "Streams    \r\n" + "  
\r\n" + " 
\r\n" + " Enable Event Reporting

\r\n" + + "
The Nodes to link to box defines which other Chat Nodes should be connected to, or from which " + "connections may be accepted. The format is ALIAS:CALL, eg BPQCHT:G8BPQ-4. If the node is not directly " + "connectable (ie is not in your NODES table) you can add a connect script. This consists of a series of commands " + "separared by |, eg NOTCHT:G8BPQ-4|C 3 GM8BPQ-9|CHAT" + + "

The Callsign of the Chat Node is not defined here - it is obtained from the bpq32.cfg APPLICATION line corresponding to the Chat Appl Number.
\r\n" + "
\n" + "Nodes to link to
" + " 
\r\n" + "Node to Node Link PACLEN \r\n" + "  

\r\n" + " Map Position
\r\n" + "
\r\n" + " Popup Type    Hover \r\n" + "Click
\r\n" + "
\r\n" + "Map Popup Text
\r\n" + " \r\n" + "

Welcome Message
\r\n" + "
\r\n" + "
\r\n" + "\r\n" + "
\r\n" + " \r\n" + " \r\n" + " \r\n" + "
\r\n" + "
\r\n" + "
\r\n"; + + return _strdup(Msg); +} + + +char * ChatStatustxt() +{ + char Msg[] = + "\r\n" + "\r\n" + "" + "\r\n" + "\r\n" + "%s's Chat Server\r\n" + " \r\n" + "\r\n" + "\r\n" + "

BPQ32 Chat Server %s

\r\n" + "\r\n" + "\r\n" + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "
StatusConfigurationNode Menu
\r\n" + "\r\n" + "
\r\n" + "\r\n" + "
\r\n" + "
\r\n" + "
\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "%s\r\n" + " \r\n" + "
UserCallsignStreamTopicQueue
\r\n" + " \r\n" + " \r\n" + "
\r\n" + "
\r\n" + "
\r\n" + "
\r\n" + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "%s\r\n" + " \r\n" + "
CallsignNodeNameTopicIdleQTH
\r\n" + "
\r\n" + "
\r\n" + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "%s\r\n" + " \r\n" + "
CallsignStateNodesUsers
\r\n" + "
\r\n" + "
\r\n"; + + return _strdup(Msg); +} diff --git a/.svn/pristine/2c/2c16fbc50a5f41b2b2a4efa5caea0caa40d24084.svn-base b/.svn/pristine/2c/2c16fbc50a5f41b2b2a4efa5caea0caa40d24084.svn-base new file mode 100644 index 0000000..8914344 --- /dev/null +++ b/.svn/pristine/2c/2c16fbc50a5f41b2b2a4efa5caea0caa40d24084.svn-base @@ -0,0 +1,193 @@ +/* +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 +*/ + +// +// Netrom Record ROute Suport Code for BPQ32 Switch +// + +// All code runs from the BPQ32 Received or Timer Routines under Semaphore. +// As most data areas are dynamically allocated, they will not survive a Timer Process Swap. +// Shared data can be used for Config Info. + + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include +//#include "vmm.h" + + +#include "cheaders.h" + + +extern int SENDNETFRAME(); +extern VOID Q_ADD(); + +VOID __cdecl Debugprintf(const char * format, ...); + +TRANSPORTENTRY * NRRSession; + +/* +datagrams (and other things) to be transported in Netrom L3 frames. +When the frametype is 0x00, the "circuit index" and "circuit id" (first 2 +bytes of the transport header) take on a different meaning, something like +"protocol family" and "protocol id". IP over netrom uses 0x0C for both +bytes, TheNet uses 0x00 for both bytes when making L3RTT measurements, and +Xnet uses family 0x00, protocol id 0x01 for Netrom Record Route. I believe +there are authors using other values too. Unfortunately there is no +co-ordinating authority for these numbers, so authors just pick an unused +one. +*/ + +VOID NRRecordRoute(UCHAR * Buff, int Len) +{ + // NRR frame for us. If We originated it, report outcome, else put our call on end, and send back + + L3MESSAGEBUFFER * Msg = (L3MESSAGEBUFFER *)Buff; + struct DEST_LIST * DEST; + char Temp[7]; + int NRRLen = Len - (21 + MSGHDDRLEN); + UCHAR Flags; + char call[10]; + int calllen; + char * Save = Buff; + + if (memcmp(&Msg->L4DATA, MYCALL, 7) == 0) + { + UCHAR * BUFFER = GetBuff(); + UCHAR * ptr1; + struct _MESSAGE * Msg; + + if (BUFFER == NULL) + return; + + ptr1 = &BUFFER[MSGHDDRLEN]; + + *ptr1++ = 0xf0; // PID + + ptr1 += sprintf(ptr1, "NRR Response:"); + + Buff += 21 + MSGHDDRLEN; + Len -= (21 + MSGHDDRLEN); + + while (Len > 0) + { + calllen = ConvFromAX25(Buff, call); + call[calllen] = 0; + ptr1 += sprintf(ptr1, " %s", call); + if ((Buff[7] & 0x80) == 0x80) // Check turnround bit + *ptr1++ = '*'; + + Buff+=8; + Len -= 8; + } + + // Add ours on end for neatness + + calllen = ConvFromAX25(MYCALL, call); + call[calllen] = 0; + ptr1 += sprintf(ptr1, " %s", call); + + *ptr1++ = 0x0d; // CR + + Len = (int)(ptr1 - BUFFER); + + Msg = (struct _MESSAGE *)BUFFER; + + Msg->LENGTH = Len; + + Msg->CHAIN = NULL; + + C_Q_ADD(&NRRSession->L4TX_Q, (UINT *)BUFFER); + + PostDataAvailable(NRRSession); + + ReleaseBuffer(Save); + + return; + } + + // Add our call on end, and increase count + + Flags = Buff[Len - 1]; + + Flags--; + + if (Flags && NRRLen < 228) // Dont update if full + { + Flags |= 0x80; // Set End of route bit + + Msg->L3PID = NRPID; + + memcpy(&Msg->L4DATA[NRRLen], MYCALL, 7); + Msg->L4DATA[NRRLen+7] = Flags; + NRRLen += 8; + } + + // We should send it back via our bast route, or recorded route could be wrong + + memcpy(Temp, Msg->L3DEST, 7); + memcpy(Msg->L3DEST, Msg->L3SRCE, 7); + memcpy(Msg->L3SRCE, Temp, 7); + + if (FindDestination(Msg->L3DEST, &DEST) == 0) + { + ReleaseBuffer(Msg); // CANT FIND DESTINATION + return; + } + + Msg->LENGTH = NRRLen + 21 + MSGHDDRLEN; + + Debugprintf("NRR TX Len %d Flags %d NRRLen %d", Msg->LENGTH, Flags, NRRLen); + + C_Q_ADD(&DEST->DEST_Q, Msg); +} + + +VOID SendNRRecordRoute(struct DEST_LIST * DEST, TRANSPORTENTRY * Session) +{ + L3MESSAGEBUFFER * Msg = GetBuff(); + int Stream = 1; + + if (Msg == NULL) + return; + + NRRSession = Session; // Save Session Pointer for reply + + Msg->Port = 0; + Msg->L3PID = NRPID; + + memcpy(Msg->L3DEST, DEST->DEST_CALL, 7); + memcpy(Msg->L3SRCE, MYCALL, 7); + + Msg->L3TTL = L3LIVES; + Msg->L4ID = 1; + Msg->L4INDEX = 0; + Msg->L4FLAGS = 0; + + memcpy(Msg->L4DATA, MYCALL, 7); + Msg->L4DATA[7] = Stream + 28; + + Msg->LENGTH = 8 + 21 + MSGHDDRLEN; + + C_Q_ADD(&DEST->DEST_Q, Msg); +} diff --git a/.svn/pristine/2d/2d86e56afbaeda649d1ccf0e5299f0773143ff4d.svn-base b/.svn/pristine/2d/2d86e56afbaeda649d1ccf0e5299f0773143ff4d.svn-base new file mode 100644 index 0000000..9976ec2 --- /dev/null +++ b/.svn/pristine/2d/2d86e56afbaeda649d1ccf0e5299f0773143ff4d.svn-base @@ -0,0 +1,15934 @@ +/* +Copyright 2001-2018 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 +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// Utility Routines + +#include "bpqmail.h" +#ifdef WIN32 +#include "Winspool.h" +#else +#include +#endif + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); + +BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen); + +BOOL Bells; +BOOL FlashOnBell; // Flash instead of Beep +BOOL StripLF; + +BOOL WarnWrap; +BOOL FlashOnConnect; +BOOL WrapInput; +BOOL CloseWindowOnBye; + +RECT ConsoleRect; + +BOOL OpenConsole; +BOOL OpenMon; + +int reportMailEvents = 0; + +FBBFilter * Filters = NULL; + +extern struct ConsoleInfo BBSConsole; + +extern char LOC[7]; + +extern BOOL MQTT; + +//#define BBSIDLETIME 120 +//#define USERIDLETIME 300 + + +#define BBSIDLETIME 900 +#define USERIDLETIME 900 + +#ifdef LINBPQ +extern BPQVECSTRUC ** BPQHOSTVECPTR; +UCHAR * GetLogDirectory(); +DllExport int APIENTRY SessionStateNoAck(int stream, int * state); +int RefreshWebMailIndex(); +#else +__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; +typedef char * (WINAPI FAR *FARPROCZ)(); +typedef int (WINAPI FAR *FARPROCX)(); +FARPROCZ pGetLOC; +FARPROCX pRefreshWebMailIndex; + +#endif + +Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall); +VOID APIENTRY md5 (char *arg, unsigned char * checksum); +int APIENTRY GetRaw(int stream, char * msg, int * len, int * count); +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); +void FreeSemaphore(struct SEM * Semaphore); +int EncryptPass(char * Pass, char * Encrypt); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); +void DeletetoRecycle(char * FN); +VOID DoImportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoExportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID TidyPrompts(); +char * ReadMessageFileEx(struct MsgInfo * MsgRec); +char * APIENTRY GetBPQDirectory(); +BOOL SendARQMail(CIRCUIT * conn); +int APIENTRY ChangeSessionIdletime(int Stream, int idletime); +int APIENTRY GetApplNum(int Stream); +VOID FormatTime(char * Time, time_t cTime); +BOOL CheckifPacket(char * Via); +char * APIENTRY GetVersionString(); +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename); +void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); +int GetCMSHash(char * Challenge, char * Password); +BOOL SendAMPRSMTP(CIRCUIT * conn); +VOID ProcessMCASTLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen); +VOID MCastTimer(); +VOID MCastConTimer(ConnectionInfo * conn); +int FindFreeBBSNumber(); +VOID DoSetMsgNo(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +BOOL ProcessYAPPMessage(CIRCUIT * conn); +void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); +void YAPPSendData(ConnectionInfo * conn); +VOID CheckBBSNumber(int i); +struct UserInfo * FindAMPR(); +VOID SaveInt64Value(config_setting_t * group, char * name, long long value); +VOID SaveIntValue(config_setting_t * group, char * name, int value); +VOID SaveStringValue(config_setting_t * group, char * name, char * value); +char *stristr (char *ch1, char *ch2); +BOOL CheckforMessagetoServer(struct MsgInfo * Msg); +void DoHousekeepingCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled); +void ListCategories(ConnectionInfo * conn); +void RebuildNNTPList(); +long long GetInt64Value(config_setting_t * group, char * name); +void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); +int ReformatSyncMessage(CIRCUIT * conn); +char * initMultipartUnpack(char ** Input); +char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg); +int decode_quoted_printable(char *ptr, int len); +void decodeblock( unsigned char in[4], unsigned char out[3]); +int encode_quoted_printable(char *s, char * out, int Len); +int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress); +int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall); +void SendMessageReadEvent(char * call, struct MsgInfo * Msg); +void SendNewMessageEvent(char * call, struct MsgInfo * Msg); +void MQTTMessageEvent(struct MsgInfo * message); + +config_t cfg; +config_setting_t * group; + +extern ULONG BBSApplMask; + +//static int SEMCLASHES = 0; + +char SecureMsg[80] = ""; // CMS Secure Signon Response + +int NumberofStreams; + +extern char VersionStringWithBuild[50]; + +#define MaxSockets 64 + +extern struct SEM OutputSEM; + +extern ConnectionInfo Connections[MaxSockets+1]; + +extern struct UserInfo ** UserRecPtr; +extern int NumberofUsers; + +extern struct UserInfo * BBSChain; // Chain of users that are BBSes + +extern struct MsgInfo ** MsgHddrPtr; +extern int NumberofMessages; + +extern int FirstMessageIndextoForward; // Lowest Message wirh a forward bit set - limits search + +extern char UserDatabaseName[MAX_PATH]; +extern char UserDatabasePath[MAX_PATH]; + +extern char MsgDatabasePath[MAX_PATH]; +extern char MsgDatabaseName[MAX_PATH]; + +extern char BIDDatabasePath[MAX_PATH]; +extern char BIDDatabaseName[MAX_PATH]; + +extern char WPDatabasePath[MAX_PATH]; +extern char WPDatabaseName[MAX_PATH]; + +extern char BadWordsPath[MAX_PATH]; +extern char BadWordsName[MAX_PATH]; + +extern char BaseDir[MAX_PATH]; +extern char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% +extern char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat + + +extern char MailDir[MAX_PATH]; + +extern BIDRec ** BIDRecPtr; +extern int NumberofBIDs; + +extern BIDRec ** TempBIDRecPtr; +extern int NumberofTempBIDs; + +extern WPRec ** WPRecPtr; +extern int NumberofWPrecs; + +extern char ** BadWords; +extern int NumberofBadWords; +extern char * BadFile; + +extern int LatestMsg; +extern struct SEM MsgNoSemaphore; // For locking updates to LatestMsg +extern int HighestBBSNumber; + +extern int MaxMsgno; +extern int BidLifetime; +extern int MaxAge; +extern int MaintInterval; +extern int MaintTime; + +extern int ProgramErrors; + +extern BOOL MonBBS; +extern BOOL MonCHAT; +extern BOOL MonTCP; + +BOOL SendNewUserMessage = TRUE; +BOOL AllowAnon = FALSE; +BOOL UserCantKillT = FALSE; + +typedef int (WINAPI FAR *FARPROCX)(); +FARPROCX pRunEventProgram; +FARPROCX pGetPortFrequency; + +int RunEventProgram(char * Program, char * Param); + + +extern BOOL EventsEnabled; + +#define BPQHOSTSTREAMS 64 + +// Although externally streams are numbered 1 to 64, internally offsets are 0 - 63 + +extern BPQVECSTRUC BPQHOSTVECTOR[BPQHOSTSTREAMS + 5]; + +/* --- G7TAJ PG Server--- */ + +void run_pg( CIRCUIT * conn, struct UserInfo * user ); +void startrun_pgThread( RUNPGARGS_PTR Args ); + +char * SERVERLIST[256][3]; + +int NUM_SERVERS = 0; + +/*------- G7TAJ END ----------*/ + + +#ifdef LINBPQ +extern BPQVECSTRUC ** BPQHOSTVECPTR; +extern char WL2KModes [54][18]; + + +#else + +__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; + + +char WL2KModes [54][18] = { + "Packet 1200", "Packet 2400", "Packet 4800", "Packet 9600", "Packet 19200", "Packet 38400", "High Speed Packet", "", "", "", "", + "", "Pactor 1", "", "", "Pactor 2", "", "Pactor 3", "", "", "Pactor 4", // 10 - 20 + "Winmor 500", "Winmor 1600", "", "", "", "", "", "", "", // 21 - 29 + "Robust Packet", "", "", "", "", "", "", "", "", "", // 30 - 39 + "ARDOP 200", "ARDOP 500", "ARDOP 1000", "ARDOP 2000", "ARDOP 2000 FM", "", "", "", "", "", // 40 - 49 + "VARA", "VARA FM", "VARA FM WIDE", "VARA 500"}; +#endif + + + + + +FILE * LogHandle[4] = {NULL, NULL, NULL, NULL}; + +time_t LastLogTime[4] = {0, 0, 0, 0}; + +char FilesNames[4][100] = {"", "", "", ""}; + +char * Logs[4] = {"BBS", "CHAT", "TCP", "DEBUG"}; + +extern struct SEM ConfigSEM; + + +BOOL OpenLogfile(int Flags) +{ + UCHAR FN[MAX_PATH]; + time_t LT; + struct tm * tm; + + LT = time(NULL); + tm = gmtime(<); + + sprintf(FN,"%s/logs/log_%02d%02d%02d_%s.txt", GetLogDirectory(), tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]); + + LogHandle[Flags] = fopen(FN, "ab"); + +#ifndef WIN32 + + if (strcmp(FN, &FilesNames[Flags][0])) + { + UCHAR SYMLINK[MAX_PATH]; + + sprintf(SYMLINK,"%s/logLatest_%s.txt", GetBPQDirectory(), Logs[Flags]); + unlink(SYMLINK); + strcpy(&FilesNames[Flags][0], FN); + symlink(FN, SYMLINK); + } + +#endif + + return (LogHandle[Flags] != NULL); +} + +typedef int (WINAPI FAR *FARPROCX)(); + +extern FARPROCX pDllBPQTRACE; + +struct SEM LogSEM = {0, 0}; + +void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags) +{ + char CRLF[2] = {0x0d,0x0a}; + struct tm * tm; + char Stamp[20]; + time_t LT; +// struct _EXCEPTION_POINTERS exinfo; + + // Write to Node BPQTRACE system + + if ((Flags == LOG_BBS || Flags == LOG_DEBUG_X) && MsgLen < 250) + { + MESSAGE Monframe; + memset(&Monframe, 0, sizeof(Monframe)); + + Monframe.PORT = 64; + Monframe.LENGTH = 12 + MsgLen; + Monframe.DEST[0] = 1; // Plain Text Monitor + + memcpy(&Monframe.DEST[1], Msg, MsgLen); + Monframe.DEST[1 + MsgLen] = 0; + + time(&Monframe.Timestamp); +#ifdef LINBPQ + GetSemaphore(&Semaphore, 88); + BPQTRACE(&Monframe, FALSE); + FreeSemaphore(&Semaphore); +#else + if (pDllBPQTRACE) + pDllBPQTRACE(&Monframe, FALSE); +#endif + } +#ifndef LINBPQ + __try + { +#endif + + + +#ifndef LINBPQ + + if (hMonitor) + { + if (Flags == LOG_TCP && MonTCP) + { + WritetoMonitorWindow((char *)&Flag, 1); + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_CHAT && MonCHAT) + { + WritetoMonitorWindow((char *)&Flag, 1); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + WritetoMonitorWindow(call, 10); + } + else + WritetoMonitorWindow(" ", 10); + + WritetoMonitorWindow(Msg, MsgLen); + if (Msg[MsgLen-1] != '\r') + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_BBS && MonBBS) + { + WritetoMonitorWindow((char *)&Flag, 1); + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + WritetoMonitorWindow(call, 10); + } + else + WritetoMonitorWindow(" ", 10); + + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_DEBUG_X) + { + WritetoMonitorWindow((char *)&Flag, 1); + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + } +#endif + + if (Flags == LOG_TCP && !LogTCP) + return; + if (Flags == LOG_BBS && !LogBBS) + return; + if (Flags == LOG_CHAT && !LogCHAT) + return; + + GetSemaphore(&LogSEM, 0); + + if (LogHandle[Flags] == NULL) + OpenLogfile(Flags); + + if (LogHandle[Flags] == NULL) + { + FreeSemaphore(&LogSEM); + return; + } + LT = time(NULL); + tm = gmtime(<); + + sprintf(Stamp,"%02d%02d%02d %02d:%02d:%02d %c", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, Flag); + + fwrite(Stamp, 1, strlen(Stamp), LogHandle[Flags]); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + fwrite(call, 1, 10, LogHandle[Flags]); + } + else + fwrite(" ", 1, 10, LogHandle[Flags]); + + fwrite(Msg, 1, MsgLen, LogHandle[Flags]); + + if (Flags == LOG_CHAT && Msg[MsgLen-1] == '\r') + fwrite(&CRLF[1], 1, 1, LogHandle[Flags]); + else + fwrite(CRLF, 1, 2, LogHandle[Flags]); + + // Don't close/reopen logs every time + +// if ((LT - LastLogTime[Flags]) > 60) + { + LastLogTime[Flags] = LT; + fclose(LogHandle[Flags]); + LogHandle[Flags] = NULL; + } + FreeSemaphore(&LogSEM); + +#ifndef LINBPQ + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +#endif +} + +int CriticalErrorHandler(char * error) +{ + Debugprintf("Critical Error %s", error); + ProgramErrors = 25; + CheckProgramErrors(); // Force close + return 0; +} + +BOOL CheckForTooManyErrors(ConnectionInfo * conn) +{ + conn->ErrorCount++; + + if (conn->ErrorCount > 4) + { + BBSputs(conn, "Too many errors - closing\r"); + conn->CloseAfterFlush = 20; + return TRUE; + } + return FALSE; +} + + + + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[16384]; + va_list(arglist); + int Len; + + va_start(arglist, format); + Len = vsprintf(Mess, format, arglist); +#ifndef LINBPQ + WriteLogLine(NULL, '!',Mess, Len, LOG_DEBUG_X); +#endif + // #ifdef _DEBUG + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + +// #endif + return; +} + +VOID __cdecl Logprintf(int LogMode, CIRCUIT * conn, int InOut, const char * format, ...) +{ + char Mess[1000]; + va_list(arglist);int Len; + + va_start(arglist, format); + Len = vsprintf(Mess, format, arglist); + WriteLogLine(conn, InOut, Mess, Len, LogMode); + + return; +} + +struct MsgInfo * GetMsgFromNumber(int msgno) +{ + if (msgno < 1 || msgno > 999999) + return NULL; + + return MsgnotoMsg[msgno]; +} + +struct UserInfo * AllocateUserRecord(char * Call) +{ + struct UserInfo * User = zalloc(sizeof (struct UserInfo)); + + strcpy(User->Call, Call); + User->Length = sizeof (struct UserInfo); + + GetSemaphore(&AllocSemaphore, 0); + + UserRecPtr=realloc(UserRecPtr,(++NumberofUsers+1) * sizeof(void *)); + UserRecPtr[NumberofUsers]= User; + + FreeSemaphore(&AllocSemaphore); + + return User; +} + +struct MsgInfo * AllocateMsgRecord() +{ + struct MsgInfo * Msg = zalloc(sizeof (struct MsgInfo)); + + GetSemaphore(&AllocSemaphore, 0); + + MsgHddrPtr=realloc(MsgHddrPtr,(++NumberofMessages+1) * sizeof(void *)); + MsgHddrPtr[NumberofMessages] = Msg; + + FreeSemaphore(&AllocSemaphore); + + return Msg; +} + +BIDRec * AllocateBIDRecord() +{ + BIDRec * BID = zalloc(sizeof (BIDRec)); + + GetSemaphore(&AllocSemaphore, 0); + + BIDRecPtr = realloc(BIDRecPtr,(++NumberofBIDs+1) * sizeof(void *)); + BIDRecPtr[NumberofBIDs] = BID; + + FreeSemaphore(&AllocSemaphore); + + return BID; +} + +BIDRec * AllocateTempBIDRecord() +{ + BIDRec * BID = zalloc(sizeof (BIDRec)); + + GetSemaphore(&AllocSemaphore, 0); + + TempBIDRecPtr=realloc(TempBIDRecPtr,(++NumberofTempBIDs+1) * sizeof(void *)); + TempBIDRecPtr[NumberofTempBIDs] = BID; + + FreeSemaphore(&AllocSemaphore); + + return BID; +} + +struct UserInfo * LookupCall(char * Call) +{ + struct UserInfo * ptr = NULL; + int i; + + for (i=1; i <= NumberofUsers; i++) + { + ptr = UserRecPtr[i]; + + if (_stricmp(ptr->Call, Call) == 0) return ptr; + + } + + return NULL; +} + +int GetNetInt(char * Line) +{ + char temp[1024]; + char * ptr = strlop(Line, ','); + int n = atoi(Line); + if (ptr == NULL) + Line[0] = 0; + else + { + strcpy(temp, ptr); + strcpy(Line, temp); + } + return n; +} + +VOID GetUserDatabase() +{ + struct UserInfo UserRec; + + FILE * Handle; + size_t ReadLen; + struct UserInfo * user; + time_t UserLimit = time(NULL) - (UserLifetime * 86400); // Oldest user to keep + int i; + + // See if user config is in main config + + group = config_lookup (&cfg, "BBSUsers"); + + if (group) + { + // We have User config in the main config file. so use that + + int index = 0; + char * stats; + struct MsgStats * Stats; + char * ptr, * ptr2; + + config_setting_t * entry = config_setting_get_elem (group, index++); + + // Initialise a new File + + UserRecPtr = malloc(sizeof(void *)); + UserRecPtr[0] = malloc(sizeof (struct UserInfo)); + memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); + UserRecPtr[0]->Length = sizeof (struct UserInfo); + + NumberofUsers = 0; + + while (entry) + { + char call[16]; + + // entry->name is call, will have * in front if a call stating woth number + + if (entry->name[0] == '*') + strcpy(call, &entry->name[1]); + else + strcpy(call, entry->name); + + user = AllocateUserRecord(call); + + ptr = entry->value.sval; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->Name, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->Address, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->HomeBBS, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->QRA, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->pass, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->ZIP, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->CMSPass, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->lastmsg = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->flags = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->PageLen = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->BBSNumber = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->RMSSSIDBits = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->WebSeqNo = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->TimeLastConnected = atol(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) Stats = &user->Total; + stats = ptr; + + if (Stats == NULL) + { + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + + Stats->ConnectsIn = GetNetInt(stats); + Stats->ConnectsOut = GetNetInt(stats); + Stats->MsgsReceived[0] = GetNetInt(stats); + Stats->MsgsReceived[1] = GetNetInt(stats); + Stats->MsgsReceived[2] = GetNetInt(stats); + Stats->MsgsReceived[3] = GetNetInt(stats); + Stats->MsgsSent[0] = GetNetInt(stats); + Stats->MsgsSent[1] = GetNetInt(stats); + Stats->MsgsSent[2] = GetNetInt(stats); + Stats->MsgsSent[3] = GetNetInt(stats); + Stats->MsgsRejectedIn[0] = GetNetInt(stats); + Stats->MsgsRejectedIn[1] = GetNetInt(stats); + Stats->MsgsRejectedIn[2] = GetNetInt(stats); + Stats->MsgsRejectedIn[3] = GetNetInt(stats); + Stats->MsgsRejectedOut[0] = GetNetInt(stats); + Stats->MsgsRejectedOut[1] = GetNetInt(stats); + Stats->MsgsRejectedOut[2] = GetNetInt(stats); + Stats->MsgsRejectedOut[3] = GetNetInt(stats); + Stats->BytesForwardedIn[0] = GetNetInt(stats); + Stats->BytesForwardedIn[1] = GetNetInt(stats); + Stats->BytesForwardedIn[2] = GetNetInt(stats); + Stats->BytesForwardedIn[3] = GetNetInt(stats); + Stats->BytesForwardedOut[0] = GetNetInt(stats); + Stats->BytesForwardedOut[1] = GetNetInt(stats); + Stats->BytesForwardedOut[2] = GetNetInt(stats); + Stats->BytesForwardedOut[3] = GetNetInt(stats); + + Stats = &user->Last; + stats = ptr2; + + if (Stats == NULL) + { + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + + Stats->ConnectsIn = GetNetInt(stats); + Stats->ConnectsOut = GetNetInt(stats); + Stats->MsgsReceived[0] = GetNetInt(stats); + Stats->MsgsReceived[1] = GetNetInt(stats); + Stats->MsgsReceived[2] = GetNetInt(stats); + Stats->MsgsReceived[3] = GetNetInt(stats); + Stats->MsgsSent[0] = GetNetInt(stats); + Stats->MsgsSent[1] = GetNetInt(stats); + Stats->MsgsSent[2] = GetNetInt(stats); + Stats->MsgsSent[3] = GetNetInt(stats); + Stats->MsgsRejectedIn[0] = GetNetInt(stats); + Stats->MsgsRejectedIn[1] = GetNetInt(stats); + Stats->MsgsRejectedIn[2] = GetNetInt(stats); + Stats->MsgsRejectedIn[3] = GetNetInt(stats); + Stats->MsgsRejectedOut[0] = GetNetInt(stats); + Stats->MsgsRejectedOut[1] = GetNetInt(stats); + Stats->MsgsRejectedOut[2] = GetNetInt(stats); + Stats->MsgsRejectedOut[3] = GetNetInt(stats); + Stats->BytesForwardedIn[0] = GetNetInt(stats); + Stats->BytesForwardedIn[1] = GetNetInt(stats); + Stats->BytesForwardedIn[2] = GetNetInt(stats); + Stats->BytesForwardedIn[3] = GetNetInt(stats); + Stats->BytesForwardedOut[0] = GetNetInt(stats); + Stats->BytesForwardedOut[1] = GetNetInt(stats); + Stats->BytesForwardedOut[2] = GetNetInt(stats); + Stats->BytesForwardedOut[3] = GetNetInt(stats); + + + if ((user->flags & F_BBS) == 0) // Not BBS - Check Age + { + if (UserLifetime && user->TimeLastConnected) // Dont delete manually added Users that havent yet connected + { + if (user->TimeLastConnected < UserLimit) + { + // Too Old - ignore + + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + } + } + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (user->lastmsg < 0 || user->lastmsg > LatestMsg) + user->lastmsg = LatestMsg; + + + entry = config_setting_get_elem (group, index++); + } + } + else + { + Handle = fopen(UserDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); + UserRecPtr[0]->Length = sizeof (struct UserInfo); + + NumberofUsers = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&UserRec, 0, sizeof (struct UserInfo)); + UserRec.Length = sizeof (struct UserInfo); + } + else + { + // See if format has changed + + if (UserRec.Length == 0) + { + // Old format without a Length field + + struct OldUserInfo * OldRec = (struct OldUserInfo *)&UserRec; + int Users = OldRec->ConnectsIn; // User Count in control record + char Backup1[MAX_PATH]; + + // Create a backup in case reversion is needed and Reposition to first User record + + fclose(Handle); + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".oldformat"); + + CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak + + Handle = fopen(UserDatabasePath, "rb"); + + ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); // Skip Control Record + + // Set up control record + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); + UserRecPtr[0]->Length = sizeof (UserRec); + + NumberofUsers = 0; + +OldNext: + + ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); + + if (ReadLen > 0) + { + if (OldRec->Call[0] < '0') + goto OldNext; // Blank record + + user = AllocateUserRecord(OldRec->Call); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + // Copy info from Old record + + user->lastmsg = OldRec->lastmsg; + user->Total.ConnectsIn = OldRec->ConnectsIn; + user->TimeLastConnected = OldRec->TimeLastConnected; + user->flags = OldRec->flags; + user->PageLen = OldRec->PageLen; + user->BBSNumber = OldRec->BBSNumber; + memcpy(user->Name, OldRec->Name, 18); + memcpy(user->Address, OldRec->Address, 61); + user->Total.MsgsReceived[0] = OldRec->MsgsReceived; + user->Total.MsgsSent[0] = OldRec->MsgsSent; + user->Total.MsgsRejectedIn[0] = OldRec->MsgsRejectedIn; // Messages we reject + user->Total.MsgsRejectedOut[0] = OldRec->MsgsRejectedOut; // Messages Rejectd by other end + user->Total.BytesForwardedIn[0] = OldRec->BytesForwardedIn; + user->Total.BytesForwardedOut[0] = OldRec->BytesForwardedOut; + user->Total.ConnectsOut = OldRec->ConnectsOut; // Forwarding Connects Out + user->RMSSSIDBits = OldRec->RMSSSIDBits; // SSID's to poll in RMS + memcpy(user->HomeBBS, OldRec->HomeBBS, 41); + memcpy(user->QRA, OldRec->QRA, 7); + memcpy(user->pass, OldRec->pass, 13); + memcpy(user->ZIP, OldRec->ZIP, 9); + + // Read any forwarding info, even if not a BBS. + // This allows a BBS to be temporarily set as a + // normal user without loosing forwarding info + + SetupForwardingStruct(user); + + if (user->flags & F_BBS) + { + // Defined as BBS - allocate and initialise forwarding structure + + // Add to BBS Chain; + + user->BBSNext = BBSChain; + BBSChain = user; + + // Save Highest BBS Number + + if (user->BBSNumber > HighestBBSNumber) HighestBBSNumber = user->BBSNumber; + } + goto OldNext; + } + + SortBBSChain(); + fclose(Handle); + + return; + } + } + + // Set up control record + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); + UserRecPtr[0]->Length = sizeof (UserRec); + + NumberofUsers = 0; + +Next: + + ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); + + if (ReadLen > 0) + { + if (UserRec.Call[0] < '0') + goto Next; // Blank record + + if (UserRec.TimeLastConnected == 0) + UserRec.TimeLastConnected = UserRec.xTimeLastConnected; + + if ((UserRec.flags & F_BBS) == 0) // Not BBS - Check Age + if (UserLifetime) // if limit set + if (UserRec.TimeLastConnected) // Dont delete manually added Users that havent yet connected + if (UserRec.TimeLastConnected < UserLimit) + goto Next; // Too Old - ignore + + user = AllocateUserRecord(UserRec.Call); + memcpy(user, &UserRec, sizeof (UserRec)); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + user->ForwardingInfo = NULL; // In case left behind on crash + user->BBSNext = NULL; + user->POP3Locked = FALSE; + + if (user->lastmsg < 0 || user->lastmsg > LatestMsg) + user->lastmsg = LatestMsg; + + goto Next; + } + fclose(Handle); + } + + // Setting up BBS struct has been moved until all user record + // have been read so we can fix corrupt BBSNUmber + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + // Read any forwarding info, even if not a BBS. + // This allows a BBS to be temporarily set as a + // normal user without loosing forwarding info + + SetupForwardingStruct(user); + + if (user->flags & F_BBS) + { + // Add to BBS Chain; + + if (user->BBSNumber == NBBBS) // Fix corrupt records + { + user->BBSNumber = FindFreeBBSNumber(); + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; // cant really do much else + } + + user->BBSNext = BBSChain; + BBSChain = user; + +// Logprintf(LOG_BBS, NULL, '?', "BBS %s BBSNumber %d", user->Call, user->BBSNumber); + + // Save Highest BBS Number + + if (user->BBSNumber > HighestBBSNumber) + HighestBBSNumber = user->BBSNumber; + } + } + + // Check for dulicate BBS numbers + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_BBS) + { + if (user->BBSNumber == 0) + user->BBSNumber = FindFreeBBSNumber(); + + CheckBBSNumber(user->BBSNumber); + } + } + + SortBBSChain(); +} + +VOID CopyUserDatabase() +{ + return; // User config now in main config file +/* + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + + // Keep 4 Generations + + strcpy(Backup2, UserDatabasePath); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, UserDatabasePath); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak +*/ +} + +VOID CopyConfigFile(char * ConfigName) +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + + // Keep 4 Generations + + strcpy(Backup2, ConfigName); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, ConfigName); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, ConfigName); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, ConfigName); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); // Move .bak to .bak.1 + + CopyFile(ConfigName, Backup1, FALSE); // Copy to .bak +} + + + +VOID SaveUserDatabase() +{ + SaveConfig(ConfigName); // User config is now in main config file + GetConfig(ConfigName); + +/* + FILE * Handle; + size_t WriteLen; + int i; + + Handle = fopen(UserDatabasePath, "wb"); + + UserRecPtr[0]->Total.ConnectsIn = NumberofUsers; + + for (i=0; i <= NumberofUsers; i++) + { + WriteLen = fwrite(UserRecPtr[i], 1, (int)sizeof (struct UserInfo), Handle); + } + + fclose(Handle); +*/ + return; +} + +VOID GetMessageDatabase() +{ + struct MsgInfo MsgRec; + FILE * Handle; + size_t ReadLen; + struct MsgInfo * Msg; + char * MsgBytes; + int FileRecsize = sizeof(struct MsgInfo); // May be changed if reformating + BOOL Reformatting = FALSE; + char HEX[3] = ""; + int n; + + // See if Message Database is in main config + + group = config_lookup (&cfg, "MSGS"); + +// group = 0; + + if (group) + { + // We have User config in the main config file. so use that + + int index = 0; + char * ptr, * ptr2; + config_setting_t * entry = config_setting_get_elem (group, index++); + + // Initialise a new File + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); + NumberofMessages = 0; + MsgHddrPtr[0]->status = 2; + + if (entry) + { + // First Record has current message number + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + ptr2 = strlop(ptr2, '|'); + if (ptr2) + LatestMsg = atoi(ptr2); + } + + entry = config_setting_get_elem (group, index++); + + while (entry) + { + // entry->name is MsgNo with 'R' in front + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + + memset(&MsgRec, 0, sizeof(struct MsgInfo)); + + MsgRec.number = atoi(&entry->name[1]); + MsgRec.type = ptr[0]; + + ptr = ptr2; + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + ptr2 = strlop(ptr, '|'); + MsgRec.status = ptr[0]; + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.length = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datereceived = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.bbsfrom, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.via, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.from, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.to, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.bid, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.B2Flags = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datecreated = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datechanged = atol(ptr); + + ptr = ptr2; + if (ptr) ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + if (ptr[0]) + { + char String[50] = "00000000000000000000"; + String[20] = 0; + memcpy(String, ptr, strlen(ptr)); + for (n = 0; n < NBMASK; n++) + { + memcpy(HEX, &String[n * 2], 2); + MsgRec.fbbs[n] = (UCHAR)strtol(HEX, 0, 16); + } + } + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + if (ptr[0]) + { + char String[50] = "00000000000000000000"; + String[20] = 0; + memcpy(String, ptr, strlen(ptr)); + for (n = 0; n < NBMASK; n++) + { + memcpy(HEX, &String[n * 2], 2); + MsgRec.forw[n] = (UCHAR)strtol(HEX, 0, 16); + } + } + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.emailfrom, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.UTF8 = atoi(ptr); + + ptr = ptr2; + + if (ptr) + { + strcpy(MsgRec.title, ptr); + + MsgBytes = ReadMessageFileEx(&MsgRec); + + if (MsgBytes) + { + free(MsgBytes); + Msg = AllocateMsgRecord(); + memcpy(Msg, &MsgRec, sizeof (MsgRec)); + + MsgnotoMsg[Msg->number] = Msg; + + // Fix Corrupted NTS Messages + + if (Msg->type == 'N') + Msg->type = 'T'; + + // Look for corrupt FROM address (ending in @) + + strlop(Msg->from, '@'); + + BuildNNTPList(Msg); // Build NNTP Groups list + + // If any forward bits are set, increment count on corresponding BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + } + } + } + entry = config_setting_get_elem (group, index++); + } + + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + + return; + } + + Handle = fopen(MsgDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); + NumberofMessages = 0; + MsgHddrPtr[0]->status = 2; + + return; + } + + // Get First Record + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&MsgRec, 0, sizeof (MsgRec)); + MsgRec.status = 2; + } + + // Set up control record + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= malloc(sizeof (MsgRec)); + memcpy(MsgHddrPtr[0], &MsgRec, sizeof (MsgRec)); + + LatestMsg=MsgHddrPtr[0]->length; + + NumberofMessages = 0; + + if (MsgRec.status == 1) // Used as file format version + // 0 = original, 1 = Extra email from addr, 2 = More BBS's. + { + char Backup1[MAX_PATH]; + + // Create a backup in case reversion is needed and Reposition to first User record + + fclose(Handle); + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".oldformat"); + + CopyFile(MsgDatabasePath, Backup1, FALSE); // Copy to .oldformat + + Handle = fopen(MsgDatabasePath, "rb"); + + FileRecsize = sizeof(struct OldMsgInfo); + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + MsgHddrPtr[0]->status = 2; + } + +Next: + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + if (ReadLen > 0) + { + // Validate Header + + if (FileRecsize == sizeof(struct MsgInfo)) + { + if (MsgRec.type == 0 || MsgRec.number == 0) + goto Next; + + MsgBytes = ReadMessageFileEx(&MsgRec); + + if (MsgBytes) + { + // MsgRec.length = strlen(MsgBytes); + free(MsgBytes); + } + else + goto Next; + + Msg = AllocateMsgRecord(); + + memcpy(Msg, &MsgRec, +sizeof (MsgRec)); + } + else + { + // Resizing - record from file is an OldRecInfo + + struct OldMsgInfo * OldMessage = (struct OldMsgInfo *) &MsgRec; + + if (OldMessage->type == 0) + goto Next; + + if (OldMessage->number > 99999 || OldMessage->number < 1) + goto Next; + + Msg = AllocateMsgRecord(); + + + Msg->B2Flags = OldMessage->B2Flags; + memcpy(Msg->bbsfrom, OldMessage->bbsfrom, 7); + memcpy(Msg->bid, OldMessage->bid, 13); + Msg->datechanged = OldMessage->datechanged; + Msg->datecreated = OldMessage->datecreated; + Msg->datereceived = OldMessage->datereceived; + memcpy(Msg->emailfrom, OldMessage->emailfrom, 41); + memcpy(Msg->fbbs , OldMessage->fbbs, 10); + memcpy(Msg->forw , OldMessage->forw, 10); + memcpy(Msg->from, OldMessage->from, 7); + Msg->length = OldMessage->length; + Msg->nntpnum = OldMessage->nntpnum; + Msg->number = OldMessage->number; + Msg->status = OldMessage->status; + memcpy(Msg->title, OldMessage->title, 61); + memcpy(Msg->to, OldMessage->to, 7); + Msg->type = OldMessage->type; + memcpy(Msg->via, OldMessage->via, 41); + } + + MsgnotoMsg[Msg->number] = Msg; + + // Fix Corrupted NTS Messages + + if (Msg->type == 'N') + Msg->type = 'T'; + + // Look for corrupt FROM address (ending in @) + + strlop(Msg->from, '@'); + + // Move Dates if first run with new format + + if (Msg->datecreated == 0) + Msg->datecreated = Msg->xdatecreated; + + if (Msg->datereceived == 0) + Msg->datereceived = Msg->xdatereceived; + + if (Msg->datechanged == 0) + Msg->datechanged = Msg->xdatechanged; + + BuildNNTPList(Msg); // Build NNTP Groups list + + Msg->Locked = 0; // In case left locked + Msg->Defered = 0; // In case left set. + + // If any forward bits are set, increment count on corresponding BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + } + + goto Next; + } + + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + + fclose(Handle); +} + +VOID CopyMessageDatabase() +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + +// return; + + // Keep 4 Generations + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak"); + + CopyFile(MsgDatabasePath, Backup2, FALSE); // Copy to .bak + +} + +VOID SaveMessageDatabase() +{ + FILE * Handle; + size_t WriteLen; + int i; + char Key[16]; + struct MsgInfo *Msg; +// char CfgName[MAX_PATH]; + char HEXString1[64]; + char HEXString2[64]; + int n; +// char * CfgBuffer; + char Cfg[1024]; +// int CfgLen = 0; +// FILE * hFile; + +// SaveConfig(ConfigName); // Message Headers now in main config +// return; + +#ifdef LINBPQ + RefreshWebMailIndex(); +#else + if (pRefreshWebMailIndex) + pRefreshWebMailIndex(); +#endif + + Handle = fopen(MsgDatabasePath, "wb"); + + if (Handle == NULL) + { + CriticalErrorHandler("Failed to open message database"); + return; + } + + MsgHddrPtr[0]->status = 2; + MsgHddrPtr[0]->number = NumberofMessages; + MsgHddrPtr[0]->length = LatestMsg; + + for (i=0; i <= NumberofMessages; i++) + { + WriteLen = fwrite(MsgHddrPtr[i], 1, sizeof (struct MsgInfo), Handle); + + if (WriteLen != sizeof(struct MsgInfo)) + { + CriticalErrorHandler("Failed to write message database record"); + return; + } + } + + if (fclose(Handle) != 0) + CriticalErrorHandler("Failed to close message database"); + + for (i = 1; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); + + n = 39; + while (n >=0 && HEXString1[n] == '0') + HEXString1[n--] = 0; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); + + n = 39; + while (n >= 0 && HEXString2[n] == '0') + HEXString2[n--] = 0; + + sprintf(Key, "R%d:\r\n", i); + + n = sprintf(Cfg, "%c|%c|%d|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, + Msg->number, Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], + &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, + &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); + } + + return; +} + +VOID GetBIDDatabase() +{ + BIDRec BIDRec; + FILE * Handle; + size_t ReadLen; + BIDRecP BID; + int index = 0; + char * ptr, * ptr2; + + // If BID info is in main config file, use it + + group = config_lookup (&cfg, "BIDS"); + + if (group) + { + config_setting_t * entry = config_setting_get_elem (group, index++); + + BIDRecPtr=malloc(sizeof(void *)); + BIDRecPtr[0]= malloc(sizeof (BIDRec)); + memset(BIDRecPtr[0], 0, sizeof (BIDRec)); + NumberofBIDs = 0; + + while (entry) + { + // entry->name is Bid with 'R' in front + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + + if (ptr && ptr2) + { + BID = AllocateBIDRecord(); + strcpy(BID->BID, &entry->name[1]); + BID->mode = atoi(ptr); + BID->u.timestamp = atoi(ptr2); + + if (BID->u.timestamp == 0) + BID->u.timestamp = LOWORD(time(NULL)/86400); + + } + entry = config_setting_get_elem (group, index++); + } + return; + } + + Handle = fopen(BIDDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + BIDRecPtr=malloc(sizeof(void *)); + BIDRecPtr[0]= malloc(sizeof (BIDRec)); + memset(BIDRecPtr[0], 0, sizeof (BIDRec)); + NumberofBIDs = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&BIDRec, 0, sizeof (BIDRec)); + } + + // Set up control record + + BIDRecPtr = malloc(sizeof(void *)); + BIDRecPtr[0] = malloc(sizeof (BIDRec)); + memcpy(BIDRecPtr[0], &BIDRec, sizeof (BIDRec)); + + NumberofBIDs = 0; + +Next: + + ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); + + if (ReadLen > 0) + { + BID = AllocateBIDRecord(); + memcpy(BID, &BIDRec, sizeof (BIDRec)); + + if (BID->u.timestamp == 0) + BID->u.timestamp = LOWORD(time(NULL)/86400); + + goto Next; + } + + fclose(Handle); +} + +VOID CopyBIDDatabase() +{ + char Backup[MAX_PATH]; + +// return; + + + strcpy(Backup, BIDDatabasePath); + strcat(Backup, ".bak"); + + CopyFile(BIDDatabasePath, Backup, FALSE); +} + +VOID SaveBIDDatabase() +{ + FILE * Handle; + size_t WriteLen; + int i; + +// return; // Bids are now in main config and are saved when message is saved + + Handle = fopen(BIDDatabasePath, "wb"); + + BIDRecPtr[0]->u.msgno = NumberofBIDs; // First Record has file size + + for (i=0; i <= NumberofBIDs; i++) + { + WriteLen = fwrite(BIDRecPtr[i], 1, sizeof (BIDRec), Handle); + } + + fclose(Handle); + + return; +} + +BIDRec * LookupBID(char * BID) +{ + BIDRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofBIDs; i++) + { + ptr = BIDRecPtr[i]; + + if (_stricmp(ptr->BID, BID) == 0) + return ptr; + } + + return NULL; +} + +BIDRec * LookupTempBID(char * BID) +{ + BIDRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofTempBIDs; i++) + { + ptr = TempBIDRecPtr[i]; + + if (_stricmp(ptr->BID, BID) == 0) return ptr; + } + + return NULL; +} + +VOID RemoveTempBIDS(CIRCUIT * conn) +{ + // Remove any Temp BID records for conn. Called when connection closes - Msgs will be complete or failed + + if (NumberofTempBIDs == 0) + return; + else + { + BIDRec * ptr = NULL; + BIDRec ** NewTempBIDRecPtr = zalloc((NumberofTempBIDs+1) * sizeof(void *)); + int i = 0, n; + + GetSemaphore(&AllocSemaphore, 0); + + for (n = 1; n <= NumberofTempBIDs; n++) + { + ptr = TempBIDRecPtr[n]; + + if (ptr) + { + if (ptr->u.conn == conn) + // Remove this entry + free(ptr); + else + NewTempBIDRecPtr[++i] = ptr; + } + } + + NumberofTempBIDs = i; + + free(TempBIDRecPtr); + + TempBIDRecPtr = NewTempBIDRecPtr; + FreeSemaphore(&AllocSemaphore); + } + +} + +VOID GetBadWordFile() +{ + FILE * Handle; + DWORD FileSize; + char * ptr1, * ptr2; + struct stat STAT; + + if (stat(BadWordsPath, &STAT) == -1) + return; + + FileSize = STAT.st_size; + + Handle = fopen(BadWordsPath, "rb"); + + if (Handle == NULL) + return; + + // Release old info in case a re-read + + if (BadWords) free(BadWords); + if (BadFile) free(BadFile); + + BadWords = NULL; + BadFile = NULL; + NumberofBadWords = 0; + + BadFile = malloc(FileSize+1); + + fread(BadFile, 1, FileSize, Handle); + + fclose(Handle); + + BadFile[FileSize]=0; + + _strlwr(BadFile); // Compares are case-insensitive + + ptr1 = BadFile; + + while (ptr1) + { + if (*ptr1 == '\n') ptr1++; + + ptr2 = strtok_s(NULL, "\r\n", &ptr1); + if (ptr2) + { + if (*ptr2 != '#') + { + BadWords = realloc(BadWords,(++NumberofBadWords+1) * sizeof(void *)); + BadWords[NumberofBadWords] = ptr2; + } + } + else + break; + } +} + +BOOL CheckBadWord(char * Word, char * Msg) +{ + char * ptr1 = Msg, * ptr2; + size_t len = strlen(Word); + + while (*ptr1) // Stop at end + { + ptr2 = strstr(ptr1, Word); + + if (ptr2 == NULL) + return FALSE; // OK + + // Only bad if it ia not part of a longer word + + if ((ptr2 == Msg) || !(isalpha(*(ptr2 - 1)))) // No alpha before + if (!(isalpha(*(ptr2 + len)))) // No alpha after + return TRUE; // Bad word + + // Keep searching + + ptr1 = ptr2 + len; + } + + return FALSE; // OK +} + +BOOL CheckBadWords(char * Msg) +{ + char * dupMsg = _strlwr(_strdup(Msg)); + int i; + + for (i = 1; i <= NumberofBadWords; i++) + { + if (CheckBadWord(BadWords[i], dupMsg)) + { + free(dupMsg); + return TRUE; // Bad + } + } + + free(dupMsg); + return FALSE; // OK + +} + +VOID SendWelcomeMsg(int Stream, ConnectionInfo * conn, struct UserInfo * user) +{ + if (user->flags & F_Expert) + ExpandAndSendMessage(conn, ExpertWelcomeMsg, LOG_BBS); + else if (conn->NewUser) + ExpandAndSendMessage(conn, NewWelcomeMsg, LOG_BBS); + else + ExpandAndSendMessage(conn, WelcomeMsg, LOG_BBS); + + if (user->HomeBBS[0] == 0 && !DontNeedHomeBBS) + BBSputs(conn, "Please enter your Home BBS using the Home command.\rYou may also enter your QTH and ZIP/Postcode using qth and zip commands.\r"); + +// if (user->flags & F_Temp_B2_BBS) +// nodeprintf(conn, "%s CMS >\r", BBSName); +// else + SendPrompt(conn, user); +} + +VOID SendPrompt(ConnectionInfo * conn, struct UserInfo * user) +{ + if (user->Temp->ListSuspended) + return; // Dont send prompt if pausing a listing + + if (user->flags & F_Expert) + ExpandAndSendMessage(conn, ExpertPrompt, LOG_BBS); + else if (conn->NewUser) + ExpandAndSendMessage(conn, NewPrompt, LOG_BBS); + else + ExpandAndSendMessage(conn, Prompt, LOG_BBS); + +// if (user->flags & F_Expert) +// nodeprintf(conn, "%s\r", ExpertPrompt); +// else if (conn->NewUser) +// nodeprintf(conn, "%s\r", NewPrompt); +// else +// nodeprintf(conn, "%s\r", Prompt); +} + + + +VOID * _zalloc(size_t len) +{ + // ?? malloc and clear + + void * ptr; + + ptr=malloc(len); + memset(ptr, 0, len); + + return ptr; +} + +BOOL isAMPRMsg(char * Addr) +{ + // See if message is addressed to ampr.org and is either + // for us or we have SendAMPRDirect (ie don't need RMS or SMTP to send it) + + size_t toLen = strlen(Addr); + + if (_memicmp(&Addr[toLen - 8], "ampr.org", 8) == 0) + { + // message is for ampr.org + + char toCall[48]; + char * via; + + strcpy(toCall, _strupr(Addr)); + + via = strlop(toCall, '@'); + + if (_stricmp(via, AMPRDomain) == 0) + { + // message is for us. + + return TRUE; + } + + if (SendAMPRDirect) + { + // We want to send ampr mail direct to host. Queue to BBS AMPR + + if (FindAMPR()) + { + // We have bbs AMPR + + return TRUE; + } + } + } + return FALSE; +} + +struct UserInfo * FindAMPR() +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, "AMPR") == 0) + return bbs; + } + + return NULL; +} + +struct UserInfo * FindRMS() +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, "RMS") == 0) + return bbs; + } + + return NULL; +} + +struct UserInfo * FindBBS(char * Name) +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, Name) == 0) + return bbs; + } + + return NULL; +} + +int CountConnectionsOnPort(int CheckPort) +{ + int n, Count = 0; + CIRCUIT * conn; + int port, sesstype, paclen, maxframe, l4window; + char callsign[11]; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active) + { + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + if (port == CheckPort) + Count++; + } + } + + return Count; +} + +/* +REJECT.SYS (\FBB\SYSTEM). + + This file is in SYSTEM-directory. With this file it is possible to reject or +hold certain types or sizes of messages. + +The first letter of each valid line specifies the action : + +R = Reject : The message will not be received. +H = Hold : The message will be received but held until the sysop reviews. +L = Local Hold : Only messages created on this BBS will be held. + + # File for rejecting messages. They are rejected with N-BID: + # + # Type, from, @BBS, to, BID, maximum size: + # + # * and ? can be used as wildcards (as in MS-DOS) + # + R B TOTO ALL TATA * 0 + R B * * VENTE * 0 + R B * VENTE * * 0 + H * P1RAT * * * 0 + L B * * * * 0 + */ + + +BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type, int Len) +{ + char ** Calls; + FBBFilter * p = Filters; + char ToCopy[256]; + + if (Type == 'B' && FilterWPBulls && _stricmp(To, "WP") == 0) + return TRUE; + + if (RejFrom && From) + { + Calls = RejFrom; + + while(Calls[0]) + { + if (_stricmp(Calls[0], From) == 0) + return TRUE; + + Calls++; + } + } + + if (RejTo && To) + { + Calls = RejTo; + + while(Calls[0]) + { + if (_stricmp(Calls[0], To) == 0) + return TRUE; + + Calls++; + } + } + + if (RejAt && ATBBS) + { + Calls = RejAt; + + while(Calls[0]) + { + if (_stricmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + + if (RejBID && BID) + { + Calls = RejBID; + + while(Calls[0]) + { + if (Calls[0][0] == '*') + { + if (stristr(BID, &Calls[0][1])) + return TRUE; + } + else + { + if (_stricmp(BID, Calls[0]) == 0) + return TRUE; + } + + Calls++; + } + } + + // check fbb reject.sys type filters + + strcpy(ToCopy, To); + _strupr(ToCopy); + + while (p) + { + if (p->Action != 'R') + goto Continue; + + if (p->Type != Type && p->Type != '*') + goto Continue; + + if (wildcardcompare(From, p->From) == 0) + goto Continue; + + if (wildcardcompare(ToCopy, p->TO) == 0) + goto Continue; + + if (ATBBS) + if (wildcardcompare(ATBBS, p->AT) == 0) + goto Continue; + + if (BID) + if (wildcardcompare(BID, p->BID) == 0) + goto Continue; + + if (p->MaxLen && Len < p->MaxLen) + goto Continue; + + return TRUE; // Hold + +Continue: + p = p->Next; + } + + return FALSE; // Ok to accept +} + +BOOL CheckValidCall(char * From) +{ + unsigned int i; + + if (DontCheckFromCall) + return TRUE; + + if (strcmp(From, "SYSOP") == 0 || strcmp(From, "SYSTEM") == 0 || strcmp(From, "SERVIC") == 0 || + strcmp(From, "IMPORT") == 0 || strcmp(From, "SMTP:") == 0 || strcmp(From, "RMS:") == 0) + return TRUE; + + for (i = 1; i < strlen(From); i++) // skip first which may also be digit + { + if (isdigit(From[i])) + { + // Has a digit. Check Last is not digit + + if (isalpha(From[strlen(From) - 1])) + return TRUE; + } + } + + // No digit, return false + + return FALSE; +} + +BOOL wildcardcompare(char * Target, char * Match); + +BOOL CheckHoldFilters(struct MsgInfo * Msg, char * From, char * To, char * ATBBS, char * BID) +{ + char ** Calls; + FBBFilter * p = Filters; + + if (HoldFrom && From) + { + Calls = HoldFrom; + + while(Calls[0]) + { + if (_stricmp(Calls[0], From) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldTo && To) + { + Calls = HoldTo; + + while(Calls[0]) + { + if (_stricmp(Calls[0], To) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldAt && ATBBS) + { + Calls = HoldAt; + + while(Calls[0]) + { + if (_stricmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldBID && BID) + { + Calls = HoldBID; + + while(Calls[0]) + { + if (Calls[0][0] == '*') + { + if (stristr(BID, &Calls[0][1])) + return TRUE; + } + else + { + if (_stricmp(BID, Calls[0]) == 0) + return TRUE; + } + + Calls++; + } + } + + // check fbb reject.sys type filters + + while (p) + { + if (p->Action != 'H') + goto Continue; + + if (p->Type != Msg->type && p->Type != '*') + goto Continue; + + if (wildcardcompare(Msg->from, p->From) == 0) + goto Continue; + + if (wildcardcompare(Msg->to, p->TO) == 0) + goto Continue; + + if (wildcardcompare(Msg->via, p->AT) == 0) + goto Continue; + + if (wildcardcompare(Msg->bid, p->BID) == 0) + goto Continue; + + if (p->MaxLen && Msg->length < p->MaxLen) + goto Continue; + + return TRUE; // Hold + +Continue: + p = p->Next; + } + + return FALSE; // Ok to accept +} + +BOOL CheckifLocalRMSUser(char * FullTo) +{ + struct UserInfo * user = LookupCall(FullTo); + + if (user) + if (user->flags & F_POLLRMS) + return TRUE; + + return FALSE; + +} + + + +int check_fwd_bit(char *mask, int bbsnumber) +{ + if (bbsnumber) + return (mask[(bbsnumber - 1) / 8] & (1 << ((bbsnumber - 1) % 8))); + else + return 0; +} + + +void set_fwd_bit(char *mask, int bbsnumber) +{ + if (bbsnumber) + mask[(bbsnumber - 1) / 8] |= (1 << ((bbsnumber - 1) % 8)); +} + + +void clear_fwd_bit (char *mask, int bbsnumber) +{ + if (bbsnumber) + mask[(bbsnumber - 1) / 8] &= (~(1 << ((bbsnumber - 1) % 8))); +} + +VOID BBSputs(CIRCUIT * conn, char * buf) +{ + // Sends to user and logs + + WriteLogLine(conn, '>',buf, (int)strlen(buf) -1, LOG_BBS); + + QueueMsg(conn, buf, (int)strlen(buf)); +} + +VOID __cdecl nodeprintf(ConnectionInfo * conn, const char * format, ...) +{ + char Mess[1000]; + int len; + va_list(arglist); + + + va_start(arglist, format); + len = vsprintf(Mess, format, arglist); + + QueueMsg(conn, Mess, len); + + WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); + + return; +} + +// nodeprintfEx add a LF if NEEFLF is set + +VOID __cdecl nodeprintfEx(ConnectionInfo * conn, const char * format, ...) +{ + char Mess[1000]; + int len; + va_list(arglist); + + + va_start(arglist, format); + len = vsprintf(Mess, format, arglist); + + QueueMsg(conn, Mess, len); + + WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); + + if (conn->BBSFlags & NEEDLF) + QueueMsg(conn, "\r", 1); + + return; +} + + +int compare( const void *arg1, const void *arg2 ); + +VOID SortBBSChain() +{ + struct UserInfo * user; + struct UserInfo * users[161]; + int i = 0, n; + + // Get array of addresses + + for (user = BBSChain; user; user = user->BBSNext) + { + users[i++] = user; + if (i > 160) break; + } + + qsort((void *)users, i, sizeof(void *), compare ); + + BBSChain = NULL; + + // Rechain (backwards, as entries ate put on front of chain) + + for (n = i-1; n >= 0; n--) + { + users[n]->BBSNext = BBSChain; + BBSChain = users[n]; + } +} + +int compare(const void *arg1, const void *arg2) +{ + // Compare Calls. Fortunately call is at start of stuct + + return _stricmp(*(char**)arg1 , *(char**)arg2); +} + +int CountMessagesTo(struct UserInfo * user, int * Unread) +{ + int i, Msgs = 0; + UCHAR * Call = user->Call; + + *Unread = 0; + + for (i = NumberofMessages; i > 0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (_stricmp(MsgHddrPtr[i]->to, Call) == 0) + { + Msgs++; + if (MsgHddrPtr[i]->status == 'N') + *Unread = *Unread + 1; + } + } + return(Msgs); +} + + + +// Custimised message handling routines. +/* + Variables - a subset of those used by FBB + + $C : Number of the next message. + $I : First name of the connected user. + $L : Number of the latest message. + $N : Number of active messages + $U : Callsign of the connected user. + $W : Inserts a carriage return. + $Z : Last message read by the user (L command). + %X : Number of messages for the user. + %x : Number of new messages for the user. +*/ + +VOID ExpandAndSendMessage(CIRCUIT * conn, char * Msg, int LOG) +{ + char NewMessage[10000]; + char * OldP = Msg; + char * NewP = NewMessage; + char * ptr, * pptr; + size_t len; + char Dollar[] = "$"; + char CR[] = "\r"; + char num[20]; + int Msgs = 0, Unread = 0; + + ptr = strchr(OldP, '$'); + + while (ptr) + { + len = ptr - OldP; // Chars before $ + memcpy(NewP, OldP, len); + NewP += len; + + switch (*++ptr) + { + case 'I': // First name of the connected user. + + pptr = conn->UserPointer->Name; + break; + + case 'L': // Number of the latest message. + + sprintf(num, "%d", LatestMsg); + pptr = num; + break; + + case 'N': // Number of active messages. + + sprintf(num, "%d", NumberofMessages); + pptr = num; + break; + + case 'U': // Callsign of the connected user. + + pptr = conn->UserPointer->Call; + break; + + case 'W': // Inserts a carriage return. + + pptr = CR; + break; + + case 'Z': // Last message read by the user (L command). + + sprintf(num, "%d", conn->UserPointer->lastmsg); + pptr = num; + break; + + case 'X': // Number of messages for the user. + + Msgs = CountMessagesTo(conn->UserPointer, &Unread); + sprintf(num, "%d", Msgs); + pptr = num; + break; + + case 'x': // Number of new messages for the user. + + Msgs = CountMessagesTo(conn->UserPointer, &Unread); + sprintf(num, "%d", Unread); + pptr = num; + break; + + case 'F': // Number of new messages to forward to this BBS. + + Msgs = CountMessagestoForward(conn->UserPointer); + sprintf(num, "%d", Msgs); + pptr = num; + break; + + default: + + pptr = Dollar; // Just Copy $ + } + + len = strlen(pptr); + memcpy(NewP, pptr, len); + NewP += len; + + OldP = ++ptr; + ptr = strchr(OldP, '$'); + } + + strcpy(NewP, OldP); + + len = RemoveLF(NewMessage, (int)strlen(NewMessage)); + + WriteLogLine(conn, '>', NewMessage, (int)len, LOG); + QueueMsg(conn, NewMessage, (int)len); +} + +BOOL isdigits(char * string) +{ + // Returns TRUE id sting is decimal digits + + size_t i, n = strlen(string); + + for (i = 0; i < n; i++) + { + if (isdigit(string[i]) == FALSE) return FALSE; + } + return TRUE; +} + +BOOL wildcardcompare(char * Target, char * Match) +{ + // Do a compare with string *string string* *string* + + // Strings should all be UC + + char Pattern[100]; + char * firststar; + + strcpy(Pattern, Match); + firststar = strchr(Pattern,'*'); + + if (firststar) + { + size_t Len = strlen(Pattern); + + if (Pattern[0] == '*' && Pattern[Len - 1] == '*') // * at start and end + { + Pattern[Len - 1] = 0; + return !(strstr(Target, &Pattern[1]) == NULL); + } + if (Pattern[0] == '*') // * at start + { + // Compare the last len - 1 chars of Target + + size_t Targlen = strlen(Target); + size_t Comparelen = Targlen - (Len - 1); + + if (Len == 1) // Just * + return TRUE; + + if (Comparelen < 0) // Too Short + return FALSE; + + return (memcmp(&Target[Comparelen], &Pattern[1], Len - 1) == 0); + } + + // Must be * at end - compare first Len-1 char + + return (memcmp(Target, Pattern, Len - 1) == 0); + } + + // No WildCards - straight strcmp + return (strcmp(Target, Pattern) == 0); +} + +#ifndef LINBPQ + +PrintMessage(HDC hDC, struct MsgInfo * Msg); + +PrintMessages(HWND hDlg, int Count, int * Indexes) +{ + int i, CurrentMsgIndex; + char MsgnoText[10]; + int Msgno; + struct MsgInfo * Msg; + int Len = MAX_PATH; + BOOL hResult; + PRINTDLG pdx = {0}; + HDC hDC; + +// CHOOSEFONT cf; + LOGFONT lf; + HFONT hFont; + + + // Initialize the PRINTDLG structure. + + pdx.lStructSize = sizeof(PRINTDLG); + pdx.hwndOwner = hWnd; + pdx.hDevMode = NULL; + pdx.hDevNames = NULL; + pdx.hDC = NULL; + pdx.Flags = PD_RETURNDC | PD_COLLATE; + pdx.nMinPage = 1; + pdx.nMaxPage = 1000; + pdx.nCopies = 1; + pdx.hInstance = 0; + pdx.lpPrintTemplateName = NULL; + + // Invoke the Print property sheet. + + hResult = PrintDlg(&pdx); + + memset(&lf, 0, sizeof(LOGFONT)); + + /* + + // Initialize members of the CHOOSEFONT structure. + + cf.lStructSize = sizeof(CHOOSEFONT); + cf.hwndOwner = (HWND)NULL; + cf.hDC = pdx.hDC; + cf.lpLogFont = &lf; + cf.iPointSize = 0; + cf.Flags = CF_PRINTERFONTS | CF_FIXEDPITCHONLY; + cf.rgbColors = RGB(0,0,0); + cf.lCustData = 0L; + cf.lpfnHook = (LPCFHOOKPROC)NULL; + cf.lpTemplateName = (LPSTR)NULL; + cf.hInstance = (HINSTANCE) NULL; + cf.lpszStyle = (LPSTR)NULL; + cf.nFontType = PRINTER_FONTTYPE; + cf.nSizeMin = 0; + cf.nSizeMax = 0; + + // Display the CHOOSEFONT common-dialog box. + + ChooseFont(&cf); + + // Create a logical font based on the user's + // selection and return a handle identifying + // that font. +*/ + + lf.lfHeight = -56; + lf.lfWeight = 600; + lf.lfOutPrecision = 3; + lf.lfClipPrecision = 2; + lf.lfQuality = 1; + lf.lfPitchAndFamily = '1'; + strcpy (lf.lfFaceName, "Courier New"); + + hFont = CreateFontIndirect(&lf); + + if (hResult) + { + // User clicked the Print button, so use the DC and other information returned in the + // PRINTDLG structure to print the document. + + DOCINFO pdi; + + pdi.cbSize = sizeof(DOCINFO); + pdi.lpszDocName = "BBS Message Print"; + pdi.lpszOutput = NULL; + pdi.lpszDatatype = "RAW"; + pdi.fwType = 0; + + hDC = pdx.hDC; + + SelectObject(hDC, hFont); + + StartDoc(hDC, &pdi); + StartPage(hDC); + + for (i = 0; i < Count; i++) + { + SendDlgItemMessage(hDlg, 0, LB_GETTEXT, Indexes[i], (LPARAM)(LPCTSTR)&MsgnoText); + + Msgno = atoi(MsgnoText); + + for (CurrentMsgIndex = 1; CurrentMsgIndex <= NumberofMessages; CurrentMsgIndex++) + { + Msg = MsgHddrPtr[CurrentMsgIndex]; + + if (Msg->number == Msgno) + { + PrintMessage(hDC, Msg); + break; + } + } + } + + EndDoc(hDC); + } + + if (pdx.hDevMode != NULL) + GlobalFree(pdx.hDevMode); + if (pdx.hDevNames != NULL) + GlobalFree(pdx.hDevNames); + + if (pdx.hDC != NULL) + DeleteDC(pdx.hDC); + + return 0; +} + +PrintMessage(HDC hDC, struct MsgInfo * Msg) +{ + int Len = MAX_PATH; + char * MsgBytes; + char * Save; + int Msglen; + + StartPage(hDC); + + Save = MsgBytes = ReadMessageFile(Msg->number); + + Msglen = Msg->length; + + if (MsgBytes) + { + char Hddr[1000]; + char FullTo[100]; + int HRes, VRes; + char * ptr1, * ptr2; + int LineLen; + + RECT Rect; + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + + sprintf(Hddr, "From: %s%s\r\nTo: %s\r\nType/Status: %c%c\r\nDate/Time: %s\r\nBid: %s\r\nTitle: %s\r\n\r\n", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); + + + if (Msg->B2Flags & B2Msg) + { + // Remove B2 Headers (up to the File: Line) + + char * ptr; + ptr = strstr(MsgBytes, "Body:"); + if (ptr) + { + Msglen = atoi(ptr + 5); + ptr = strstr(ptr, "\r\n\r\n"); + } + if (ptr) + MsgBytes = ptr + 4; + } + + HRes = GetDeviceCaps(hDC, HORZRES) - 50; + VRes = GetDeviceCaps(hDC, VERTRES) - 50; + + Rect.top = 50; + Rect.left = 50; + Rect.right = HRes; + Rect.bottom = VRes; + + DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_CALCRECT | DT_WORDBREAK); + DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_WORDBREAK); + + // process message a line at a time. When page is full, output a page break + + ptr1 = MsgBytes; + ptr2 = ptr1; + + while (Msglen-- > 0) + { + if (*ptr1++ == '\r') + { + // Output this line + + // First check if it will fit + + Rect.top = Rect.bottom; + Rect.right = HRes; + Rect.bottom = VRes; + + LineLen = ptr1 - ptr2 - 1; + + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); + + if (Rect.bottom >= VRes) + { + EndPage(hDC); + StartPage(hDC); + + Rect.top = 50; + Rect.bottom = VRes; + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); + } + + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_WORDBREAK); + + if (*(ptr1) == '\n') + { + ptr1++; + Msglen--; + } + + ptr2 = ptr1; + } + } + + free(Save); + + EndPage(hDC); + + } + return 0; +} + +#endif + + +int ImportMessages(CIRCUIT * conn, char * FN, BOOL Nopopup) +{ + char FileName[MAX_PATH] = "Messages.in"; + int Files = 0; + int WriteLen=0; + FILE *in; + CIRCUIT dummyconn; + struct UserInfo User; + int Index = 0; + + char Buffer[100000]; + char *buf = Buffer; + + if (FN[0]) // Name supplled + strcpy(FileName, FN); + + else + { +#ifndef LINBPQ + OPENFILENAME Ofn; + + memset(&Ofn, 0, sizeof(Ofn)); + + Ofn.lStructSize = sizeof(OPENFILENAME); + Ofn.hInstance = hInst; + Ofn.hwndOwner = MainWnd; + Ofn.lpstrFilter = NULL; + Ofn.lpstrFile= FileName; + Ofn.nMaxFile = sizeof(FileName)/ sizeof(*FileName); + Ofn.lpstrFileTitle = NULL; + Ofn.nMaxFileTitle = 0; + Ofn.lpstrInitialDir = BaseDir; + Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + Ofn.lpstrTitle = NULL;//; + + if (!GetOpenFileName(&Ofn)) + return 0; +#endif + } + + in = fopen(FileName, "rb"); + + if (!(in)) + { + char msg[500]; + sprintf_s(msg, sizeof(msg), "Failed to open %s", FileName); + if (conn) + nodeprintf(conn, "%s\r", msg); +#ifdef WIN32 + else + if (Nopopup == FALSE) + MessageBox(NULL, msg, "BPQMailChat", MB_OK); +#endif + return 0; + } + + memset(&dummyconn, 0, sizeof(CIRCUIT)); + memset(&User, 0, sizeof(struct UserInfo)); + + if (conn == 0) + { + conn = &dummyconn; + + dummyconn.UserPointer = &User; // Was SYSOPCall, but I think that is wrong. + strcpy(User.Call, "IMPORT"); + User.flags |= F_EMAIL; + dummyconn.sysop = TRUE; + dummyconn.BBSFlags = BBS; + + strcpy(dummyconn.Callsign, "IMPORT"); + } + + while(fgets(Buffer, 99999, in)) + { + // First line should start SP/SB ?ST? + + char * From = NULL; + char * BID = NULL; + char * ATBBS = NULL; + char seps[] = " \t\r"; + struct MsgInfo * Msg; + char To[100]= ""; + int msglen; + char * Context; + char * Arg1, * Cmd; + +NextMessage: + + From = NULL; + BID = NULL; + ATBBS = NULL; + To[0]= 0; + + Sleep(100); + + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + + if (Buffer[0] == 0) //Blank Line + continue; + + WriteLogLine(conn, '>', Buffer, (int)strlen(Buffer), LOG_BBS); + + if (dummyconn.sysop == 0) + { + nodeprintf(conn, "%s\r", Buffer); + Flush(conn); + } + + Cmd = strtok_s(Buffer, seps, &Context); + + if (Cmd == NULL) + { + fclose(in); + return Files; + } + + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1 == NULL) + { + if (dummyconn.sysop) + Debugprintf("Bad Import Line %s", Buffer); + else + nodeprintf(conn, "Bad Import Line %s\r", Buffer); + + fclose(in); + return Files; + } + + strcpy(To, Arg1); + + if (DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + { + if (CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL)) + { + Msg = conn->TempMsg; + + // SP is Ok, read message; + + ClearQueue(conn); + + fgets(Buffer, 99999, in); + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + if (strlen(Buffer) > 60) + Buffer[60] = 0; + + strcpy(Msg->title, Buffer); + + // Read the lines + + conn->Flags |= GETTINGMESSAGE; + + Buffer[0] = 0; + + fgets(Buffer, 99999, in); + + while ((conn->Flags & GETTINGMESSAGE) && Buffer[0]) + { + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + msglen = (int)strlen(Buffer); + Buffer[msglen++] = 13; + ProcessMsgLine(conn, conn->UserPointer,Buffer, msglen); + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + } + + // Message completed (or off end of file) + + Files ++; + + ClearQueue(conn); + + if (Buffer[0]) + goto NextMessage; // We have read the SP/SB line; + else + { + fclose(in); + return Files; + } + } + else + { + // Create failed + + Flush(conn); + } + } + + // Search for next message + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + + while (Buffer[0]) + { + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + + if (_stricmp(Buffer, "/EX") == 0) + { + // Found end + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + + if (dummyconn.sysop) + ClearQueue(conn); + else + Flush(conn); + + if (Buffer[0]) + goto NextMessage; // We have read the SP/SB line; + } + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + } + } + + fclose(in); + + if (dummyconn.sysop) + ClearQueue(conn); + else + Flush(conn); + + return Files; +} +char * ReadMessageFileEx(struct MsgInfo * MsgRec) +{ + // Sets Message Size from File Size + + int msgno = MsgRec->number; + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes=malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize]=0; + MsgRec->length = FileSize; + + return MsgBytes; +} + +char * ReadMessageFile(int msgno) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes = malloc(FileSize + 100); // A bit of space for alias substitution on B2 + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize]=0; + + return MsgBytes; +} + + +int QueueMsg(ConnectionInfo * conn, char * msg, int len) +{ + // Add Message to queue for this connection + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // Create or extend buffer + + GetSemaphore(&OutputSEM, 0); + + conn->OutputQueue=realloc(conn->OutputQueue, conn->OutputQueueLength + len); + + if (conn->OutputQueue == NULL) + { + // relloc failed - should never happen, but clean up + + CriticalErrorHandler("realloc failed to expand output queue"); + FreeSemaphore(&OutputSEM); + return 0; + } + + memcpy(&conn->OutputQueue[conn->OutputQueueLength], msg, len); + conn->OutputQueueLength += len; + FreeSemaphore(&OutputSEM); + + return len; +} + +void TrytoSend() +{ + // call Flush on any connected streams with queued data + + ConnectionInfo * conn; + struct ConsoleInfo * Cons; + + int n; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active == TRUE) + { + Flush(conn); + + // if an FLARQ mail has been sent see if queues have cleared + + if (conn->BBSFlags & YAPPTX) + { + YAPPSendData(conn); + } + else if (conn->OutputQueue == NULL && (conn->BBSFlags & ARQMAILACK)) + { + int n = TXCount(conn->BPQStream); // All Sent and Acked? + + if (n == 0) + { + struct MsgInfo * Msg = conn->FwdMsg; + + conn->ARQClearCount--; + + if (conn->ARQClearCount <= 0) + { + Logprintf(LOG_BBS, conn, '>', "ARQ Send Complete"); + + // Mark mail as sent, and look for more + + clear_fwd_bit(Msg->fbbs, conn->UserPointer->BBSNumber); + set_fwd_bit(Msg->forw, conn->UserPointer->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(Msg->fbbs, zeros, NBMASK) == 0) + { + Msg->status = 'F'; // Mark as forwarded + Msg->datechanged=time(NULL); + } + + conn->BBSFlags &= ~ARQMAILACK; + conn->UserPointer->ForwardingInfo->MsgCount--; + + SaveMessageDatabase(); + SendARQMail(conn); // See if any more - close if not + } + } + else + conn->ARQClearCount = 10; + } + } + } +#ifndef LINBPQ + for (Cons = ConsHeader[0]; Cons; Cons = Cons->next) + { + if (Cons->Console) + Flush(Cons->Console); + } +#endif +} + + +void Flush(CIRCUIT * conn) +{ + int tosend, len, sent; + + // Try to send data to user. May be stopped by user paging or node flow control + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // BOOL Paging; // Set if user wants paging + // int LinesSent; // Count when paging + // int PageLen; // Lines per page + + + if (conn->OutputQueue == NULL) + { + // Nothing to send. If Close after Flush is set, disconnect + + if (conn->CloseAfterFlush) + { + conn->CloseAfterFlush--; + + if (conn->CloseAfterFlush) + return; + + Disconnect(conn->BPQStream); + conn->ErrorCount = 0; + } + + return; // Nothing to send + } + tosend = conn->OutputQueueLength - conn->OutputGetPointer; + + sent=0; + + while (tosend > 0) + { + if (TXCount(conn->BPQStream) > 15) + return; // Busy + + if (conn->BBSFlags & SYSOPCHAT) // Suspend queued output while sysop chatting + return; + + if (conn->Paging && (conn->LinesSent >= conn->PageLen)) + return; + + if (tosend <= conn->paclen) + len=tosend; + else + len=conn->paclen; + + GetSemaphore(&OutputSEM, 0); + + if (conn->Paging) + { + // look for CR chars in message to send. Increment LinesSent, and stop if at limit + + UCHAR * ptr1 = &conn->OutputQueue[conn->OutputGetPointer]; + UCHAR * ptr2; + int lenleft = len; + + ptr2 = memchr(ptr1, 0x0d, len); + + while (ptr2) + { + conn->LinesSent++; + ptr2++; + lenleft = len - (int)(ptr2 - ptr1); + + if (conn->LinesSent >= conn->PageLen) + { + len = (int)(ptr2 - &conn->OutputQueue[conn->OutputGetPointer]); + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + conn->OutputGetPointer+=len; + tosend-=len; + SendUnbuffered(conn->BPQStream, "
bort, Continue..>", 25); + FreeSemaphore(&OutputSEM); + return; + + } + ptr2 = memchr(ptr2, 0x0d, lenleft); + } + } + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + + conn->OutputGetPointer+=len; + + FreeSemaphore(&OutputSEM); + + tosend-=len; + sent++; + + if (sent > 15) + return; + } + + // All Sent. Free buffers and reset pointers + + conn->LinesSent = 0; + + ClearQueue(conn); +} + +VOID ClearQueue(ConnectionInfo * conn) +{ + if (conn->OutputQueue == NULL) + return; + + GetSemaphore(&OutputSEM, 0); + + free(conn->OutputQueue); + + conn->OutputQueue=NULL; + conn->OutputGetPointer=0; + conn->OutputQueueLength=0; + + FreeSemaphore(&OutputSEM); +} + + + +VOID FlagAsKilled(struct MsgInfo * Msg, BOOL SaveDB) +{ + struct UserInfo * user; + + Msg->status='K'; + Msg->datechanged=time(NULL); + + // Remove any forwarding references + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + for (user = BBSChain; user; user = user->BBSNext) + { + if (check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + user->ForwardingInfo->MsgCount--; + clear_fwd_bit(Msg->fbbs, user->BBSNumber); + } + } + } + if (SaveDB) + SaveMessageDatabase(); + RebuildNNTPList(); +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(Msg); +#endif + +} + +void DoDeliveredCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + struct MsgInfo * Msg; + + while (Arg1) + { + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + goto Next; + } + + if (Msg->type != 'T') + { + nodeprintf(conn, "Message %d not an NTS Message\r", msgno); + goto Next; + } + + if (Msg->status == 'N') + nodeprintf(conn, "Warning - Message has status N\r"); + + Msg->status = 'D'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + + nodeprintf(conn, "Message #%d Flagged as Delivered\r", msgno); + Next: + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; +} + +void DoUnholdCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + // Param is either ALL or a list of numbers + + if (Arg1 == NULL) + { + nodeprintf(conn, "No message number\r"); + return; + } + + if (_stricmp(Arg1, "ALL") == 0) + { + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if (Msg->status == 'H') + { + if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + else + Msg->status = 'N'; + + nodeprintf(conn, "Message #%d Unheld\r", Msg->number); + } + } + return; + } + + while (Arg1) + { + msgno = atoi(Arg1); + Msg = GetMsgFromNumber(msgno); + + if (Msg) + { + if (Msg->status == 'H') + { + if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + else + Msg->status = 'N'; + + nodeprintf(conn, "Message #%d Unheld\r", msgno); + } + else + { + nodeprintf(conn, "Message #%d was not held\r", msgno); + } + } + else + nodeprintf(conn, "Message #%d not found\r", msgno); + + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; +} + +void DoKillCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + switch (toupper(Cmd[1])) + { + + case 0: // Just K + + while (Arg1) + { + msgno = atoi(Arg1); + KillMessage(conn, user, msgno); + + Arg1 = strtok_s(NULL, " \r", &Context); + } + + SaveMessageDatabase(); + return; + + case 'M': // Kill Mine + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + { + if (Msg->type == 'P' && Msg->status == 'Y') + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", Msg->number); + } + } + } + + SaveMessageDatabase(); + return; + + case 'H': // Kill Held + + if (conn->sysop) + { + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if (Msg->status == 'H') + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", Msg->number); + } + } + } + SaveMessageDatabase(); + return; + + case '>': // K> - Kill to + + if (conn->sysop) + { + if (Arg1) + if (KillMessagesTo(conn, user, Arg1) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + } + + case '<': + + if (conn->sysop) + { + if (Arg1) + if (KillMessagesFrom(conn, user, Arg1) == 0); + BBSputs(conn, "No Messages found\r"); + + return; + } + } + + nodeprintf(conn, "*** Error: Invalid Kill option %c\r", Cmd[1]); + + return; + +} + +int KillMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call) +{ + int i, Msgs = 0; + struct MsgInfo * Msg; + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + if (Msg->status != 'K' && _stricmp(Msg->to, Call) == 0) + { + Msgs++; + KillMessage(conn, user, MsgHddrPtr[i]->number); + } + } + + SaveMessageDatabase(); + return(Msgs); +} + +int KillMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call) +{ + int i, Msgs = 0; + struct MsgInfo * Msg; + + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + if (Msg->status != 'K' && _stricmp(Msg->from, Call) == 0) + { + Msgs++; + KillMessage(conn, user, MsgHddrPtr[i]->number); + } + } + + SaveMessageDatabase(); + return(Msgs); +} + +BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg) +{ + if (SYSOP || (Msg->type == 'T' && UserCantKillT == FALSE)) + return TRUE; + + if (Msg->type == 'P') + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return TRUE; + + if (Msg->type == 'B') + if (_stricmp(Msg->from, Call) == 0) + return TRUE; + + return FALSE; +} + +void KillMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) +{ + struct MsgInfo * Msg; + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL || Msg->status == 'K') + { + nodeprintf(conn, "Message %d not found\r", msgno); + return; + } + + if (OkToKillMessage(conn->sysop, user->Call, Msg)) + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", msgno); + } + else + nodeprintf(conn, "Not your message\r"); +} + + +BOOL ListMessage(struct MsgInfo * Msg, ConnectionInfo * conn, struct TempUserInfo * Temp) +{ + char FullFrom[80]; + char FullTo[80]; + + strcpy(FullFrom, Msg->from); + + if ((_stricmp(Msg->from, "RMS:") == 0) || (_stricmp(Msg->from, "SMTP:") == 0) || + Temp->SendFullFrom || (_stricmp(Msg->emailfrom, "@winlink.org") == 0)) + strcat(FullFrom, Msg->emailfrom); + + if (_stricmp(Msg->to, "RMS") == 0) + { + sprintf(FullTo, "RMS:%s", Msg->via); + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); + } + else + + if (Msg->to[0] == 0 && Msg->via[0] != 0) + { + sprintf(FullTo, "smtp:%s", Msg->via); + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); + } + + else + if (Msg->via[0] != 0) + { + char Via[80]; + strcpy(Via, Msg->via); + strlop(Via, '.'); // Only show first part of via + nodeprintf(conn, "%-6d %s %c%c %5d %-7s@%-6s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, Via, FullFrom, Msg->title); + } + else + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, FullFrom, Msg->title); + + // if paging, stop two before page lengh. This lets us send the continue prompt, save status + // and exit without triggering the system paging code. We can then read a message then resume listing + + if (Temp->ListActive && conn->Paging) + { + Temp->LinesSent++; + + if ((Temp->LinesSent + 1) >= conn->PageLen) + { + nodeprintf(conn, "bort, , = Continue..>"); + Temp->LastListedInPagedMode = Msg->number; + Temp->ListSuspended = TRUE; + return TRUE; + } + } + + return FALSE; +} + +void DoListCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, BOOL Resuming, char * Context) +{ + struct TempUserInfo * Temp = user->Temp; + struct MsgInfo * Msg; + + // Allow compound selection, eg LTN or LFP + + // types P N T + // Options LL LR L< L> L@ LM LC (L* used internally for just L, ie List New + // Status N Y H F K D + + // Allowing options in any order complicates paging. May be best to parse options once and restore if paging. + + Temp->ListActive = TRUE; + Temp->LinesSent = 0; + + if (Resuming) + { + // Entered after a paging pause. Selection fields are already set up + + // We have reentered list command after a pause. The next message to list is in Temp->LastListedInPagedMode + +// Start = Temp->LastListedInPagedMode; + Temp->ListSuspended = FALSE; + } + else + { + Temp->ListRangeEnd = LatestMsg; + Temp->ListRangeStart = 1; + Temp->LLCount = 0; + Temp->SendFullFrom = 0; + Temp->ListType = 0; + Temp->ListStatus = 0; + Temp->ListSelector = 0; + Temp->UpdateLatest = 0; + Temp->LastListParams[0] = 0; + Temp->IncludeKilled = 1; // SYSOP include Killed except LM + + //Analyse L params. + + _strupr(Cmd); + + if (strcmp(Cmd, "LC") == 0) // List Bull Categories + { + ListCategories(conn); + return; + } + + // if command is just L or LR start from last listed + + if (Arg1 == NULL) + { + if (strcmp(Cmd, "L") == 0 || strcmp(Cmd, "LR") == 0) + { + if (LatestMsg == conn->lastmsg) + { + BBSputs(conn, "No New Messages\r"); + return; + } + + Temp->UpdateLatest = 1; + Temp->ListRangeStart = conn->lastmsg; + } + } + + if (strchr(Cmd, 'V')) // Verbose + Temp->SendFullFrom = 'V'; + + if (strchr(Cmd, 'R')) + Temp->ListDirn = 'R'; + else + Temp->ListDirn = '*'; // Default newest first + + Cmd++; // skip L + + if (strchr(Cmd, 'T')) + Temp->ListType = 'T'; + else if (strchr(Cmd, 'P')) + Temp->ListType = 'P'; + else if (strchr(Cmd, 'B')) + Temp->ListType = 'B'; + + if (strchr(Cmd, 'N')) + Temp->ListStatus = 'N'; + else if (strchr(Cmd, 'Y')) + Temp->ListStatus = 'Y'; + else if (strchr(Cmd, 'F')) + Temp->ListStatus = 'F'; + else if (strchr(Cmd, '$')) + Temp->ListStatus = '$'; + else if (strchr(Cmd, 'H')) + Temp->ListStatus = 'H'; + else if (strchr(Cmd, 'K')) + Temp->ListStatus = 'K'; + else if (strchr(Cmd, 'D')) + Temp->ListStatus = 'D'; + + // H or K only by Sysop + + switch (Temp->ListStatus) + { + case 'K': + case 'H': // List Status + + if (conn->sysop) + break; + + BBSputs(conn, "LH or LK can only be used by SYSOP\r"); + return; + } + + if (strchr(Cmd, '<')) + Temp->ListSelector = '<'; + else if (strchr(Cmd, '>')) + Temp->ListSelector = '>'; + else if (strchr(Cmd, '@')) + Temp->ListSelector = '@'; + else if (strchr(Cmd, 'M')) + { + Temp->ListSelector = 'M'; + Temp->IncludeKilled = FALSE; + } + + // Param could be single number, number range or call + + if (Arg1) + { + if (strchr(Cmd, 'L')) // List Last + { + // Param is number + + if (Arg1) + Temp->LLCount = atoi(Arg1); + } + else + { + // Range nnn-nnn or single value or callsign + + char * Arg2, * Arg3, * Range; + char seps[] = " \t\r"; + UINT From=LatestMsg, To=0; + + Arg2 = strtok_s(NULL, seps, &Context); + Arg3 = strtok_s(NULL, seps, &Context); + + if (Temp->ListSelector && Temp->ListSelector != 'M') + { + // < > or @ - first param is callsign + + strcpy(Temp->LastListParams, Arg1); + + // Just possible number range + + Arg1 = Arg2; + Arg2 = Arg3; + Arg3 = strtok_s(NULL, seps, &Context); + } + + if (Arg1) + { + Range = strchr(Arg1, '-'); + + // A number could be a Numeric Bull Dest (eg 44) + // I think this can only resaonably be > + + if (isdigits(Arg1)) + To = From = atoi(Arg1); + + if (Arg2) + From = atoi(Arg2); + else + { + if (Range) + { + Arg3 = strlop(Arg1, '-'); + + To = atoi(Arg1); + + if (Arg3 && Arg3[0]) + From = atoi(Arg3); + else + From = LatestMsg; + } + } + if (From > 100000 || To > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + Temp->ListRangeStart = To; + Temp->ListRangeEnd = From; + } + } + } + } + + // Run through all messages (either forwards or backwards) and list any that match all selection criteria + + while (1) + { + if (Temp->ListDirn == 'R') + Msg = GetMsgFromNumber(Temp->ListRangeStart); + else + Msg = GetMsgFromNumber(Temp->ListRangeEnd); + + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop, Temp->IncludeKilled)) // Check if user is allowed to list this message + { + // Check filters + + if (Temp->ListStatus && Temp->ListStatus != Msg->status) + goto skip; + + if (Temp->ListType && Temp->ListType != Msg->type) + goto skip; + + if (Temp->ListSelector == '<') + if (_stricmp(Msg->from, Temp->LastListParams) != 0) + goto skip; + + if (Temp->ListSelector == '>') + if (_stricmp(Msg->to, Temp->LastListParams) != 0) + goto skip; + + if (Temp->ListSelector == '@') + if (_memicmp(Msg->via, Temp->LastListParams, strlen(Temp->LastListParams)) != 0 && + (_stricmp(Temp->LastListParams, "SMTP:") != 0 || Msg->to[0] != 0)) + goto skip; + + if (Temp->ListSelector == 'M') + if (_stricmp(Msg->to, user->Call) != 0 && + (_stricmp(Msg->to, "SYSOP") != 0 || ((user->flags & F_SYSOP_IN_LM) == 0))) + + goto skip; + + if (ListMessage(Msg, conn, Temp)) + { + if (Temp->ListDirn == 'R') + Temp->ListRangeStart++; + else + Temp->ListRangeEnd--; + + return; // Hit page limit + } + + if (Temp->LLCount) + { + Temp->LLCount--; + if (Temp->LLCount == 0) + return; // LL count reached + } +skip:; + } + + if (Temp->ListRangeStart == Temp->ListRangeEnd) + { + // if using L or LR (list new) update last listed field + + if (Temp->UpdateLatest) + conn->lastmsg = LatestMsg; + + return; + } + + if (Temp->ListDirn == 'R') + Temp->ListRangeStart++; + else + Temp->ListRangeEnd--; + + if (Temp->ListRangeStart > 100000 || Temp->ListRangeEnd < 0) // Loop protection! + return; + + } + +/* + + switch (Cmd[0]) + { + + case '*': // Just L + case 'R': // LR = List Reverse + + if (Arg1) + { + // Range nnn-nnn or single value + + char * Arg2, * Arg3; + char * Context; + char seps[] = " -\t\r"; + UINT From=LatestMsg, To=0; + char * Range = strchr(Arg1, '-'); + + Arg2 = strtok_s(Arg1, seps, &Context); + Arg3 = strtok_s(NULL, seps, &Context); + + if (Arg2) + To = From = atoi(Arg2); + + if (Arg3) + From = atoi(Arg3); + else + if (Range) + From = LatestMsg; + + if (From > 100000 || To > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + if (Cmd[1] == 'R') + { + if (Start) + To = Start + 1; + + ListMessagesInRangeForwards(conn, user, user->Call, From, To, Temp->SendFullFrom); + } + else + { + if (Start) + From = Start - 1; + + ListMessagesInRange(conn, user, user->Call, From, To, Temp->SendFullFrom); + } + } + else + + if (LatestMsg == conn->lastmsg) + BBSputs(conn, "No New Messages\r"); + else if (Cmd[1] == 'R') + ListMessagesInRangeForwards(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); + else + ListMessagesInRange(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); + + conn->lastmsg = LatestMsg; + + return; + + + case 'L': // List Last + + if (Arg1) + { + int i = atoi(Arg1); + int m = NumberofMessages; + + if (Resuming) + i = Temp->LLCount; + else + Temp->LLCount = i; + + for (; i>0 && m != 0; i--) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + i++; + continue; + } + + Temp->LLCount--; + + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + m--; + } + } + } + return; + + case 'M': // LM - List Mine + + if (ListMessagesTo(conn, user, user->Call, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + return; + + case '>': // L> - List to + + if (Arg1) + if (ListMessagesTo(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + + return; + + case '<': + + if (Arg1) + if (ListMessagesFrom(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + + case '@': + + if (Arg1) + if (ListMessagesAT(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + + case 'N': + case 'Y': + case 'F': + case '$': + case 'D': // Delivered NTS Traffic can be listed by anyone + { + int m = NumberofMessages; + + while (m > 0) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + continue; + } + + if (Temp->ListType) + { + if (MsgHddrPtr[m]->status == Cmd[1] && MsgHddrPtr[m]->type == Temp->ListType) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + } + else + { + if (MsgHddrPtr[m]->status == toupper(Cmd[1])) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + } + m--; + } + } + } + return; + + case 'K': + case 'H': // List Status + + if (conn->sysop) + { + int i, Msgs = Start; + + for (i=NumberofMessages; i>0; i--) + { + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (MsgHddrPtr[i]->status == toupper(Cmd[1])) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, SendFullFrom)) + return; // Hit page limit + + } + } + + if (Msgs == 0) + BBSputs(conn, "No Messages found\r"); + } + else + BBSputs(conn, "LH or LK can only be used by SYSOP\r"); + + return; + + case 'C': + { + struct NNTPRec * ptr = FirstNNTPRec; + char Cat[100]; + char NextCat[100]; + int Line = 0; + int Count; + + while (ptr) + { + // if the next name is the same, combine counts + + strcpy(Cat, ptr->NewsGroup); + strlop(Cat, '.'); + Count = ptr->Count; + Catloop: + if (ptr->Next) + { + strcpy(NextCat, ptr->Next->NewsGroup); + strlop(NextCat, '.'); + if (strcmp(Cat, NextCat) == 0) + { + ptr = ptr->Next; + Count += ptr->Count; + goto Catloop; + } + } + + nodeprintf(conn, "%-6s %-3d", Cat, Count); + Line += 10; + if (Line > 80) + { + Line = 0; + nodeprintf(conn, "\r"); + } + + ptr = ptr->Next; + } + + if (Line) + nodeprintf(conn, "\r\r"); + else + nodeprintf(conn, "\r"); + + return; + } + } + + // Could be P B or T if specified without a status + + switch (Temp->ListType) + { + case 'P': + case 'B': + case 'T': // NTS Traffic can be listed by anyone + { + int m = NumberofMessages; + + while (m > 0) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + continue; + } + + if (MsgHddrPtr[m]->type == Temp->ListType) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + m--; + } + } + + return; + } + } + +*/ + nodeprintf(conn, "*** Error: Invalid List option %c\r", Cmd[1]); + +} + +void ListCategories(ConnectionInfo * conn) +{ + // list bull categories + struct NNTPRec * ptr = FirstNNTPRec; + char Cat[100]; + char NextCat[100]; + int Line = 0; + int Count; + + while (ptr) + { + // if the next name is the same, combine counts + + strcpy(Cat, ptr->NewsGroup); + strlop(Cat, '.'); + Count = ptr->Count; +Catloop: + if (ptr->Next) + { + strcpy(NextCat, ptr->Next->NewsGroup); + strlop(NextCat, '.'); + if (strcmp(Cat, NextCat) == 0) + { + ptr = ptr->Next; + Count += ptr->Count; + goto Catloop; + } + } + + nodeprintf(conn, "%-6s %-3d", Cat, Count); + Line += 10; + if (Line > 80) + { + Line = 0; + nodeprintf(conn, "\r"); + } + + ptr = ptr->Next; + } + + if (Line) + nodeprintf(conn, "\r\r"); + else + nodeprintf(conn, "\r"); + + return; +} + +/* +int ListMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) +{ + int i, Msgs = Start; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if ((_stricmp(MsgHddrPtr[i]->to, Call) == 0) || + ((conn->sysop) && _stricmp(Call, SYSOPCall) == 0 && + _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0 && (user->flags & F_SYSOP_IN_LM))) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + break; // Hit page limit + } + } + + return(Msgs); +} + +int ListMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) +{ + int i, Msgs = 0; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (_stricmp(MsgHddrPtr[i]->from, Call) == 0) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + return Msgs; // Hit page limit + + } + } + + return(Msgs); +} + +int ListMessagesAT(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom,int Start) +{ + int i, Msgs = 0; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (_memicmp(MsgHddrPtr[i]->via, Call, strlen(Call)) == 0 || + (_stricmp(Call, "SMTP:") == 0 && MsgHddrPtr[i]->to[0] == 0)) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + break; // Hit page limit + } + } + + return(Msgs); +} +*/ +int GetUserMsg(int m, char * Call, BOOL SYSOP) +{ + struct MsgInfo * Msg; + + // Get Next (usually backwards) message which should be shown to this user + // ie Not Deleted, and not Private unless to or from Call + + do + { + Msg=MsgHddrPtr[m]; + + if (SYSOP) return m; // Sysop can list or read anything + + if (Msg->status != 'K') + { + + if (Msg->status != 'H') + { + if (Msg->type == 'B' || Msg->type == 'T') return m; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return m; + } + } + } + + m--; + + } while (m> 0); + + return 0; +} + + +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled) +{ + // Return TRUE if user is allowed to read message + + if (Msg->status == 'K' && IncludeKilled == 0) + return FALSE; + + if (SYSOP) + return TRUE; // Sysop can list or read anything + + if ((Msg->status != 'K') && (Msg->status != 'H')) + { + if (Msg->type == 'B' || Msg->type == 'T') return TRUE; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return TRUE; + } + } + + return FALSE; +} +/* +int GetUserMsgForwards(int m, char * Call, BOOL SYSOP) +{ + struct MsgInfo * Msg; + + // Get Next (usually backwards) message which should be shown to this user + // ie Not Deleted, and not Private unless to or from Call + + do + { + Msg=MsgHddrPtr[m]; + + if (Msg->status != 'K') + { + if (SYSOP) return m; // Sysop can list or read anything + + if (Msg->status != 'H') + { + if (Msg->type == 'B' || Msg->type == 'T') return m; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return m; + } + } + } + + m++; + + } while (m <= NumberofMessages); + + return 0; + +} + + +void ListMessagesInRange(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom) +{ + int m; + struct MsgInfo * Msg; + + for (m = Start; m >= End; m--) + { + Msg = GetMsgFromNumber(m); + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) + if (ListMessage(Msg, conn, Temp->SendFullFrom)) + return; // Hit page limit + + } +} + + +void ListMessagesInRangeForwards(ConnectionInfo * conn, struct UserInfo * user, char * Call, int End, int Start, BOOL SendFullFrom) +{ + int m; + struct MsgInfo * Msg; + + for (m = Start; m <= End; m++) + { + Msg = GetMsgFromNumber(m); + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) + if (ListMessage(Msg, conn, Temp->SendFullFrom)) + return; // Hit page limit + } +} +*/ + +void DoReadCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + + switch (toupper(Cmd[1])) + { + case 0: // Just R + + while (Arg1) + { + msgno = atoi(Arg1); + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + ReadMessage(conn, user, msgno); + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; + + case 'M': // Read Mine (Unread Messages) + + if (toupper(Cmd[2]) == 'R') + { + for (i = 1; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + if (Msg->status == 'N') + ReadMessage(conn, user, Msg->number); + } + } + else + { + for (i = NumberofMessages; i > 0; i--) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + if (Msg->status == 'N') + ReadMessage(conn, user, Msg->number); + } + } + + return; + } + + nodeprintf(conn, "*** Error: Invalid Read option %c\r", Cmd[1]); + + return; +} + +int RemoveLF(char * Message, int len) +{ + // Remove lf chars and nulls + + char * ptr1, * ptr2; + + ptr1 = ptr2 = Message; + + while (len-- > 0) + { + while (*ptr1 == 0 && len) + { + ptr1++; + len--; + } + + *ptr2 = *ptr1; + + if (*ptr1 == '\r') + if (*(ptr1+1) == '\n') + { + ptr1++; + len--; + } + ptr1++; + ptr2++; + } + + return (int)(ptr2 - Message); +} + + + +int RemoveNulls(char * Message, int len) +{ + // Remove nulls + + char * ptr1, * ptr2; + + ptr1 = ptr2 = Message; + + while (len-- > 0) + { + while (*ptr1 == 0 && len) + { + ptr1++; + len--; + } + + *ptr2 = *ptr1; + + ptr1++; + ptr2++; + } + + return (int)(ptr2 - Message); +} + +void ReadMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) +{ + struct MsgInfo * Msg; + char * MsgBytes, * Save; + char FullTo[100]; + int Index = 0; + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return; + } + + if (!CheckUserMsg(Msg, user->Call, conn->sysop, TRUE)) + { + nodeprintf(conn, "Message %d not for you\r", msgno); + return; + } + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + + nodeprintf(conn, "From: %s%s\rTo: %s\rType/Status: %c%c\rDate/Time: %s\rBid: %s\rTitle: %s\r\r", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); + + MsgBytes = Save = ReadMessageFile(msgno); + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else if (Msg->type == 'T') + Index = TMSG; + + if (MsgBytes) + { + int Length = Msg->length; + + if (Msg->B2Flags & B2Msg) + { + char * ptr; + + // if message has attachments, display them if plain text + + if (Msg->B2Flags & Attachments) + { + char * FileName[100]; + int FileLen[100]; + int Files = 0; + int BodyLen, NewLen; + int i; + char *ptr2; + char Msg[512]; + int Len; + + ptr = MsgBytes; + + Len = sprintf(Msg, "Message has Attachments\r\r"); + QueueMsg(conn, Msg, Len); + + while(*ptr != 13) + { + ptr2 = strchr(ptr, 10); // Find CR + + if (memcmp(ptr, "Body: ", 6) == 0) + { + BodyLen = atoi(&ptr[6]); + } + + if (memcmp(ptr, "File: ", 6) == 0) + { + char * ptr1 = strchr(&ptr[6], ' '); // Find Space + + FileLen[Files] = atoi(&ptr[6]); + + FileName[Files++] = &ptr1[1]; + *(ptr2 - 1) = 0; + } + + ptr = ptr2; + ptr++; + } + + ptr += 2; // Over Blank Line and Separator + + NewLen = RemoveLF(ptr, BodyLen); + + QueueMsg(conn, ptr, NewLen); // Display Body + + ptr += BodyLen + 2; // to first file + + for (i = 0; i < Files; i++) + { + char Msg[512]; + int Len, n; + char * p = ptr; + char c; + + // Check if message is probably binary + + int BinCount = 0; + + NewLen = RemoveLF(ptr, FileLen[i]); // Removes LF agter CR but not on its own + + for (n = 0; n < NewLen; n++) + { + c = *p; + + if (c == 10) + *p = 13; + + if (c==0 || (c & 128)) + BinCount++; + + p++; + + } + + if (BinCount > NewLen/10) + { + // File is probably Binary + + Len = sprintf(Msg, "\rAttachment %s is a binary file\r", FileName[i]); + QueueMsg(conn, Msg, Len); + } + else + { + Len = sprintf(Msg, "\rAttachment %s\r\r", FileName[i]); + QueueMsg(conn, Msg, Len); + + user->Total.MsgsSent[Index] ++; + user->Total.BytesForwardedOut[Index] += NewLen; + + QueueMsg(conn, ptr, NewLen); + } + + ptr += FileLen[i]; + ptr +=2; // Over separator + } + goto sendEOM; + } + + // Remove B2 Headers (up to the File: Line) + + ptr = strstr(MsgBytes, "Body:"); + + if (ptr) + { + MsgBytes = ptr; + Length = (int)strlen(ptr); + } + } + + // Remove lf chars + + Length = RemoveLF(MsgBytes, Length); + + user->Total.MsgsSent[Index] ++; + user->Total.BytesForwardedOut[Index] += Length; + + QueueMsg(conn, MsgBytes, Length); + +sendEOM: + + free(Save); + + nodeprintf(conn, "\r\r[End of Message #%d from %s%s]\r", msgno, Msg->from, Msg->emailfrom); + + if ((_stricmp(Msg->to, user->Call) == 0) || ((conn->sysop) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + SendMessageReadEvent(user->Call, Msg); +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(Msg); +#endif + } + } + } + } + else + { + nodeprintf(conn, "File for Message %d not found\r", msgno); + } +} + struct MsgInfo * FindMessage(char * Call, int msgno, BOOL sysop) + { + int m=NumberofMessages; + + struct MsgInfo * Msg; + + do + { + m = GetUserMsg(m, Call, sysop); + + if (m == 0) + return NULL; + + Msg=MsgHddrPtr[m]; + + if (Msg->number == msgno) + return Msg; + + m--; + + } while (m> 0); + + return NULL; + +} + + +char * ReadInfoFile(char * File) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + char * ptr1 = 0, * ptr2; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s", BaseDir, File); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + 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++; + } + + return MsgBytes; +} + +char * FormatDateAndTime(time_t Datim, BOOL DateOnly) +{ + struct tm *tm; + static char Date[]="xx-xxx hh:mmZ"; + + tm = gmtime(&Datim); + + if (tm) + sprintf_s(Date, sizeof(Date), "%02d-%3s %02d:%02dZ", + tm->tm_mday, month[tm->tm_mon], tm->tm_hour, tm->tm_min); + + if (DateOnly) + { + Date[6]=0; + return Date; + } + + return Date; +} + +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char * To, char ** ATBBS, char ** BID); + + +BOOL DoSendCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY + + char * From = NULL; + char * BID = NULL; + char * ATBBS = NULL; + char seps[] = " \t\r"; + struct MsgInfo * OldMsg; + char OldTitle[62]; + char NewTitle[62]; + char To[100]= ""; + int msgno; + + if (Cmd[1] == 0) Cmd[1] ='P'; // Just S means SP + + switch (toupper(Cmd[1])) + { + case 'B': + + if (RefuseBulls) + { + nodeprintf(conn, "*** Error: This system doesn't allow sending Bulls\r"); + return FALSE; + } + + if (user->flags & F_NOBULLS) + { + nodeprintf(conn, "*** Error: You are not allowed to send Bulls\r"); + return FALSE; + } + + + case 'P': + case 'T': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); + return FALSE; + } + + strcpy(To, Arg1); + + if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + return FALSE; + + return CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL); + + case 'R': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: Message Number is missing\r"); + return FALSE; + } + + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return FALSE; + } + + OldMsg = FindMessage(user->Call, msgno, conn->sysop); + + if (OldMsg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return FALSE; + } + + Arg1=&OldMsg->from[0]; + + strcpy(To, Arg1); + + if (_stricmp(Arg1, "SMTP:") == 0 || _stricmp(Arg1, "RMS:") == 0 || OldMsg->emailfrom) + { + // SMTP message. Need to get the real sender from the message + + sprintf(To, "%s%s", Arg1, OldMsg->emailfrom); + } + + if (!DecodeSendParams(conn, "", &From, To, &ATBBS, &BID)) + return FALSE; + + strcpy(OldTitle, OldMsg->title); + + if (strlen(OldTitle) > 57) OldTitle[57] = 0; + + strcpy(NewTitle, "Re:"); + strcat(NewTitle, OldTitle); + + return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); + + return TRUE; + + case 'C': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: Message Number is missing\r"); + return FALSE; + } + + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return FALSE; + } + + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); + return FALSE; + } + + strcpy(To, Arg1); + + if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + return FALSE; + + OldMsg = FindMessage(user->Call, msgno, conn->sysop); + + if (OldMsg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return FALSE; + } + + strcpy(OldTitle, OldMsg->title); + + if (strlen(OldTitle) > 56) OldTitle[56] = 0; + + strcpy(NewTitle, "Fwd:"); + strcat(NewTitle, OldTitle); + + conn->CopyBuffer = ReadMessageFile(msgno); + + return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); + } + + + nodeprintf(conn, "*** Error: Invalid Send option %c\r", Cmd[1]); + + return FALSE; +} + +char * CheckToAddress(CIRCUIT * conn, char * Addr) +{ + // Check one element of Multiple Address + + if (conn == NULL || !(conn->BBSFlags & BBS)) + { + // if a normal user, check that TO and/or AT are known and warn if not. + + if (_stricmp(Addr, "SYSOP") == 0) + { + return _strdup(Addr); + } + + if (SendBBStoSYSOPCall) + if (_stricmp(Addr, BBSName) == 0) + return _strdup(SYSOPCall); + + + if (strchr(Addr, '@') == 0) + { + // No routing, if not a user and not known to forwarding or WP warn + + struct UserInfo * ToUser = LookupCall(Addr); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + char * NewAddr = malloc(250); + if (conn) + nodeprintf(conn, "Address %s - @%s added from HomeBBS\r", Addr, ToUser->HomeBBS); + sprintf(NewAddr, "%s@%s", Addr, ToUser->HomeBBS); + return NewAddr; + } + } + else + { + WPRecP WP = LookupWP(Addr); + + if (WP) + { + char * NewAddr = malloc(250); + + if (conn) + nodeprintf(conn, "Address %s - @%s added from WP\r", Addr, WP->first_homebbs); + sprintf(NewAddr, "%s@%s", Addr, WP->first_homebbs); + return NewAddr; + } + } + } + } + + // Check SMTP and RMS Addresses + + if ((_memicmp(Addr, "rms:", 4) == 0) || (_memicmp(Addr, "rms/", 4) == 0)) + { + Addr[3] = ':'; // Replace RMS/ with RMS: + + if (conn && !FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + } + else if ((_memicmp(Addr, "smtp:", 5) == 0) || (_memicmp(Addr, "smtp/", 5) == 0)) + { + Addr[4] = ':'; // Replace smpt/ with smtp: + + if (ISP_Gateway_Enabled) + { + if (conn && (conn->UserPointer->flags & F_EMAIL) == 0) + { + nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); + return FALSE; + } + } + else + { + if (conn) + nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); + return FALSE; + } + } + + return _strdup(Addr); +} + + +char Winlink[] = "WINLINK.ORG"; + +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char *To, char ** ATBBS, char ** BID) +{ + char * ptr; + char seps[] = " \t\r"; + WPRecP WP; + char * ToCopy = _strdup(To); + int Len; + + conn->ToCount = 0; + + // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY + + // Having trailing ; will mess up parsing multiple addresses, so remove. + + while (To[strlen(To) - 1] == ';') + To[strlen(To) - 1] = 0; + + if (strchr(Context, ';') || strchr(To, ';')) + { + // Multiple Addresses - put address list back together + + char * p; + + To[strlen(To)] = ' '; + Context = To; + + while (p = strchr(Context, ';')) + { + // Multiple Addressees + + To = strtok_s(NULL, ";", &Context); + Len = (int)strlen(To); + conn->To = realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); + if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) + conn->ToCount++; + } + + To = strtok_s(NULL, seps, &Context); + + Len = (int)strlen(To); + conn->To=realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); + if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) + conn->ToCount++; + } + else + { + // Single Call + + // accept CALL!CALL for source routed message + + if (strchr(To, '@') == 0 && strchr(To, '!')) // Bang route without @ + { + char * bang = strchr(To, '!'); + + memmove(bang + 1, bang, strlen(bang)); // Move !call down one + + *ATBBS = strlop(To, '!');; + } + + // Accept call@call (without spaces) - but check for smtp addresses + + if (_memicmp(To, "smtp:", 5) != 0 && _memicmp(To, "rms:", 4) != 0 && _memicmp(To, "rms/", 4) != 0) + { + ptr = strchr(To, '@'); + + if (ptr) + { + // If looks like a valid email address, treat as such + + int tolen; + *ATBBS = strlop(To, '@'); + + strlop(To, '-'); // Cant have SSID on BBS Name + + tolen = (int)strlen(To); + + if (tolen > 6 || !CheckifPacket(*ATBBS)) + { + // Probably Email address. Add smtp: or rms: + + if (FindRMS() || strchr(*ATBBS, '!')) // have RMS or source route + sprintf(To, "rms:%s", ToCopy); + else if (ISP_Gateway_Enabled) + sprintf(To, "smtp:%s", ToCopy); + else if (isAMPRMsg(ToCopy)) + sprintf(To, "rms:%s", ToCopy); + + } + } + } + } + + free(ToCopy); + + // Look for Optional fields; + + ptr = strtok_s(NULL, seps, &Context); + + while (ptr) + { + if (strcmp(ptr, "@") == 0) + { + *ATBBS = _strupr(strtok_s(NULL, seps, &Context)); + } + else if(strcmp(ptr, "<") == 0) + { + *From = strtok_s(NULL, seps, &Context); + } + else if (ptr[0] == '$') + *BID = &ptr[1]; + else + { + nodeprintf(conn, "*** Error: Invalid Format\r"); + return FALSE; + } + ptr = strtok_s(NULL, seps, &Context); + } + + // Only allow < from a BBS + + if (*From) + { + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "*** < can only be used by a BBS\r"); + return FALSE; + } + } + + if (!*From) + *From = conn->UserPointer->Call; + + if (!(conn->BBSFlags & BBS)) + { + // if a normal user, check that TO and/or AT are known and warn if not. + + if (_stricmp(To, "SYSOP") == 0) + { + conn->LocalMsg = TRUE; + return TRUE; + } + + if (!*ATBBS && conn->ToCount == 0) + { + // No routing, if not a user and not known to forwarding or WP warn + + struct UserInfo * ToUser = LookupCall(To); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->flags & F_RMSREDIRECT) + { + // sent to Winlink + + *ATBBS = Winlink; + nodeprintf(conn, "Redirecting to winlink.org\r", *ATBBS); + } + else if (ToUser->HomeBBS[0]) + { + *ATBBS = ToUser->HomeBBS; + nodeprintf(conn, "Address @%s added from HomeBBS\r", *ATBBS); + } + else + { + conn->LocalMsg = TRUE; + } + } + else + { + conn->LocalMsg = FALSE; + WP = LookupWP(To); + + if (WP) + { + *ATBBS = WP->first_homebbs; + nodeprintf(conn, "Address @%s added from WP\r", *ATBBS); + } + } + } + } + return TRUE; +} + +BOOL CreateMessage(CIRCUIT * conn, char * From, char * ToCall, char * ATBBS, char MsgType, char * BID, char * Title) +{ + struct MsgInfo * Msg, * TestMsg; + char * via = NULL; + char * FromHA; + + // Create a temp msg header entry + + if (conn->ToCount) + { + } + else + { + if (CheckRejFilters(From, ToCall, ATBBS, BID, MsgType, 0)) + { + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - REJECTED\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error - Message Filters prevent sending this message\r"); + + return FALSE; + } + } + + Msg = malloc(sizeof (struct MsgInfo)); + + if (Msg == 0) + { + CriticalErrorHandler("malloc failed for new message header"); + return FALSE; + } + + memset(Msg, 0, sizeof (struct MsgInfo)); + + conn->TempMsg = Msg; + + Msg->type = MsgType; + + if (conn->UserPointer->flags & F_HOLDMAIL) + Msg->status = 'H'; + else + Msg->status = 'N'; + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + if (BID) + { + BIDRec * TempBID; + + // If P Message, dont immediately reject on a Duplicate BID. Check if we still have the message + // If we do, reject it. If not, accept it again. (do we need some loop protection ???) + + TempBID = LookupBID(BID); + + if (TempBID) + { + if (MsgType == 'B') + { + // Duplicate bid + + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - BID\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error- Duplicate BID\r"); + + return FALSE; + } + + TestMsg = GetMsgFromNumber(TempBID->u.msgno); + + // if the same TO we will assume the same message + + if (TestMsg && strcmp(TestMsg->to, ToCall) == 0) + { + // We have this message. If we have already forwarded it, we should accept it again + + if ((TestMsg->status == 'N') || (TestMsg->status == 'Y')|| (TestMsg->status == 'H')) + { + // Duplicate bid + + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - BID\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error- Duplicate BID\r"); + + return FALSE; + } + } + } + + if (strlen(BID) > 12) BID[12] = 0; + strcpy(Msg->bid, BID); + + // Save BID in temp list in case we are offered it again before completion + + TempBID = AllocateTempBIDRecord(); + strcpy(TempBID->BID, BID); + TempBID->u.conn = conn; + + } + + if (conn->ToCount) + { + } + else + { + if (_memicmp(ToCall, "rms:", 4) == 0) + { + // Could be ampr.org message + + if (!isAMPRMsg(ToCall)) + { + if (!FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + } + via=strlop(ToCall, ':'); + _strupr(ToCall); + + } + else if (_memicmp(ToCall, "rms/", 4) == 0) + { + if (!FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + + via=strlop(ToCall, '/'); + _strupr(ToCall); + } + else if (_memicmp(ToCall, "smtp:", 5) == 0) + { + if (ISP_Gateway_Enabled) + { + if ((conn->UserPointer->flags & F_EMAIL) == 0) + { + nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); + return FALSE; + } + via=strlop(ToCall, ':'); + ToCall[0] = 0; + } + else + { + nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); + return FALSE; + } + } + else + { + _strupr(ToCall); + if (ATBBS) + via=_strupr(ATBBS); + } + + strlop(ToCall, '-'); // Remove any (illegal) ssid + if (strlen(ToCall) > 6) ToCall[6] = 0; + + strcpy(Msg->to, ToCall); + + if (SendBBStoSYSOPCall) + if (_stricmp(ToCall, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if (via) + { + if (strlen(via) > 40) via[40] = 0; + + strcpy(Msg->via, via); + } + + } // End of Multiple Dests + + // Look for HA in From (even if we shouldn't be getting it!) + + FromHA = strlop(From, '@'); + + + strlop(From, '-'); // Remove any (illegal) ssid + if (strlen(From) > 6) From[6] = 0; + strcpy(Msg->from, From); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + + if (Title) // Only used by SR and SC + { + strcpy(Msg->title, Title); + conn->Flags |= GETTINGMESSAGE; + + // Create initial buffer of 10K. Expand if needed later + + conn->MailBuffer=malloc(10000); + conn->MailBufferSize=10000; + + nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); + return TRUE; + } + + if (conn->BBSFlags & FLARQMODE) + return TRUE; + + if (!(conn->BBSFlags & FBBCompressed)) + conn->Flags |= GETTINGTITLE; + + if (!(conn->BBSFlags & BBS)) + nodeprintf(conn, "Enter Title (only):\r"); + else + if (!(conn->BBSFlags & FBBForwarding)) + nodeprintf(conn, "OK\r"); + + return TRUE; +} + +VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int msglen) +{ + + conn->Flags &= ~GETTINGTITLE; + + if (msglen == 1) + { + nodeprintf(conn, "*** Message Cancelled\r"); + SendPrompt(conn, user); + return; + } + + if (msglen > 60) msglen = 60; + + Buffer[msglen-1] = 0; + + strcpy(conn->TempMsg->title, Buffer); + + // Create initial buffer of 10K. Expand if needed later + + conn->MailBuffer=malloc(10000); + conn->MailBufferSize=10000; + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to create Message Buffer\r"); + return; + } + + conn->Flags |= GETTINGMESSAGE; + + if (!conn->BBSFlags & BBS) + nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); + +} + +VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int msglen) +{ + char * ptr2 = NULL; + + if (((msglen < 3) && (Buffer[0] == 0x1a)) || ((msglen == 4) && (_memicmp(Buffer, "/ex", 3) == 0))) + { + int Index = 0; + + if (conn->TempMsg->type == 'P') + Index = PMSG; + else if (conn->TempMsg->type == 'B') + Index = BMSG; + else if (conn->TempMsg->type == 'T') + Index = TMSG; + + conn->Flags &= ~GETTINGMESSAGE; + + user->Total.MsgsReceived[Index]++; + user->Total.BytesForwardedIn[Index] += conn->TempMsg->length; + + if (conn->ToCount) + { + // Multiple recipients + + struct MsgInfo * Msg = conn->TempMsg; + int i; + struct MsgInfo * SaveMsg = Msg; + char * SaveBody = conn->MailBuffer; + int SaveMsgLen = Msg->length; + BOOL SentToRMS = FALSE; + int ToLen = 0; + char * ToString = zalloc(conn->ToCount * 100); + + // If no BID provided, allocate one + + if (Msg->bid[0] == 0) + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg + 1, BBSName); + + for (i = 0; i < conn->ToCount; i++) + { + char * Addr = conn->To[i]; + char * Via; + + if (_memicmp (Addr, "SMTP:", 5) == 0) + { + // For Email + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 10); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + Msg->to[0] = 0; + strcpy(Msg->via, &Addr[5]); + + CreateMessageFromBuffer(conn); + continue; + } + + if (_memicmp (Addr, "RMS:", 4) == 0) + { + // Add to B2 Message for RMS + + Addr+=4; + + Via = strlop(Addr, '@'); + + if (Via && _stricmp(Via, "winlink.org") == 0) + { + if (CheckifLocalRMSUser(Addr)) + { + // Local RMS - Leave Here + + Via = 0; // Drop Through + goto PktMsg; + } + else + { + ToLen = sprintf(&ToString[strlen(ToString)], "To: %s\r\n", Addr); + continue; + } + } + + ToLen = sprintf(&ToString[strlen(ToString)], "To: %s@%s\r\n", Addr, Via); + continue; + } + + _strupr(Addr); + + Via = strlop(Addr, '@'); + + if (Via && _stricmp(Via, "winlink.org") == 0) + { + if (CheckifLocalRMSUser(Addr)) + { + // Local RMS - Leave Here + + Via = 0; // Drop Through + } + else + { + ToLen = sprintf(&ToString[strlen(ToString)], "To: %s\r\n", Addr); + + // Add to B2 Message for RMS + + continue; + } + } + + PktMsg: + + conn->LocalMsg = FALSE; + + // Normal BBS Message + + if (_stricmp(Addr, "SYSOP") == 0) + conn->LocalMsg = TRUE; + else + { + struct UserInfo * ToUser = LookupCall(Addr); + + if (ToUser) + conn->LocalMsg = TRUE; + } + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 10); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + strcpy(Msg->to, Addr); + + if (Via) + { + Msg->bid[0] = 0; // if we are forwarding it, we must change BID to be safe + strcpy(Msg->via, Via); + } + + CreateMessageFromBuffer(conn); + } + + if (ToLen) + { + char * B2Hddr = zalloc(ToLen + 1000); + int B2HddrLen; + char DateString[80]; + struct tm * tm; + time_t Date = time(NULL); + char Type[16] = "Private"; + + // Get Type + + if (conn->TempMsg->type == 'B') + strcpy(Type, "Bulletin"); + else if (conn->TempMsg->type == 'T') + strcpy(Type, "Traffic"); + + tm = gmtime(&Date); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000 + ToLen); + + Msg->B2Flags = B2Msg; + + B2HddrLen = sprintf(B2Hddr, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\n%sSubject: %s\r\nMbo: %s\r\nBody: %d\r\n\r\n", + SaveMsg->bid, DateString, Type, + SaveMsg->from, ToString, SaveMsg->title, BBSName, SaveMsgLen); + + memcpy(conn->MailBuffer, B2Hddr, B2HddrLen); + memcpy(&conn->MailBuffer[B2HddrLen], SaveBody, SaveMsgLen); + + Msg->length += B2HddrLen; + + strcpy(Msg->to, "RMS"); + + CreateMessageFromBuffer(conn); + + free(B2Hddr); + } + + free(SaveMsg); + free(SaveBody); + conn->MailBuffer = NULL; + conn->MailBufferSize=0; + + if (!(conn->BBSFlags & BBS)) + SendPrompt(conn, conn->UserPointer); + else + if (!(conn->BBSFlags & FBBForwarding)) + { + if (conn->BBSFlags & OUTWARDCONNECT) + BBSputs(conn, "F>\r"); // if Outward connect must be reverse forward + else + BBSputs(conn, ">\r"); + } + + /* + // From a client - Create one copy with all RMS recipients, and another for each packet recipient + + // Merge all RMS To: lines + + ToLen = 0; + ToString[0] = 0; + + for (i = 0; i < Recipients; i++) + { + if (LocalMsg[i]) + continue; // For a local RMS user + + if (_stricmp(Via[i], "WINLINK.ORG") == 0 || _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + { + ToLen += strlen(HddrTo[i]); + strcat(ToString, HddrTo[i]); + } + } + + if (ToLen) + { + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], ToString, ToLen); + + conn->TempMsg->length += ToLen; + + strcpy(Msg->to, "RMS"); + strcpy(Msg->via, "winlink.org"); + + // Must Change the BID + + Msg->bid[0] = 0; + + CreateMessageFromBuffer(conn); + } + + } + + free(ToString); + + for (i = 0; i < Recipients; i++) + { + // Only Process Non - RMS Dests or local RMS Users + + if (LocalMsg[i] == 0) + if (_stricmp (Via[i], "WINLINK.ORG") == 0 || + _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + continue; + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + // Add our To: + + ToLen = strlen(HddrTo[i]); + + if (_memicmp(HddrTo[i], "CC", 2) == 0) // Replace CC: with TO: + memcpy(HddrTo[i], "To", 2); + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], HddrTo[i], ToLen); + + conn->TempMsg->length += ToLen; + + strcpy(Msg->to, RecpTo[i]); + strcpy(Msg->via, Via[i]); + + Msg->bid[0] = 0; + + CreateMessageFromBuffer(conn); + } + } // End not from RMS + + free(SaveMsg); + free(SaveBody); + conn->MailBuffer = NULL; + conn->MailBufferSize=0; + + SetupNextFBBMessage(conn); + return; + + } My__except_Routine("Process Multiple Destinations"); + + BBSputs(conn, "*** Program Error Processing Multiple Destinations\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + + return; +*/ + + conn->ToCount = 0; + + return; + } + + + CreateMessageFromBuffer(conn); + return; + + } + + Buffer[msglen++] = 0x0a; + + if ((conn->TempMsg->length + msglen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to extend Message Buffer\r"); + + conn->Flags &= ~GETTINGMESSAGE; + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, msglen); + + conn->TempMsg->length += msglen; +} + +VOID CreateMessageFromBuffer(CIRCUIT * conn) +{ + struct MsgInfo * Msg; + BIDRec * BIDRec; + char * ptr1, * ptr2 = NULL; + char * ptr3, * ptr4; + int FWDCount = 0; + char OldMess[] = "\r\n\r\nOriginal Message:\r\n\r\n"; + time_t Age; + int OurCount; + char * HoldReason = "User has Hold Messages flag set"; + struct UserInfo * user; + + +#ifndef LINBPQ + struct _EXCEPTION_POINTERS exinfo; +#endif + + // If doing SC, Append Old Message + + if (conn->CopyBuffer) + { + if ((conn->TempMsg->length + (int) strlen(conn->CopyBuffer) + 80 )> conn->MailBufferSize) + { + conn->MailBufferSize += (int)strlen(conn->CopyBuffer) + 80; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to extend Message Buffer\r"); + + conn->Flags &= ~GETTINGMESSAGE; + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], OldMess, strlen(OldMess)); + + conn->TempMsg->length += (int)strlen(OldMess); + + memcpy(&conn->MailBuffer[conn->TempMsg->length], conn->CopyBuffer, strlen(conn->CopyBuffer)); + + conn->TempMsg->length += (int)strlen(conn->CopyBuffer); + + free(conn->CopyBuffer); + conn->CopyBuffer = NULL; + } + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + memcpy(Msg, conn->TempMsg, sizeof(struct MsgInfo)); + + free(conn->TempMsg); + + // Set number here so they remain in sequence + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + FreeSemaphore(&MsgNoSemaphore); + MsgnotoMsg[Msg->number] = Msg; + + if (Msg->status == 0) + Msg->status = 'N'; + + // Create BID if non supplied + + if (Msg->bid[0] == 0) + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + // if message body had R: lines, get date created from last (not very accurate, but best we can do) + + // Also check if we have had message before to detect loops + + ptr1 = conn->MailBuffer; + OurCount = 0; + + // If it is a B2 Message, Must Skip B2 Header + + if (Msg->B2Flags & B2Msg) + { + ptr1 = strstr(ptr1, "\r\n\r\n"); + if (ptr1) + ptr1 += 4; + else + ptr1 = conn->MailBuffer; + } + +nextline: + + if (memcmp(ptr1, "R:", 2) == 0) + { + // Is if ours? + + // BPQ RLINE Format R:090920/1041Z 6542@N4JOA.#WPBFL.FL.USA.NOAM BPQ1.0.2 + + ptr3 = strchr(ptr1, '@'); + ptr4 = strchr(ptr1, '.'); + + if (ptr3 && ptr4 && (ptr4 > ptr3)) + { + if (memcmp(ptr3+1, BBSName, ptr4-ptr3-1) == 0) + OurCount++; + } + + GetWPBBSInfo(ptr1); // Create WP /I record from R: Line + + // see if another + + ptr2 = ptr1; // save + ptr1 = strchr(ptr1, '\r'); + if (ptr1 == 0) + { + Debugprintf("Corrupt Message %s from %s - truncated within R: line", Msg->bid, Msg->from); + return; + } + ptr1++; + if (*ptr1 == '\n') ptr1++; + + goto nextline; + } + + // ptr2 points to last R: line (if any) + + if (ptr2) + { + struct tm rtime; + time_t result; + + memset(&rtime, 0, sizeof(struct tm)); + + if (ptr2[10] == '/') + { + // Dodgy 4 char year + + sscanf(&ptr2[2], "%04d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + rtime.tm_year -= 1900; + rtime.tm_mon--; + } + else if (ptr2[8] == '/') + { + sscanf(&ptr2[2], "%02d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + + if (rtime.tm_year < 90) + rtime.tm_year += 100; // Range 1990-2089 + rtime.tm_mon--; + } + + // Otherwise leave date as zero, which should be rejected + + // result = _mkgmtime(&rtime); + + if ((result = mktime(&rtime)) != (time_t)-1 ) + { + result -= (time_t)_MYTIMEZONE; + + Msg->datecreated = result; + Age = (time(NULL) - result)/86400; + + if ( Age < -7) + { + Msg->status = 'H'; + HoldReason = "Suspect Date Sent"; + } + else if (Age > BidLifetime || Age > MaxAge) + { + Msg->status = 'H'; + HoldReason = "Message too old"; + + } + else + GetWPInfoFromRLine(Msg->from, ptr2, result); + } + else + { + // Can't decode R: Datestamp + + Msg->status = 'H'; + HoldReason = "Corrupt R: Line - can't determine age"; + } + + if (OurCount > 1) + { + // Message is looping + + Msg->status = 'H'; + HoldReason = "Message may be looping"; + + } + } + + if (strcmp(Msg->to, "WP") == 0) + { + // If Reject WP Bulls is set, Kill message here. + // It should only get here if B2 - otherwise it should be + // rejected earlier + + if (Msg->type == 'B' && FilterWPBulls) + Msg->status = 'K'; + + } + + conn->MailBuffer[Msg->length] = 0; + + if (CheckBadWords(Msg->title) || CheckBadWords(conn->MailBuffer)) + { + Msg->status = 'H'; + HoldReason = "Bad word in title or body"; + } + + if (CheckHoldFilters(Msg, Msg->from, Msg->to, Msg->via, Msg->bid)) + { + Msg->status = 'H'; + HoldReason = "Matched Hold Filters"; + } + + if (CheckValidCall(Msg->from) == 0) + { + Msg->status = 'H'; + HoldReason = "Probable Invalid From Call"; + } + + // Process any WP Messages + + if (strcmp(Msg->to, "WP") == 0) + { + if (Msg->status == 'N') + { + ProcessWPMsg(conn->MailBuffer, Msg->length, ptr2); + + if (Msg->type == 'P') // Kill any processed private WP messages. + { + char VIA[80]; + + strcpy(VIA, Msg->via); + strlop(VIA, '.'); + + if (strcmp(VIA, BBSName) == 0) + Msg->status = 'K'; + } + } + } + + CreateMessageFile(conn, Msg); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + if (Msg->length > MaxTXSize) + { + Msg->status = 'H'; + HoldReason = "Message too long"; + + if (!(conn->BBSFlags & BBS)) + nodeprintf(conn, "*** Warning Message length exceeds sysop-defined maximum of %d - Message will be held\r", MaxTXSize); + } + + // Check for message to internal server + + if (Msg->via[0] == 0 + || _stricmp(Msg->via, BBSName) == 0 // our BBS a + || _stricmp(Msg->via, AMPRDomain) == 0) // our AMPR Address + { + if (CheckforMessagetoServer(Msg)) + { + // Flag as killed and send prompt + + FlagAsKilled(Msg, TRUE); + + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "Message %d to Server Processed and Killed.\r", Msg->number); + SendPrompt(conn, conn->UserPointer); + } + return; // no need to process further + } + } + + if (Msg->to[0]) + FWDCount = MatchMessagetoBBSList(Msg, conn); + else + { + // If addressed @winlink.org, and to a local user, Keep here. + + char * Call; + char * AT; + + // smtp or rms - don't warn no route + + FWDCount = 1; + + Call = _strupr(_strdup(Msg->via)); + AT = strlop(Call, '@'); + + if (AT && _stricmp(AT, "WINLINK.ORG") == 0) + { + struct UserInfo * user = LookupCall(Call); + + if (user) + { + if (user->flags & F_POLLRMS) + { + Logprintf(LOG_BBS, conn, '?', "SMTP Message @ winlink.org, but local RMS user - leave here"); + strcpy(Msg->to, Call); + strcpy(Msg->via, AT); + if (user->flags & F_BBS) // User is a BBS, so set FWD bit so he can get it + set_fwd_bit(Msg->fbbs, user->BBSNumber); + + } + } + } + free(Call); + } + + // Warn SYSOP if P or T forwarded in, and has nowhere to go + + if ((conn->BBSFlags & BBS) && Msg->type != 'B' && FWDCount == 0 && WarnNoRoute && + strcmp(Msg->to, "SYSOP") && strcmp(Msg->to, "WP")) + { + if (Msg->via[0]) + { + if (_stricmp(Msg->via, BBSName)) // Not for our BBS a + if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address + SendWarningToSYSOP(Msg); + } + else + { + // No via - is it for a local user? + + if (LookupCall(Msg->to) == 0) + SendWarningToSYSOP(Msg); + } + } + + if ((conn->BBSFlags & SYNCMODE) == 0) + { + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "Message: %d Bid: %s Size: %d\r", Msg->number, Msg->bid, Msg->length); + + if (Msg->via[0]) + { + if (_stricmp(Msg->via, BBSName)) // Not for our BBS a + if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address + + if (FWDCount == 0 && Msg->to[0] != 0) // unless smtp msg + nodeprintf(conn, "@BBS specified, but no forwarding info is available - msg may not be delivered\r"); + } + else + { + if (FWDCount == 0 && conn->LocalMsg == 0 && Msg->type != 'B') + // Not Local and no forward route + nodeprintf(conn, "Message is not for a local user, and no forwarding info is available - msg may not be delivered\r"); + } + if (conn->ToCount == 0) + SendPrompt(conn, conn->UserPointer); + } + else + { + if (!(conn->BBSFlags & FBBForwarding)) + { + if (conn->ToCount == 0) + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + } + } + + if(Msg->to[0] == 0) + SMTPMsgCreated=TRUE; + + if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + + if (Msg->status == 'H') + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, HoldReason); + SendMessageToSYSOP(Title, MailBuffer, Length); + } + + BuildNNTPList(Msg); // Build NNTP Groups list + + SaveMessageDatabase(); + SaveBIDDatabase(); + + // If Event Notifications enabled report a new message event + + user = LookupCall(Msg->to); + + SendNewMessageEvent(user->Call, Msg); +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(Msg); +#endif + + if (EnableUI) +#ifdef LINBPQ + SendMsgUI(Msg); +#else + __try + { + SendMsgUI(Msg); + } + My__except_Routine("SendMsgUI"); +#endif + + if (user && (user->flags & F_APRSMFOR)) + { + char APRS[128]; + char Call[16]; + int SSID = user->flags >> 28; + + if (SSID) + sprintf(Call, "%s-%d", Msg->to, SSID); + else + strcpy(Call, Msg->to); + + sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from); + APISendAPRSMessage(APRS, Call); + } + + return; +} + +VOID CreateMessageFile(ConnectionInfo * conn, struct MsgInfo * Msg) +{ + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + char Mess[255]; + int len; + BOOL AutoImport = FALSE; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + // If title is "Batched messages for AutoImport from BBS xxxxxx and first line is S? and it is + // for this BBS, Import file and set message as Killed. May need to strip B2 Header and R: lines + + if (strcmp(Msg->to, BBSName) == 0 && strstr(Msg->title, "Batched messages for AutoImport from BBS ")) + { + UCHAR * ptr = conn->MailBuffer; + + // If it is a B2 Message, Must Skip B2 Header + + if (Msg->B2Flags & B2Msg) + { + ptr = strstr(ptr, "\r\n\r\n"); + if (ptr) + ptr += 4; + else + ptr = conn->MailBuffer; + } + + if (memcmp(ptr, "R:", 2) == 0) + { + ptr = strstr(ptr, "\r\n\r\n"); //And remove R: Lines + if (ptr) + ptr += 4; + } + + if (*(ptr) == 'S' && ptr[2] == ' ') + { + int HeaderLen = (int)(ptr - conn->MailBuffer); + Msg->length -= HeaderLen; + memmove(conn->MailBuffer, ptr, Msg->length); + Msg->status = 'K'; + AutoImport = TRUE; + } + } + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(conn->MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + if (AutoImport) + ImportMessages(NULL, MsgFile, TRUE); + + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + if (WriteLen != Msg->length) + { + len = sprintf_s(Mess, sizeof(Mess), "Failed to create Message File\r"); + QueueMsg(conn, Mess, len); + Debugprintf(Mess); + } + return; +} + + +VOID SendUnbuffered(int stream, char * msg, int len) +{ +#ifndef LINBPQ + if (stream < 0) + WritetoConsoleWindow(stream, msg, len); + else +#endif + SendMsg(stream, msg, len); +} + +BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen); + +BOOL FindMessagestoForward (CIRCUIT * conn) +{ + struct UserInfo * user = conn->UserPointer; + +#ifndef LINBPQ + + struct _EXCEPTION_POINTERS exinfo; + + __try { +#endif + + // This is a hack so Winpack or WLE users can use forwarding + // protocols to get their messages without being defined as a BBS + + // !!IMPORTANT Getting this wrong can see message repeatedly proposed !! + // !! Anything sent using this must be killed if sent or rejected. + + // I'm not sure about this. I think I only need the PacLinkCalls + // if from RMS Express, and it always sends an FW; line + // Ah, no. What about WinPack ?? + // If from RMS Express must have Temp_B2 or BBS set or SID will + // be rejected. So maybe just Temp_B2 && BBS = 0?? + // No, someone may have Temp_B2 set and not be using WLE ?? So what ?? + + if ((user->flags & F_Temp_B2_BBS) && ((user->flags & F_BBS) == 0) || conn->RMSExpress || conn->PAT) + { + if (conn->PacLinkCalls == NULL) + { + // create a list with just the user call + + char * ptr1; + + conn->PacLinkCalls = zalloc(30); + + ptr1 = (char *)conn->PacLinkCalls; + ptr1 += 16; // Must be room for a null pointer on end (64 bit bug) + strcpy(ptr1, user->Call); + + conn->PacLinkCalls[0] = ptr1; + } + } + + if (conn->SendT && FindMessagestoForwardLoop(conn, 'T', conn->MaxTLen)) + { + conn->LastForwardType = 'T'; + return TRUE; + } + + if (conn->LastForwardType == 'T') + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (conn->SendP && FindMessagestoForwardLoop(conn, 'P', conn->MaxPLen)) + { + conn->LastForwardType = 'P'; + return TRUE; + } + + if (conn->LastForwardType == 'P') + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (conn->SendB && FindMessagestoForwardLoop(conn, 'B', conn->MaxBLen)) + { + conn->LastForwardType = 'B'; + return TRUE; + } + + conn->LastForwardType = 0; + return FALSE; +#ifndef LINBPQ + } My__except_Routine("FindMessagestoForward"); +#endif + return FALSE; + +} + + +BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen) +{ + // See if any messages are queued for this BBS + + int m; + struct MsgInfo * Msg; + struct UserInfo * user = conn->UserPointer; + struct FBBHeaderLine * FBBHeader; + BOOL Found = FALSE; + char RLine[100]; + int TotalSize = 0; + time_t NOW = time(NULL); + +// Debugprintf("FMTF entered Call %s Type %c Maxlen %d NextMsg = %d BBSNo = %d", +// conn->Callsign, Type, MaxLen, conn->NextMessagetoForward, user->BBSNumber); + + if (conn->PacLinkCalls || (conn->UserPointer->flags & F_NTSMPS)) // Looking for all messages, so reset + conn->NextMessagetoForward = 1; + + conn->FBBIndex = 0; + + for (m = conn->NextMessagetoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + // If an NTS MPS, see if anything matches + + if (Type == 'T' && (conn->UserPointer->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + int depth; + + if (Msg->type == 'T' && Msg->status == 'N' && Msg->length <= MaxLen && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + goto Forwardit; + + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + goto Forwardit; + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + goto Forwardit; + } + } + + // If forwarding to Paclink or RMS Express, look for any message matching the + // requested call list with status 'N' (maybe should also be 'P' ??) + + if (conn->PacLinkCalls) + { + int index = 1; + + char * Call = conn->PacLinkCalls[0]; + + while (Call) + { + if (Msg->type == Type && Msg->status == 'N') + { +// Debugprintf("Comparing RMS Call %s %s", Msg->to, Call); + if (_stricmp(Msg->to, Call) == 0) + if (Msg->status == 'N' && Msg->type == Type && Msg->length <= MaxLen) + goto Forwardit; + else + Debugprintf("Call Match but Wrong Type/Len %c %d", Msg->type, Msg->length); + } + Call = conn->PacLinkCalls[index++]; + } +// continue; + } + + if (Msg->type == Type && Msg->length <= MaxLen && (Msg->status != 'H') + && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + // Message to be sent - do a consistancy check (State, etc) + + Forwardit: + + if (Msg->Defered > 0) // = response received + { + Msg->Defered--; + Debugprintf("Message %d deferred", Msg->number); + continue; + } + + if ((Msg->from[0] == 0) || (Msg->to[0] == 0)) + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, "Missing From: or To: field"); + SendMessageToSYSOP(Title, MailBuffer, Length); + + Msg->status = 'H'; + continue; + } + + conn->NextMessagetoForward = m + 1; // So we don't offer again if defered + + Msg->Locked = 1; // So other MPS can't pick it up + + // if FBB forwarding add to list, eise save pointer + + if (conn->BBSFlags & FBBForwarding) + { + struct tm *tm; + time_t temp; + + FBBHeader = &conn->FBBHeaders[conn->FBBIndex++]; + + FBBHeader->FwdMsg = Msg; + FBBHeader->MsgType = Msg->type; + FBBHeader->Size = Msg->length; + TotalSize += Msg->length; + strcpy(FBBHeader->From, Msg->from); + strcpy(FBBHeader->To, Msg->to); + strcpy(FBBHeader->ATBBS, Msg->via); + strcpy(FBBHeader->BID, Msg->bid); + + // Set up R:Line, so se can add its length to the sise + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + FBBHeader->Size += sprintf_s(RLine, sizeof(RLine),"R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + Msg->number, BBSName, HRoute, RlineVer); + + // If using B2 forwarding we need the message size and Compressed size for FC proposal + + if (conn->BBSFlags & FBBB2Mode) + { + if (CreateB2Message(conn, FBBHeader, RLine) == FALSE) + { + char * MailBuffer = malloc(100); + char Title[100]; + int Length; + + // Corrupt B2 Message + + Debugprintf("Corrupt B2 Message found - Message %d will be held", Msg->number); + Msg->status = 'H'; + SaveMessageDatabase(); + + conn->FBBIndex--; + TotalSize -= Msg->length; + memset(&conn->FBBHeaders[conn->FBBIndex], 0, sizeof(struct FBBHeaderLine)); + + Length = sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, "Corrupt B2 Message"); + SendMessageToSYSOP(Title, MailBuffer, Length); + + continue; + } + } + + if (conn->FBBIndex == 5 || TotalSize > user->ForwardingInfo->MaxFBBBlockSize) + return TRUE; // Got max number or too big + + Found = TRUE; // Remember we have some + } + else + { + conn->FwdMsg = Msg; + return TRUE; + } + } + } + + return Found; +} + +BOOL SeeifMessagestoForward (int BBSNumber, CIRCUIT * conn) +{ + // See if any messages are queued for this BBS + + // if Conn is not NULL, also check Msg Type + + int m; + struct MsgInfo * Msg; + + for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + if (conn) + { + char Type = Msg->type; + + if ((conn->SendB && Type == 'B') || (conn->SendP && Type == 'P') || (conn->SendT && Type == 'T')) + { +// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); + return TRUE; + } + } + else + { +// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); + return TRUE; + } + } + } + + return FALSE; +} + +int CountMessagestoForward (struct UserInfo * user) +{ + // See if any messages are queued for this BBS + + int m, n=0; + struct MsgInfo * Msg; + int BBSNumber = user->BBSNumber; + int FirstMessage = FirstMessageIndextoForward; + + if ((user->flags & F_NTSMPS)) + FirstMessage = 1; + + for (m = FirstMessage; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + n++; + continue; // So we dont count twice if Flag set and NTS MPS + } + + // if an NTS MPS, also check for any matches + + if (Msg->type == 'T' && (user->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + int depth; + + if (Msg->status == 'N' && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + { + n++; + continue; + } + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + { + n++; + continue; + } + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + { + n++; + continue; + } + } + } + } + + return n; +} + +int CountBytestoForward (struct UserInfo * user) +{ + // See if any messages are queued for this BBS. If so return total bytes queued + + int m, n=0; + struct MsgInfo * Msg; + int BBSNumber = user->BBSNumber; + int FirstMessage = FirstMessageIndextoForward; + + if ((user->flags & F_NTSMPS)) + FirstMessage = 1; + + for (m = FirstMessage; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + n += Msg->length; + continue; // So we dont count twice if Flag set and NTS MPS + } + + // if an NTS MPS, also check for any matches + + if (Msg->type == 'T' && (user->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + int depth; + + if (Msg->status == 'N' && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + { + n += Msg->length; + continue; + } + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + { + n += Msg->length; + continue; + } + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + { + n += Msg->length; + continue; + } + } + } + } + + return n; +} + +int ListMessagestoForward(CIRCUIT * conn, struct UserInfo * user) +{ + // See if any messages are queued for this BBS + + int m, n=0; + struct MsgInfo * Msg; + int BBSNumber = user->BBSNumber; + int FirstMessage = FirstMessageIndextoForward; + + if ((user->flags & F_NTSMPS)) + FirstMessage = 1; + + for (m = FirstMessage; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; // So we dont count twice in Flag set and NTS MPS + } + + // if an NTS MPS, also check for any matches + + if (Msg->type == 'T' && (user->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + int depth; + + if (Msg->status == 'N' && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + } + } + } + + return n; +} + +VOID SendWarningToSYSOP(struct MsgInfo * Msg) +{ + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Warning - Message %d has nowhere to go\r\n%s@%s", Msg->number, Msg->to, Msg->via); + sprintf(Title, "Warning - Message %d has nowhere to go", Msg->number); + SendMessageToSYSOP(Title, MailBuffer, Length); +} + + + +VOID SendMessageToSYSOP(char * Title, char * MailBuffer, int Length) +{ + struct MsgInfo * Msg = AllocateMsgRecord(); + BIDRec * BIDRec; + + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + + Msg->length = Length; + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + FreeSemaphore(&MsgNoSemaphore); + + strcpy(Msg->from, "SYSTEM"); + if (SendSYStoSYSOPCall) + strcpy(Msg->to, SYSOPCall); + else + strcpy(Msg->to, "SYSOP"); + + strcpy(Msg->title, Title); + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + BIDRec = AllocateBIDRecord(); + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, NULL); + free(MailBuffer); +} + +VOID CheckBBSNumber(int i) +{ + // Make sure number is unique + + int Count = 0; + struct UserInfo * user; + + for (user = BBSChain; user; user = user->BBSNext) + { + if (user->BBSNumber == i) + { + Count++; + + if (Count > 1) + { + // Second with same number - Renumber this one + + user->BBSNumber = FindFreeBBSNumber(); + + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; // cant really do much else + + Logprintf(LOG_BBS, NULL, '?', "Duplicate BBS Number found. BBS %s Old BBSNumber %d New BBS Number %d", user->Call, i, user->BBSNumber); + + } + } + } +} + + +int FindFreeBBSNumber() +{ + // returns the lowest number not used by any bbs or message. + + struct MsgInfo * Msg; + struct UserInfo * user; + int i, m; + + for (i = 1; i<= NBBBS; i++) + { + for (user = BBSChain; user; user = user->BBSNext) + { + if (user->BBSNumber == i) + goto nexti; // In use + } + + // Not used by BBS - check messages + + for (m = 1; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if (check_fwd_bit(Msg->fbbs, i)) + goto nexti; // In use + + if (check_fwd_bit(Msg->forw, i)) + goto nexti; // In use + } + + // Not in Use + + return i; + +nexti:; + + } + + return 0; // All used +} + +BOOL SetupNewBBS(struct UserInfo * user) +{ + user->BBSNumber = FindFreeBBSNumber(); + + if (user->BBSNumber == 0) + return FALSE; + + user->BBSNext = BBSChain; + BBSChain = user; + + SortBBSChain(); + + ReinitializeFWDStruct(user); + + return TRUE; +} + +VOID DeleteBBS(struct UserInfo * user) +{ + struct UserInfo * BBSRec, * PrevBBS = NULL; + +#ifndef LINBPQ + RemoveMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber, MF_BYCOMMAND); +#endif + for (BBSRec = BBSChain; BBSRec; PrevBBS = BBSRec, BBSRec = BBSRec->BBSNext) + { + if (user == BBSRec) + { + if (PrevBBS == NULL) // First in chain; + { + BBSChain = BBSRec->BBSNext; + break; + } + PrevBBS->BBSNext = BBSRec->BBSNext; + break; + } + } +} + + +VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo); + +VOID SetupForwardingStruct(struct UserInfo * user) +{ + struct BBSForwardingInfo * ForwardingInfo; + + char Key[100] = "BBSForwarding."; + char Temp[100]; + + HKEY hKey=0; + char RegKey[100] = "SOFTWARE\\G8BPQ\\BPQ32\\BPQMailChat\\BBSForwarding\\"; + + int m; + struct MsgInfo * Msg; + + ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + + if (UsingingRegConfig == 0) + { + // Config from file + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + strcat(Key, "*"); + + strcat(Key, user->Call); + + group = config_lookup (&cfg, Key); + + if (group == NULL) // No info + return; + else + { + ForwardingInfo->TOCalls = GetMultiStringValue(group, "TOCalls"); + ForwardingInfo->ConnectScript = GetMultiStringValue(group, "ConnectScript"); + ForwardingInfo->ATCalls = GetMultiStringValue(group, "ATCalls"); + ForwardingInfo->Haddresses = GetMultiStringValue(group, "HRoutes"); + ForwardingInfo->HaddressesP = GetMultiStringValue(group, "HRoutesP"); + ForwardingInfo->FWDTimes = GetMultiStringValue(group, "FWDTimes"); + + ForwardingInfo->Enabled = GetIntValue(group, "Enabled"); + ForwardingInfo->ReverseFlag = GetIntValue(group, "RequestReverse"); + ForwardingInfo->AllowBlocked = GetIntValue(group, "AllowBlocked"); + ForwardingInfo->AllowCompressed = GetIntValue(group, "AllowCompressed"); + ForwardingInfo->AllowB1 = GetIntValue(group, "UseB1Protocol"); + ForwardingInfo->AllowB2 = GetIntValue(group, "UseB2Protocol"); + ForwardingInfo->SendCTRLZ = GetIntValue(group, "SendCTRLZ"); + + if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) + ForwardingInfo->AllowCompressed = TRUE; + + if (ForwardingInfo->AllowCompressed) + ForwardingInfo->AllowBlocked = TRUE; + + ForwardingInfo->PersonalOnly = GetIntValue(group, "FWDPersonalsOnly"); + ForwardingInfo->SendNew = GetIntValue(group, "FWDNewImmediately"); + ForwardingInfo->FwdInterval = GetIntValue(group, "FwdInterval"); + ForwardingInfo->RevFwdInterval = GetIntValue(group, "RevFWDInterval"); + ForwardingInfo->MaxFBBBlockSize = GetIntValue(group, "MaxFBBBlock"); + ForwardingInfo->ConTimeout = GetIntValue(group, "ConTimeout"); + + if (ForwardingInfo->MaxFBBBlockSize == 0) + ForwardingInfo->MaxFBBBlockSize = 10000; + + if (ForwardingInfo->FwdInterval == 0) + ForwardingInfo->FwdInterval = 3600; + + if (ForwardingInfo->ConTimeout == 0) + ForwardingInfo->ConTimeout = 120; + + GetStringValue(group, "BBSHA", Temp, 100); + + if (Temp[0]) + ForwardingInfo->BBSHA = _strdup(Temp); + else + ForwardingInfo->BBSHA = _strdup(""); + } + } + else + { +#ifndef LINBPQ + + int retCode,Type,Vallen; + + strcat(RegKey, user->Call); + retCode = RegOpenKeyEx (REGTREE, RegKey, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode != ERROR_SUCCESS) + return; + else + { + ForwardingInfo->ConnectScript = RegGetMultiStringValue(hKey, "Connect Script"); + ForwardingInfo->TOCalls = RegGetMultiStringValue(hKey, "TOCalls"); + ForwardingInfo->ATCalls = RegGetMultiStringValue(hKey, "ATCalls"); + ForwardingInfo->Haddresses = RegGetMultiStringValue(hKey, "HRoutes"); + ForwardingInfo->HaddressesP = RegGetMultiStringValue(hKey, "HRoutesP"); + ForwardingInfo->FWDTimes = RegGetMultiStringValue(hKey, "FWD Times"); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Enabled", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->Enabled,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "RequestReverse", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->ReverseFlag,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "AllowCompressed", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowCompressed,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Use B1 Protocol", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB1,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Use B2 Protocol", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB2,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "SendCTRLZ", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendCTRLZ,(ULONG *)&Vallen); + + if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) + ForwardingInfo->AllowCompressed = TRUE; + + Vallen=4; + retCode += RegQueryValueEx(hKey, "FWD Personals Only", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->PersonalOnly,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "FWD New Immediately", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendNew,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"FWDInterval",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->FwdInterval,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"RevFWDInterval",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->RevFwdInterval,(ULONG *)&Vallen); + + RegQueryValueEx(hKey,"MaxFBBBlock",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->MaxFBBBlockSize,(ULONG *)&Vallen); + + if (ForwardingInfo->MaxFBBBlockSize == 0) + ForwardingInfo->MaxFBBBlockSize = 10000; + + if (ForwardingInfo->FwdInterval == 0) + ForwardingInfo->FwdInterval = 3600; + + Vallen=0; + retCode = RegQueryValueEx(hKey,"BBSHA",0 , (ULONG *)&Type,NULL, (ULONG *)&Vallen); + + if (retCode != 0) + { + // No Key - Get from WP?? + + WPRec * ptr = LookupWP(user->Call); + + if (ptr) + { + if (ptr->first_homebbs) + { + ForwardingInfo->BBSHA = _strdup(ptr->first_homebbs); + } + } + } + + if (Vallen) + { + ForwardingInfo->BBSHA = malloc(Vallen); + RegQueryValueEx(hKey, "BBSHA", 0, (ULONG *)&Type, ForwardingInfo->BBSHA,(ULONG *)&Vallen); + } + + RegCloseKey(hKey); + } +#endif + } + + // Convert FWD Times and H Addresses + + if (ForwardingInfo->FWDTimes) + SetupFwdTimes(ForwardingInfo); + + if (ForwardingInfo->Haddresses) + SetupHAddreses(ForwardingInfo); + + if (ForwardingInfo->HaddressesP) + SetupHAddresesP(ForwardingInfo); + + if (ForwardingInfo->BBSHA) + { + if (ForwardingInfo->BBSHA[0]) + SetupHAElements(ForwardingInfo); + else + { + free(ForwardingInfo->BBSHA); + ForwardingInfo->BBSHA = NULL; + } + } + + for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + // If any forward bits are set, increment count on BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + user->ForwardingInfo->MsgCount++; + } + } + } +} + +VOID * GetMultiStringValue(config_setting_t * group, char * ValueName) +{ + char * ptr1; + char * MultiString = NULL; + const char * ptr; + int Count = 0; + char ** Value; + config_setting_t *setting; + char * Save; + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + setting = config_setting_get_member (group, ValueName); + + if (setting) + { + ptr = config_setting_get_string (setting); + + Save = _strdup(ptr); // DOnt want to change config string + ptr = Save; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + if (strlen(ptr)) // ignore null elements + { + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count++] = _strdup(ptr); + } + ptr = ptr1; + } + free(Save); + } + + Value[Count] = NULL; + return Value; +} + + +VOID * RegGetMultiStringValue(HKEY hKey, char * ValueName) +{ +#ifdef LINBPQ + return NULL; +#else + int retCode,Type,Vallen; + char * MultiString = NULL; + int ptr, len; + int Count = 0; + char ** Value; + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + + Value[0] = NULL; + + Vallen=0; + + + retCode = RegQueryValueEx(hKey, ValueName, 0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if ((retCode != 0) || (Vallen < 3)) // Two nulls means empty multistring + { + free(Value); + return FALSE; + } + + MultiString = malloc(Vallen); + + retCode = RegQueryValueEx(hKey, ValueName, 0, + (ULONG *)&Type,(UCHAR *)MultiString,(ULONG *)&Vallen); + + ptr=0; + + while (MultiString[ptr]) + { + len=strlen(&MultiString[ptr]); + + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count++] = _strdup(&MultiString[ptr]); + ptr+= (len + 1); + } + + Value[Count] = NULL; + + free(MultiString); + + return Value; +#endif +} + +VOID FreeForwardingStruct(struct UserInfo * user) +{ + struct BBSForwardingInfo * ForwardingInfo; + int i; + + + ForwardingInfo = user->ForwardingInfo; + + FreeList(ForwardingInfo->TOCalls); + FreeList(ForwardingInfo->ATCalls); + FreeList(ForwardingInfo->Haddresses); + FreeList(ForwardingInfo->HaddressesP); + + i=0; + if(ForwardingInfo->HADDRS) + { + while(ForwardingInfo->HADDRS[i]) + { + FreeList(ForwardingInfo->HADDRS[i]); + i++; + } + free(ForwardingInfo->HADDRS); + free(ForwardingInfo->HADDROffet); + } + + i=0; + if(ForwardingInfo->HADDRSP) + { + while(ForwardingInfo->HADDRSP[i]) + { + FreeList(ForwardingInfo->HADDRSP[i]); + i++; + } + free(ForwardingInfo->HADDRSP); + } + + FreeList(ForwardingInfo->ConnectScript); + FreeList(ForwardingInfo->FWDTimes); + if (ForwardingInfo->FWDBands) + { + i=0; + while(ForwardingInfo->FWDBands[i]) + { + free(ForwardingInfo->FWDBands[i]); + i++; + } + free(ForwardingInfo->FWDBands); + } + if (ForwardingInfo->BBSHAElements) + { + i=0; + while(ForwardingInfo->BBSHAElements[i]) + { + free(ForwardingInfo->BBSHAElements[i]); + i++; + } + free(ForwardingInfo->BBSHAElements); + } + free(ForwardingInfo->BBSHA); + +} + +VOID FreeList(char ** Hddr) +{ + VOID ** Save; + + if (Hddr) + { + Save = (void **)Hddr; + while(Hddr[0]) + { + free(Hddr[0]); + Hddr++; + } + free(Save); + } +} + + +VOID ReinitializeFWDStruct(struct UserInfo * user) +{ + if (user->ForwardingInfo) + { + FreeForwardingStruct(user); + free(user->ForwardingInfo); + } + + SetupForwardingStruct(user); + +} + +VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo) +{ + char ** Times = ForwardingInfo->FWDTimes; + int Start, End; + int Count = 0; + + ForwardingInfo->FWDBands = zalloc(sizeof(struct FWDBAND)); + + if (Times) + { + while(Times[0]) + { + ForwardingInfo->FWDBands = realloc(ForwardingInfo->FWDBands, (Count+2)* sizeof(struct FWDBAND)); + ForwardingInfo->FWDBands[Count] = zalloc(sizeof(struct FWDBAND)); + + Start = atoi(Times[0]); + End = atoi(&Times[0][5]); + + ForwardingInfo->FWDBands[Count]->FWDStartBand = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; + ForwardingInfo->FWDBands[Count]->FWDEndBand = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; + + Count++; + Times++; + } + ForwardingInfo->FWDBands[Count] = NULL; + } +} +void StartForwarding(int BBSNumber, char ** TempScript) +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + time_t NOW = time(NULL); + + + for (user = BBSChain; user; user = user->BBSNext) + { + // See if any messages are queued for this BBS + + ForwardingInfo = user->ForwardingInfo; + + if ((BBSNumber == 0) || (user->BBSNumber == BBSNumber)) + if (ForwardingInfo) + if (ForwardingInfo->Enabled || BBSNumber) // Menu Command overrides enable + if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) + if (BBSNumber || SeeifMessagestoForward(user->BBSNumber, NULL) || + (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) // Menu Command overrides Reverse + { + user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used + + // See if TempScript requested + + if (user->ForwardingInfo->TempConnectScript) + FreeList(user->ForwardingInfo->TempConnectScript); + + user->ForwardingInfo->TempConnectScript = TempScript; + + if (ConnecttoBBS(user)) + ForwardingInfo->Forwarding = TRUE; + } + } + + return; +} + +size_t fwritex(CIRCUIT * conn, void * _Str, size_t _Size, size_t _Count, FILE * _File) +{ + if (_File) + return fwrite(_Str, _Size, _Count, _File); + + // Appending to MailBuffer + + memcpy(&conn->MailBuffer[conn->InputLen], _Str, _Count); + conn->InputLen += (int)_Count; + + return _Count; +} + + +BOOL ForwardMessagestoFile(CIRCUIT * conn, char * FN) +{ + BOOL AddCRLF = FALSE; + BOOL AutoImport = FALSE; + FILE * Handle = NULL; + char * Context; + BOOL Email = FALSE; + time_t now = time(NULL); + char * param; + + FN = strtok_s(FN, " ,", &Context); + + param = strtok_s(NULL, " ,", &Context); + + if (param) + { + if (_stricmp(param, "ADDCRLF") == 0) + AddCRLF = TRUE; + + if (_stricmp(param, "AutoImport") == 0) + AutoImport = TRUE; + + param = strtok_s(NULL, " ,", &Context); + + if (param) + { + if (_stricmp(param, "ADDCRLF") == 0) + AddCRLF = TRUE; + + if (_stricmp(param, "AutoImport") == 0) + AutoImport = TRUE; + + } + } + // If FN is an email address, write to a temp file, and send via rms or emali gateway + + if (strchr(FN, '@') || _memicmp(FN, "RMS:", 4) == 0) + { + Email = TRUE; + AddCRLF =TRUE; + conn->MailBuffer=malloc(100000); + conn->MailBufferSize=100000; + conn->InputLen = 0; + } + else + { + Handle = fopen(FN, "ab"); + + if (Handle == NULL) + { + int err = GetLastError(); + Logprintf(LOG_BBS, conn, '!', "Failed to open Export File %s", FN); + return FALSE; + } + } + + while (FindMessagestoForward(conn)) + { + struct MsgInfo * Msg; + struct tm * tm; + time_t temp; + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + int MsgLen; + char * MsgPtr; + char Line[256]; + int len; + struct UserInfo * user = conn->UserPointer; + int Index = 0; + + Msg = conn->FwdMsg; + + if (Email) + if (conn->InputLen + Msg->length + 500 > conn->MailBufferSize) + break; + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else if (Msg->type == 'T') + Index = TMSG; + + + if (Msg->via[0]) + len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, + Msg->via, Msg->from, Msg->bid); + else + len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); + + fwritex(conn, Line, 1, len, Handle); + + len = sprintf(Line, "%s\r\n", Msg->title); + fwritex(conn, Line, 1, len, Handle); + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r\n"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + MsgLen = conn->FwdMsg->length; + + // If a B2 Message, remove B2 Header + + if (conn->FwdMsg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + MsgPtr = strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + conn->FwdMsg->number, BBSName, HRoute, RlineVer); + + fwritex(conn, Line, 1, len, Handle); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + fwritex(conn, "\r\n", 1, 2, Handle); + + fwritex(conn, MsgPtr, 1, MsgLen, Handle); + + if (MsgPtr[MsgLen - 2] == '\r') + fwritex(conn, "/EX\r\n", 1, 5, Handle); + else + fwritex(conn, "\r\n/EX\r\n", 1, 7, Handle); + + if (AddCRLF) + fwritex(conn, "\r\n", 1, 2, Handle); + + free(MsgBytes); + + user->Total.MsgsSent[Index]++; + user->Total.BytesForwardedOut[Index] += MsgLen; + + Msg->datechanged = now; + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->UserPointer->ForwardingInfo->MsgCount--; + } + + if (Email) + { + struct MsgInfo * Msg; + BIDRec * BIDRec; + + if (conn->InputLen == 0) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + return TRUE; + } + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + FreeSemaphore(&MsgNoSemaphore); + MsgnotoMsg[Msg->number] = Msg; + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datecreated = Msg->datechanged = Msg->datereceived = now; + + strcpy(Msg->from, BBSName); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + if (AutoImport) + sprintf(Msg->title, "Batched messages for AutoImport from BBS %s", BBSName); + else + sprintf(Msg->title, "Batched messages from BBS %s", BBSName); + + Msg->length = conn->InputLen; + CreateMessageFile(conn, Msg); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + if (_memicmp(FN, "SMTP:", 5) == 0) + { + strcpy(Msg->via, &FN[5]); + SMTPMsgCreated=TRUE; + } + else + { + strcpy(Msg->to, "RMS"); + if (_memicmp(FN, "RMS:", 4) == 0) + strcpy(Msg->via, &FN[4]); + else + strcpy(Msg->via, FN); + } + + MatchMessagetoBBSList(Msg, conn); + + SaveMessageDatabase(); + SaveBIDDatabase(); + } + else + fclose(Handle); + + SaveMessageDatabase(); + return TRUE; +} + +BOOL ForwardMessagetoFile(struct MsgInfo * Msg, FILE * Handle) +{ + struct tm * tm; + time_t temp; + + char * MsgBytes = ReadMessageFile(Msg->number); + char * MsgPtr; + char Line[256]; + int len; + int MsgLen = Msg->length; + + if (Msg->via[0]) + len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, + Msg->via, Msg->from, Msg->bid); + else + len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); + + fwrite(Line, 1, len, Handle); + + len = sprintf(Line, "%s\r\n", Msg->title); + fwrite(Line, 1, len, Handle); + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r\n"); + MsgLen = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + + // If a B2 Message, remove B2 Header + + if (Msg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + + MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + Msg->number, BBSName, HRoute, RlineVer); + + fwrite(Line, 1, len, Handle); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + fwrite("\r\n", 1, 2, Handle); + + fwrite(MsgPtr, 1, MsgLen, Handle); + + if (MsgPtr[MsgLen - 2] == '\r') + fwrite("/EX\r\n", 1, 5, Handle); + else + fwrite("\r\n/EX\r\n", 1, 7, Handle); + + free(MsgBytes); + + return TRUE; + +} + +BOOL ConnecttoBBS (struct UserInfo * user) +{ + int n, p; + CIRCUIT * conn; + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + + for (n = NumberofStreams-1; n >= 0 ; n--) + { + conn = &Connections[n]; + + if (conn->Active == FALSE) + { + p = conn->BPQStream; + memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything + conn->BPQStream = p; + + // Can't set Active until Connected or Stuck Session detertor can clear session. + // But must set Active before Connected() runs or will appear is Incoming Connect. + // Connected() is semaphored, so get semaphore before ConnectUsingAppl + // Probably better to semaphore lost session code instead + + + strcpy(conn->Callsign, user->Call); + conn->BBSFlags |= (RunningConnectScript | OUTWARDCONNECT); + conn->UserPointer = user; + + Logprintf(LOG_BBS, conn, '|', "Connecting to BBS %s", user->Call); + + ForwardingInfo->MoreLines = TRUE; + + GetSemaphore(&ConSemaphore, 1); + conn->Active = TRUE; + ConnectUsingAppl(conn->BPQStream, BBSApplMask); + FreeSemaphore(&ConSemaphore); + + // If we are sending to a dump pms we may need to connect using the message sender's callsign. + // But we wont know until we run the connect script, which is a bit late to change call. Could add + // flag to forwarding config, but easier to look for SETCALLTOSENDER in the connect script. + + if (strstr(ForwardingInfo->ConnectScript[0], "SETCALLTOSENDER")) + { + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + // We have a message to send + + struct MsgInfo * Msg; + unsigned char AXCall[7]; + + Msg = conn->FwdMsg; + ConvToAX25(Msg->from, AXCall); + ChangeSessionCallsign(p, AXCall); + + conn->BBSFlags |= TEXTFORWARDING | SETCALLTOSENDER | NEWPACCOM; + conn->NextMessagetoForward = 0; // was set by FindMessages + } + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = FALSE; + } +#ifdef LINBPQ + { + BPQVECSTRUC * SESS; + SESS = &BPQHOSTVECTOR[conn->BPQStream - 1]; + + if (SESS->HOSTSESSION == NULL) + { + Logprintf(LOG_BBS, NULL, '|', "No L4 Sessions for connect to BBS %s", user->Call); + return FALSE; + } + + SESS->HOSTSESSION->Secure_Session = 1; + } +#endif + + strcpy(conn->Callsign, user->Call); + + // Connected Event will trigger connect to remote system + + RefreshMainWindow(); + + return TRUE; + } + } + + Logprintf(LOG_BBS, NULL, '|', "No Free Streams for connect to BBS %s", user->Call); + + return FALSE; + +} + +struct DelayParam +{ + struct UserInfo * User; + CIRCUIT * conn; + int Delay; +}; + +struct DelayParam DParam; // Not 100% safe, but near enough + +VOID ConnectDelayThread(struct DelayParam * DParam) +{ + struct UserInfo * User = DParam->User; + int Delay = DParam->Delay; + + User->ForwardingInfo->Forwarding = TRUE; // Minimize window for two connects + + Sleep(Delay); + + User->ForwardingInfo->Forwarding = TRUE; + ConnecttoBBS(User); + + return; +} + +VOID ConnectPauseThread(struct DelayParam * DParam) +{ + CIRCUIT * conn = DParam->conn; + int Delay = DParam->Delay; + char Msg[] = "Pause Ok\r "; + + Sleep(Delay); + + ProcessBBSConnectScript(conn, Msg, 9); + + return; +} + + +/* +BOOL ProcessBBSConnectScriptInner(CIRCUIT * conn, char * Buffer, int len); + + +BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) +{ + BOOL Ret; + GetSemaphore(&ScriptSEM); + Ret = ProcessBBSConnectScriptInner(conn, Buffer, len); + FreeSemaphore(&ScriptSEM); + + return Ret; +} +*/ + +BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) +{ + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + char ** Scripts; + char callsign[10]; + int port, sesstype, paclen, maxframe, l4window; + char * ptr, * ptr2; + + WriteLogLine(conn, '<',Buffer, len-1, LOG_BBS); + + Buffer[len]=0; + _strupr(Buffer); + + if (ForwardingInfo->TempConnectScript) + Scripts = ForwardingInfo->TempConnectScript; + else + Scripts = ForwardingInfo->ConnectScript; + + if (ForwardingInfo->ScriptIndex == -1) + { + // First Entry - if first line is TIMES, check and skip forward if necessary + + int n = 0; + int Start, End; + time_t now = time(NULL), StartSecs, EndSecs; + char * Line; + + if (Localtime) + now -= (time_t)_MYTIMEZONE; + + now %= 86400; + Line = Scripts[n]; + + // Skip comments + + while (Line && ((strcmp(Line, " ") == 0 || Line[0] == ';' || Line[0] == '#'))) + { + n++; + Line = Scripts[n]; + } + + if (Line == NULL) + { + // No more lines - Disconnect + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Line, "TIMES", 5) == 0) + { + NextBand: + Start = atoi(&Line[6]); + End = atoi(&Line[11]); + + StartSecs = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; + EndSecs = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; + + if ((StartSecs <= now) && (EndSecs >= now)) + goto InBand; // In band + + // Look for next TIME + NextLine: + Line = Scripts[++n]; + + if (Line == NULL) + { + // No more lines - Disconnect + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Line, "TIMES", 5) != 0) + goto NextLine; + else + goto NextBand; +InBand: + ForwardingInfo->ScriptIndex = n; + } + + } + else + { + // Dont check first time through + + if (strcmp(Buffer, "*** CONNECTED ") != 0) + { + if (Scripts[ForwardingInfo->ScriptIndex] == NULL || + _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished + _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished + { + ForwardingInfo->MoreLines = FALSE; + } + if (!ForwardingInfo->MoreLines) + goto CheckForSID; + } + } + + if (strstr(Buffer, "BUSY") || strstr(Buffer, "FAILURE") || + (strstr(Buffer, "DOWNLINK") && strstr(Buffer, "ATTEMPTING") == 0) || + strstr(Buffer, "SORRY") || strstr(Buffer, "INVALID") || strstr(Buffer, "RETRIED") || + strstr(Buffer, "NO CONNECTION TO") || strstr(Buffer, "ERROR - ") || + strstr(Buffer, "UNABLE TO CONNECT") || strstr(Buffer, "DISCONNECTED") || + strstr(Buffer, "FAILED TO CONNECT") || strstr(Buffer, "REJECTED")) + { + // Connect Failed + + char * Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + int Delay = 1000; + + // Look for an alternative connect block (Starting with ELSE) + + ElseLoop: + + // Skip any comments + + while (Cmd && ((strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#'))) + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + + // TIMES terminates a script + + if (Cmd == 0 || _memicmp(Cmd, "TIMES", 5) == 0) // Only Check until script is finished + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "ELSE", 4) != 0) + { + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + goto ElseLoop; + } + + if (_memicmp(&Cmd[5], "DELAY", 5) == 0) + Delay = atoi(&Cmd[10]) * 1000; + else + Delay = 1000; + + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + + DParam.Delay = Delay; + DParam.User = conn->UserPointer; + + _beginthread((void (*)(void *))ConnectDelayThread, 0, &DParam); + + return FALSE; + } + + // The pointer is only updated when we get the connect, so we can tell when the last line is acked + // The first entry is always from Connected event, so don't have to worry about testing entry -1 below + + + // NETROM to KA node returns + + //c 1 milsw + //WIRAC:N9PMO-2} Connected to MILSW + //###CONNECTED TO NODE MILSW(N9ZXS) CHANNEL A + //You have reached N9ZXS's KA-Node MILSW + //ENTER COMMAND: B,C,J,N, or Help ? + + //C KB9PRF-7 + //###LINK MADE + //###CONNECTED TO NODE KB9PRF-7(KB9PRF-4) CHANNEL A + + // Look for (Space)Connected so we aren't fooled by ###CONNECTED TO NODE, which is not + // an indication of a connect. + + if (strstr(Buffer, " CONNECTED") || strstr(Buffer, "PACLEN") || strstr(Buffer, "IDLETIME") || + strstr(Buffer, "OK") || strstr(Buffer, "###LINK MADE") || strstr(Buffer, "VIRTUAL CIRCUIT ESTABLISHED")) + { + // If connected to SYNC, save IP address and port + + char * Cmd; + + if (strstr(Buffer, "*** CONNECTED TO SYNC")) + { + char * IPAddr = &Buffer[22]; + char * Port = strlop(IPAddr, ':'); + + if (Port) + { + if (conn->SyncHost) + free(conn->SyncHost); + + conn->SyncHost = _strdup(IPAddr); + conn->SyncPort = atoi(Port); + } + } + + if (conn->SkipConn) + { + conn->SkipConn = FALSE; + return TRUE; + } + + LoopBack: + + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + + // Only Check until script is finished + + if (Cmd && (strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#')) + goto LoopBack; // Blank line + + if (Cmd && _memicmp(Cmd, "TIMES", 5) != 0 && _memicmp(Cmd, "ELSE", 4) != 0) // Only Check until script is finished + { + if (_memicmp(Cmd, "MSGTYPE", 7) == 0) + { + char * ptr; + + // Select Types to send. Only send types in param. Only reverse if R in param + + _strupr(Cmd); + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = FALSE; + + strcpy(conn->MSGTYPES, &Cmd[8]); + + if (strchr(&Cmd[8], 'R')) conn->DoReverse = TRUE; + + ptr = strchr(&Cmd[8], 'B'); + + if (ptr) + { + conn->SendB = TRUE; + conn->MaxBLen = atoi(++ptr); + if (conn->MaxBLen == 0) conn->MaxBLen = 99999999; + } + + ptr = strchr(&Cmd[8], 'T'); + + if (ptr) + { + conn->SendT = TRUE; + conn->MaxTLen = atoi(++ptr); + if (conn->MaxTLen == 0) conn->MaxTLen = 99999999; + } + ptr = strchr(&Cmd[8], 'P'); + + if (ptr) + { + conn->SendP = TRUE; + conn->MaxPLen = atoi(++ptr); + if (conn->MaxPLen == 0) conn->MaxPLen = 99999999; + } + + // If nothing to do, terminate script + + if (conn->DoReverse || SeeifMessagestoForward(conn->UserPointer->BBSNumber, conn)) + goto LoopBack; + + Logprintf(LOG_BBS, conn, '?', "Nothing to do - quitting"); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "INTERLOCK ", 10) == 0) + { + // Used to limit connects on a port to 1 + + int Port; + char Option[80]; + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + sscanf(&Cmd[10], "%d %s", &Port, &Option[0]); + + if (CountConnectionsOnPort(Port)) + { + Logprintf(LOG_BBS, conn, '?', "Interlocked Port is busy - quitting"); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + goto LoopBack; + } + + if (_memicmp(Cmd, "IDLETIME ", 9) == 0) + { + int idle = atoi(&Cmd[9]); + + ChangeSessionIdletime(conn->BPQStream, idle); + goto LoopBack; + } + + if (_memicmp(Cmd, "RADIO AUTH", 10) == 0) + { + // Generate a Password to enable RADIO commands on a remote node + char AuthCommand[80]; + + _strupr(Cmd); + strcpy(AuthCommand, Cmd); + + CreateOneTimePassword(&AuthCommand[11], &Cmd[11], 0); + + nodeprintf(conn, "%s\r", AuthCommand); + return TRUE; + } + + if (_memicmp(Cmd, "SKIPCON", 7) == 0) + { + // Remote Node sends Connected in CTEXT - we need to swallow it + + conn->SkipConn = TRUE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SendWL2KPM", 10) == 0|| _memicmp(Cmd, "SendWL2KFW", 10) == 0) + { + // Send ;FW: command + + conn->SendWL2KFW = TRUE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SKIPPROMPT", 10) == 0) + { + // Remote Node sends > at end of CTEXT - we need to swallow it + + conn->SkipPrompt++; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "TEXTFORWARDING", 10) == 0) + { + conn->BBSFlags |= TEXTFORWARDING; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SETCALLTOSENDER", 15) == 0) + { + conn->BBSFlags |= TEXTFORWARDING | SETCALLTOSENDER; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "RADIOONLY", 9) == 0) + { + conn->BBSFlags |= WINLINKRO; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SYNC", 4) == 0) + { + conn->BBSFlags |= SYNCMODE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "NEEDLF", 6) == 0) + { + conn->BBSFlags |= NEEDLF; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "MCASTRX", 6) == 0) + { + conn->BBSFlags |= MCASTRX; + conn->MCastListenTime = atoi(&Cmd[7]) * 6; // Time to run session for *6 as value is mins put timer ticks 10 secs + + // send MCAST to Node + + nodeprintfEx(conn, "MCAST\r"); + return TRUE; + } + + if (_memicmp(Cmd, "FLARQ", 5) == 0) + { + conn->BBSFlags |= FLARQMAIL; + + CheckForEnd: + if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || + memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished + memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished + ForwardingInfo->MoreLines = FALSE; + + goto LoopBack; + } + if (_memicmp(Cmd, "PAUSE", 5) == 0) + { + // Pause script + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + DParam.Delay = atoi(&Cmd[6]) * 1000; + DParam.conn = conn; + + _beginthread((void (*)(void *))ConnectPauseThread, 0, &DParam); + + return TRUE; + } + + if (_memicmp(Cmd, "FILE", 4) == 0) + { + if (Cmd[4] == 0) + { + // Missing Filename + + Logprintf(LOG_BBS, conn, '!', "Export file name missing"); + } + else + ForwardMessagestoFile(conn, &Cmd[5]); + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "SMTP", 4) == 0) + { + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + + SendAMPRSMTP(conn); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + + if (_memicmp(Cmd, "IMPORT", 6) == 0) + { + char * File, * Context; + int Num; + char * Temp = _strdup(Cmd); + + File = strtok_s(&Temp[6], " ", &Context); + + if (File && File[0]) + { + Num = ImportMessages(NULL, File, TRUE); + + Logprintf(LOG_BBS, NULL, '|', "Imported %d Message(s) from %s", Num, File); + + if (Context && _stricmp(Context, "delete") == 0) + DeleteFile(File); + } + free(Temp); + + if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || + memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished + memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + goto LoopBack; + } + + // Anything else is sent to Node + + // Replace \ with # so can send commands starting with # + + if (Cmd[0] == '\\') + { + Cmd[0] = '#'; + nodeprintfEx(conn, "%s\r", Cmd); + Cmd[0] = '\\'; // Put \ back in script + } + else + nodeprintfEx(conn, "%s\r", Cmd); + + return TRUE; + } + + // End of script. + + ForwardingInfo->MoreLines = FALSE; + + if (conn->BBSFlags & MCASTRX) + { + // No session with Multicast, so no SID + + conn->BBSFlags &= ~RunningConnectScript; + return TRUE; + } + + if (conn->BBSFlags & FLARQMAIL) + { + // FLARQ doesnt send a prompt - Just send message(es) + + conn->UserPointer->Total.ConnectsOut++; + conn->BBSFlags &= ~RunningConnectScript; + ForwardingInfo->LastReverseForward = time(NULL); + + // Update Paclen + + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + + if (paclen > 0) + conn->paclen = paclen; + + SendARQMail(conn); + return TRUE; + } + + + return TRUE; + } + + ptr = strchr(Buffer, '}'); + + if (ptr && ForwardingInfo->MoreLines) // Beware it could be part of ctext + { + // Could be respsonse to Node Command + + ptr+=2; + + ptr2 = strchr(&ptr[0], ' '); + + if (ptr2) + { + if (_memicmp(ptr, Scripts[ForwardingInfo->ScriptIndex], ptr2-ptr) == 0) // Reply to last sscript command + { + if (Scripts[ForwardingInfo->ScriptIndex+1] && _memicmp(Scripts[ForwardingInfo->ScriptIndex+1], "else", 4) == 0) + { + // stray match or misconfigured + + return TRUE; + } + + ForwardingInfo->ScriptIndex++; + + if (Scripts[ForwardingInfo->ScriptIndex]) + if (_memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) != 0) + nodeprintf(conn, "%s\r", Scripts[ForwardingInfo->ScriptIndex]); + + return TRUE; + } + } + } + + // Not Success or Fail. If last line is still outstanding, wait fot Response + // else look for SID or Prompt + + if (conn->SkipPrompt && Buffer[len-2] == '>') + { + conn->SkipPrompt--; + return TRUE; + } + + if (ForwardingInfo->MoreLines) + return TRUE; + + // No more steps, Look for SID or Prompt + +CheckForSID: + + if (strstr(Buffer, "POSYNCHELLO")) // RMS RELAY Sync process + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + ForwardingInfo->LastReverseForward = time(NULL); + + ProcessLine(conn, 0, Buffer, len); + return FALSE; + } + + if (strstr(Buffer, "SORRY, NO")) // URONODE + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (memcmp(Buffer, ";PQ: ", 5) == 0) + { + // Secure CMS challenge + + int Len; + struct UserInfo * User = conn->UserPointer; + char * Pass = User->CMSPass; + int Response ; + char RespString[12]; + char ConnectingCall[10]; + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + + SESS += conn->BPQStream - 1; + + ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); + + strlop(ConnectingCall, ' '); + + if (Pass[0] == 0) + { + Pass = User->pass; // Old Way + if (Pass[0] == 0) + { + strlop(ConnectingCall, '-'); + User = LookupCall(ConnectingCall); + if (User) + Pass = User->CMSPass; + } + } + + // + + Response = GetCMSHash(&Buffer[5], Pass); + + sprintf(RespString, "%010d", Response); + + Len = sprintf(conn->SecureMsg, ";PR: %s\r", &RespString[2]); + + // Save challengs in case needed for FW lines + + strcpy(conn->PQChallenge, &Buffer[5]); + + return FALSE; + } + + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + { + // Update PACLEN + + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + + if (paclen > 0) + conn->paclen = paclen; + + + Parse_SID(conn, &Buffer[1], len-4); + + if (conn->BBSFlags & FBBForwarding) + { + conn->FBBIndex = 0; // ready for first block; + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + conn->FBBChecksum = 0; + } + + return TRUE; + } + + if (memcmp(Buffer, "[PAKET ", 7) == 0) + { + conn->BBSFlags |= BBS; + conn->BBSFlags |= MBLFORWARDING; + } + + if (Buffer[len-2] == '>') + { + if (conn->SkipPrompt) + { + conn->SkipPrompt--; + return TRUE; + } + + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + conn->BBSFlags &= ~RunningConnectScript; + ForwardingInfo->LastReverseForward = time(NULL); + + if (memcmp(Buffer, "[AEA PK", 7) == 0 || (conn->BBSFlags & TEXTFORWARDING)) + { + // PK232. Don't send a SID, and switch to Text Mode + + conn->BBSFlags |= (BBS | TEXTFORWARDING); + conn->Flags |= SENDTITLE; + + // Send Message. There is no mechanism for reverse forwarding + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg; + + // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ + + Msg = conn->FwdMsg; + + if ((conn->BBSFlags & SETCALLTOSENDER)) + nodeprintf(conn, "S%c %s @ %s \r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call); + else + nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, + Msg->from, Msg->bid); + } + else + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + return TRUE; + } + + if (strcmp(conn->Callsign, "RMS") == 0 || conn->SendWL2KFW) + { + // Build a ;FW: line with all calls with PollRMS Set + + // According to Lee if you use secure login the first + // must be the BBS call + // Actually I don't think we need the first, + // as that is implied + + // If a secure password is available send the new + // call|response format. + + // I think this should use the session callsign, which + // normally will be the BBS ApplCall, and not the BBS Name, + // but coudl be changed by *** LINKED + + int i, s; + char FWLine[10000] = ";FW: "; + struct UserInfo * user; + char RMSCall[20]; + char ConnectingCall[10]; + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + + SESS += conn->BPQStream - 1; + + ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); + strlop(ConnectingCall, ' '); + + strcat (FWLine, ConnectingCall); + + for (i = 0; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_POLLRMS) + { + if (user->RMSSSIDBits == 0) user->RMSSSIDBits = 1; + + for (s = 0; s < 16; s++) + { + if (user->RMSSSIDBits & (1 << s)) + { + if (s) + sprintf(RMSCall, "%s-%d", user->Call, s); + else + sprintf(RMSCall, "%s", user->Call); + + // We added connectingcall at front + + if (strcmp(RMSCall, ConnectingCall) != 0) + { + strcat(FWLine, " "); + strcat(FWLine, RMSCall); + + if (user->CMSPass[0]) + { + int Response = GetCMSHash(conn->PQChallenge, user->CMSPass); + char RespString[12]; + + sprintf(RespString, "%010d", Response); + strcat(FWLine, "|"); + strcat(FWLine, &RespString[2]); + } + } + } + } + } + } + + strcat(FWLine, "\r"); + + nodeprintf(conn, FWLine); + } + + // Only declare B1 and B2 if other end did, and we are configued for it + + nodeprintfEx(conn, BBSSID, "BPQ-", + Ver[0], Ver[1], Ver[2], Ver[3], + (conn->BBSFlags & FBBCompressed) ? "B" : "", + (conn->BBSFlags & FBBB1Mode && !(conn->BBSFlags & FBBB2Mode)) ? "1" : "", + (conn->BBSFlags & FBBB2Mode) ? "2" : "", + (conn->BBSFlags & FBBForwarding) ? "F" : "", + (conn->BBSFlags & WINLINKRO) ? "" : "J"); + + if (conn->SecureMsg[0]) + { + struct UserInfo * user; + BBSputs(conn, conn->SecureMsg); + conn->SecureMsg[0] = 0; + + // Also send a Location Comment Line + + //; GM8BPQ-10 DE G8BPQ (IO92KX) + //; WL2K DE GM8BPQ () (PAT) + + user = LookupCall(BBSName); + + if (LOC && LOC[0]) + nodeprintf(conn, "; WL2K DE %s (%s)\r", BBSName, LOC); + } + + if (conn->BPQBBS && conn->MSGTYPES[0]) + + // Send a ; MSGTYPES to control what he sends us + + nodeprintf(conn, "; MSGTYPES %s\r", conn->MSGTYPES); + + if (conn->BBSFlags & FBBForwarding) + { + if (!FBBDoForward(conn)) // Send proposal if anthing to forward + { + if (conn->DoReverse) + FBBputs(conn, "FF\r"); + else + { + FBBputs(conn, "FQ\r"); + conn->CloseAfterFlush = 20; // 2 Secs + } + } + + return TRUE; + } + + return TRUE; + } + + return TRUE; +} + +VOID Parse_SID(CIRCUIT * conn, char * SID, int len) +{ + ChangeSessionIdletime(conn->BPQStream, BBSIDLETIME); // Default Idletime for BBS Sessions + + // scan backwards for first '-' + + if (strstr(SID, "BPQCHATSERVER")) + { + Disconnect(conn->BPQStream); + return; + } + + if (strstr(SID, "RMS Ex") || strstr(SID, "Winlink Ex")) + { + conn->RMSExpress = TRUE; + conn->Paclink = FALSE; + conn->PAT = FALSE; + + // Set new RMS Users as RMS User + + if (conn->NewUser) + conn->UserPointer->flags |= F_Temp_B2_BBS; + } + + if (stristr(SID, "PAT")) + { + // Set new PAT Users as RMS User + + conn->RMSExpress = FALSE; + conn->Paclink = FALSE; + conn->PAT = TRUE; + + if (conn->NewUser) + conn->UserPointer->flags |= F_Temp_B2_BBS; + } + if (strstr(SID, "Paclink")) + { + conn->RMSExpress = FALSE; + conn->Paclink = TRUE; + } + + if (strstr(SID, "WL2K-")) + { + conn->WL2K = TRUE; + conn->BBSFlags |= WINLINKRO; + } + + if (strstr(SID, "MFJ-")) + { + conn->BBSFlags |= MFJMODE; + } + + if (_memicmp(SID, "OpenBCM", 7) == 0) + { + // We should really only do this on Telnet Connections, as OpenBCM flag is used to remove relnet transparency + + + conn->OpenBCM = TRUE; + } + + if (_memicmp(SID, "PMS-3.2", 7) == 0) + { + // Paccom TNC that doesn't send newline prompt ater receiving subject + + conn->BBSFlags |= NEWPACCOM; + } + + // See if BPQ for selective forwarding + + if (strstr(SID, "BPQ")) + conn->BPQBBS = TRUE; + + while (len > 0) + { + switch (SID[len--]) + { + case '-': + + len=0; + break; + + case '$': + + conn->BBSFlags |= BBS | MBLFORWARDING; + conn->Paging = FALSE; + + break; + + case 'F': // FBB Blocked Forwarding + + // We now support blocked uncompressed. Not necessarily compatible with FBB + + if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) + { + // We need to allocate a forwarding structure + + conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; + conn->UserPointer->ForwardingInfo->AllowBlocked = TRUE; + conn->UserPointer->BBSNumber = NBBBS; + } + + if (conn->UserPointer->ForwardingInfo->AllowBlocked) + { + conn->BBSFlags |= FBBForwarding | BBS; + conn->BBSFlags &= ~MBLFORWARDING; + + conn->Paging = FALSE; + + if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) + { + // We need to allocate a forwarding structure + + conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; + conn->UserPointer->BBSNumber = NBBBS; + } + + // Allocate a Header Block + + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + } + break; + + case 'J': + + // Suspected to be associated with Winlink Radio Only + + conn->BBSFlags &= ~WINLINKRO; + break; + + case 'B': + + if (conn->UserPointer->ForwardingInfo->AllowCompressed) + { + conn->BBSFlags |= FBBCompressed; + conn->DontSaveRestartData = FALSE; // Allow restarts + + // Look for 1 or 2 or 12 as next 2 chars + + if (SID[len+2] == '1') + { + if (conn->UserPointer->ForwardingInfo->AllowB1 || + conn->UserPointer->ForwardingInfo->AllowB2) // B2 implies B1 + conn->BBSFlags |= FBBB1Mode; + + if (SID[len+3] == '2') + if (conn->UserPointer->ForwardingInfo->AllowB2) + conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) + + break; + } + + if (SID[len+2] == '2') + { + if (conn->UserPointer->ForwardingInfo->AllowB2) + conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) + + if (conn->UserPointer->ForwardingInfo->AllowB1) + conn->BBSFlags |= FBBB1Mode; // B2 should allow fallback to B1 (but RMS doesnt!) + + } + break; + } + + break; + } + } + + // Only allow blocked non-binary to other BPQ Nodes + + if ((conn->BBSFlags & FBBForwarding) && ((conn->BBSFlags & FBBCompressed) == 0) && (conn->BPQBBS == 0)) + { + // Switch back to MBL + + conn->BBSFlags |= MBLFORWARDING; + conn->BBSFlags &= ~FBBForwarding; // Turn off FBB Blocked + } + + return; +} + +VOID BBSSlowTimer() +{ + ConnectionInfo * conn; + int n; + + // Called every 10 seconds + + MCastTimer(); + + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active == TRUE) + { + // Check for stuck BBS sessions (BBS session but no Node Session) + + int state; + + GetSemaphore(&ConSemaphore, 1); + SessionStateNoAck(conn->BPQStream, &state); + FreeSemaphore(&ConSemaphore); + + if (state == 0) // No Node Session + { + // is it safe just to clear Active ?? + + conn->InputMode = 0; // So Disconnect wont save partial transfer + conn->BBSFlags = 0; + Disconnected (conn->BPQStream); + continue; + } + + if (conn->BBSFlags & MCASTRX) + MCastConTimer(conn); + + + // Check SIDTImers - used to detect failure to compete SID Handshake + + if (conn->SIDResponseTimer) + { + conn->SIDResponseTimer--; + if (conn->SIDResponseTimer == 0) + { + // Disconnect Session + + Disconnect(conn->BPQStream); + } + } + } + } + + // Flush logs + + for (n = 0; n < 4; n++) + { + if (LogHandle[n]) + { + time_t LT = time(NULL); + if ((LT - LastLogTime[n]) > 30) + { + LastLogTime[n] = LT; + fclose(LogHandle[n]); + LogHandle[n] = NULL; + } + } + } +} + + +VOID FWDTimerProc() +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + time_t NOW = time(NULL); + + // Entered every 2 seconds + + for (user = BBSChain; user; user = user->BBSNext) + { + // See if any messages are queued for this BBS + + ForwardingInfo = user->ForwardingInfo; + ForwardingInfo->FwdTimer += 2; + + if (ForwardingInfo->FwdTimer >= ForwardingInfo->FwdInterval) + { + ForwardingInfo->FwdTimer=0; + + if (ForwardingInfo->FWDBands && ForwardingInfo->FWDBands[0]) + { + // Check Timebands + + struct FWDBAND ** Bands = ForwardingInfo->FWDBands; + int Count = 0; + time_t now = time(NULL); + + if (Localtime) + now -= (time_t)_MYTIMEZONE; + + now %= 86400; // Secs in day + + while(Bands[Count]) + { + if ((Bands[Count]->FWDStartBand < now) && (Bands[Count]->FWDEndBand >= now)) + goto FWD; // In band + + Count++; + } + continue; // Out of bands + } + FWD: + if (ForwardingInfo->Enabled) + { + if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) + { + //Temp Debug Code + +// Debugprintf("ReverseFlag = %d, Msgs to Forward Flag %d Msgs to Forward Count %d", +// ForwardingInfo->ReverseFlag, +// SeeifMessagestoForward(user->BBSNumber, NULL), +// CountMessagestoForward(user)); + + if (SeeifMessagestoForward(user->BBSNumber, NULL) || + (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) + + { + user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used + + + // remove any old TempScript + + if (user->ForwardingInfo->TempConnectScript) + { + FreeList(user->ForwardingInfo->TempConnectScript); + user->ForwardingInfo->TempConnectScript = NULL; + } + + if (ConnecttoBBS(user)) + ForwardingInfo->Forwarding = TRUE; + } + } + } + } + } +} + +VOID * _zalloc_dbg(size_t len, int type, char * file, int line) +{ + // ?? malloc and clear + + void * ptr; + +#ifdef WIN32 + ptr=_malloc_dbg(len, type, file, line); +#else + ptr = malloc(len); +#endif + if (ptr) + memset(ptr, 0, len); + + return ptr; +} + +struct MsgInfo * FindMessageByNumber(int msgno) + { + int m=NumberofMessages; + + struct MsgInfo * Msg; + + do + { + Msg=MsgHddrPtr[m]; + + if (Msg->number == msgno) + return Msg; + + if (Msg->number && Msg->number < msgno) // sometimes get zero msg number + return NULL; // Not found + + m--; + + } while (m > 0); + + return NULL; +} + +struct MsgInfo * FindMessageByBID(char * BID) +{ + int m = NumberofMessages; + + struct MsgInfo * Msg; + + while (m > 0) + { + Msg = MsgHddrPtr[m]; + + if (strcmp(Msg->bid, BID) == 0) + return Msg; + + m--; + } + + return NULL; +} + +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len) +{ + unsigned char hash[50]; + unsigned char key[100]; + unsigned int i, j = 0, val1, val2; + unsigned char hostname[100]=""; + + gethostname(hostname, 100); + + strcpy(key, hostname); + strcat(key, ISPPOP3Name); + + md5(key, hash); + memcpy(&hash[16], hash, 16); // in case very long password + + // String is now encoded as hex pairs, but still need to decode old format + + for (i=0; i < len; i++) + { + if (Encrypt[i] < '0' || Encrypt[i] > 'F') + goto OldFormat; + } + + // Only '0' to 'F' + + for (i=0; i < len; i++) + { + val1 = Encrypt[i++]; + val1 -= '0'; + if (val1 > 9) + val1 -= 7; + + val2 = Encrypt[i]; + val2 -= '0'; + if (val2 > 9) + val2 -= 7; + + Pass[j] = (val1 << 4) | val2; + Pass[j] ^= hash[j]; + j++; + } + + return; + +OldFormat: + + for (i=0; i < len; i++) + { + Pass[i] = Encrypt[i] ^ hash[i]; + } + + return; +} + +int EncryptPass(char * Pass, char * Encrypt) +{ + unsigned char hash[50]; + unsigned char key[100]; + unsigned int i, val; + unsigned char hostname[100]; + unsigned char extendedpass[100]; + unsigned int passlen; + unsigned char * ptr; + + gethostname(hostname, 100); + + strcpy(key, hostname); + strcat(key, ISPPOP3Name); + + md5(key, hash); + memcpy(&hash[16], hash, 16); // in case very long password + + // if password is less than 16 chars, extend with zeros + + passlen=(int)strlen(Pass); + + strcpy(extendedpass, Pass); + + if (passlen < 16) + { + for (i=passlen+1; i <= 16; i++) + { + extendedpass[i] = 0; + } + + passlen = 16; + } + + ptr = Encrypt; + Encrypt[0] = 0; + + for (i=0; i < passlen; i++) + { + val = extendedpass[i] ^ hash[i]; + ptr += sprintf(ptr, "%02X", val); + } + + return passlen * 2; +} + + + +VOID SaveIntValue(config_setting_t * group, char * name, int value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT); + if(setting) + config_setting_set_int(setting, value); +} + +VOID SaveInt64Value(config_setting_t * group, char * name, long long value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT64); + if(setting) + config_setting_set_int64(setting, value); +} + +VOID SaveFloatValue(config_setting_t * group, char * name, double value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_FLOAT); + if (setting) + config_setting_set_float(setting, value); +} + +VOID SaveStringValue(config_setting_t * group, char * name, char * value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, value); + +} + + +VOID SaveOverride(config_setting_t * group, char * name, struct Override ** values) +{ + config_setting_t *setting; + struct Override ** Calls; + char Multi[10000]; + char * ptr = &Multi[1]; + + *ptr = 0; + + if (values) + { + Calls = values; + + while(Calls[0]) + { + ptr += sprintf(ptr, "%s, %d|", Calls[0]->Call, Calls[0]->Days); + Calls++; + } + *(--ptr) = 0; + } + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, &Multi[1]); + +} + + +VOID SaveMultiStringValue(config_setting_t * group, char * name, char ** values) +{ + config_setting_t *setting; + char ** Calls; + char Multi[100000]; + char * ptr = &Multi[1]; + + *ptr = 0; + + if (values) + { + Calls = values; + + while(Calls[0]) + { + strcpy(ptr, Calls[0]); + ptr += strlen(Calls[0]); + *(ptr++) = '|'; + Calls++; + } + *(--ptr) = 0; + } + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, &Multi[1]); + +} + +int configSaved = 0; + +VOID SaveConfig(char * ConfigName) +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + config_setting_t *root, *group, *bbs; + int i; + char Size[80]; + struct BBSForwardingInfo DummyForwardingInfo; + char Line[1024]; + char FBBString[8192]= ""; + FBBFilter * p = Filters; + char * ptr = FBBString; + + GetSemaphore(&ConfigSEM, 60); + + if (configSaved == 0) + { + // only create backup once per run + + CopyConfigFile(ConfigName); + configSaved = 1; + } + + memset(&DummyForwardingInfo, 0, sizeof(struct BBSForwardingInfo)); + + // Get rid of old config before saving + + config_destroy(&cfg); + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); + + SaveIntValue(group, "Streams", MaxStreams); + SaveIntValue(group, "BBSApplNum", BBSApplNum); + SaveStringValue(group, "BBSName", BBSName); + SaveStringValue(group, "SYSOPCall", SYSOPCall); + SaveStringValue(group, "H-Route", HRoute); + SaveStringValue(group, "AMPRDomain", AMPRDomain); + SaveIntValue(group, "EnableUI", EnableUI); + SaveIntValue(group, "RefuseBulls", RefuseBulls); + SaveIntValue(group, "OnlyKnown", OnlyKnown); + SaveIntValue(group, "reportMailEvents", reportMailEvents); + SaveIntValue(group, "SendSYStoSYSOPCall", SendSYStoSYSOPCall); + SaveIntValue(group, "SendBBStoSYSOPCall", SendBBStoSYSOPCall); + SaveIntValue(group, "DontHoldNewUsers", DontHoldNewUsers); + SaveIntValue(group, "DefaultNoWINLINK", DefaultNoWINLINK); + SaveIntValue(group, "AllowAnon", AllowAnon); + SaveIntValue(group, "DontNeedHomeBBS", DontNeedHomeBBS); + SaveIntValue(group, "DontCheckFromCall", DontCheckFromCall); + SaveIntValue(group, "UserCantKillT", UserCantKillT); + + SaveIntValue(group, "ForwardToMe", ForwardToMe); + SaveIntValue(group, "SMTPPort", SMTPInPort); + SaveIntValue(group, "POP3Port", POP3InPort); + SaveIntValue(group, "NNTPPort", NNTPInPort); + SaveIntValue(group, "RemoteEmail", RemoteEmail); + SaveIntValue(group, "SendAMPRDirect", SendAMPRDirect); + + SaveIntValue(group, "MailForInterval", MailForInterval); + SaveStringValue(group, "MailForText", MailForText); + + EncryptedPassLen = EncryptPass(ISPAccountPass, EncryptedISPAccountPass); + + SaveIntValue(group, "AuthenticateSMTP", SMTPAuthNeeded); + + SaveIntValue(group, "MulticastRX", MulticastRX); + + SaveIntValue(group, "SMTPGatewayEnabled", ISP_Gateway_Enabled); + SaveIntValue(group, "ISPSMTPPort", ISPSMTPPort); + SaveIntValue(group, "ISPPOP3Port", ISPPOP3Port); + SaveIntValue(group, "POP3PollingInterval", ISPPOP3Interval); + + SaveStringValue(group, "MyDomain", MyDomain); + SaveStringValue(group, "ISPSMTPName", ISPSMTPName); + SaveStringValue(group, "ISPEHLOName", ISPEHLOName); + SaveStringValue(group, "ISPPOP3Name", ISPPOP3Name); + SaveStringValue(group, "ISPAccountName", ISPAccountName); + SaveStringValue(group, "ISPAccountPass", EncryptedISPAccountPass); + + + // Save Window Sizes + +#ifndef LINBPQ + + if (ConsoleRect.right) + { + sprintf(Size,"%d,%d,%d,%d",ConsoleRect.left, ConsoleRect.right, + ConsoleRect.top, ConsoleRect.bottom); + + SaveStringValue(group, "ConsoleSize", Size); + } + + sprintf(Size,"%d,%d,%d,%d,%d",MonitorRect.left,MonitorRect.right,MonitorRect.top,MonitorRect.bottom, hMonitor ? 1 : 0); + SaveStringValue(group, "MonitorSize", Size); + + sprintf(Size,"%d,%d,%d,%d",MainRect.left,MainRect.right,MainRect.top,MainRect.bottom); + SaveStringValue(group, "WindowSize", Size); + + SaveIntValue(group, "Bells", Bells); + SaveIntValue(group, "FlashOnBell", FlashOnBell); + SaveIntValue(group, "StripLF", StripLF); + SaveIntValue(group, "WarnWrap", WarnWrap); + SaveIntValue(group, "WrapInput", WrapInput); + SaveIntValue(group, "FlashOnConnect", FlashOnConnect); + SaveIntValue(group, "CloseWindowOnBye", CloseWindowOnBye); + +#endif + + SaveIntValue(group, "Log_BBS", LogBBS); + SaveIntValue(group, "Log_TCP", LogTCP); + + sprintf(Size,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); + SaveStringValue(group, "Version", Size); + + // Save Welcome Messages and prompts + + SaveStringValue(group, "WelcomeMsg", WelcomeMsg); + SaveStringValue(group, "NewUserWelcomeMsg", NewWelcomeMsg); + SaveStringValue(group, "ExpertWelcomeMsg", ExpertWelcomeMsg); + + SaveStringValue(group, "Prompt", Prompt); + SaveStringValue(group, "NewUserPrompt", NewPrompt); + SaveStringValue(group, "ExpertPrompt", ExpertPrompt); + SaveStringValue(group, "SignoffMsg", SignoffMsg); + + SaveMultiStringValue(group, "RejFrom", RejFrom); + SaveMultiStringValue(group, "RejTo", RejTo); + SaveMultiStringValue(group, "RejAt", RejAt); + SaveMultiStringValue(group, "RejBID", RejBID); + + SaveMultiStringValue(group, "HoldFrom", HoldFrom); + SaveMultiStringValue(group, "HoldTo", HoldTo); + SaveMultiStringValue(group, "HoldAt", HoldAt); + SaveMultiStringValue(group, "HoldBID", HoldBID); + + // Save FBB Filters + + while (p) + { + ptr += sprintf(ptr, "%c|%c|%s|%s|%s|%s|%d|", + p->Action, p->Type, p->From, p->TO, p->AT, p->BID, p->MaxLen); + + p = p->Next; + } + + SaveStringValue(group, "FBBFilters", FBBString); + + SaveIntValue(group, "SendWP", SendWP); + SaveIntValue(group, "SendWPType", SendWPType); + SaveIntValue(group, "FilterWPBulls", FilterWPBulls); + SaveIntValue(group, "NoWPGuesses", NoWPGuesses); + + SaveStringValue(group, "SendWPTO", SendWPTO); + SaveStringValue(group, "SendWPVIA", SendWPVIA); + + SaveMultiStringValue(group, "SendWPAddrs", SendWPAddrs); + + // Save Forwarding Config + + // Interval and Max Sizes and Aliases are not user specific + + SaveIntValue(group, "MaxTXSize", MaxTXSize); + SaveIntValue(group, "MaxRXSize", MaxRXSize); + SaveIntValue(group, "ReaddressLocal", ReaddressLocal); + SaveIntValue(group, "ReaddressReceived", ReaddressReceived); + SaveIntValue(group, "WarnNoRoute", WarnNoRoute); + SaveIntValue(group, "Localtime", Localtime); + SaveIntValue(group, "SendPtoMultiple", SendPtoMultiple); + SaveIntValue(group, "FOURCHARCONT", FOURCHARCONT); + + SaveMultiStringValue(group, "FWDAliases", AliasText); + + bbs = config_setting_add(root, "BBSForwarding", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + ForwardingInfo = user->ForwardingInfo; + + if (ForwardingInfo == NULL) + continue; + + if (memcmp(ForwardingInfo, &DummyForwardingInfo, sizeof(struct BBSForwardingInfo)) == 0) + continue; // Ignore empty records; + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + { + char Key[20] = "*"; + strcat (Key, user->Call); + group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); + } + else + group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); + + SaveMultiStringValue(group, "TOCalls", ForwardingInfo->TOCalls); + SaveMultiStringValue(group, "ConnectScript", ForwardingInfo->ConnectScript); + SaveMultiStringValue(group, "ATCalls", ForwardingInfo->ATCalls); + SaveMultiStringValue(group, "HRoutes", ForwardingInfo->Haddresses); + SaveMultiStringValue(group, "HRoutesP", ForwardingInfo->HaddressesP); + SaveMultiStringValue(group, "FWDTimes", ForwardingInfo->FWDTimes); + + SaveIntValue(group, "Enabled", ForwardingInfo->Enabled); + SaveIntValue(group, "RequestReverse", ForwardingInfo->ReverseFlag); + SaveIntValue(group, "AllowBlocked", ForwardingInfo->AllowBlocked); + SaveIntValue(group, "AllowCompressed", ForwardingInfo->AllowCompressed); + SaveIntValue(group, "UseB1Protocol", ForwardingInfo->AllowB1); + SaveIntValue(group, "UseB2Protocol", ForwardingInfo->AllowB2); + SaveIntValue(group, "SendCTRLZ", ForwardingInfo->SendCTRLZ); + + SaveIntValue(group, "FWDPersonalsOnly", ForwardingInfo->PersonalOnly); + SaveIntValue(group, "FWDNewImmediately", ForwardingInfo->SendNew); + SaveIntValue(group, "FwdInterval", ForwardingInfo->FwdInterval); + SaveIntValue(group, "RevFWDInterval", ForwardingInfo->RevFwdInterval); + SaveIntValue(group, "MaxFBBBlock", ForwardingInfo->MaxFBBBlockSize); + SaveIntValue(group, "ConTimeout", ForwardingInfo->ConTimeout); + + SaveStringValue(group, "BBSHA", ForwardingInfo->BBSHA); + } + + + // Save Housekeeping config + + group = config_setting_add(root, "Housekeeping", CONFIG_TYPE_GROUP); + + SaveInt64Value(group, "LastHouseKeepingTime", LastHouseKeepingTime); + SaveInt64Value(group, "LastTrafficTime", LastTrafficTime); + SaveIntValue(group, "MaxMsgno", MaxMsgno); + SaveIntValue(group, "BidLifetime", BidLifetime); + SaveIntValue(group, "MaxAge", MaxAge); + SaveIntValue(group, "LogLifetime", LogAge); + SaveIntValue(group, "LogLifetime", LogAge); + SaveIntValue(group, "MaintInterval", MaintInterval); + SaveIntValue(group, "UserLifetime", UserLifetime); + SaveIntValue(group, "MaintTime", MaintTime); + SaveFloatValue(group, "PR", PR); + SaveFloatValue(group, "PUR", PUR); + SaveFloatValue(group, "PF", PF); + SaveFloatValue(group, "PNF", PNF); + SaveIntValue(group, "BF", BF); + SaveIntValue(group, "BNF", BNF); + SaveIntValue(group, "NTSD", NTSD); + SaveIntValue(group, "NTSF", NTSF); + SaveIntValue(group, "NTSU", NTSU); +// SaveIntValue(group, "AP", AP); +// SaveIntValue(group, "AB", AB); + SaveIntValue(group, "DeletetoRecycleBin", DeletetoRecycleBin); + SaveIntValue(group, "SuppressMaintEmail", SuppressMaintEmail); + SaveIntValue(group, "MaintSaveReg", SaveRegDuringMaint); + SaveIntValue(group, "OverrideUnsent", OverrideUnsent); + SaveIntValue(group, "SendNonDeliveryMsgs", SendNonDeliveryMsgs); + SaveIntValue(group, "GenerateTrafficReport", GenerateTrafficReport); + + SaveOverride(group, "LTFROM", LTFROM); + SaveOverride(group, "LTTO", LTTO); + SaveOverride(group, "LTAT", LTAT); + + // Save UI config + + for (i=1; i <= GetNumberofPorts(); i++) + { + char Key[100]; + + sprintf(Key, "UIPort%d", i); + + group = config_setting_add(root, Key, CONFIG_TYPE_GROUP); + + if (group) + { + SaveIntValue(group, "Enabled", UIEnabled[i]); + SaveIntValue(group, "SendMF", UIMF[i]); + SaveIntValue(group, "SendHDDR", UIHDDR[i]); + SaveIntValue(group, "SendNull", UINull[i]); + + if (UIDigi[i]) + SaveStringValue(group, "Digis", UIDigi[i]); + } + } + + // Save User Config + + bbs = config_setting_add(root, "BBSUsers", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofUsers; i++) + { + char stats[256], stats2[256]; + struct MsgStats * Stats; + char Key[20] = "*"; + + user = UserRecPtr[i]; + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + { + strcat (Key, user->Call); +// group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); + } + else + { + strcpy(Key, user->Call); +// group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); + } + /* + SaveStringValue(group, "Name", user->Name); + SaveStringValue(group, "Address", user->Address); + SaveStringValue(group, "HomeBBS", user->HomeBBS); + SaveStringValue(group, "QRA", user->QRA); + SaveStringValue(group, "pass", user->pass); + SaveStringValue(group, "ZIP", user->ZIP); + SaveStringValue(group, "CMSPass", user->CMSPass); + + SaveIntValue(group, "lastmsg", user->lastmsg); + SaveIntValue(group, "flags", user->flags); + SaveIntValue(group, "PageLen", user->PageLen); + SaveIntValue(group, "BBSNumber", user->BBSNumber); + SaveIntValue(group, "RMSSSIDBits", user->RMSSSIDBits); + SaveIntValue(group, "WebSeqNo", user->WebSeqNo); + + SaveInt64Value(group, "TimeLastConnected", user->TimeLastConnected); +*/ + Stats = &user->Total; + +// sprintf(stats, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + sprintf(stats, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", + Stats->ConnectsIn, Stats->ConnectsOut, + Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], + Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], + Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], + Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], + Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], + Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); + +// SaveStringValue(group, "Totsl", stats); + + Stats = &user->Last; + + sprintf(stats2, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", +// sprintf(stats2, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + Stats->ConnectsIn, Stats->ConnectsOut, + Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], + Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], + Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], + Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], + Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], + Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); + +// SaveStringValue(group, "Last", stats2); + + sprintf(Line,"%s^%s^%s^%s^%s^%s^%s^%d^%d^%d^%d^%d^%d^%lld^%s^%s", + user->Name, user->Address, user->HomeBBS, user->QRA, user->pass, user->ZIP, user->CMSPass, + user->lastmsg, user->flags, user->PageLen, user->BBSNumber, user->RMSSSIDBits, user->WebSeqNo, + user->TimeLastConnected, stats, stats2); + + if (strlen(Line) < 10) + continue; + + SaveStringValue(bbs, Key, Line); + } + +/* + wp = config_setting_add(root, "WP", CONFIG_TYPE_GROUP); + + for (i = 0; i <= NumberofWPrecs; i++) + { + char WPString[1024]; + long long val1, val2; + + WP = WPRecPtr[i]; + val1 = WP->last_modif; + val2 = WP->last_seen; + + sprintf(Key, "R%d", i); + + sprintf(WPString, "%s|%s|%d|%d|%d|%s|%s|%s|%s|%s|%s|%ld|%ld", + &WP->callsign[0], &WP->name[0], WP->Type, WP->changed, WP->seen, &WP->first_homebbs[0], + &WP->secnd_homebbs[0], &WP->first_zip[0], &WP->secnd_zip[0], &WP->first_qth[0], &WP->secnd_qth[0], + val1, val2); + + SaveStringValue(wp, Key, WPString); + } + + // Save Message Headers + + msgs = config_setting_add(root, "MSGS", CONFIG_TYPE_GROUP); + + memset(MsgHddrPtr[0], 0, sizeof(struct MsgInfo)); + + MsgHddrPtr[0]->type = 'X'; + MsgHddrPtr[0]->status = '2'; + MsgHddrPtr[0]->number = 0; + MsgHddrPtr[0]->length = LatestMsg; + + + for (i = 0; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); + + n = 39; + while (n >=0 && HEXString1[n] == '0') + HEXString1[n--] = 0; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); + + n = 39; + while (n >= 0 && HEXString2[n] == '0') + HEXString2[n--] = 0; + + sprintf(Key, "R%d", Msg->number); + + n = sprintf(Line, "%c|%c|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, + Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], + &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, + &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); + + SaveStringValue(msgs, Key, Line); + } + + // Save Bids + + msgs = config_setting_add(root, "BIDS", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofBIDs; i++) + { + sprintf(Key, "R%s", BIDRecPtr[i]->BID); + sprintf(Line, "%d|%d", BIDRecPtr[i]->mode, BIDRecPtr[i]->u.timestamp); + SaveStringValue(msgs, Key, Line); + } + +#ifdef LINBPQ + + if(!config_write_file(&cfg,"/dev/shm/linmail.cfg.temp" )) + { + print("Error while writing file.\n"); + config_destroy(&cfg); + FreeSemaphore(&ConfigSEM); + return; + } + + CopyFile("/dev/shm/linmail.cfg.temp", ConfigName, FALSE); + +#else +*/ + if(! config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + FreeSemaphore(&ConfigSEM); + + return; + } + +//#endif + + config_destroy(&cfg); + +/* + +#ifndef LINBPQ + + // Save a copy with current Date/Time Stamp for debugging + + { + char Backup[MAX_PATH]; + time_t LT; + struct tm * tm; + + LT = time(NULL); + tm = gmtime(<); + + sprintf(Backup,"%s.%02d%02d%02d%02d%02d.save", ConfigName, tm->tm_year-100, tm->tm_mon+1, + tm->tm_mday, tm->tm_hour, tm->tm_min); + + CopyFile(ConfigName, Backup, FALSE); // Copy to .bak + } +#endif +*/ + + FreeSemaphore(&ConfigSEM); +} + +int GetIntValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return 0; +} + +long long GetInt64Value(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int64 (setting); + + return 0; +} + +double GetFloatValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + + if (setting) + { + return config_setting_get_float (setting); + } + return 0; +} + +int GetIntValueWithDefault(config_setting_t * group, char * name, int Default) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return Default; +} + + +BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen) +{ + char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + { + str = (char *)config_setting_get_string (setting); + if (strlen(str) > maxlen) + { + Debugprintf("Suspect config record %s", str); + str[maxlen] = 0; + } + strcpy(value, str); + return TRUE; + } + value[0] = 0; + return FALSE; +} + +BOOL GetConfig(char * ConfigName) +{ + int i; + char Size[80]; + config_setting_t *setting; + char * ptr1; + char FBBString[8192]= ""; + FBBFilter f; + config_init(&cfg); + + /* Read the file. If there is an error, report it and exit. */ + + if(! config_read_file(&cfg, ConfigName)) + { + char Msg[256]; + sprintf(Msg, "Config File Line %d - %s\n", + config_error_line(&cfg), config_error_text(&cfg)); +#ifdef WIN32 + MessageBox(NULL, Msg, "BPQMail", MB_ICONSTOP); +#else + printf("%s", Msg); +#endif + config_destroy(&cfg); + return(EXIT_FAILURE); + } +/* +#if LIBCONFIG_VER_MINOR > 5 + config_set_option(&cfg, CONFIG_OPTION_AUTOCONVERT, 1); +#else + config_set_auto_convert (&cfg, 1); +#endif +*/ + group = config_lookup (&cfg, "main"); + + if (group == NULL) + return EXIT_FAILURE; + + SMTPInPort = GetIntValue(group, "SMTPPort"); + POP3InPort = GetIntValue(group, "POP3Port"); + NNTPInPort = GetIntValue(group, "NNTPPort"); + RemoteEmail = GetIntValue(group, "RemoteEmail"); + MaxStreams = GetIntValue(group, "Streams"); + BBSApplNum = GetIntValue(group, "BBSApplNum"); + EnableUI = GetIntValue(group, "EnableUI"); + MailForInterval = GetIntValue(group, "MailForInterval"); + RefuseBulls = GetIntValue(group, "RefuseBulls"); + OnlyKnown = GetIntValue(group, "OnlyKnown"); + reportMailEvents = GetIntValue(group, "reportMailEvents"); + + SendSYStoSYSOPCall = GetIntValue(group, "SendSYStoSYSOPCall"); + SendBBStoSYSOPCall = GetIntValue(group, "SendBBStoSYSOPCall"); + DontHoldNewUsers = GetIntValue(group, "DontHoldNewUsers"); + DefaultNoWINLINK = GetIntValue(group, "DefaultNoWINLINK"); + ForwardToMe = GetIntValue(group, "ForwardToMe"); + AllowAnon = GetIntValue(group, "AllowAnon"); + UserCantKillT = GetIntValue(group, "UserCantKillT"); + + DontNeedHomeBBS = GetIntValue(group, "DontNeedHomeBBS"); + DontCheckFromCall = GetIntValue(group, "DontCheckFromCall"); + MaxTXSize = GetIntValue(group, "MaxTXSize"); + MaxRXSize = GetIntValue(group, "MaxRXSize"); + ReaddressLocal = GetIntValue(group, "ReaddressLocal"); + ReaddressReceived = GetIntValue(group, "ReaddressReceived"); + WarnNoRoute = GetIntValue(group, "WarnNoRoute"); + SendPtoMultiple = GetIntValue(group, "SendPtoMultiple"); + FOURCHARCONT = GetIntValue(group, "FOURCHARCONT"); + + Localtime = GetIntValue(group, "Localtime"); + AliasText = GetMultiStringValue(group, "FWDAliases"); + GetStringValue(group, "BBSName", BBSName, 100); + GetStringValue(group, "MailForText", MailForText, 100); + GetStringValue(group, "SYSOPCall", SYSOPCall, 100); + GetStringValue(group, "H-Route", HRoute, 100); + GetStringValue(group, "AMPRDomain", AMPRDomain, 100); + SendAMPRDirect = GetIntValue(group, "SendAMPRDirect"); + ISP_Gateway_Enabled = GetIntValue(group, "SMTPGatewayEnabled"); + ISPPOP3Interval = GetIntValue(group, "POP3PollingInterval"); + GetStringValue(group, "MyDomain", MyDomain, 50); + GetStringValue(group, "ISPSMTPName", ISPSMTPName, 50); + GetStringValue(group, "ISPPOP3Name", ISPPOP3Name, 50); + ISPSMTPPort = GetIntValue(group, "ISPSMTPPort"); + ISPPOP3Port = GetIntValue(group, "ISPPOP3Port"); + GetStringValue(group, "ISPAccountName", ISPAccountName, 50); + GetStringValue(group, "ISPAccountPass", EncryptedISPAccountPass, 100); + + sprintf(SignoffMsg, "73 de %s\r", BBSName); // Default + GetStringValue(group, "SignoffMsg", SignoffMsg, 50); + + DecryptPass(EncryptedISPAccountPass, ISPAccountPass, (int)strlen(EncryptedISPAccountPass)); + + SMTPAuthNeeded = GetIntValue(group, "AuthenticateSMTP"); + LogBBS = GetIntValue(group, "Log_BBS"); + LogTCP = GetIntValue(group, "Log_TCP"); + + MulticastRX = GetIntValue(group, "MulticastRX"); + +#ifndef LINBPQ + + GetStringValue(group, "MonitorSize", Size, sizeof(Size)); + sscanf(Size,"%d,%d,%d,%d,%d",&MonitorRect.left,&MonitorRect.right,&MonitorRect.top,&MonitorRect.bottom,&OpenMon); + + GetStringValue(group, "WindowSize", Size, sizeof(Size)); + sscanf(Size,"%d,%d,%d,%d",&MainRect.left,&MainRect.right,&MainRect.top,&MainRect.bottom); + + Bells = GetIntValue(group, "Bells"); + + FlashOnBell = GetIntValue(group, "FlashOnBell"); + + StripLF = GetIntValue(group, "StripLF"); + CloseWindowOnBye = GetIntValue(group, "CloseWindowOnBye"); + WarnWrap = GetIntValue(group, "WarnWrap"); + WrapInput = GetIntValue(group, "WrapInput"); + FlashOnConnect = GetIntValue(group, "FlashOnConnect"); + + GetStringValue(group, "ConsoleSize", Size, 80); + sscanf(Size,"%d,%d,%d,%d,%d", &ConsoleRect.left, &ConsoleRect.right, + &ConsoleRect.top, &ConsoleRect.bottom,&OpenConsole); + +#endif + + // Get Welcome Messages + + setting = config_setting_get_member (group, "WelcomeMsg"); + + if (setting && setting->value.sval[0]) + { + WelcomeMsg = _strdup(config_setting_get_string (setting)); + } + else + WelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); + + + setting = config_setting_get_member (group, "NewUserWelcomeMsg"); + + if (setting && setting->value.sval[0]) + NewWelcomeMsg = _strdup(config_setting_get_string (setting)); + else + NewWelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); + + + setting = config_setting_get_member (group, "ExpertWelcomeMsg"); + + if (setting && setting->value.sval[0]) + ExpertWelcomeMsg = _strdup(config_setting_get_string (setting)); + else + ExpertWelcomeMsg = _strdup(""); + + // Get Prompts + + setting = config_setting_get_member (group, "Prompt"); + + if (setting && setting->value.sval[0]) + Prompt = _strdup(config_setting_get_string (setting)); + else + { + Prompt = malloc(20); + sprintf(Prompt, "de %s>\r\n", BBSName); + } + + setting = config_setting_get_member (group, "NewUserPrompt"); + + if (setting && setting->value.sval[0]) + NewPrompt = _strdup(config_setting_get_string (setting)); + else + { + NewPrompt = malloc(20); + sprintf(NewPrompt, "de %s>\r\n", BBSName); + } + + setting = config_setting_get_member (group, "ExpertPrompt"); + + if (setting && setting->value.sval[0]) + ExpertPrompt = _strdup(config_setting_get_string (setting)); + else + { + ExpertPrompt = malloc(20); + sprintf(ExpertPrompt, "de %s>\r\n", BBSName); + } + + TidyPrompts(); + + RejFrom = GetMultiStringValue(group, "RejFrom"); + RejTo = GetMultiStringValue(group, "RejTo"); + RejAt = GetMultiStringValue(group, "RejAt"); + RejBID = GetMultiStringValue(group, "RejBID"); + + HoldFrom = GetMultiStringValue(group, "HoldFrom"); + HoldTo = GetMultiStringValue(group, "HoldTo"); + HoldAt = GetMultiStringValue(group, "HoldAt"); + HoldBID = GetMultiStringValue(group, "HoldBID"); + + // Get FBB Filters + + GetStringValue(group, "FBBFilters", FBBString, sizeof(FBBString)); + + ptr1 = FBBString; + + // delete old list + + while(Filters && Filters->Next) + { + FBBFilter * next = Filters->Next; + free(Filters); + Filters = next; + } + + free(Filters); + Filters = NULL; + + while (ptr1 && ptr1[0]) + { + FBBFilter * PFilter; + + f.Action = ptr1[0]; + f.Type = ptr1[2]; + ptr1 = &ptr1[4]; + + memcpy(f.From, ptr1, 10); + strlop(f.From, '|'); + ptr1 = strlop(ptr1, '|'); + + memcpy(f.TO, ptr1, 10); + strlop(f.TO, '|'); + ptr1 = strlop(ptr1, '|'); + + memcpy(f.AT, ptr1, 10); + strlop(f.AT, '|'); + ptr1 = strlop(ptr1, '|'); + + memcpy(f.BID, ptr1, 10); + strlop(f.BID, '|'); + ptr1 = strlop(ptr1, '|'); + + f.MaxLen = atoi(ptr1); + + // add to list + + f.Next = 0; + + PFilter = zalloc(sizeof(FBBFilter)); + + memcpy(PFilter, &f, sizeof(FBBFilter)); + + if (Filters == 0) + Filters = PFilter; + else + { + FBBFilter * p = Filters; + + while (p->Next) + p = p->Next; + + p->Next = PFilter; + } + + ptr1 = strlop(ptr1, '|'); + } + + + + + +//f.Action, f.Type, f.From, f.TO, f.AT, f.BID, &f.MaxLen); + +/* while (p) + { + ptr += sprintf(ptr, "%c|%c|%s|%s|%s|%s|%d|", + p->Action, p->Type, p->From, p->TO, p->AT, p->BID, p->MaxLen); + + p = p->Next; + } + +*/ + + // Send WP Params + + SendWP = GetIntValue(group, "SendWP"); + SendWPType = GetIntValue(group, "SendWPType"); + + GetStringValue(group, "SendWPTO", SendWPTO, sizeof(SendWPTO)); + GetStringValue(group, "SendWPVIA", SendWPVIA, sizeof(SendWPVIA)); + + SendWPAddrs = GetMultiStringValue(group, "SendWPAddrs"); + + FilterWPBulls = GetIntValue(group, "FilterWPBulls"); + NoWPGuesses = GetIntValue(group, "NoWPGuesses"); + + if (SendWPAddrs[0] == NULL && SendWPTO[0]) + { + // convert old format TO and VIA to entry in SendWPAddrs + + SendWPAddrs = realloc(SendWPAddrs, 8); // Add entry + + if (SendWPVIA[0]) + { + char WP[256]; + + sprintf(WP, "%s@%s", SendWPTO, SendWPVIA); + SendWPAddrs[0] = _strdup(WP); + } + else + SendWPAddrs[0] = _strdup(SendWPTO); + + + SendWPAddrs[1] = 0; + + SendWPTO[0] = 0; + SendWPVIA[0] = 0; + } + + GetStringValue(group, "Version", Size, sizeof(Size)); + sscanf(Size,"%d,%d,%d,%d", &LastVer[0], &LastVer[1], &LastVer[2], &LastVer[3]); + + for (i =1 ; i <= GetNumberofPorts(); i++) + { + char Key[100]; + + sprintf(Key, "UIPort%d", i); + + group = config_lookup (&cfg, Key); + + if (group) + { + UIEnabled[i] = GetIntValue(group, "Enabled"); + UIMF[i] = GetIntValueWithDefault(group, "SendMF", UIEnabled[i]); + UIHDDR[i] = GetIntValueWithDefault(group, "SendHDDR", UIEnabled[i]); + UINull[i] = GetIntValue(group, "SendNull"); + Size[0] = 0; + GetStringValue(group, "Digis", Size, sizeof(Size)); + if (Size[0]) + UIDigi[i] = _strdup(Size); + } + } + + group = config_lookup (&cfg, "Housekeeping"); + + if (group) + { + LastHouseKeepingTime = GetIntValue(group, "LastHouseKeepingTime"); + LastTrafficTime = GetIntValue(group, "LastTrafficTime"); + MaxMsgno = GetIntValue(group, "MaxMsgno"); + LogAge = GetIntValue(group, "LogLifetime"); + BidLifetime = GetIntValue(group, "BidLifetime"); + MaxAge = GetIntValue(group, "MaxAge"); + if (MaxAge == 0) + MaxAge = 30; + UserLifetime = GetIntValue(group, "UserLifetime"); + MaintInterval = GetIntValue(group, "MaintInterval"); + + if (MaintInterval == 0) + MaintInterval = 24; + + MaintTime = GetIntValue(group, "MaintTime"); + + PR = GetFloatValue(group, "PR"); + PUR = GetFloatValue(group, "PUR"); + PF = GetFloatValue(group, "PF"); + PNF = GetFloatValue(group, "PNF"); + + BF = GetIntValue(group, "BF"); + BNF = GetIntValue(group, "BNF"); + NTSD = GetIntValue(group, "NTSD"); + NTSU = GetIntValue(group, "NTSU"); + NTSF = GetIntValue(group, "NTSF"); +// AP = GetIntValue(group, "AP"); +// AB = GetIntValue(group, "AB"); + DeletetoRecycleBin = GetIntValue(group, "DeletetoRecycleBin"); + SuppressMaintEmail = GetIntValue(group, "SuppressMaintEmail"); + SaveRegDuringMaint = GetIntValue(group, "MaintSaveReg"); + OverrideUnsent = GetIntValue(group, "OverrideUnsent"); + SendNonDeliveryMsgs = GetIntValue(group, "SendNonDeliveryMsgs"); + OverrideUnsent = GetIntValue(group, "OverrideUnsent"); + GenerateTrafficReport = GetIntValueWithDefault(group, "GenerateTrafficReport", 1); + + LTFROM = GetOverrides(group, "LTFROM"); + LTTO = GetOverrides(group, "LTTO"); + LTAT = GetOverrides(group, "LTAT"); + } + + return EXIT_SUCCESS; +} + + +int Connected(int Stream) +{ + int n, Mask; + CIRCUIT * conn; + struct UserInfo * user = NULL; + char callsign[10]; + int port, paclen, maxframe, l4window; + char ConnectedMsg[] = "*** CONNECTED "; + char Msg[100]; + char Title[100]; + int64_t Freq = 0; + int Mode = 0; + BPQVECSTRUC * SESS; + TRANSPORTENTRY * Sess1 = NULL, * Sess2; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active) + { + // Probably an outgoing connect + + ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + conn->ErrorCount = 0; + + if (conn->BBSFlags & RunningConnectScript) + { + // BBS Outgoing Connect + + conn->paclen = 236; + + // Run first line of connect script + + ChangeSessionIdletime(Stream, BBSIDLETIME); // Default Idletime for BBS Sessions + ProcessBBSConnectScript(conn, ConnectedMsg, 15); + return 0; + } + } + + // Incoming Connect + + // Try to find port, freq, mode, etc + +#ifdef LINBPQ + SESS = &BPQHOSTVECTOR[0]; +#else + SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + SESS +=(Stream - 1); + + if (SESS) + Sess1 = SESS->HOSTSESSION; + + if (Sess1) + { + Sess2 = Sess1->L4CROSSLINK; + + if (Sess2) + { + // See if L2 session - if so, get info from WL2K report line + + // if Session has report info, use it + + if (Sess2->Mode) + { + Freq = Sess2->Frequency; + Mode = Sess2->Mode; + } + else if (Sess2->L4CIRCUITTYPE & L2LINK) + { + LINKTABLE * LINK = Sess2->L4TARGET.LINK; + PORTCONTROLX * PORT = LINK->LINKPORT; + + Freq = PORT->WL2KInfo.Freq; + Mode = PORT->WL2KInfo.mode; + } + else + { + if (Sess2->RMSCall[0]) + { + Freq = Sess2->Frequency; + Mode = Sess2->Mode; + } + } + } + } + + memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything + conn->Active = TRUE; + conn->BPQStream = Stream; + ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions + + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + conn->ErrorCount = 0; + + conn->Secure_Session = GetConnectionInfo(Stream, callsign, + &port, &conn->SessType, &paclen, &maxframe, &l4window); + + strlop(callsign, ' '); // Remove trailing spaces + + if (strcmp(&callsign[strlen(callsign) - 2], "-T") == 0) + conn->RadioOnlyMode = 'T'; + else if (strcmp(&callsign[strlen(callsign) - 2], "-R") == 0) + conn->RadioOnlyMode = 'R'; + else + conn->RadioOnlyMode = 0; + + memcpy(conn->Callsign, callsign, 10); + + strlop(callsign, '-'); // Remove any SSID + + user = LookupCall(callsign); + + if (user == NULL) + { + int Length=0; + + if (OnlyKnown) + { + // Unknown users not allowed + + n = sprintf_s(Msg, sizeof(Msg), "Incoming Connect from unknown user %s Rejected", callsign); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + Disconnect(Stream); + return 0; + } + + user = AllocateUserRecord(callsign); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (SendNewUserMessage) + { + int64_t LongFreq = Freq; + + char * MailBuffer = malloc(100); + + if (Freq == 0 && port) + { + // Get Port Freq if available + + char FreqString[256]; + +#ifdef WIN32 + if (pGetPortFrequency) + LongFreq = pGetPortFrequency(port, FreqString); +#else + LongFreq = GetPortFrequency(port, FreqString); +#endif + } + Length += sprintf(MailBuffer, "New User %s Connected to Mailbox on Port %d Freq %d Mode %ld\r\n", callsign, port, LongFreq, Mode); + + sprintf(Title, "New User %s", callsign); + + SendMessageToSYSOP(Title, MailBuffer, Length); + } + + if (user == NULL) return 0; // Cant happen?? + + if (!DontHoldNewUsers) + user->flags |= F_HOLDMAIL; + + if (DefaultNoWINLINK) + user->flags |= F_NOWINLINK; + + // Always set WLE User - can't see it doing any harm + + user->flags |= F_Temp_B2_BBS; + + conn->NewUser = TRUE; + } + + user->TimeLastConnected = time(NULL); + user->Total.ConnectsIn++; + + conn->UserPointer = user; + + conn->lastmsg = user->lastmsg; + + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (paclen == 0) + { + paclen = 236; + + if (conn->SessType & Sess_PACTOR) + paclen = 100; + } + + conn->paclen = paclen; + + // Set SYSOP flag if user is defined as SYSOP and Host Session + + if (((conn->SessType & Sess_BPQHOST) == Sess_BPQHOST) && (user->flags & F_SYSOP)) + conn->sysop = TRUE; + + if (conn->Secure_Session && (user->flags & F_SYSOP)) + conn->sysop = TRUE; + + Mask = 1 << (GetApplNum(Stream) - 1); + + if (user->flags & F_Excluded) + { + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s Rejected by Exclude Flag", user->Call); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + Disconnect(Stream); + return 0; + } + + if (port) + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s on Port %d Freq %d Mode %s", + user->Call, port, Freq, WL2KModes[Mode]); + else + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call); + + // Send SID and Prompt (Unless Sync) + + if (user->ForwardingInfo && user->ForwardingInfo->ConTimeout) + conn->SIDResponseTimer = user->ForwardingInfo->ConTimeout / 10; // 10 sec ticks + else + conn->SIDResponseTimer = 12; // Allow a couple of minutes for response to SID + + { + BOOL B1 = FALSE, B2 = FALSE, BIN = FALSE, BLOCKED = FALSE; + BOOL WL2KRO = FALSE; + + struct BBSForwardingInfo * ForwardingInfo; + + if (conn->RadioOnlyMode == 'R') + WL2KRO = 1; + + conn->PageLen = user->PageLen; + conn->Paging = (user->PageLen > 0); + + if (user->flags & F_Temp_B2_BBS) + { + // An RMS Express user that needs a temporary BBS struct + + if (user->ForwardingInfo == NULL) + { + // we now save the Forwarding info if BBS flag is cleared, + // so there may already be a ForwardingInfo + + user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + } + + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; + + ForwardingInfo = user->ForwardingInfo; + + ForwardingInfo->AllowCompressed = TRUE; + B1 = ForwardingInfo->AllowB1 = FALSE; + B2 = ForwardingInfo->AllowB2 = TRUE; + BLOCKED = ForwardingInfo->AllowBlocked = TRUE; + } + + if (conn->NewUser) + { + BLOCKED = TRUE; + BIN = TRUE; + B2 = TRUE; + } + + if (user->ForwardingInfo) + { + BLOCKED = user->ForwardingInfo->AllowBlocked; + if (BLOCKED) + { + BIN = user->ForwardingInfo->AllowCompressed; + B1 = user->ForwardingInfo->AllowB1; + B2 = user->ForwardingInfo->AllowB2; + } + } + + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + if (conn->RadioOnlyMode) + nodeprintf(conn,";WL2K-Radio/Internet_Network\r"); + + if (!(conn->BBSFlags & SYNCMODE)) + { + + nodeprintf(conn, BBSSID, "BPQ-", + Ver[0], Ver[1], Ver[2], Ver[3], + BIN ? "B" : "", B1 ? "1" : "", B2 ? "2" : "", + BLOCKED ? "FW": "", WL2KRO ? "" : "J"); + + // if (user->flags & F_Temp_B2_BBS) + // nodeprintf(conn,";PQ: 66427529\r"); + + // nodeprintf(conn,"[WL2K-BPQ.1.0.4.39-B2FWIHJM$]\r"); + } + } + + if ((user->Name[0] == 0) & AllowAnon) + strcpy(user->Name, user->Call); + + if (!(conn->BBSFlags & SYNCMODE)) + { + if (user->Name[0] == 0) + { + conn->Flags |= GETTINGUSER; + BBSputs(conn, NewUserPrompt); + } + else + SendWelcomeMsg(Stream, conn, user); + } + else + { + // Seems to be a timing problem - see if this fixes it + + Sleep(500); + } + + RefreshMainWindow(); + + return 0; + } + } + + return 0; +} + +int Disconnected (int Stream) +{ + struct UserInfo * user = NULL; + CIRCUIT * conn; + int n; + char Msg[255]; + int len; + char DiscMsg[] = "DISCONNECTED "; + + for (n = 0; n <= NumberofStreams-1; n++) + { + conn=&Connections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active == FALSE) + return 0; + + // if still running connect script, reenter it to see if + // there is an else + + if (conn->BBSFlags & RunningConnectScript) + { + // We need to see if we got as far as connnected, + // as if we have we need to reset the connect script + // over the ELSE + + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + char ** Scripts; + + if (ForwardingInfo->TempConnectScript) + Scripts = ForwardingInfo->TempConnectScript; + else + Scripts = ForwardingInfo->ConnectScript; + + // First see if any script left + + if (Scripts[ForwardingInfo->ScriptIndex]) + { + if (ForwardingInfo->MoreLines == FALSE) + { + // Have reached end of script, so need to set back over ELSE + + ForwardingInfo->ScriptIndex--; + ForwardingInfo->MoreLines = TRUE; + } + + // if (Scripts[ForwardingInfo->ScriptIndex] == NULL || + // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished + // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished + + + ProcessBBSConnectScript(conn, DiscMsg, 15); + return 0; + } + } + + user = conn->UserPointer; + + if (user && (conn->lastmsg > user->lastmsg)) + { + user->lastmsg = conn->lastmsg; + SaveUserDatabase(); + } + + + // if sysop was chatting to user clear link +#ifndef LINBPQ + if (conn->BBSFlags & SYSOPCHAT) + { + SendUnbuffered(-1, "User has disconnected\n", 23); + BBSConsole.Console->SysopChatStream = 0; + } +#endif + ClearQueue(conn); + + if (conn->PacLinkCalls) + free(conn->PacLinkCalls); + + if (conn->InputBuffer) + { + free(conn->InputBuffer); + conn->InputBuffer = NULL; + conn->InputBufferLen = 0; + } + + /* ---- G7TAJ PG SERVER ---- */ + if (conn->UserPointer && conn->UserPointer->Temp && conn->UserPointer->Temp->RUNPGPARAMS) + { + Debugprintf("Freeing RUNPGPARAMS"); + free(conn->UserPointer->Temp->RUNPGPARAMS); + conn->UserPointer->Temp->RUNPGPARAMS = NULL; + } + + /*------- G7TAJ END --------- */ + + if (conn->InputMode == 'B') + { + // Save partly received message for a restart + + if (conn->BBSFlags & FBBB1Mode) + if (conn->Paclink == 0) // Paclink doesn't do restarts + if (strcmp(conn->Callsign, "RMS") != 0) // Neither does RMS Packet. + if (conn->DontSaveRestartData == FALSE) + SaveFBBBinary(conn); + } + + conn->Active = FALSE; + + if (conn->FwdMsg) + conn->FwdMsg->Locked = 0; // Unlock + + RefreshMainWindow(); + + RemoveTempBIDS(conn); + + len=sprintf_s(Msg, sizeof(Msg), "%s Disconnected", conn->Callsign); + WriteLogLine(conn, '|',Msg, len, LOG_BBS); + + if (conn->FBBHeaders) + { + struct FBBHeaderLine * FBBHeader; + int n; + + for (n = 0; n < 5; n++) + { + FBBHeader = &conn->FBBHeaders[n]; + + if (FBBHeader->FwdMsg) + FBBHeader->FwdMsg->Locked = 0; // Unlock + + } + + free(conn->FBBHeaders); + conn->FBBHeaders = NULL; + } + + if (conn->UserPointer) + { + struct BBSForwardingInfo * FWDInfo = conn->UserPointer->ForwardingInfo; + + if (FWDInfo) + { + FWDInfo->Forwarding = FALSE; + +// if (FWDInfo->UserCall[0]) // Will be set if RMS +// { +// FindNextRMSUser(FWDInfo); +// } +// else + FWDInfo->FwdTimer = 0; + } + } + + conn->BBSFlags = 0; // Clear ARQ Mode + + return 0; + } + } + return 0; +} + +int DoReceivedData(int Stream) +{ + int count, InputLen; + size_t MsgLen; + int n; + CIRCUIT * conn; + struct UserInfo * user; + char * ptr, * ptr2; + char * Buffer; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (Stream == conn->BPQStream) + { + conn->SIDResponseTimer = 0; // Got a message, so cancel timeout. + + do + { + // May have several messages per packet, or message split over packets + + OuterLoop: + if (conn->InputLen + 1000 > conn->InputBufferLen ) // Shouldnt have lines longer than this in text mode + { + conn->InputBufferLen += 1000; + conn->InputBuffer = realloc(conn->InputBuffer, conn->InputBufferLen); + } + + GetMsg(Stream, &conn->InputBuffer[conn->InputLen], &InputLen, &count); + + if (InputLen == 0 && conn->InputMode != 'Y') + return 0; + + conn->InputLen += InputLen; + + if (conn->InputLen == 0) return 0; + + conn->Watchdog = 900; // 15 Minutes + + if (conn->InputMode == 'Y') // YAPP + { + if (ProcessYAPPMessage(conn)) // Returns TRUE if there could be more to process + goto OuterLoop; + + return 0; + } + + + /* ---------- G7TAJ START - PG server --------- */ + + if (conn->InputMode == 'P') // Inside PG Server + { + user = conn->UserPointer; + run_pg(conn, user); + return 0; + } + /* ---------- G7TAJ END --------- */ + + if (conn->InputMode == 'B') + { + // if in OpenBCM mode, remove FF transparency + + if (conn->OpenBCM) // Telnet, so escape any 0xFF + { + unsigned char * ptr1 = conn->InputBuffer; + unsigned char * ptr2; + int Len; + unsigned char c; + + // We can come through here again for the + // same data as we wait for a full packet + // So only check last InputLen bytes + + ptr1 += (conn->InputLen - InputLen); + ptr2 = ptr1; + Len = InputLen; + + while (Len--) + { + c = *(ptr1++); + + if (conn->InTelnetExcape) // Last char was ff + { + conn->InTelnetExcape = FALSE; + continue; + } + + *(ptr2++) = c; + + if (c == 0xff) // + conn->InTelnetExcape = TRUE; + } + + conn->InputLen = (int)(ptr2 - conn->InputBuffer); + } + + UnpackFBBBinary(conn); + goto OuterLoop; + } + else + { + + loop: + + if (conn->InputLen == 1 && conn->InputBuffer[0] == 0) // Single Null + { + conn->InputLen = 0; + return 0; + } + + user = conn->UserPointer; + + if (conn->BBSFlags & (MCASTRX | SYNCMODE)) + { + // MCAST and SYNCMODE deliver full packets + + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); + + conn->InputLen=0; + continue; + } + + // This looks for CR, CRLF, LF or CR/Null and removes any LF or NULL, + // but this relies on both arriving in same packet. + // Need to check for LF and start of packet and ignore it + // But what if client is only using LF?? + // (WLE sends SID with CRLF, other packets with CR only) + + // We don't get here on the data part of a binary transfer, so + // don't need to worry about messing up binary data. + + ptr = memchr(conn->InputBuffer, '\r', conn->InputLen); + ptr2 = memchr(conn->InputBuffer, '\n', conn->InputLen); + + if (ptr) + conn->usingCR = 1; + + if ((ptr2 && ptr2 < ptr) || ptr == 0) // LF before CR, or no CR + ptr = ptr2; // Use LF + + if (ptr) // CR or LF in buffer + { + conn->lastLineEnd = *(ptr); + + *(ptr) = '\r'; // In case was LF + + ptr2 = &conn->InputBuffer[conn->InputLen]; + + if (++ptr == ptr2) + { + // Usual Case - single msg in buffer + + // if Length is 1 and Term is LF and normal line end is CR + // this is from a split CRLF - Ignore it + + if (conn->InputLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) + Debugprintf("BPQMail split Line End Detected"); + else + { + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); + } + conn->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = conn->InputLen - (ptr2-ptr); + + Buffer = malloc(MsgLen + 100); + + memcpy(Buffer, conn->InputBuffer, MsgLen); + + // if Length is 1 and Term is LF and normal line end is CR + // this is from a split CRLF - Ignore it + + if (MsgLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) + Debugprintf("BPQMail split Line End Detected"); + else + { + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, Buffer, (int)MsgLen); + else + ProcessLine(conn, user, Buffer, (int)MsgLen); + } + free(Buffer); + + if (*ptr == 0 || *ptr == '\n') + { + /// CR LF or CR Null + + ptr++; + conn->InputLen--; + } + + memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); + + conn->InputLen -= (int)MsgLen; + + goto loop; + + } + } + else + { + // Could be a YAPP Header + + + if (conn->InputLen == 2 && conn->InputBuffer[0] == ENQ && conn->InputBuffer[1] == 1) // YAPP Send_Init + { + UCHAR YAPPRR[2]; + YAPPRR[0] = ACK; + YAPPRR[1] = 1; + + conn->InputMode = 'Y'; + QueueMsg(conn, YAPPRR, 2); + + conn->InputLen = 0; + return 0; + } + } + } + + } while (count > 0); + + return 0; + } + } + + // Socket not found + + return 0; + +} +int DoBBSMonitorData(int Stream) +{ +// UCHAR Buffer[1000]; + UCHAR buff[500]; + + int len = 0,count=0; + int stamp; + + do + { + stamp=GetRaw(Stream, buff,&len,&count); + + if (len == 0) return 0; + + SeeifBBSUIFrame((struct _MESSAGEX *)buff, len); + } + + while (count > 0); + + + return 0; + +} + +VOID ProcessFLARQLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen) +{ + Buffer[MsgLen] = 0; + + if (MsgLen == 1 && Buffer[0] == 13) + return; + + if (strcmp(Buffer, "ARQ::ETX\r") == 0) + { + // Decode it. + + UCHAR * ptr1, * ptr2, * ptr3; + int len, linelen; + struct MsgInfo * Msg = conn->TempMsg; + time_t Date; + char FullTo[100]; + char FullFrom[100]; + char ** RecpTo = NULL; // May be several Recipients + char ** HddrTo = NULL; // May be several Recipients + char ** Via = NULL; // May be several Recipients + int LocalMsg[1000] ; // Set if Recipient is a local wl2k address + + int B2To; // Offset to To: fields in B2 header + int Recipients = 0; + int RMSMsgs = 0, BBSMsgs = 0; + +// Msg->B2Flags |= B2Msg; + + + ptr1 = conn->MailBuffer; + len = Msg->length; + ptr1[len] = 0; + + if (strstr(ptr1, "ARQ:ENCODING::")) + { + // a file, not a message. If is called "BBSPOLL" do a reverse forward else Ignore for now + + _strupr(conn->MailBuffer); + if (strstr(conn->MailBuffer, "BBSPOLL")) + { + SendARQMail(conn); + } + + free(conn->MailBuffer); + conn->MailBuffer = NULL; + conn->MailBufferSize = 0; + + return; + } + Loop: + ptr2 = strchr(ptr1, '\r'); + + linelen = (int)(ptr2 - ptr1); + + if (_memicmp(ptr1, "From:", 5) == 0 && linelen > 6) // Can have empty From: + { + char SaveFrom[100]; + char * FromHA; + + memcpy(FullFrom, ptr1, linelen); + FullFrom[linelen] = 0; + + // B2 From may now contain an @BBS + + strcpy(SaveFrom, FullFrom); + + FromHA = strlop(SaveFrom, '@'); + + if (strlen(SaveFrom) > 12) SaveFrom[12] = 0; + + strcpy(Msg->from, &SaveFrom[6]); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + + // Remove any SSID + + ptr3 = strchr(Msg->from, '-'); + if (ptr3) *ptr3 = 0; + + } + else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0) + { + HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *)); + HddrTo[Recipients] = zalloc(100); + + memset(FullTo, 0, 99); + memcpy(FullTo, &ptr1[4], linelen-4); + memcpy(HddrTo[Recipients], ptr1, linelen+2); + LocalMsg[Recipients] = FALSE; + + _strupr(FullTo); + + B2To = (int)(ptr1 - conn->MailBuffer); + + if (_memicmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + strcpy(FullTo, "RMS"); + strcpy(Msg->via, &FullTo[4]); + } + else + { + ptr3 = strchr(FullTo, '@'); + + if (ptr3) + { + *ptr3++ = 0; + strcpy(Msg->via, ptr3); + } + else + Msg->via[0] = 0; + } + + if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) + { + // Airmail Sends MARS messages as SMTP + + if (CheckifPacket(Msg->via)) + { + // Packet Message + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + _strupr(Msg->via); + + // Update the saved to: line (remove the smtp:) + + strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); + BBSMsgs++; + goto BBSMsg; + } + + // If a winlink.org address we need to convert to call + + if (_stricmp(Msg->via, "winlink.org") == 0) + { + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + { + memcpy(Msg->via, &ptr1[9], linelen); + Msg->via[linelen - 9] = 0; + strcpy(FullTo,"RMS"); + } +// FullTo[0] = 0; + + BBSMsg: + _strupr(FullTo); + _strupr(Msg->via); + } + + if (memcmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + } + + if (strcmp(Msg->via, "RMS") == 0) + { + // replace RMS with @winlink.org + + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo); + } + + if (strlen(FullTo) > 6) + FullTo[6] = 0; + + strlop(FullTo, '-'); + + strcpy(Msg->to, FullTo); + + if (SendBBStoSYSOPCall) + if (_stricmp(FullTo, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0)) + { + // No routing - check @BBS and WP + + struct UserInfo * ToUser = LookupCall(FullTo); + + Msg->via[0] = 0; // In case BPQ and not found + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + } + } + else + { + WPRecP WP = LookupWP(FullTo); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + + } + } + + // Fix To: address in B2 Header + + if (Msg->via[0]) + sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via); + else + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + + } + + RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *)); + RecpTo[Recipients] = zalloc(10); + + Via=realloc(Via, (Recipients+1) * sizeof(void *)); + Via[Recipients] = zalloc(50); + + strcpy(Via[Recipients], Msg->via); + strcpy(RecpTo[Recipients++], FullTo); + + // Remove the To: Line from the buffer + + } + else if (_memicmp(ptr1, "Type:", 4) == 0) + { + if (ptr1[6] == 'N') + Msg->type = 'T'; // NTS + else + Msg->type = ptr1[6]; + } + else if (_memicmp(ptr1, "Subject:", 8) == 0) + { + size_t Subjlen = ptr2 - &ptr1[9]; + if (Subjlen > 60) Subjlen = 60; + memcpy(Msg->title, &ptr1[9], Subjlen); + + goto ProcessBody; + } +// else if (_memicmp(ptr1, "Body:", 4) == 0) +// { +// MsgLen = atoi(&ptr1[5]); +// StartofMsg = ptr1; +// } + else if (_memicmp(ptr1, "File:", 5) == 0) + { + Msg->B2Flags |= Attachments; + } + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: 2009/07/25 10:08 + + sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d:%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + sscanf(&ptr1[5], "%02d/%02d/%04d %02d:%02d:%02d", + &rtime.tm_mday, &rtime.tm_mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + Msg->datecreated = Date; + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip cr + goto Loop; + } + + + // Processed all headers +ProcessBody: + + ptr2 +=2; // skip crlf + + Msg->length = (int)(&conn->MailBuffer[Msg->length] - ptr2); + + memmove(conn->MailBuffer, ptr2, Msg->length); + + CreateMessageFromBuffer(conn); + + conn->BBSFlags = 0; // Clear ARQ Mode + return; + } + + // File away the data + + Buffer[MsgLen++] = 0x0a; // BBS Msgs stored with crlf + + if ((conn->TempMsg->length + MsgLen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, MsgLen); + + conn->TempMsg->length += MsgLen; + + return; + + // Not sure what to do yet with files, but will process emails (using text style forwarding + +/* +ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::96 +ARQ::STX +//FLARQ COMPOSER +Date: 16/01/2014 22:26:06 +To: g8bpq +From: +Subject: test message + +Hello +Hello + +ARQ::ETX +*/ + + return; +} + +VOID ProcessTextFwdLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int len) +{ + Buffer[len] = 0; +// Debugprintf(Buffer); + + // With TNC2 body prompt is a single CR, so that shouldn't be ignored. + + // If thia causes problems with other TNC PMS implementations I'll have to revisit this + +// if (len == 1 && Buffer[0] == 13) +// return; + + if (conn->Flags & SENDTITLE) + { + // Waiting for Subject: prompt + + struct MsgInfo * Msg = conn->FwdMsg; + + nodeprintf(conn, "%s\r", Msg->title); + + conn->Flags &= ~SENDTITLE; + conn->Flags |= SENDBODY; + + // New Paccom PMS (V3.2) doesn't prompt for body so drop through and send it + if ((conn->BBSFlags & NEWPACCOM) == 0) + return; + + } + + if (conn->Flags & SENDBODY) + { + // Waiting for Enter Message Prompt + + struct tm * tm; + time_t temp; + + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + char * MsgPtr; + int MsgLen; + int Index = 0; + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + MsgLen = conn->FwdMsg->length; + + // If a B2 Message, remove B2 Header + + if (conn->FwdMsg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &conn->FwdMsg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + nodeprintf(conn, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + conn->FwdMsg->number, BBSName, HRoute, RlineVer); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + BBSputs(conn, "\r"); + + MsgLen = RemoveLF(MsgPtr, MsgLen); + + QueueMsg(conn, MsgPtr, MsgLen); + + if (user->ForwardingInfo->SendCTRLZ) + nodeprintf(conn, "\r\x1a"); + else + nodeprintf(conn, "\r/ex\r"); + + free(MsgBytes); + + conn->FBBMsgsSent = TRUE; + + + if (conn->FwdMsg->type == 'P') + Index = PMSG; + else if (conn->FwdMsg->type == 'B') + Index = BMSG; + else if (conn->FwdMsg->type == 'T') + Index = TMSG; + + user->Total.MsgsSent[Index]++; + user->Total.BytesForwardedOut[Index] += MsgLen; + + conn->Flags &= ~SENDBODY; + conn->Flags |= WAITPROMPT; + + return; + } + + if (conn->Flags & WAITPROMPT) + { + if (Buffer[len-2] != '>') + return; + + conn->Flags &= ~WAITPROMPT; + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + SaveMessageDatabase(); + +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(conn->FwdMsg); +#endif + + conn->UserPointer->ForwardingInfo->MsgCount--; + + // See if any more to forward + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg; + + // If we are using SETCALLTOSENDER make sure this message is from the same sender + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + unsigned char AXCall[7]; + + Msg = conn->FwdMsg; + ConvToAX25(Msg->from, AXCall); + if (memcmp(SESS[conn->BPQStream - 1].HOSTSESSION->L4USER, AXCall, 7) != 0) + { + Disconnect(conn->BPQStream); + return; + } + + // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ + + conn->Flags |= SENDTITLE; + + + if ((conn->BBSFlags & SETCALLTOSENDER)) + nodeprintf(conn, "S%c %s @ %s \r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call); + else + nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, + Msg->from, Msg->bid); + } + else + { + Disconnect(conn->BPQStream); + } + return; + } +} + + +#define N 2048 /* buffer size */ +#define F 60 /* lookahead buffer size */ +#define THRESHOLD 2 +#define NIL N /* leaf of tree */ + +extern UCHAR * infile; + +BOOL CheckforMIME(SocketConn * sockptr, char * Msg, char ** Body, int * MsgLen); + +/* ---G7TAJ PG Server --- */ +#ifndef WIN32 + +#define verbose 1 +#define TRUE 1 +#define FALSE 0 +#include +#include + +// G8BPQ Version of Steve G7TAJ's code + +int pgret = 9999; +int pindex = 0; + +void sigchild_handler(int sig , siginfo_t * siginfo, void * ucontext) +{ +/* • SIGCHLD fills in si_pid, si_uid, si_status, si_utime, and + si_stime, providing information about the child. The si_pid + field is the process ID of the child; si_uid is the child's + real user ID. The si_status field contains the exit status of + the child (if si_code is CLD_EXITED), or the signal number + that caused the process to change state. +*/ +// printf("SIGCHLD PID %d Code %d status %d\n", siginfo->si_pid, siginfo->si_code, siginfo->si_status); + pgret = siginfo->si_status; +} + + +void run_pg(CIRCUIT * conn, struct UserInfo * user) +{ + register char *cp; + FILE *iop; + int argc, pdes[2]; + pid_t pid; + + pgret = 9999; + + int index = user->Temp->PG_INDEX; + + iop = NULL; + + conn->InputBuffer[conn->InputLen] = 0; + strlop(conn->InputBuffer, 13); + conn->InputLen = 0; + + if (!user->Temp->RUNPGPARAMS) + user->Temp->RUNPGPARAMS = (RUNPGARGS_PTR) zalloc(sizeof(RUNPGARGS)); + + user->Temp->RUNPGPARAMS->user = user; + user->Temp->RUNPGPARAMS->conn = conn; + strncpy(user->Temp->RUNPGPARAMS->InputBuffer, conn->InputBuffer, 80); // needs to be length of actual input! + user->Temp->RUNPGPARAMS->Len = conn->InputLen; + + if (conn == 0 || user == 0) + { + Debugprintf("run_pg conn or user null"); + return; + } + + // Build command line. Parmas are: + + // - Callsign (format as F6FBB-8). + // - Level number (0 is the first time, up to 99). + // - Flags of the user (binary number as user`s mask of INIT.SRV). + // - Record number of the user in INF.SYS. + // - Received data (each word is a new argument). + + // BPQ doesn't support params 3 and 4 (but may supply copy of user record later) + + char cmd[20]; + char *ptr = cmd; + char pg_dir[MAX_PATH]; + char log_file[50] = "pg.log"; + char call[10]; + char data[80]; + char line[80]; + size_t bufsize = 80; + + strcpy(pg_dir, BaseDir); + strcat(pg_dir, "/PG/"); + sprintf(cmd, "./%s", SERVERLIST[user->Temp->PG_SERVER][1] ); + + sprintf(line, "%s%s", pg_dir, SERVERLIST[user->Temp->PG_SERVER][1]); +// printf("PG Prog %s%s\n", pg_dir, SERVERLIST[user->Temp->PG_SERVER][1]); + + // check file exists and is executable + + if (access(line, F_OK) == -1 || access(line, X_OK) == -1) + { + Debugprintf("%s FileNotFound || not executable", line); + BBSputs(conn, "Error running PG Server\r"); + conn->InputMode=0; + SendPrompt(conn, user); + return; + } + + strcpy(call, conn->UserPointer->Call); + index = user->Temp->PG_INDEX; + + // remove ';' from input for security reasons + + ptr = strchr(user->Temp->RUNPGPARAMS->InputBuffer, ';'); + if (ptr) + *ptr = '\0'; + + sprintf(data, "%s %d 0 0 %s", call, index, user->Temp->RUNPGPARAMS->InputBuffer); +// printf("PG Params %s\n", data); + + conn->InputBufferLen = 0; + + char buf[256]; + + sprintf (buf, "%s %s", line, data); // buf is command to exec +// printf ("PG exec cmd %s\n", buf); + + // Create pipe for reading PG program STDOUT + + if (pipe(pdes) < 0) + { + Debugprintf("run_pg pipe failed"); + BBSputs(conn, "Error running PG Server (pipe() failed)\r"); + conn->InputMode=0; + SendPrompt(conn, user); + return; + } + + // We will just fork and execute program. For now don't create a new thread + + // Trap sigchild so we can tell when it exits and get return code + + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_flags = SA_RESETHAND | SA_SIGINFO; // Restore default handler when called + act.sa_sigaction = sigchild_handler; + sigaction(SIGCHLD, &act, NULL); + + switch(pid = fork()) + { + case -1: /* error */ + (void)close(pdes[0]); + (void)close(pdes[1]); + Debugprintf("run_pg fork failed"); + BBSputs(conn, "Error running PG Server (fork() failed)\r"); + conn->InputMode=0; + SendPrompt(conn, user); + + return; + + case 0: /* child */ + + if (pdes[1] != 1) + { + dup2(pdes[1], 1); + dup2(pdes[1], 2); + (void)close(pdes[1]); + } + (void)close(pdes[0]); + + setpgid(0, pid); + + char *args[] = {"sh", "-c", buf, NULL}; + execve("/bin/sh", args, NULL); + + _exit(1); + } + + /* parent */ + +// printf("child PID %d\n", pid); + + struct timespec duration; + duration.tv_sec = 5; + duration.tv_nsec = 0; + + nanosleep(&duration, &duration); // Will be interrupted by SIGCHLD + +// printf("PG retcode %d\n", pgret); + + if (pgret == 9999) // Process still running + { + BBSputs(conn, "PG Program Looping\r"); + kill(pid, SIGKILL); + user->Temp->PG_INDEX = 0; + conn->InputMode=0; + SendPrompt(conn, user); + return; + } + + if (pgret > 127) + { + // Probably killed by signal + + int err = pgret - 128; + char errmsg[256]; + + sprintf(errmsg, "PG Signal %s received\n", strsignal(err)); + + BBSputs(conn, errmsg); + user->Temp->PG_INDEX = 0; + conn->InputMode=0; + SendPrompt(conn, user); + return; + } + + // Send STDOUT from PG program to BBS user + + iop = fdopen(pdes[0], "r"); + (void)close(pdes[1]); + + char buffer[128]; + while (fgets(buffer, sizeof(buffer), iop) != NULL) + { + BBSputs(conn, buffer); + buffer[0] = '\0'; + } + + switch (pgret) + { + case -1: // ERROR or forced closed + case 0: + index=0; // Goodbye/Exit + conn->InputMode=0; + SendPrompt(conn, user); + break; + case 1: + index++; // inc & keep in PG + break; + + case 2: + + index=0; // disconnect + conn->InputMode=0; + Disconnect(conn->BPQStream); + break; + + case 3: + Debugprintf("data->BBS & end"); + break; + + case 4: + Debugprintf("data->BBS and inc %d", pindex++); + break; + case 5: + Debugprintf("call no inc %d", pgret); + break; + + default: + BBSputs(conn, "PG unexexpected response\r"); + user->Temp->PG_INDEX = 0; + conn->InputMode=0; + SendPrompt(conn, user); + return; + } + + + user->Temp->PG_INDEX = index; + +// printf("runpg return index = %d\n", index); +} + + +/*---- G7TAJ END ----- */ + +#else + +#define BUFSIZE 4096 + +HANDLE g_hChildStd_IN_Rd = NULL; +HANDLE g_hChildStd_IN_Wr = NULL; +HANDLE g_hChildStd_OUT_Rd = NULL; +HANDLE g_hChildStd_OUT_Wr = NULL; + +HANDLE g_hInputFile = NULL; + +int CreateChildProcess(void); +void WriteToPipe(void); +void ReadFromPipe(void); + + +void run_pg( CIRCUIT * conn, struct UserInfo * user ) +{ + // Run PG program, read anything from program's stdout to the user + + int retcode = -1; + SECURITY_ATTRIBUTES saAttr; + char szCmdline[256] = "C:\\test\\hello.exe g8bpq 0"; + PROCESS_INFORMATION piProcInfo; + STARTUPINFO siStartInfo; + BOOL bSuccess = FALSE; + DWORD dwRead; + CHAR chBuf[BUFSIZE]; + int index = 0; + int ret = 0; + + // if first entry allocate RUNPGPARAMS + if (!user->Temp->RUNPGPARAMS) + { + user->Temp->RUNPGPARAMS = (RUNPGARGS_PTR) zalloc(sizeof(RUNPGARGS)); + } + + user->Temp->RUNPGPARAMS->user = user; + user->Temp->RUNPGPARAMS->conn = conn; + strncpy(user->Temp->RUNPGPARAMS->InputBuffer, conn->InputBuffer, 80); // needs to be length of actual input! + user->Temp->RUNPGPARAMS->Len = conn->InputLen; + index = user->Temp->PG_INDEX; + + conn->InputBuffer[conn->InputLen] = 0; + strlop(conn->InputBuffer, 13); + conn->InputLen = 0; + + // Build command line. Parmas are: + + // - Callsign (format as F6FBB-8). + // - Level number (0 is the first time, up to 99). + // - Flags of the user (binary number as user`s mask of INIT.SRV). + // - Record number of the user in INF.SYS. + // - Received data (each word is a new argument). + + // BPQ doesn't support params 3 and 4 (but may supply copy of user record later) + + sprintf(szCmdline, "%s/PG/%s %s %d 0 0 %s", BaseDir, + SERVERLIST[user->Temp->PG_SERVER][1], user->Call, index, conn->InputBuffer); + + // Set the bInheritHandle flag so pipe handles are inherited. + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + // Create a pipe for the child process's STDOUT. + + if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) ) + return; + + // Ensure the read handle to the pipe for STDOUT is not inherited. + + if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ) + return; + + // Create the child process. + + + // Set up members of the PROCESS_INFORMATION structure. + + ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) ); + + // Set up members of the STARTUPINFO structure. + // This structure specifies the STDIN and STDOUT handles for redirection. + + ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) ); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.hStdError = g_hChildStd_OUT_Wr; + siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; + siStartInfo.hStdInput = g_hChildStd_IN_Rd; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + // Create the child process. + + bSuccess = CreateProcess(NULL, + szCmdline, // command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &piProcInfo); // receives PROCESS_INFORMATION + + // If an error occurs, exit the application. + + if (!bSuccess) + retcode = -1; + else + { + // Wait until child process exits. + + if (WaitForSingleObject(piProcInfo.hProcess, 5000) == 0) // Wait max 5 seconds + { + // Success + + GetExitCodeProcess(piProcInfo.hProcess, &retcode); + } + else + { + // Failed or ran too long - kill + + TerminateProcess(piProcInfo.hProcess, 0); + } + + // Close handles to the child process and its primary thread. + // Some applications might keep these handles to monitor the status + // of the child process, for example. + + CloseHandle(piProcInfo.hProcess); + CloseHandle(piProcInfo.hThread); + + // Close handles to the stdin and stdout pipes no longer needed by the child process. + // If they are not explicitly closed, there is no way to recognize that the child process has ended. + + CloseHandle(g_hChildStd_OUT_Wr); + + // Send output to User + + for (;;) + { + bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL); + if( ! bSuccess || dwRead == 0 ) break; + + chBuf[dwRead] = 0; + + if (retcode == 4) + ProcessLine(conn, user, chBuf, dwRead); + else + BBSputs(conn, chBuf); + + if (! bSuccess ) break; + } + } + + + switch (retcode) + { + case -1: // ERROR or forced closed + + BBSputs(conn, "Problem running PG program\r"); + index=0; + conn->InputMode=0; + SendPrompt(conn, user); + break; + + case 0: + + // Goodbye/Exit + + index=0; + conn->InputMode=0; + SendPrompt(conn, user); + break; + + case 1: + index++; // inc & keep in PG + break; + + case 2: + index=0; // disconnect + conn->InputMode=0; + Disconnect(conn->BPQStream); + break; + + case 3: + printf("data->BBS & end\n"); + break; + + case 4: + + // Send Output to BBS - was done above + break; + + case 5: + printf("call no inc %d\n", ret); + break; + } + + user->Temp->PG_INDEX=index; + + // The remaining open handles are cleaned up when this process terminates. + // To avoid resource leaks in a larger application, close handles explicitly. + + return; +} + + + +#endif + + +VOID ProcessLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + char * Cmd, * Arg1; + char * Context; + char seps[] = " \t\r"; + int CmdLen; + + if (_memicmp(Buffer, "POSYNCLOGON", 11) == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->BBSFlags |= SYNCMODE; + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + + Sleep(500); + + BBSputs(conn, "OK\r"); + Flush(conn); + return; + } + + if (_memicmp(Buffer, "POSYNCHELLO", 11) == 0) + { + // This is first message received after connecting to SYNC + // Save Callsign + + char Reply[32]; + conn->BBSFlags |= SYNCMODE; + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + + sprintf(Reply, "POSYNCLOGON %s\r", BBSName); + BBSputs(conn, Reply); + return; + } + + if (conn->BBSFlags & SYNCMODE) + { + ProcessSyncModeMessage(conn, user, Buffer, len); + return; + } + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // A few messages should be trapped here and result in an immediate disconnect, whatever mode I think the session is in (it could be wrong) + + // *** Protocol Error + // Already Connected + // Invalid Command + + if (_memicmp(Buffer, "Already Connected", 17) == 0 || + _memicmp(Buffer, "Invalid Command", 15) == 0 || + _memicmp(Buffer, "*** Protocol Error", 18) == 0) + { + conn->BBSFlags |= DISCONNECTING; + Disconnect(conn->BPQStream); + return; + } + + if (conn->BBSFlags & FBBForwarding) + { + ProcessFBBLine(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & FLARQMODE) + { + ProcessFLARQLine(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & MCASTRX) + { + ProcessMCASTLine(conn, user, Buffer, len); + return; + } + + + if (conn->BBSFlags & TEXTFORWARDING) + { + ProcessTextFwdLine(conn, user, Buffer, len); + return; + } + + // if chatting to sysop pass message to BBS console + + if (conn->BBSFlags & SYSOPCHAT) + { + SendUnbuffered(-1, Buffer,len); + return; + } + + if (conn->Flags & GETTINGMESSAGE) + { + ProcessMsgLine(conn, user, Buffer, len); + return; + } + if (conn->Flags & GETTINGTITLE) + { + ProcessMsgTitle(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & MBLFORWARDING) + { + ProcessMBLLine(conn, user, Buffer, len); + return; + } + + if (conn->Flags & GETTINGUSER || conn->NewUser) // Could be new user but dont need name + { + if (memcmp(Buffer, ";FW:", 4) == 0 || Buffer[0] == '[') + { + struct BBSForwardingInfo * ForwardingInfo; + + conn->Flags &= ~GETTINGUSER; + + // New User is a BBS - create a temp struct for it + + if ((user->flags & (F_BBS | F_Temp_B2_BBS)) == 0) // It could already be a BBS without a user name + { + // Not defined as BBS - allocate and initialise forwarding structure + + user->flags |= F_Temp_B2_BBS; + + // An RMS Express user that needs a temporary BBS struct + + ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + + ForwardingInfo->AllowCompressed = TRUE; + ForwardingInfo->AllowBlocked = TRUE; + conn->UserPointer->ForwardingInfo->AllowB2 = TRUE; + } + SaveUserDatabase(); + } + else + { + if (conn->Flags & GETTINGUSER) + { + conn->Flags &= ~GETTINGUSER; + if (len > 18) + len = 18; + + memcpy(user->Name, Buffer, len-1); + SendWelcomeMsg(conn->BPQStream, conn, user); + SaveUserDatabase(); + UpdateWPWithUserInfo(user); + return; + } + } + } + + // Process Command + + if (conn->Paging && (conn->LinesSent >= conn->PageLen)) + { + // Waiting for paging prompt + + if (len > 1) + { + if (_memicmp(Buffer, "Abort", 1) == 0) + { + ClearQueue(conn); + conn->LinesSent = 0; + + nodeprintf(conn, AbortedMsg); + + if (conn->UserPointer->Temp->ListSuspended) + nodeprintf(conn, "bort, , = Continue..>"); + + SendPrompt(conn, user); + return; + } + } + + conn->LinesSent = 0; + return; + } + + if (user->Temp->ListSuspended) + { + // Paging limit hit when listing. User may abort, continue, or read one or more messages + + ProcessSuspendedListCommand(conn, user, Buffer, len); + return; + } + if (len == 1) + { + SendPrompt(conn, user); + return; + } + + Buffer[len] = 0; + + if (strstr(Buffer, "ARQ:FILE:")) + { + // Message from FLARQ + + conn->BBSFlags |= FLARQMODE; + strcpy(conn->ARQFilename, &Buffer[10]); // Will need name when we decide what to do with files + + // Create a Temp Messge Stucture + + CreateMessage(conn, conn->Callsign, "", "", 'P', NULL, NULL); + + Buffer[len++] = 0x0a; // BBS Msgs stored with crlf + + if ((conn->TempMsg->length + len) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); + + conn->TempMsg->length += len; + + return; + } + if (Buffer[0] == ';') // WL2K Comment + { + if (memcmp(Buffer, ";FW:", 4) == 0) + { + // Paclink User Select (poll for list) + + char * ptr1,* ptr2, * ptr3; + int index=0; + + // Convert string to Multistring + + Buffer[len-1] = 0; + + conn->PacLinkCalls = zalloc(len*3); + + ptr1 = &Buffer[5]; + ptr2 = (char *)conn->PacLinkCalls; + ptr2 += (len * 2); + strcpy(ptr2, ptr1); + + while (ptr2) + { + ptr3 = strlop(ptr2, ' '); + + if (strlen(ptr2)) + conn->PacLinkCalls[index++] = ptr2; + + ptr2 = ptr3; + } + + return; + } + + if (memcmp(Buffer, ";FR:", 4) == 0) + { + // New Message from TriMode - Just igonre till I know what to do with it + + return; + } + + // Ignore other ';' message + + return; + } + + + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + { + // If a BBS, set BBS Flag + + if (user->flags & ( F_BBS | F_Temp_B2_BBS)) + { + if (user->ForwardingInfo) + { + if (user->ForwardingInfo->Forwarding && ((conn->BBSFlags & OUTWARDCONNECT) == 0)) + { + BBSputs(conn, "Already Connected\r"); + Flush(conn); + Sleep(500); + Disconnect(conn->BPQStream); + return; + } + } + + if (user->ForwardingInfo) + { + user->ForwardingInfo->Forwarding = TRUE; + user->ForwardingInfo->FwdTimer = 0; // So we dont send to immediately + } + } + + if (user->flags & ( F_BBS | F_PMS | F_Temp_B2_BBS)) + { + Parse_SID(conn, &Buffer[1], len-4); + + if (conn->BBSFlags & FBBForwarding) + { + conn->FBBIndex = 0; // ready for first block; + conn->FBBChecksum = 0; + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + } + else + FBBputs(conn, ">\r"); + + } + + return; + } + + Cmd = strtok_s(Buffer, seps, &Context); + + if (Cmd == NULL) + { + if (!CheckForTooManyErrors(conn)) + BBSputs(conn, "Invalid Command\r"); + + SendPrompt(conn, user); + return; + } + + Arg1 = strtok_s(NULL, seps, &Context); + CmdLen = (int)strlen(Cmd); + + // Check List first. If any other, save last listed to user record. + + if (_memicmp(Cmd, "L", 1) == 0 && _memicmp(Cmd, "LISTFILES", 3) != 0) + { + DoListCommand(conn, user, Cmd, Arg1, FALSE, Context); + SendPrompt(conn, user); + return; + } + + if (conn->lastmsg > user->lastmsg) + { + user->lastmsg = conn->lastmsg; + SaveUserDatabase(); + } + + if (_stricmp(Cmd, "SHOWRMSPOLL") == 0) + { + DoShowRMSCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "AUTH") == 0) + { + DoAuthCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "Abort", 1) == 0) + { + ClearQueue(conn); + conn->LinesSent = 0; + + nodeprintf(conn, AbortedMsg); + + if (conn->UserPointer->Temp->ListSuspended) + nodeprintf(conn, "bort, , = Continue..>"); + + SendPrompt(conn, user); + return; + } + if (_memicmp(Cmd, "Bye", CmdLen) == 0 || _stricmp(Cmd, "ELSE") == 0) + { + ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); + Flush(conn); + Sleep(1000); + + if (conn->BPQStream > 0) + Disconnect(conn->BPQStream); +#ifndef LINBPQ + else + CloseConsole(conn->BPQStream); +#endif + return; + } + if (_memicmp(Cmd, "Node", 4) == 0) + { + ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); + Flush(conn); + Sleep(1000); + + if (conn->BPQStream > 0) + ReturntoNode(conn->BPQStream); +#ifndef LINBPQ + else + CloseConsole(conn->BPQStream); +#endif + return; + } + + if (_memicmp(Cmd, "IDLETIME", 4) == 0) + { + DoSetIdleTime(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "SETNEXTMESSAGENUMBER") == 0) + { + DoSetMsgNo(conn, user, Arg1, Context); + return; + } + + if (strlen(Cmd) < 12 && _memicmp(Cmd, "D", 1) == 0) + { + DoDeliveredCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "K", 1) == 0) + { + DoKillCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + + if (_memicmp(Cmd, "LISTFILES", 3) == 0 || _memicmp(Cmd, "FILES", 5) == 0) + { + ListFiles(conn, user, Arg1); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "READFILE", 4) == 0) + { + ReadBBSFile(conn, user, Arg1); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "REROUTEMSGS", 7) == 0) + { + if (conn->sysop == 0) + nodeprintf(conn, "Reroute Messages needs SYSOP status\r"); + else + { + ReRouteMessages(); + nodeprintf(conn, "Ok\r"); + } + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "YAPP", 4) == 0) + { + YAPPSendFile(conn, user, Arg1); + return; + } + + if (_memicmp(Cmd, "UH", 2) == 0 && conn->sysop) + { + DoUnholdCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_stricmp(Cmd, "IMPORT") == 0) + { + DoImportCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "EXPORT") == 0) + { + DoExportCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "I", 1) == 0) + { + char * Save; + char * MsgBytes; + + if (Arg1) + { + // User WP lookup + + DoWPLookup(conn, user, Cmd[1], Arg1); + SendPrompt(conn, user); + return; + } + + + MsgBytes = Save = ReadInfoFile("info.txt"); + if (MsgBytes) + { + int Length; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(Save); + } + else + BBSputs(conn, "SYSOP has not created an INFO file\r"); + + + SendPrompt(conn, user); + return; + } + + + if (_memicmp(Cmd, "Name", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 17) + Arg1[17] = 0; + + strcpy(user->Name, Arg1); + UpdateWPWithUserInfo(user); + + } + + SendWelcomeMsg(conn->BPQStream, conn, user); + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "OP", 2) == 0) + { + int Lines; + + // Paging Control. Param is number of lines per page + + if (Arg1) + { + Lines = atoi(Arg1); + + if (Lines) // Sanity Check + { + if (Lines < 10) + { + nodeprintf(conn,"Page Length %d is too short\r", Lines); + SendPrompt(conn, user); + return; + } + } + + user->PageLen = Lines; + conn->PageLen = Lines; + conn->Paging = (Lines > 0); + SaveUserDatabase(); + } + + nodeprintf(conn,"Page Length is %d\r", user->PageLen); + SendPrompt(conn, user); + + return; + } + + if (_memicmp(Cmd, "QTH", CmdLen) == 0) + { + if (Arg1) + { + // QTH may contain spaces, so put back together, and just split at cr + + Arg1[strlen(Arg1)] = ' '; + strtok_s(Arg1, "\r", &Context); + + if (strlen(Arg1) > 60) + Arg1[60] = 0; + + strcpy(user->Address, Arg1); + UpdateWPWithUserInfo(user); + + } + + nodeprintf(conn,"QTH is %s\r", user->Address); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "ZIP", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 8) + Arg1[8] = 0; + + strcpy(user->ZIP, _strupr(Arg1)); + UpdateWPWithUserInfo(user); + } + + nodeprintf(conn,"ZIP is %s\r", user->ZIP); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "CMSPASS", 7) == 0) + { + if (Arg1 == 0) + { + nodeprintf(conn,"Must specify a password\r"); + } + else + { + if (strlen(Arg1) > 15) + Arg1[15] = 0; + + strcpy(user->CMSPass, Arg1); + nodeprintf(conn,"CMS Password Set\r"); + SaveUserDatabase(); + } + + SendPrompt(conn, user); + + return; + } + + if (_memicmp(Cmd, "PASS", CmdLen) == 0) + { + if (Arg1 == 0) + { + nodeprintf(conn,"Must specify a password\r"); + } + else + { + if (strlen(Arg1) > 12) + Arg1[12] = 0; + + strcpy(user->pass, Arg1); + nodeprintf(conn,"BBS Password Set\r"); + SaveUserDatabase(); + } + + SendPrompt(conn, user); + + return; + } + + + if (_memicmp(Cmd, "R", 1) == 0) + { + DoReadCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "S", 1) == 0) + { + if (!DoSendCommand(conn, user, Cmd, Arg1, Context)) + SendPrompt(conn, user); + return; + } + + if ((_memicmp(Cmd, "Help", CmdLen) == 0) || (_memicmp(Cmd, "?", 1) == 0)) + { + char * Save; + char * MsgBytes = Save = ReadInfoFile("help.txt"); + + if (MsgBytes) + { + int Length; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(Save); + } + else + { + BBSputs(conn, "A - Abort Output\r"); + BBSputs(conn, "B - Logoff\r"); + BBSputs(conn, "CMSPASS Password - Set CMS Password\r"); + BBSputs(conn, "D - Flag NTS Message(s) as Delivered - D num\r"); + BBSputs(conn, "HOMEBBS - Display or get HomeBBS\r"); + BBSputs(conn, "INFO - Display information about this BBS\r"); + BBSputs(conn, "I CALL - Lookup CALL in WP Allows *CALL CALL* *CALL* wildcards\r"); + BBSputs(conn, "I@ PARAM - Lookup @BBS in WP\r"); + BBSputs(conn, "IZ PARAM - Lookup Zip Codes in WP\r"); + BBSputs(conn, "IH PARAM - Lookup HA elements in WP - eg USA EU etc\r"); + + BBSputs(conn, "K - Kill Message(s) - K num, KM (Kill my read messages)\r"); + BBSputs(conn, "L - List Message(s) - \r"); + BBSputs(conn, " L = List New, LR = List New (Oldest first)\r"); + BBSputs(conn, " LM = List Mine, L> Call, L< Call, L@ = List to, from or at\r"); + BBSputs(conn, " LL num = List msg num, L num-num = List Range\r"); + BBSputs(conn, " LN LY LH LK LF L$ LD = List Message with corresponding Status\r"); + BBSputs(conn, " LB LP LT = List Mesaage with corresponding Type\r"); + BBSputs(conn, " LC = List TO fields of all active bulletins\r"); + BBSputs(conn, " You can combine most selections eg LMP, LMN LB< G8BPQ\r"); + BBSputs(conn, "LISTFILES or FILES - List files available for download\r"); + + BBSputs(conn, "N Name - Set Name\r"); + BBSputs(conn, "NODE - Return to Node\r"); + BBSputs(conn, "OP n - Set Page Length (Output will pause every n lines)\r"); + BBSputs(conn, "PASS Password - Set BBS Password\r"); + BBSputs(conn, "POLLRMS - Manage Polling for messages from RMS \r"); + BBSputs(conn, "Q QTH - Set QTH\r"); + BBSputs(conn, "R - Read Message(s) - R num \r"); + BBSputs(conn, " RM (Read new messages to me), RMR (RM oldest first)\r"); + BBSputs(conn, "READ Name - Read File\r"); + + BBSputs(conn, "S - Send Message - S or SP Send Personal, SB Send Bull, ST Send NTS,\r"); + BBSputs(conn, " SR Num - Send Reply, SC Num - Send Copy\r"); + BBSputs(conn, "X - Toggle Expert Mode\r"); + BBSputs(conn, "YAPP - Download file from BBS using YAPP protocol\r"); + if (conn->sysop) + { + BBSputs(conn, "DOHOUSEKEEPING - Run Housekeeping process\r"); + BBSputs(conn, "EU - Edit User Flags - Type EU for Help\r"); + BBSputs(conn, "EXPORT - Export messages to file - Type EXPORT for Help\r"); + BBSputs(conn, "FWD - Control Forwarding - Type FWD for Help\r"); + BBSputs(conn, "IMPORT - Import messages from file - Type IMPORT for Help\r"); + BBSputs(conn, "REROUTEMSGS - Rerun message routing process\r"); + BBSputs(conn, "SETNEXTMESSAGENUMBER - Sets next message number\r"); + BBSputs(conn, "SHOWRMSPOLL - Displays your RMS polling list\r"); + BBSputs(conn, "UH - Unhold Message(s) - UH ALL or UH num num num...\r"); + } + } + + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "Ver", CmdLen) == 0) + { + nodeprintf(conn, "BBS Version %s\rNode Version %s\r", VersionStringWithBuild, GetVersionString()); + + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "HOMEBBS", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 40) Arg1[40] = 0; + + strcpy(user->HomeBBS, _strupr(Arg1)); + UpdateWPWithUserInfo(user); + + if (!strchr(Arg1, '.')) + BBSputs(conn, "Please enter HA with HomeBBS eg g8bpq.gbr.eu - this will help message routing\r"); + } + + nodeprintf(conn,"HomeBBS is %s\r", user->HomeBBS); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if ((_memicmp(Cmd, "EDITUSER", 5) == 0) || (_memicmp(Cmd, "EU", 2) == 0)) + { + DoEditUserCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "POLLRMS") == 0) + { + DoPollRMSCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "DOHOUSEKEEPING") == 0) + { + DoHousekeepingCmd(conn, user, Arg1, Context); + return; + } + + + if (_stricmp(Cmd, "FWD") == 0) + { + DoFwdCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "X", 1) == 0) + { + user->flags ^= F_Expert; + + if (user->flags & F_Expert) + BBSputs(conn, "Expert Mode\r"); + else + BBSputs(conn, "Expert Mode off\r"); + + SaveUserDatabase(); + SendPrompt(conn, user); + return; + } + + /*---- G7TAJ PG Server ----- */ + + + if (_stricmp(Cmd, "PG") == 0) + { + if ( NUM_SERVERS == 0 ) + { + BBSputs(conn, "No PG Servers currently defined\r"); + SendPrompt(conn, user); + Flush(conn); + return; + } + + if ( !Arg1 ) + { + char reply[80]; + int i; + for (i=0; i< NUM_SERVERS; i++ ) + { + sprintf(reply, "%s -> %s\r", SERVERLIST[i][0], SERVERLIST[i][2]); + BBSputs(conn, reply); + } + SendPrompt(conn, user); + return; + } + else + { + int i; + for (i=0; i < NUM_SERVERS; i++ ) + { + if ( _stricmp( _strupr(Arg1), SERVERLIST[i][0] ) == 0 ) { + user->Temp->PG_SERVER = i; // index to server to run + user->Temp->PG_INDEX = 0; // newly starting PG + conn->InputMode = 'P'; // Inside PG Server + + // conn->InputBuffer is altered above and split into Cmd,Arg1,Context + // so put it back and call PG (removing PG) + sprintf( conn->InputBuffer, "%s", Context); + conn->InputLen = strlen(Context); + run_pg( conn, user ); + return; + } + } + BBSputs(conn, "No server found\r"); + SendPrompt(conn, user); + return; + } + } + + /*---- G7TAJ END ---- */ + + if (conn->Flags == 0) + { + if (!CheckForTooManyErrors(conn)) + BBSputs(conn, "Invalid Command\r"); + + SendPrompt(conn, user); + } + + // Send if possible + + Flush(conn); +} + +VOID __cdecl nprintf(CIRCUIT * conn, const char * format, ...) +{ + // seems to be printf to a socket + + char buff[600]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(buff, format, arglist); + + BBSputs(conn, buff); +} + +// Code to delete obsolete files from Mail folder + +#ifdef WIN32 + +int DeleteRedundantMessages() +{ + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + char File[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + int Msgno; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, MailDir); + strcat(szDir, "\\*.mes"); + + + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + return 0; + } + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugString(ffd.cFileName); + } + else + { + Msgno = atoi(&ffd.cFileName[2]); + + if (MsgnotoMsg[Msgno] == 0) + { + sprintf(File, "%s/%s%c", MailDir, ffd.cFileName, 0); + Debugprintf("Tidy Mail - Delete %s\n", File); + +// if (DeletetoRecycleBin) + DeletetoRecycle(File); +// else +// DeleteFile(File); + } + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + return 0; +} + +#else + +#include + +int MsgFilter(const struct dirent * dir) +{ + return (strstr(dir->d_name, ".mes") != 0); +} + +int DeleteRedundantMessages() +{ + struct dirent **namelist; + int n; + struct stat STAT; + int Msgno = 0, res; + char File[100]; + + n = scandir("Mail", &namelist, MsgFilter, alphasort); + + if (n < 0) + perror("scandir"); + else + { + while(n--) + { + if (stat(namelist[n]->d_name, &STAT) == 0); + { + Msgno = atoi(&namelist[n]->d_name[2]); + + if (MsgnotoMsg[Msgno] == 0) + { + sprintf(File, "Mail/%s", namelist[n]->d_name); + printf("Deleting %s\n", File); + unlink(File); + } + } + free(namelist[n]); + } + free(namelist); + } + return 0; +} +#endif + +VOID TidyWelcomeMsg(char ** pPrompt) +{ + // Make sure Welcome Message doesn't ends with > + + char * Prompt = *pPrompt; + + int i = (int)strlen(Prompt) - 1; + + *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it + + Prompt = *pPrompt; + + while (Prompt[i] == 10 || Prompt[i] == 13) + { + Prompt[i--] = 0; + } + + while (i >= 0 && Prompt[i] == '>') + Prompt[i--] = 0; + + strcat(Prompt, "\r\n"); +} + +VOID TidyPrompt(char ** pPrompt) +{ + // Make sure prompt ends > CR LF + + char * Prompt = *pPrompt; + + int i = (int)strlen(Prompt) - 1; + + *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it + + Prompt = *pPrompt; + + while (Prompt[i] == 10 || Prompt[i] == 13) + { + Prompt[i--] = 0; + } + + if (Prompt[i] != '>') + strcat(Prompt, ">"); + + strcat(Prompt, "\r\n"); +} + +VOID TidyPrompts() +{ + TidyPrompt(&Prompt); + TidyPrompt(&NewPrompt); + TidyPrompt(&ExpertPrompt); +} + +BOOL SendARQMail(CIRCUIT * conn) +{ + conn->NextMessagetoForward = FirstMessageIndextoForward; + + // Send Message. There is no mechanism for reverse forwarding + + if (FindMessagestoForward(conn)) + { + struct MsgInfo * Msg; + char MsgHddr[512]; + int HddrLen; + char TimeString[64]; + char * WholeMessage; + + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + int MsgLen; + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + Msg = conn->FwdMsg; + WholeMessage = malloc(Msg->length + 512); + + FormatTime(TimeString, (time_t)Msg->datecreated); + +/* +ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::96 +ARQ::STX +//FLARQ COMPOSER +Date: 16/01/2014 22:26:06 +To: g8bpq +From: +Subject: test message + +Hello +Hello + +ARQ::ETX +*/ + Logprintf(LOG_BBS, conn, '>', "ARQ Send Msg %d From %s To %s", Msg->number, Msg->from, Msg->to); + + HddrLen = sprintf(MsgHddr, "Date: %s\nTo: %s\nFrom: %s\nSubject %s\n\n", + TimeString, Msg->to, Msg->from, Msg->title); + + MsgLen = sprintf(WholeMessage, "ARQ:FILE::Msg%s_%d\nARQ:EMAIL::\nARQ:SIZE::%d\nARQ::STX\n%s%s\nARQ::ETX\n", + BBSName, Msg->number, (int)(HddrLen + strlen(MsgBytes)), MsgHddr, MsgBytes); + + WholeMessage[MsgLen] = 0; + QueueMsg(conn,WholeMessage, MsgLen); + + free(WholeMessage); + free(MsgBytes); + + // FLARQ doesn't ACK the message, so set flag to look for all acked + + conn->BBSFlags |= ARQMAILACK; + conn->ARQClearCount = 10; // To make sure clear isn't reported too soon + + return TRUE; + } + + // Nothing to send - close + + Logprintf(LOG_BBS, conn, '>', "ARQ Send - Nothing to Send - Closing"); + + conn->CloseAfterFlush = 20; + return FALSE; +} + +char *stristr (char *ch1, char *ch2) +{ + char *chN1, *chN2; + char *chNdx; + char *chRet = NULL; + + chN1 = _strdup (ch1); + chN2 = _strdup (ch2); + if (chN1 && chN2) + { + chNdx = chN1; + while (*chNdx) + { + *chNdx = (char) tolower (*chNdx); + chNdx ++; + } + chNdx = chN2; + while (*chNdx) + { + *chNdx = (char) tolower (*chNdx); + chNdx ++; + } + + chNdx = strstr (chN1, chN2); + if (chNdx) + chRet = ch1 + (chNdx - chN1); + } + free (chN1); + free (chN2); + return chRet; +} + +#ifdef WIN32 + +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetBPQDirectory()); + strcat(szDir, "\\BPQMailChat\\Files\\*.*"); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + nodeprintf(conn, "No Files\r"); + return; + } + + // List all the files in the directory with some info about them. + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + {} + else + { + if (filename == NULL || stristr(ffd.cFileName, filename)) + nodeprintf(conn, "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); +} + +#else + +#include + +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + struct dirent **namelist; + int n, i; + struct stat STAT; + time_t now = time(NULL); + int Age = 0, res; + char FN[256]; + + n = scandir("Files", &namelist, NULL, alphasort); + + if (n < 0) + perror("scandir"); + else + { + for (i = 0; i < n; i++) + { + sprintf(FN, "Files/%s", namelist[i]->d_name); + + if (filename == NULL || stristr(namelist[i]->d_name, filename)) + if (FN[6] != '.' && stat(FN, &STAT) == 0) + nodeprintf(conn, "%s %d\r", namelist[i]->d_name, STAT.st_size); + + free(namelist[i]); + } + free(namelist); + } + return; +} +#endif + +void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + char * MsgBytes; + + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + struct stat STAT; + + if (filename == NULL) + { + nodeprintf(conn, "Missing Filename\r"); + return; + } + + if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) + { + nodeprintf(conn, "Invalid filename\r"); + return; + } + + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + int Length; + + MsgBytes=malloc(FileSize+1); + fread(MsgBytes, 1, FileSize, hFile); + fclose(hFile); + + MsgBytes[FileSize]=0; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(MsgBytes); + + nodeprintf(conn, "\r\r[End of File %s]\r", filename); + return; + } + } + + nodeprintf(conn, "File %s not found\r", filename); +} + +VOID ProcessSuspendedListCommand(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + struct TempUserInfo * Temp = user->Temp; + + Buffer[len] = 0; + + // Command entered during listing pause. May be A R or C (or ) + + if (Buffer[0] == 'A' || Buffer[0] == 'a') + { + // Abort + + Temp->ListActive = Temp->ListSuspended = FALSE; + SendPrompt(conn, user); + return; + } + + if (_memicmp(Buffer, "R ", 2) == 0) + { + // Read Message(es) + + int msgno; + char * ptr; + char * Context; + + ptr = strtok_s(&Buffer[2], " ", &Context); + + while (ptr) + { + msgno = atoi(ptr); + ReadMessage(conn, user, msgno); + + ptr = strtok_s(NULL, " ", &Context); + } + + nodeprintf(conn, "bort, , = Continue..>"); + return; + } + + if (Buffer[0] == 'C' || Buffer[0] == 'c' || Buffer[0] == '\r' ) + { + // Resume Listing from where we left off + + DoListCommand(conn, user, Temp->LastListCommand, Temp->LastListParams, TRUE, ""); + SendPrompt(conn, user); + return; + } + + nodeprintf(conn, "bort, , = Continue..>"); + +} +/* +CreateMessageWithAttachments() +{ + int i; + char * ptr, * ptr2, * ptr3, * ptr4; + char Boundary[1000]; + BOOL Multipart = FALSE; + BOOL ALT = FALSE; + int Partlen; + char * Save; + BOOL Base64 = FALSE; + BOOL QuotedP = FALSE; + + char FileName[100][250] = {""}; + int FileLen[100]; + char * FileBody[100]; + char * MallocSave[100]; + UCHAR * NewMsg; + + int Files = 0; + + ptr = Msg; + + if ((sockptr->MailSize + 2000) > sockptr->MailBufferSize) + { + sockptr->MailBufferSize += 2000; + sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to extend Message Buffer"); + shutdown(sockptr->socket, 0); + return FALSE; + } + } + + + NewMsg = sockptr->MailBuffer + 1000; + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", FileLen[0]); + + for (i = 1; i < Files; i++) + { + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[i], FileName[i]); + } + + NewMsg += sprintf(NewMsg, "\r\n"); + + for (i = 0; i < Files; i++) + { + memcpy(NewMsg, FileBody[i], FileLen[i]); + NewMsg += FileLen[i]; + free(MallocSave[i]); + NewMsg += sprintf(NewMsg, "\r\n"); + } + + *MsgLen = NewMsg - (sockptr->MailBuffer + 1000); + *Body = sockptr->MailBuffer + 1000; + + return TRUE; // B2 Message +} + +*/ +VOID CreateUserReport() +{ + struct UserInfo * User; + int i; + char Line[200]; + int len; + char File[MAX_PATH]; + FILE * hFile; + + sprintf(File, "%s/UserList.csv", BaseDir); + + hFile = fopen(File, "wb"); + + if (hFile == NULL) + { + Debugprintf("Failed to create UserList.csv"); + return; + } + + for (i=1; i <= NumberofUsers; i++) + { + User = UserRecPtr[i]; + + len = sprintf(Line, "%s,%d,%s,%x,%s,\"%s\",%x,%s,%s,%s\r\n", + User->Call, + User->lastmsg, + FormatDateAndTime((time_t)User->TimeLastConnected, FALSE), + User->flags, + User->Name, + User->Address, + User->RMSSSIDBits, + User->HomeBBS, + User->QRA, + User->ZIP +// struct MsgStats Total; +// struct MsgStats Last; + ); + fwrite(Line, 1, len, hFile); + } + + fclose(hFile); +} + +BOOL ProcessYAPPMessage(CIRCUIT * conn) +{ + int Len = conn->InputLen; + UCHAR * Msg = conn->InputBuffer; + int pktLen = Msg[1]; + char Reply[2] = {ACK}; + int NameLen, SizeLen, OptLen; + char * ptr; + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char Mess[255]; + int len; + char * FN = &Msg[2]; + + switch (Msg[0]) + { + case ENQ: // YAPP Send_Init + + // Shouldn't occur in session. Reset state + + Mess[0] = ACK; + Mess[1] = 1; + QueueMsg(conn, Mess, 2); + Flush(conn); + conn->InputLen = 0; + if (conn->MailBuffer) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + } + return TRUE; + + case SOH: + + // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) + + // YAPPC has date/time in dos format + + if (Len < Msg[1] + 1) + return 0; + + NameLen = (int)strlen(FN); + strcpy(conn->ARQFilename, FN); + ptr = &Msg[3 + NameLen]; + SizeLen = (int)strlen(ptr); + FileSize = atoi(ptr); + + // Check file name for unsafe characters (.. / \) + + if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) + { + Mess[0] = NAK; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPP File Name %s invalid\r", FN); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + OptLen = pktLen - (NameLen + SizeLen + 2); + + conn->YAPPDate = 0; + + if (OptLen >= 8) // We have a Date/Time for YAPPC + { + ptr = ptr + SizeLen + 1; + conn->YAPPDate = strtol(ptr, NULL, 16); + } + + // Check Size + + if (FileSize > MaxRXSize) + { + Mess[0] = NAK; + Mess[1] = sprintf(&Mess[2], "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); + QueueMsg(conn, Mess, Mess[1] + 2); + + Flush(conn); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + // Make sure file does not exist + + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + Mess[0] = NAK; + Mess[1] = sprintf(&Mess[2], "YAPP File %s already exists\r", conn->ARQFilename);; + QueueMsg(conn, Mess, Mess[1] + 2); + + Flush(conn); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s already exists\r", conn->ARQFilename); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + fclose(hFile); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + + conn->MailBufferSize = FileSize; + conn->MailBuffer=malloc(FileSize); + conn->YAPPLen = 0; + + if (conn->YAPPDate) // If present use YAPPC + Reply[1] = ACK; //Receive_TPK + else + Reply[1] = 2; //Rcv_File + + QueueMsg(conn, Reply, 2); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP upload to %s started", conn->ARQFilename); + WriteLogLine(conn, '!', Mess, len, LOG_BBS); + + conn->InputLen = 0; + return FALSE; + + case STX: + + // Data Packet + + // Check we have it all + + if (conn->YAPPDate) // If present use YAPPC so have checksum + { + if (pktLen > (Len - 3)) // -3 for header and checksum + return 0; // Wait for rest + } + else + { + if (pktLen > (Len - 2)) // -2 for header + return 0; // Wait for rest + } + + // Save data and remove from buffer + + // if YAPPC check checksum + + if (conn->YAPPDate) + { + UCHAR Sum = 0; + int i; + UCHAR * uptr = &Msg[2]; + + i = pktLen; + + while(i--) + Sum += *(uptr++); + + if (Sum != *uptr) + { + // Checksum Error + + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPPC Checksum Error\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + return TRUE; + } + } + + if ((conn->YAPPLen) + pktLen > conn->MailBufferSize) + { + // Too Big ?? + + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPP Too much data received\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + return TRUE; + } + + + memcpy(&conn->MailBuffer[conn->YAPPLen], &Msg[2], pktLen); + conn->YAPPLen += pktLen; + + if (conn->YAPPDate) + ++pktLen; // Add Checksum + + conn->InputLen -= (pktLen + 2); + memmove(conn->InputBuffer, &conn->InputBuffer[pktLen + 2], conn->InputLen); + + return TRUE; + + case ETX: + + // End Data + + + + if (conn->YAPPLen == conn->MailBufferSize) + { + // All received + + int ret; + DWORD Written = 0; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); + +#ifdef WIN32 + hFile = CreateFile(MsgFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + ret = WriteFile(hFile, conn->MailBuffer, conn->YAPPLen, &Written, NULL); + + if (conn->YAPPDate) + { + FILETIME FileTime; + struct tm TM; + struct timeval times[2]; + time_t TT; +/* + The MS-DOS date. The date is a packed value with the following format. + + cant use DosDateTimeToFileTime on Linux + + Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + wFatTime + The MS-DOS time. The time is a packed value with the following format. + Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) +*/ + memset(&TM, 0, sizeof(TM)); + + TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; + TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); + TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); + + TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); + TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; + TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; + + Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); + + TT = mktime(&TM); + times[0].tv_sec = times[1].tv_sec = + times[0].tv_usec = times[1].tv_usec = 0; + + DosDateTimeToFileTime((WORD)(conn->YAPPDate >> 16), (WORD)conn->YAPPDate & 0xFFFF, &FileTime); + ret = SetFileTime(hFile, &FileTime, &FileTime, &FileTime); + ret = GetLastError(); + + } + CloseHandle(hFile); + } +#else + + hFile = fopen(MsgFile, "wb"); + if (hFile) + { + Written = fwrite(conn->MailBuffer, 1, conn->YAPPLen, hFile); + fclose(hFile); + + if (conn->YAPPDate) + { + struct tm TM; + struct timeval times[2]; +/* + The MS-DOS date. The date is a packed value with the following format. + + cant use DosDateTimeToFileTime on Linux + + Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + wFatTime + The MS-DOS time. The time is a packed value with the following format. + Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) +*/ + memset(&TM, 0, sizeof(TM)); + + TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; + TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); + TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); + + TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); + TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; + TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; + + Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); + + times[0].tv_sec = times[1].tv_sec = mktime(&TM); + times[0].tv_usec = times[1].tv_usec = 0; + } + } +#endif + + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + if (Written != conn->YAPPLen) + { + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "Failed to save YAPP File\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + } + } + + Reply[1] = 3; //Ack_EOF + QueueMsg(conn, Reply, 2); + Flush(conn); + conn->InputLen = 0; + + return TRUE; + + case EOT: + + // End Session + + Reply[1] = 4; // Ack_EOT + QueueMsg(conn, Reply, 2); + Flush(conn); + conn->InputLen = 0; + conn->InputMode = 0; + + len = sprintf_s(Mess, sizeof(Mess), "YAPP file %s received\r", conn->ARQFilename); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + + return TRUE; + + case CAN: + + // Abort + + Mess[0] = ACK; + Mess[1] = 5; // CAN Ack + QueueMsg(conn, Mess, 2); + Flush(conn); + + if (conn->MailBuffer) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + } + + // There may be a reason after the CAN + + len = Msg[1]; + + if (len) + { + char * errormsg = &Msg[2]; + errormsg[len] = 0; + nodeprintf(conn, "File Rejected - %s\r", errormsg); + } + else + + nodeprintf(conn, "File Rejected\r"); + + + len = sprintf_s(Mess, sizeof(Mess), "YAPP Transfer cancelled by Terminal\r"); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + + return FALSE; + + case ACK: + + switch (Msg[1]) + { + case 1: // Rcv_Rdy + + // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) + + len = (int)strlen(conn->ARQFilename) + 3; + + strcpy(&Mess[2], conn->ARQFilename); + len += sprintf(&Mess[len], "%d", conn->MailBufferSize); + len++; // include null + Mess[0] = SOH; + Mess[1] = len - 2; + + QueueMsg(conn, Mess, len); + Flush(conn); + conn->InputLen = 0; + + return FALSE; + + case 2: + + // Start sending message + + YAPPSendData(conn); + conn->InputLen = 0; + return FALSE; + + case 3: + + // ACK EOF - Send EOT + + + Mess[0] = EOT; + Mess[1] = 1; + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->InputLen = 0; + return FALSE; + + case 4: + + // ACK EOT + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + + conn->InputLen = 0; + return FALSE; + + default: + conn->InputLen = 0; + return FALSE; + + + + } + + case NAK: + + // Either Reject or Restart + + // RE Resume NAK len R NULL (File size in ASCII) NULL + + if (conn->InputLen > 2 && Msg[2] == 'R' && Msg[3] == 0) + { + int posn = atoi(&Msg[4]); + + conn->YAPPLen += posn; + conn->MailBufferSize -= posn; + + YAPPSendData(conn); + conn->InputLen = 0; + return FALSE; + + } + + // There may be a reason after the ack + + len = Msg[1]; + + if (len) + { + char * errormsg = &Msg[2]; + errormsg[len] = 0; + nodeprintf(conn, "File Rejected - %s\r", errormsg); + } + else + + nodeprintf(conn, "File Rejected\r"); + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + conn->InputLen = 0; + SendPrompt(conn, conn->UserPointer); + return FALSE; + } + + nodeprintf(conn, "Unexpected message during YAPP Transfer. Transfer canncelled\r"); + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + conn->InputLen = 0; + SendPrompt(conn, conn->UserPointer); + + return FALSE; + +} + +void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + struct stat STAT; + + if (filename == NULL) + { + nodeprintf(conn, "Filename missing\r"); + SendPrompt(conn, user); + return; + } + + if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) + { + nodeprintf(conn, "Invalid filename\r"); + SendPrompt(conn, user); + return; + } + + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + char Mess[255]; + strcpy(conn->ARQFilename, filename); + conn->MailBuffer = malloc(FileSize); + conn->MailBufferSize = FileSize; + conn->YAPPLen = 0; + fread(conn->MailBuffer, 1, FileSize, hFile); + fclose(hFile); + + Mess[0] = ENQ; + Mess[1] = 1; + + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->InputMode = 'Y'; + + return; + } + } + + nodeprintf(conn, "File %s not found\r", filename); + SendPrompt(conn, user); +} + +void YAPPSendData(ConnectionInfo * conn) +{ + char Mess[258]; + + conn->BBSFlags |= YAPPTX; + + while (TXCount(conn->BPQStream) < 15) + { + int Left = conn->MailBufferSize; + + if (Left == 0) + { + // Finished - send End Data + + Mess[0] = ETX; + Mess[1] = 1; + + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->BBSFlags &= ~YAPPTX; + break; + } + + if (Left > conn->paclen - 2) // 2 byte header + Left = conn->paclen -2; + + memcpy(&Mess[2], &conn->MailBuffer[conn->YAPPLen], Left); + Mess[0] = STX; + Mess[1] = Left; + + QueueMsg(conn, Mess, Left + 2); + Flush(conn); + + conn->YAPPLen += Left; + conn->MailBufferSize -= Left; + } +} + +char * AddUser(char * Call, char * password, BOOL BBSFlag) +{ + struct UserInfo * USER; + + strlop(Call, '-'); + + if (strlen(Call) > 6) + Call[6] = 0; + + _strupr(Call); + + if (Call[0] == 0 || LookupCall(Call)) + { + return("User already exists\r\n"); + } + + USER = AllocateUserRecord(Call); + USER->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (strlen(password) > 12) + password[12] = 0; + + strcpy(USER->pass, password); + + if (BBSFlag) + { + if(SetupNewBBS(USER)) + USER->flags |= F_BBS; + else + printf("Cannot set user to be a BBS - you already have 160 BBS's defined\r\n"); + } + + SaveUserDatabase(); + UpdateWPWithUserInfo(USER); + + return("User added\r\n"); +} + +// Server Support Code + +// For the moment only internal REQDIR and REQFIL. + +// May add WPSERV and user implemented servers +/* +F6FBB BBS > + SP REQDIR @ F6ABJ.FRA.EU + Title of message : + YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU + Text of message : + /EX + + F6FBB BBS > + SP REQFIL @ F6ABJ.FRA.EU + Title of message : + DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU + Text of message : + /EX + + Note Text not used. + +*/ + +VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To); + +BOOL ProcessReqDir(struct MsgInfo * Msg) +{ + char * Buffer; + int Len = 0; + char * ptr; + + // Parse title - gives directory and return address + + // YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU + + // At the moment we don't allow subdirectories but no harm handling here + + char Pattern[64]; + char * Address; + char * filename = NULL; // ?? Pattern Match ?? + +#ifdef WIN32 + + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + +#else + + #include + + struct dirent **namelist; + int n, i; + struct stat STAT; + int res; + char FN[256]; + +#endif + + strcpy(Pattern, Msg->title); + + ptr = strchr(Pattern, '@'); + + if (ptr == NULL) + + // if we don't have return address no point + // but could we default to sender?? + + return FALSE; + + *ptr++ = 0; // Terminate Path + + strlop(Pattern, ' '); + + while (*ptr == ' ') + ptr++; // accept with or without spaces round @ + + Address = ptr; + + ptr = Buffer = malloc(MaxTXSize); + +#ifdef WIN32 + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetBPQDirectory()); + strcat(szDir, "\\BPQMailChat\\Files\\"); + strcat(szDir, Pattern); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + Len = sprintf(Buffer, "No Files\r"); + } + else + { + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + {} + else + { + if (filename == NULL || stristr(ffd.cFileName, filename)) + Len += sprintf(&Buffer[Len], "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + } + +#else + + n = scandir("Files", &namelist, NULL, alphasort); + + if (n < 0) + perror("scandir"); + else + { + for (i = 0; i < n; i++) + { + sprintf(FN, "Files/%s", namelist[i]->d_name); + + if (filename == NULL || stristr(namelist[i]->d_name, filename)) + if (FN[6] != '.' && stat(FN, &STAT) == 0) + Len += sprintf(&Buffer[Len], "%s %d\r", namelist[i]->d_name, STAT.st_size); + + free(namelist[i]); + } + free(namelist); + } + +#endif + + // Build Message + + SendServerReply("REQDIR Reply", Buffer, Len, _strupr(Address)); + return TRUE; +} + +/* + ' Augment Message ID with the Message Pickup Station we're directing this message to. + ' + Dim strAugmentedMessageID As String + If GetMidRMS(MessageId) <> "" Then + ' The MPS RMS is already set on the message ID + strAugmentedMessageID = MessageId + strMPS = GetMidRMS(MessageId) + ' "@R" at the end of the MID means route message only via radio + If GetMidForwarding(MessageId) = "" And (blnRadioOnly Or UploadThroughInternet()) Then + strAugmentedMessageID &= "@" & strHFOnlyFlag + End If + ElseIf strMPS <> "" Then + ' Add MPS to the message ID + strAugmentedMessageID = MessageId & "@" & strMPS + ' "@R" at the end of the MID means route message only via radio + If blnRadioOnly Or UploadThroughInternet() Then + strAugmentedMessageID &= "@" & strHFOnlyFlag + End If + Else + strAugmentedMessageID = MessageId + End If + +*/ + +void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + Buffer[len] = 0; + + if (conn->Flags & GETTINGSYNCMESSAGE) + { + // Data + + if ((conn->TempMsg->length + len) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); + + conn->TempMsg->length += len; + + if (conn->TempMsg->length >= conn->SyncCompressedLen) + { + // Complete - decompress it + + conn->BBSFlags |= FBBCompressed; + Decode(conn, 1); + + conn->Flags &= !GETTINGSYNCMESSAGE; + + BBSputs(conn, "OK\r"); + return; + } + return; + } + + if (conn->Flags & PROPOSINGSYNCMSG) + { + // Waiting for response to TR AddMessage + + if (strcmp(Buffer, "OK\r") == 0) + { + char Msg[256]; + int n; + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // Send the message, it has already been built + + conn->Flags &= !PROPOSINGSYNCMSG; + conn->Flags |= SENDINGSYNCMSG; + + n = sprintf_s(Msg, sizeof(Msg), "Sending SYNC message %s", conn->FwdMsg->bid); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + QueueMsg(conn, conn->SyncMessage, conn->SyncCompressedLen); + return; + } + + if (strcmp(Buffer, "NO\r") == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // Message Rejected - ? duplicate + + if (conn->FwdMsg) + { + // Zap the entry + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + conn->UserPointer->ForwardingInfo->MsgCount--; + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->FwdMsg->Locked = 0; // Unlock + } + } + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->Flags &= !PROPOSINGSYNCMSG; + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (conn->Flags & SENDINGSYNCMSG) + { + if (strcmp(Buffer, "OK\r") == 0) + { + // Message Sent + + conn->Flags &= !SENDINGSYNCMSG; + free(conn->SyncMessage); + + if (conn->FwdMsg) + { + char Msg[256]; + int n; + + n = sprintf_s(Msg, sizeof(Msg), "SYNC message %s Sent", conn->FwdMsg->bid); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + conn->UserPointer->ForwardingInfo->MsgCount--; + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->FwdMsg->Locked = 0; // Unlock + } + + // drop through to send any more + } + else + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + conn->Flags &= !SENDINGSYNCMSG; + free(conn->SyncMessage); + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + + return; + } + } + + if (strcmp(Buffer, "OK\r") == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // Send Message(?s) to RMS Relay SYNC + +/* +OK +>TR AddMessage_V5JLSGH591JR 786 1219 522 True +BYE*/ + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg = conn->FwdMsg; + char Buffer[128]; + char * Message; + + Message = FormatSYNCMessage(conn, Msg); + + // Need to compress it + + conn->SyncMessage = malloc(conn->SyncXMLLen + conn->SyncMsgLen + 4096); + + conn->SyncCompressedLen = Encode(Message, conn->SyncMessage, conn->SyncXMLLen + conn->SyncMsgLen, 0, 1); + + sprintf(Buffer, "TR AddMessage_%s %d %d %d True\r", // The True on end indicates compressed + Msg->bid, conn->SyncCompressedLen, conn->SyncXMLLen, conn->SyncMsgLen); + + free(Message); + + conn->Flags |= PROPOSINGSYNCMSG; + + BBSputs(conn, Buffer); + return; + } + + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (memcmp(Buffer, "TR ", 2) == 0) + { + // Messages have TR_COMMAND_BID Compressed Len XML Len Bosy Len + + char * Command; + char * BIDptr; + + BIDRec * BID; + char *ptr2, *context; + + // TR AddMessage_1145_G8BPQ 727 1202 440 True + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + Command = strtok_s(&Buffer[3], "_", &context); + BIDptr = strtok_s(NULL, " ", &context); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncCompressedLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncXMLLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncMsgLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + + // If addmessage need to check bid doesn't exist + + if (strcmp(Command, "AddMessage") == 0) + { + strlop(BIDptr, '@'); // sometimes has @CALL@R + if (strlen(BIDptr) > 12) + BIDptr[12] = 0; + + BID = LookupBID(BIDptr); + + if (BID) + { + BBSputs(conn, "Rejected - Duplicate BID\r"); + return; + } + } + + conn->TempMsg = zalloc(sizeof(struct MsgInfo)); + + conn->Flags |= GETTINGSYNCMESSAGE; + + BBSputs(conn, "OK\r"); + return; + } + + if (memcmp(Buffer, "BYE\r", 4) == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (memcmp(Buffer, "BBS\r", 4) == 0) + { + // Out of Sync + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->BBSFlags &= ~SYNCMODE; + return; + } + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + WriteLogLine(conn, '<', "Unexpected SYNC Message", 23, LOG_BBS); + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; +} +BOOL ProcessReqFile(struct MsgInfo * Msg) +{ + char FN[128]; + char * Buffer; + int Len = 0; + char * ptr; + struct stat STAT; + char MsgFile[MAX_PATH]; + FILE * hFile; + int FileSize; + char * MsgBytes; + + // Parse title - gives file and return address + + // DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU + + // At the moment we don't allow subdirectories but no harm handling here + + char * Address; + char * filename = NULL; // ?? Pattern Match ?? + + strcpy(FN, Msg->title); + + ptr = strchr(FN, '@'); + + if (ptr == NULL) + + // if we don't have return address no point + // but could we default to sender?? + + return FALSE; + + *ptr++ = 0; // Terminate Path + + strlop(FN, ' '); + + while (*ptr == ' ') + ptr++; // accept with or without spaces round @ + + Address = ptr; + + ptr = Buffer = malloc(MaxTXSize + 1); // Allow terminating Null + + // Build Message + + if (FN == NULL) + { + Len = sprintf(Buffer, "Missing Filename\r"); + } + else if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) + { + Len = sprintf(Buffer,"Invalid filename %s\r", FN); + } + else + { + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, FN); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", FN); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + int Length; + + if (FileSize > MaxTXSize) + FileSize = MaxTXSize; // Truncate to max size + + MsgBytes=malloc(FileSize+1); + fread(MsgBytes, 1, FileSize, hFile); + fclose(hFile); + + MsgBytes[FileSize]=0; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + Len = sprintf(Buffer, "%s", MsgBytes); + free(MsgBytes); + } + } + else + Len = sprintf(Buffer, "File %s not found\r", FN); + } + + SendServerReply("REQFIL Reply", Buffer, Len, _strupr(Address)); + return TRUE; +} + +BOOL CheckforMessagetoServer(struct MsgInfo * Msg) +{ + if (_stricmp(Msg->to, "REQDIR") == 0) + return ProcessReqDir(Msg); + + if (_stricmp(Msg->to, "REQFIL") == 0) + return ProcessReqFile(Msg); + + return FALSE; +} + +VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To) +{ + struct MsgInfo * Msg = AllocateMsgRecord(); + BIDRec * BIDRec; + char * Via; + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + + Msg->length = Length; + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + FreeSemaphore(&MsgNoSemaphore); + + strcpy(Msg->from, BBSName); + Via = strlop(To, '@'); + + if (Via) + strcpy(Msg->via, Via); + + strcpy(Msg->to, To); + strcpy(Msg->title, Title); + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + BIDRec = AllocateBIDRecord(); + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, NULL); + free(MailBuffer); +} + +void SendRequestSync(CIRCUIT * conn) +{ + // Only need XML Header + + char * Buffer = malloc(4096); + int Len = 0; + + struct tm *tm; + char Date[32]; + char MsgTime[32]; + time_t Time = time(NULL); + + tm = gmtime(&Time); + + sprintf_s(Date, sizeof(Date), "%04d%02d%02d%02d%02d%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + sprintf_s(MsgTime, sizeof(Date), "%04d/%02d/%02d %02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + Len += sprintf(&Buffer[Len], "\r\n"); + + Len += sprintf(&Buffer[Len], "\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " request_sync\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Date); + Len += sprintf(&Buffer[Len], " %s\r\n", BBSName); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " BBSName\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", conn->SyncHost); + Len += sprintf(&Buffer[Len], " %d\r\n", conn->SyncPort); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], "\r\n"); + +/* + + + + request_sync + 20230205100652 + GI8BPQ + + + GI8BPQ + + 127.0.0.1 + 8780 + + + +*/ + + // Need to compress it + + conn->SyncXMLLen = Len; + conn->SyncMsgLen = 0; + + conn->SyncMessage = malloc(conn->SyncXMLLen + 4096); + + conn->SyncCompressedLen = Encode(Buffer, conn->SyncMessage, conn->SyncXMLLen, 0, 1); + + sprintf(Buffer, "TR RequestSync_%s_%d %d %d 0 True\r", // The True on end indicates compressed + 50, conn->SyncCompressedLen, conn->SyncXMLLen); + + free(Buffer); + + conn->Flags |= REQUESTINGSYNC; + + BBSputs(conn, Buffer); + return; +} + + +void ProcessSyncXML(CIRCUIT * conn, char * XML) +{ + // Process XML from RMS Relay Sync + + // All seem to start + + // + // + // + // + + char * Type = strstr(XML, ""); + + if (Type == NULL) + return; + + Type += strlen(""); + + if (memcmp(Type, "rms_location", 12) == 0) + { + return; + } + + + if (memcmp(Type, "request_sync", 12) == 0) + { + char * Call; + struct UserInfo * BBSREC; + + // This isn't requesting a poll, it is asking to be added as a sync partner + + Call = strstr(Type, ""); + + if (Call == NULL) + return; + + Call += 10; + strlop(Call, '<'); + BBSREC = FindBBS(Call); + + if (BBSREC == NULL) + return; + + if (BBSREC->ForwardingInfo->Forwarding == 0) + StartForwarding(BBSREC->BBSNumber, NULL); + + return; + } + + if (memcmp(Type, "remove_message", 14) == 0) + { + char * MID = strstr(Type, ""); + struct MsgInfo * Msg; + + if (MID == NULL) + return; + + MID += 11; + strlop(MID, '<'); + + strlop(MID, '@'); // sometimes has @CALL@R + if (strlen(MID) > 12) + MID[12] = 0; + + Msg = FindMessageByBID(MID); + + if (Msg == NULL) + return; + + Logprintf(LOG_BBS, conn, '|', "Killing Msg %d %s", Msg->number, Msg->bid); + + FlagAsKilled(Msg, TRUE); + return; + } + + if (memcmp(Type, "delivered", 9) == 0) + { + char * MID = strstr(Type, ""); + struct MsgInfo * Msg; + + if (MID == NULL) + return; + + MID += 11; + strlop(MID, '<'); + + strlop(MID, '@'); // sometimes has @CALL@R + if (strlen(MID) > 12) + MID[12] = 0; + + Msg = FindMessageByBID(MID); + + if (Msg == NULL) + return; + + Logprintf(LOG_BBS, conn, '|', "Message Msg %d %s Delivered", Msg->number, Msg->bid); + return; + } + + Debugprintf(Type); + return; + +/* + + + + request_sync + 20230205100652 + GI8BPQ + + + GI8BPQ + + 127.0.0.1 + 8780 + + + +} + + + + delivered + 20230205093113 + G8BPQ + + + 10845_GM8BPB + G8BPQ + G8BPQ + 3 + + + + Public Enum MessageDeliveryMethod + ' + ' Method used to deliver a message. None if the message hasn't been delivered. + ' + Unspecified = -1 + None = 0 + Telnet = 1 + CMS = 2 + Radio = 3 + Email = 4 +End Enum +*/ +} + +int ReformatSyncMessage(CIRCUIT * conn) +{ + // Message has been decompressed - reformat to look like a WLE message + + char * MsgBit; + char *ptr1, *ptr2; + int linelen; + char FullFrom[80]; + char FullTo[80]; + char BID[80]; + time_t Date; + char Mon[80]; + char Subject[80]; + int i = 0; + char * Boundary; + char * Input; + char * via = NULL; + char * NewMsg = conn->MailBuffer; + char * SaveMsg = NewMsg; + char DateString[80]; + struct tm * tm; + char Type[16] = "Private"; + char * part[100] = {""}; + char * partname[100]; + int partLen[100]; + char xml[4096]; + + // Message has an XML header then the message + + // The XML may have control info, so examine it. + + /* + Date: Mon, 25 Oct 2021 10:22:00 -0000 + From: GM8BPQ + Subject: Test + To: 2E1BGT + Message-ID: ALYJQJRXVQAO + X-Source: GM8BPQ + X-Relay: G8BPQ + MIME-Version: 1.0 + MIME-Version: 1.0 + Content-Type: multipart/mixed; boundary="boundaryBSoxlw==" + + --boundaryBSoxlw== + Content-Type: text/plain; charset="iso-8859-1" + Content-Transfer-Encoding: quoted-printable + + Hello Hello + + --boundaryBSoxlw==-- + */ + + // I think the best way is to reformat as if from Winlink Express, then pass + //through the normal B2 code. + +// WriteLogLine(conn, '<', conn->MailBuffer, conn->TempMsg->length, LOG_BBS); + + // display the message for testing + + conn->MailBuffer[conn->TempMsg->length] = 0; + +// OutputDebugString(conn->MailBuffer); + memcpy(xml, conn->MailBuffer, conn->SyncXMLLen); + xml[conn->SyncXMLLen] = 0; + + if (conn->SyncMsgLen == 0) + { + // No message, Just xml. Looks like a status report + + ProcessSyncXML(conn, xml); + return 0; + } + + MsgBit = &conn->MailBuffer[conn->SyncXMLLen]; + conn->TempMsg->length -= conn->SyncXMLLen; + + ptr1 = MsgBit; + +Loop: + + ptr2 = strchr(ptr1, '\r'); + + linelen = (int)(ptr2 - ptr1); + + if (_memicmp(ptr1, "From:", 5) == 0) + { + memcpy(FullFrom, &ptr1[6], linelen - 6); + FullFrom[linelen - 6] = 0; + } + + if (_memicmp(ptr1, "To:", 3) == 0) + { + memcpy(FullTo, &ptr1[4], linelen - 4); + FullTo[linelen - 4] = 0; + } + + else if (_memicmp(ptr1, "Subject:", 8) == 0) + { + memcpy(Subject, &ptr1[9], linelen - 9); + Subject[linelen - 9] = 0; + } + + else if (_memicmp(ptr1, "Message-ID", 10) == 0) + { + memcpy(BID, &ptr1[12], linelen - 12); + BID[linelen - 12] = 0; + } + + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: Mon, 25 Oct 2021 10:22:00 -0000 + + sscanf(&ptr1[11], "%02d %s %04d %02d:%02d:%02d", + &rtime.tm_mday, &Mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + rtime.tm_year -= 1900; + + for (i = 0; i < 12; i++) + { + if (strcmp(Mon, month[i]) == 0) + break; + } + + rtime.tm_mon = i; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip crlf + goto Loop; + } + + // Unpack Body - seems to be multipart even if only one + + // Can't we just send the whole body through ?? + // No, Attachment format is different + + // Mbo: GM8BPQ + // Body: 17 + // File: 1471 leadercoeffs.txt + + Input = MsgBit; + Boundary = initMultipartUnpack(&Input); + + i = 0; + + if (Boundary) + { + // input should be start of part + + // Find End of part - ie -- Boundary + CRLF or -- + + char * ptr, * saveptr; + char * Msgptr; + size_t BLen = strlen(Boundary); + size_t Partlen; + + saveptr = Msgptr = ptr = Input; + + while(ptr) // Just in case we run off end + { + if (*ptr == '-' && *(ptr+1) == '-') + { + if (memcmp(&ptr[2], Boundary, BLen) == 0) + { + // Found Boundary + + char * p1, *p2, *ptr3, *ptr4; + int llen; + int Base64 = 0; + int QuotedP = 0; + char * BoundaryStart = ptr; + + Partlen = ptr - Msgptr; + + ptr += (BLen + 2); // End of Boundary + + if (*ptr == '-') // Terminating Boundary + Input = NULL; + else + Input = ptr + 2; + + // Will check for quoted printable + + p1 = Msgptr; +Loop2: + p2 = strchr(p1, '\r'); + llen = (int)(p2 - p1); + + if (llen) + { + + if (_memicmp(p1, "Content-Transfer-Encoding:", 26) == 0) + { + if (_memicmp(&p1[27], "base64", 6) == 0) + Base64 = TRUE; + else if (_memicmp(&p1[27], "quoted", 6) == 0) + QuotedP = TRUE; + } + else if (_memicmp(p1, "Content-Disposition: ", 21) == 0) + { + ptr3 = strstr(&p1[21], "name"); + + if (ptr3) + { + ptr3 += 5; + if (*ptr3 == '"') ptr3++; + ptr4 = strchr(ptr3, '"'); + if (ptr4) *ptr4 = 0; + + partname[i] = ptr3; + } + } + + if (llen) // Not Null line + { + p1 = p2 + 2; // Skip crlf + goto Loop2; + } + } + + part[i] = strstr(p2, "\r\n"); // Over separator + + if (part[i]) + { + part[i] += 2; + partLen[i] = BoundaryStart - part[i] - 2; + if (QuotedP) + partLen[i] = decode_quoted_printable(part[i], partLen[i]); + else if (Base64) + { + int Len = partLen[i], NewLen; + char * ptr = part[i]; + char * ptr2 = part[i]; + + // WLE sends base64 with embedded crlf, so remove them + + while (Len-- > 0) + { + if ((*ptr) != 10 && (*ptr) != 13) + *(ptr2++) = *(ptr++); + else + ptr ++; + } + + Len = ptr2 - part[i]; + ptr = part[i]; + ptr2 = part[i]; + + while (Len > 0) + { + decodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - part[i]); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + partLen[i] = NewLen; + } + } + Msgptr = ptr = Input; + i++; + continue; } + + // See if more parts + } + ptr++; + } + ptr++; + } + + + // Build the message + + tm = gmtime(&Date); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + NewMsg += sprintf(NewMsg, + "MID: %s\r\n" + "Date: %s\r\n" + "Type: %s\r\n" + "From: %s\r\n", + BID, DateString, Type, FullFrom); + +// if (ToCalls) +// { +// int i; + +// for (i = 0; i < Calls; i++) +// NewMsg += sprintf(NewMsg, "To: %s\r\n", ToCalls[i]); + +// } +// else + { + NewMsg += sprintf(NewMsg, "To: %s\r\n", + FullTo); + } +// if (WebMail->CC && WebMail->CC[0]) +// NewMsg += sprintf(NewMsg, "CC: %s\r\n", WebMail->CC); + + NewMsg += sprintf(NewMsg, + "Subject: %s\r\n" + "Mbo: %s\r\n", + Subject, BBSName); + + // Write the Body: line and any File Lines + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", partLen[0]); + + i = 1; + + while (part[i]) + { + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", + partLen[i], partname[i]); + + i++; + } + + NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line to end header + + // Now add parts + + i = 0; + + while (part[i]) + { + memmove(NewMsg, part[i], partLen[i]); + NewMsg += partLen[i]; + i++; + NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line between attachments + } + + conn->TempMsg->length = NewMsg - SaveMsg; + conn->TempMsg->datereceived = conn->TempMsg->datechanged = time(NULL); + conn->TempMsg->datecreated = Date; + strcpy(conn->TempMsg->bid, BID); + + if (strlen(Subject) > 60) + Subject[60] = 0; + + strcpy(conn->TempMsg->title, Subject); + + return TRUE; +} + +char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg) +{ + // First an XML Header + + char * Buffer = malloc(4096 + Msg->length); + int Len = 0; + + struct tm *tm; + char Date[32]; + char MsgTime[32]; + char Separator[33]=""; + time_t Time = time(NULL); + char * MailBuffer; + int BodyLen; + char * Encoded; + + // Get the message - may need length in header + + MailBuffer = ReadMessageFile(Msg->number); + + BodyLen = Msg->length; + + // Remove any B2 Header + + if (Msg->B2Flags & B2Msg) + { + // Remove B2 Headers (up to the File: Line) + + char * ptr; + ptr = strstr(MailBuffer, "Body:"); + if (ptr) + { + BodyLen = atoi(ptr + 5); + ptr = strstr(ptr, "\r\n\r\n"); + } + if (ptr) + { + memcpy(MailBuffer, ptr + 4, BodyLen); + MailBuffer[BodyLen] = 0; + } + } + + // encode body as quoted printable; + + Encoded = malloc(Msg->length * 3); + + BodyLen = encode_quoted_printable(MailBuffer, Encoded, BodyLen); + + // Create multipart Boundary + + CreateOneTimePassword(&Separator[0], "Key", 0); + CreateOneTimePassword(&Separator[16], "Key", 1); + + + tm = gmtime(&Time); + + sprintf_s(Date, sizeof(Date), "%04d%02d%02d%02d%02d%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + tm = gmtime((time_t *)&Msg->datecreated); + + sprintf_s(MsgTime, sizeof(Date), "%04d/%02d/%02d %02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + Len += sprintf(&Buffer[Len], "\r\n"); + + Len += sprintf(&Buffer[Len], "\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " add_message\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Date); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->bid); + Len += sprintf(&Buffer[Len], " \r\n", MsgTime); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], " 2\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", (Msg->B2Flags & Attachments) ? "true" : "false"); + Len += sprintf(&Buffer[Len], " %d\r\n", BodyLen); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->title); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->bid); + Len += sprintf(&Buffer[Len], " 450443\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->to); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " 0\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " True\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], "\r\n"); + +// Debugprintf(Buffer); + + conn->SyncXMLLen = Len; + + Len += sprintf(&Buffer[Len], "Date: Sat, 04 Feb 2023 11:19:00 +0000\r\n"); + Len += sprintf(&Buffer[Len], "From: %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], "Subject: %s\r\n", Msg->title); + Len += sprintf(&Buffer[Len], "To: %s\r\n", Msg->to); + Len += sprintf(&Buffer[Len], "Message-ID: %s\r\n", Msg->bid); +// Len += sprintf(&Buffer[Len], "X-Source: G8BPQ\r\n"); +// Len += sprintf(&Buffer[Len], "X-Location: 52.979167N, 1.125000W (GRID SQUARE)\r\n"); +// Len += sprintf(&Buffer[Len], "X-RMS-Originator: G8BPQ\r\n"); +// Len += sprintf(&Buffer[Len], "X-RMS-Path: G8BPQ@2023-02-04-11:19:29\r\n"); + Len += sprintf(&Buffer[Len], "X-Relay: %s\r\n", BBSName); + + Len += sprintf(&Buffer[Len], "MIME-Version: 1.0\r\n"); + Len += sprintf(&Buffer[Len], "Content-Type: multipart/mixed; boundary=\"%s\"\r\n", Separator); + + Len += sprintf(&Buffer[Len], "\r\n"); // Blank line before separator + Len += sprintf(&Buffer[Len], "--%s\r\n", Separator); + Len += sprintf(&Buffer[Len], "Content-Type: text/plain; charset=\"iso-8859-1\"\r\n"); + Len += sprintf(&Buffer[Len], "Content-Transfer-Encoding: quoted-printable\r\n"); + Len += sprintf(&Buffer[Len], "\r\n"); // Blank line before body + + Len += sprintf(&Buffer[Len], "%s\r\n", Encoded); + Len += sprintf(&Buffer[Len], "--%s--\r\n", Separator); + + conn->SyncMsgLen = Len - conn->SyncXMLLen; + + free(Encoded); + free(MailBuffer); + + return Buffer; +} + +int encode_quoted_printable(char *s, char * out, int Len) +{ + int n = 0; + char * start = out; + + while(Len--) + { + if (n >= 73 && *s != 10 && *s != 13) + {strcpy(out, "=\r\n"); n = 0; out +=3;} + if (*s == 10 || *s == 13) {putchar(*s); n = 0;} + else if (*s<32 || *s==61 || *s>126) + out += sprintf(out, "=%02x", (unsigned char)*s); + else if (*s != 32 || (*(s+1) != 10 && *(s+1) != 13)) + {*(out++) = *s; n++;} + else n += printf("=20"); + + s++; + } + *out = 0; + + return out - start; +} + +int decode_quoted_printable(char *ptr, int len) +{ + // overwrite input with decoded version + + char * ptr2 = ptr; + char * End = ptr + len; + char * Start = ptr; + + while (ptr < End) + { + if ((*ptr) == '=') + { + char c = *(++ptr); + char d; + + c = c - 48; + if (c < 0) + { + // = CRLF as a soft break + + ptr += 2; + continue; + } + if (c > 9) c -= 7; + d = *(++ptr); + d = d - 48; + if (d > 9) d -= 7; + + *(ptr2) = c << 4 | d; + ptr2++; + ptr++; + } + else + *ptr2++ = *ptr++; + } + return ptr2 - Start; +} + + +VOID GetPGConfig() +{ + char FN[256]; + FILE *file; + char buf[256],errbuf[256]; + char * p_prog, * p_name, * p_desc; + int n = 0; + int i = 0; + + + strcpy(FN, BaseDir); + strcat(FN, "/"); + strcat(FN, "PG/PGList.txt"); + + if ((file = fopen(FN, "r")) == NULL) + { + return; + } + + while(fgets(buf, 255, file) != NULL) + { + if ( buf[0] == '#') + continue; + + strcpy(errbuf,buf); // save in case of error + + p_prog = strtok(buf, ",\n\r"); + p_name = strtok(NULL, ",\n\r"); + p_desc = strtok(NULL, ",\n\r"); + + + if (p_desc && p_desc[0]) + { + while(*(p_name) == ' ') // Remove leading spaces + p_name++; + while(*(p_desc) == ' ') + p_desc++; + SERVERLIST[n][0] = _strdup(p_prog); + SERVERLIST[n][1] = _strdup(p_name); + SERVERLIST[n++][2] = _strdup(p_desc); + } + if (n > 255) + break; + } + + + NUM_SERVERS = n; + fclose(file); + + /*------- G7TAJ PG SERVER ----------*/ + Debugprintf("Number of PG Servers = %d", NUM_SERVERS ); + for (i=0; i< NUM_SERVERS; i++ ) + { + Debugprintf("Server #%d,%s,%s,%s", i, SERVERLIST[i][0], SERVERLIST[i][1], SERVERLIST[i][2]); + } + /*------- G7TAJ END ----------*/ + +} + +void SendMessageReadEvent(char * call, struct MsgInfo * Msg) +{ + if (reportMailEvents) + { + char msg[512]; + + //12345 B 2053 TEST@ALL F6FBB 920325 This is the subject + + struct tm *tm = gmtime((time_t *)&Msg->datecreated); + + sprintf_s(msg, sizeof(msg),"%-6d %c %c %6d %-13s %-6s %02d%02d%02d %s\r", + Msg->number, Msg->type, Msg->status, Msg->length, Msg->to, + Msg->from, tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Msg->title); + +// sprintf(msg, "%s Read %d\r", user->Call, Msg->number); + +#ifdef WIN32 + if (pRunEventProgram) + pRunEventProgram("MailMsgRead.exe", msg); +#else + { + char prog[256]; + sprintf(prog, "%s/%s", BPQDirectory, "MailMsgRead"); + RunEventProgram(prog, msg); + } +#endif + } +} + +void SendMessageForwardedToM0LTE(char * call, struct MsgInfo * Msg) +{ +} + + +void SendNewMessageEvent(char * call, struct MsgInfo * Msg) +{ + if (reportMailEvents) + { + char msg[512]; + + //12345 B 2053 TEST@ALL F6FBB 920325 This is the subject + + struct tm *tm = gmtime((time_t *)&Msg->datecreated); + + sprintf_s(msg, sizeof(msg),"%-6d %c %c %6d %-13s %-6s %02d%02d%02d %s\r", + Msg->number, Msg->type, Msg->status, Msg->length, Msg->to, + Msg->from, tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Msg->title); + +#ifdef WIN32 + if (pRunEventProgram) + pRunEventProgram("MailNewMsg.exe", msg); +#else + { + char prog[256]; + sprintf(prog, "%s/%s", BPQDirectory, "MailNewMsg"); + RunEventProgram(prog, msg); + } +#endif + } +} + + + diff --git a/.svn/pristine/2e/2e81af55215c83b3c479129651ce4f66a5296f59.svn-base b/.svn/pristine/2e/2e81af55215c83b3c479129651ce4f66a5296f59.svn-base new file mode 100644 index 0000000..823a0b0 --- /dev/null +++ b/.svn/pristine/2e/2e81af55215c83b3c479129651ce4f66a5296f59.svn-base @@ -0,0 +1,297 @@ + +/* pngerror.c - stub functions for i/o and memory allocation + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all error handling. Users who + * need special error handling are expected to write replacement functions + * and use png_set_error_fn() to use those functions. See the instructions + * at each function. + */ + +#define PNG_INTERNAL +#include "png.h" + +void Myabort(); + +static void /* PRIVATE */ +png_default_error PNGARG((png_structp png_ptr, + png_const_charp error_message)); +static void /* PRIVATE */ +png_default_warning PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +/* This function is called whenever there is a fatal error. This function + * should not be changed. If there is a need to handle errors differently, + * you should supply a replacement error function and use png_set_error_fn() + * to replace the error function at run-time. + */ +void PNGAPI +png_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + char msg[16]; + if (png_ptr->flags&(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) + { + if (*error_message == '#') + { + int offset; + for (offset=1; offset<15; offset++) + if (*(error_message+offset) == ' ') + break; + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + int i; + for (i=0; iflags&PNG_FLAG_STRIP_ERROR_TEXT) + { + msg[0]='0'; + msg[1]='\0'; + error_message=msg; + } + } + } +#endif + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_ptr, error_message); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, error_message); +} + +/* This function is called whenever there is a non-fatal error. This function + * should not be changed. If there is a need to handle warnings differently, + * you should supply a replacement warning function and use + * png_set_error_fn() to replace the warning function at run-time. + */ +void PNGAPI +png_warning(png_structp png_ptr, png_const_charp warning_message) +{ + int offset = 0; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (png_ptr->flags&(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) +#endif + { + if (*warning_message == '#') + { + for (offset=1; offset<15; offset++) + if (*(warning_message+offset) == ' ') + break; + } + } + if (png_ptr != NULL && png_ptr->warning_fn != NULL) + (*(png_ptr->warning_fn))(png_ptr, warning_message+offset); + else + png_default_warning(png_ptr, warning_message+offset); +} + +/* These utilities are used internally to build an error message that relates + * to the current chunk. The chunk name comes from png_ptr->chunk_name, + * this is used to prefix the message. The message is limited in length + * to 63 bytes, the name characters are output as hex digits wrapped in [] + * if the character is invalid. + */ +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) +static PNG_CONST char png_digit[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' +}; + +static void /* PRIVATE */ +png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp + error_message) +{ + int iout = 0, iin = 0; + + while (iin < 4) + { + int c = png_ptr->chunk_name[iin++]; + if (isnonalpha(c)) + { + buffer[iout++] = '['; + buffer[iout++] = png_digit[(c & 0xf0) >> 4]; + buffer[iout++] = png_digit[c & 0x0f]; + buffer[iout++] = ']'; + } + else + { + buffer[iout++] = (png_byte)c; + } + } + + if (error_message == NULL) + buffer[iout] = 0; + else + { + buffer[iout++] = ':'; + buffer[iout++] = ' '; + png_strncpy(buffer+iout, error_message, 63); + buffer[iout+63] = 0; + } +} + +void PNGAPI +png_chunk_error(png_structp png_ptr, png_const_charp error_message) +{ + char msg[18+64]; + png_format_buffer(png_ptr, msg, error_message); + png_error(png_ptr, msg); +} + +void PNGAPI +png_chunk_warning(png_structp png_ptr, png_const_charp warning_message) +{ + char msg[18+64]; + png_format_buffer(png_ptr, msg, warning_message); + png_warning(png_ptr, msg); +} + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void /* PRIVATE */ +png_default_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifndef PNG_NO_CONSOLE_IO +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*error_message == '#') + { + int offset; + char error_number[16]; + for (offset=0; offset<15; offset++) + { + error_number[offset] = *(error_message+offset+1); + if (*(error_message+offset) == ' ') + break; + } + if((offset > 1) && (offset < 15)) + { + error_number[offset-1]='\0'; + fprintf(stderr, "libpng error no. %s: %s\n", error_number, + error_message+offset); + } + else + fprintf(stderr, "libpng error: %s, offset=%d\n", error_message,offset); + } + else +#endif + fprintf(stderr, "libpng error: %s\n", error_message); +#endif + +#ifdef PNG_SETJMP_SUPPORTED +# ifdef USE_FAR_KEYWORD + { + jmp_buf jmpbuf; + png_memcpy(jmpbuf,png_ptr->jmpbuf,png_sizeof(jmp_buf)); + longjmp(jmpbuf, 1); + } +# else + longjmp(png_ptr->jmpbuf, 1); +# endif +#else + /* make compiler happy */ ; + if (png_ptr) + PNG_ABORT(); +#endif +#ifdef PNG_NO_CONSOLE_IO + /* make compiler happy */ ; + // if (&error_message != NULL) + return; +#endif +} + +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want them to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void /* PRIVATE */ +png_default_warning(png_structp png_ptr, png_const_charp warning_message) +{ +#ifndef PNG_NO_CONSOLE_IO +# ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*warning_message == '#') + { + int offset; + char warning_number[16]; + for (offset=0; offset<15; offset++) + { + warning_number[offset]=*(warning_message+offset+1); + if (*(warning_message+offset) == ' ') + break; + } + if((offset > 1) && (offset < 15)) + { + warning_number[offset-1]='\0'; + fprintf(stderr, "libpng warning no. %s: %s\n", warning_number, + warning_message+offset); + } + else + fprintf(stderr, "libpng warning: %s\n", warning_message); + } + else +# endif + fprintf(stderr, "libpng warning: %s\n", warning_message); +#else + /* make compiler happy */ ; + if (warning_message) + return; +#endif + /* make compiler happy */ ; + if (png_ptr) + return; +} + +/* This function is called when the application wants to use another method + * of handling errors and warnings. Note that the error function MUST NOT + * return to the calling routine or serious problems will occur. The return + * method used in the default routine calls longjmp(png_ptr->jmpbuf, 1) + */ +void PNGAPI +png_set_error_fn(png_structp png_ptr, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warning_fn) +{ + png_ptr->error_ptr = error_ptr; + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; +} + + +/* This function returns a pointer to the error_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_error_ptr(png_structp png_ptr) +{ + return ((png_voidp)png_ptr->error_ptr); +} + + +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +void PNGAPI +png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode) +{ + if(png_ptr != NULL) + { + png_ptr->flags &= + ((~(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); + } +} +#endif diff --git a/.svn/pristine/2e/2eb492170d68e89d8d3544729e7a81d5d2178a11.svn-base b/.svn/pristine/2e/2eb492170d68e89d8d3544729e7a81d5d2178a11.svn-base new file mode 100644 index 0000000..be3315c --- /dev/null +++ b/.svn/pristine/2e/2eb492170d68e89d8d3544729e7a81d5d2178a11.svn-base @@ -0,0 +1,3172 @@ +/* +Copyright 2001-2018 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 +*/ + +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" +#include "bpqmail.h" + +#ifdef WIN32 +//#include "C:\Program Files (x86)\GnuWin32\include\iconv.h" +#else +#include +#endif + +extern char NodeTail[]; +extern char BBSName[10]; + +extern char LTFROMString[2048]; +extern char LTTOString[2048]; +extern char LTATString[2048]; + +//static UCHAR BPQDirectory[260]; + +extern ConnectionInfo Connections[]; + +extern int NumberofStreams; +extern time_t MaintClock; // Time to run housekeeping + +extern int SMTPMsgs; + +extern int ChatApplNum; +extern int MaxChatStreams; + +extern char Position[81]; +extern char PopupText[251]; +extern int PopupMode; +extern int reportMailEvents; + +#define MaxCMS 10 // Numbr of addresses we can keep - currently 4 are used. + +struct UserInfo * BBSLIST[NBBBS + 1]; + +int MaxBBS = 0; + +#define MAIL +#include "httpconnectioninfo.h" + +struct TCPINFO * TCP; + +VOID ProcessMailSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, int * RLen); +static struct HTTPConnectionInfo * FindSession(char * Key); +VOID ProcessUserUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID ProcessMsgFwdUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID SendConfigPage(char * Reply, int * ReplyLen, char * Key); +VOID ProcessConfUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID ProcessUIUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID SendUserSelectPage(char * Reply, int * ReplyLen, char * Key); +VOID SendFWDSelectPage(char * Reply, int * ReplyLen, char * Key); +int EncryptPass(char * Pass, char * Encrypt); +VOID ProcessFWDUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID SendStatusPage(char * Reply, int * ReplyLen, char * Key); +VOID SendUIPage(char * Reply, int * ReplyLen, char * Key); +VOID GetParam(char * input, char * key, char * value); +BOOL GetConfig(char * ConfigName); +VOID ProcessDisUser(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +int APIENTRY SessionControl(int stream, int command, int param); +int SendMessageDetails(struct MsgInfo * Msg, char * Reply, char * Key); +VOID ProcessMsgUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID ProcessMsgAction(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +int APIENTRY GetNumberofPorts(); +int APIENTRY GetPortNumber(int portslot); +UCHAR * APIENTRY GetPortDescription(int portslot, char * Desc); +struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); +VOID SendHouseKeeping(char * Reply, int * ReplyLen, char * Key); +VOID SendWelcomePage(char * Reply, int * ReplyLen, char * Key); +VOID SaveWelcome(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key); +VOID GetMallocedParam(char * input, char * key, char ** value); +VOID SaveMessageText(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID SaveHousekeeping(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key); +VOID SaveWP(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key); +int SendWPDetails(WPRec * WP, char * Reply, char * Key); +int SendUserDetails(struct HTTPConnectionInfo * Session, char * Reply, char * Key); +int SetupNodeMenu(char * Buff); +VOID SendFwdSelectPage(char * Reply, int * ReplyLen, char * Key); +VOID SendFwdDetails(struct UserInfo * User, char * Reply, int * ReplyLen, char * Key); +VOID SetMultiStringValue(char ** values, char * Multi); +VOID SendFwdMainPage(char * Reply, int * ReplyLen, char * Key); +VOID SaveFwdCommon(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID SaveFwdDetails(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +char ** SeparateMultiString(char * MultiString, BOOL NoToUpper); +VOID TidyPrompts(); +char * GetTemplateFromFile(int Version, char * FN); +VOID FormatTime(char * Time, time_t cTime); +struct MsgInfo * GetMsgFromNumber(int msgno); +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP); +BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg); +int MulticastStatusHTML(char * Reply); +void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL LOCAL, char * Method, char * NodeURL, char * input, char * Reply, int * RLen, int InputLen); +int SendWebMailHeader(char * Reply, char * Key, struct HTTPConnectionInfo * Session); +struct UserInfo * FindBBS(char * Name); +void ReleaseWebMailStruct(WebMailInfo * WebMail); +VOID TidyWelcomeMsg(char ** pPrompt); +int MailAPIProcessHTTPMessage(struct HTTPConnectionInfo * Session, char * response, char * Method, char * URL, char * request, BOOL LOCAL, char * Param, char * Token); + +char UNC[] = ""; +char CHKD[] = "checked=checked "; +char sel[] = "selected"; + +char Sent[] = "#98FFA0"; +char ToSend[] = "#FFFF00"; +char NotThisOne[] = "#FFFFFF"; + +static char PassError[] = "

Sorry, User or Password is invalid - please try again

"; + +static char BusyError[] = "

Sorry, No sessions available - please try later

"; + +extern char WebMailSignon[]; + +char MailSignon[] = "BPQ32 Mail Server Access" + "

BPQ32 Mail Server %s Access

" + "

Please enter Callsign and Password to access the BBS

" + "
" + "" + "" + "
User
Password
" + "

"; + + +char MailPage[] = "%s's BBS Web Server" + "" + "" + "

BPQ32 BBS %s

" + "

" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
StatusConfigurationUsersMessagesForwardingWelcome Msgs & PromptsHousekeepingWP UpdateWebMailNode Menu
"; + +char RefreshMainPage[] = "" + "" + "" + "%s's BBS Web Server" + "

BPQ32 BBS %s

" + "

" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
StatusConfigurationUsersMessagesForwardingWelcome Msgs & PromptsHousekeepingWP UpdateWebMailNode Menu
"; + +char StatusPage [] = + +"

" +"
User     Callsign   Stream Queue
" +"


"; + +char StatusTail[] = +"Msgs      
" +"Sysop Msgs
" +"Held Msgs 
" +"SMTP Msgs 
"; + + +char UIHddr [] = "
Mailfor Header
" +"           " +"    (use \\r to insert newline in message)

" +"Enable Port           " +"             Path           " +"                   Send: MailFor Headers Empty Mailfor

"; + +char UILine[] = " %s " +" " +"    " +"       
"; + +char UITail[] = "

" +"
"; + +char FWDSelectHddr[] = + "
" + "Max Size to Send   
" + "Max Size to Receive
" + "Warn if no route for P or T
" + "Use Local Time              " + "

" + "Aliases                   Select BBS
" + "  
         " + " 
"; + +char UserSelectHddr[] = + "
" + "Please Select User



" + "" + "
" + " " + "
"; + +char UserUpdateHddr[] = + "

Update User %s

" + "
"; + +char UserUpdateLine[] = ""; + +// +//
+ + +char FWDUpdate[] = +"

Update Forwarding for BBS %s

" +"    " +"TO            " +"AT          " +"TIMES         Connect Script
" +"" +" " +" " +"
" +"" +"

" +"Enable Forwarding  Interval" +"(Secs) Request Reverse" +" Interval (Secs)
" +"Send new messages without waiting for poll timer
" +"BBS HA FBB Max Block
" +"Send Personal Mail Only  " +"Allow Binary     Use B1 " +"Protocol   Use B2 Protocol

" +" " +"

"; + +static char MailDetailPage[] = +"" +"MsgEdit

Message %d

" +"
" +"From  Sent   " +"       " +"Type     
" +"To    " +" Received      " +"Status   
" +"BID   Last Changed  " +"Size 

" +"%s" // Email from Line +" VIA 
" +"Title 

" +" " +" " +"
" +"" +//" " +" " +"

" +"Green = Sent, Yellow = Queued" +""; + +char MailDetailTail[] = "
"; + +char Welcome[] = "
" +"Normal User Welcome
" +"
" +"New User Welcome
" +"
" +"Expert User Welcome
" +"
" +"Normal User Prompt
" +"
" +"New User Prompt
" +"
" +"Expert User Prompt
" +"
" +"Signoff
" +"

" +"$U:Callsign of the user  $I:First name of the user $X:Messages for user $x:Unread messages
" +"$L:Number of the latest message $N:Number of active messages. $Z:Last message read by user

" +" "; + +static char MsgEditPage[] = "" +"" +"
" +"

" +"
"; + +static char WPDetail[] = "
" +"
" + +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"
Call
Name
Home BBS 1
Home BBS 2
QTH 1
QTH 2
ZIP 1
ZIP 2
Last Seen
Last Modified
Type
Changed
Seen
" +"
" +" " +"
"; + + +static char LostSession[] = "" +"
" +"Sorry, Session had been lost

    " +"
"; + + +char * MsgEditTemplate = NULL; +char * HousekeepingTemplate = NULL; +char * ConfigTemplate = NULL; +char * WPTemplate = NULL; +char * UserListTemplate = NULL; +char * UserDetailTemplate = NULL; +char * FwdTemplate = NULL; +char * FwdDetailTemplate = NULL; +char * WebMailTemplate = NULL; +char * WebMailMsgTemplate = NULL; +char * jsTemplate = NULL; + + +#ifdef LINBPQ +UCHAR * GetBPQDirectory(); +#endif + +static int compare(const void *arg1, const void *arg2) +{ + // Compare Calls. Fortunately call is at start of stuct + + return _stricmp(*(char**)arg1 , *(char**)arg2); +} + +int SendHeader(char * Reply, char * Key) +{ + return sprintf(Reply, MailPage, BBSName, BBSName, Key, Key, Key, Key, Key, Key, Key, Key); +} + + +void ConvertTitletoUTF8(WebMailInfo * WebMail, char * Title, char * UTF8Title, int Len) +{ + Len = strlen(Title); + + if (WebIsUTF8(Title, Len) == FALSE) + { + int code = TrytoGuessCode(Title, Len); + + if (code == 437) + Len = Convert437toUTF8(Title, Len, UTF8Title); + else if (code == 1251) + Len = Convert1251toUTF8(Title, Len, UTF8Title); + else + Len = Convert1252toUTF8(Title, Len, UTF8Title); + + UTF8Title[Len] = 0; + } + else + strcpy(UTF8Title, Title); +} + +BOOL GotFirstMessage = 0; + +void ProcessMailHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen, int InputLen, char * Token) +{ + char * Context = 0, * NodeURL; + int ReplyLen; + BOOL LOCAL = FALSE; + char * Key; + char Appl = 'M'; + + if (URL[0] == 0 || Method == NULL) + return; + + if (strstr(input, "Host: 127.0.0.1")) + LOCAL = TRUE; + + if (Session->TNC == (void *)1) // Re-using an address as a flag + LOCAL = TRUE; + + NodeURL = strtok_s(URL, "?", &Context); + + Key = Session->Key; + + if (_memicmp(URL, "/WebMail", 8) == 0) + { + // Pass All Webmail messages to Webmail + + ProcessWebMailMessage(Session, Context, LOCAL, Method, NodeURL, input, Reply, RLen, InputLen); + return; + + } + + + if (_memicmp(URL, "/Mail/API/v1/", 13) == 0) + { + *RLen = MailAPIProcessHTTPMessage(Session, Reply, Method, URL, input, LOCAL, Context, Token); + return; + } + + // There is a problem if Mail is reloaded without reloading the node + + if (GotFirstMessage == 0) + { + if (_stricmp(NodeURL, "/Mail/Header") == 0 || _stricmp(NodeURL, "/Mail/Lost") == 0) + { + *RLen = SendHeader(Reply, Session->Key); + } + else + { + *RLen = sprintf(Reply, "", Session->Key); + } + + GotFirstMessage = 1; + return; + } + + + if (strcmp(Method, "POST") == 0) + { + if (_stricmp(NodeURL, "/Mail/Header") == 0) + { + *RLen = SendHeader(Reply, Session->Key); + return; + } + + + if (_stricmp(NodeURL, "/Mail/Config") == 0) + { + NodeURL[strlen(NodeURL)] = ' '; // Undo strtok + ProcessConfUpdate(Session, input, Reply, RLen, Key); + return ; + } + + if (_stricmp(NodeURL, "/Mail/UI") == 0) + { + NodeURL[strlen(NodeURL)] = ' '; // Undo strtok + ProcessUIUpdate(Session, input, Reply, RLen, Key); + return ; + } + if (_stricmp(NodeURL, "/Mail/FwdCommon") == 0) + { + SaveFwdCommon(Session, input, Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/Mail/DisSession") == 0) + { + ProcessDisUser(Session, input, Reply, RLen, Key); + return ; + } + + if (_stricmp(NodeURL, "/Mail/UserDetails") == 0) + { + char * param = strstr(input, "\r\n\r\n"); // End of headers + + if (param) + { + Session->User = LookupCall(param+4); + if (Session->User) + { + * RLen = SendUserDetails(Session, Reply, Key); + return; + } + } + } + + + if (_stricmp(NodeURL, "/Mail/UserSave") == 0) + { + ProcessUserUpdate(Session, input, Reply, RLen, Key); + return ; + } + + if (_stricmp(NodeURL, "/Mail/MsgDetails") == 0) + { + char * param = strstr(input, "\r\n\r\n"); // End of headers + + if (param) + { + int Msgno = atoi(param + 4); + struct MsgInfo * Msg = FindMessageByNumber(Msgno); + + Session->Msg = Msg; // Save current Message + + * RLen = SendMessageDetails(Msg, Reply, Key); + return; + } + } + + if (_stricmp(NodeURL, "/Mail/MsgSave") == 0) + { + ProcessMsgUpdate(Session, input, Reply, RLen, Key); + return ; + } + + if (_stricmp(NodeURL, "/Mail/EMSave") == 0) + { + // Save Message Text + + SaveMessageText(Session, input, Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/Mail/MsgAction") == 0) + { + ProcessMsgAction(Session, input, Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/Mail/MsgFwdUpdate") == 0) + { + ProcessMsgFwdUpdate(Session, input, Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/Mail/Welcome") == 0) + { + SaveWelcome(Session, input, Reply, RLen, Key); + return; + } + if (_stricmp(NodeURL, "/Mail/HK") == 0) + { + SaveHousekeeping(Session, input, Reply, RLen, Key); + return; + } + if (_stricmp(NodeURL, "/Mail/WPDetails") == 0) + { + char * param = strstr(input, "\r\n\r\n"); // End of headers + + if (param) + { + WPRec * WP = LookupWP(param+4); + Session->WP = WP; // Save current Message + + * RLen = SendWPDetails(WP, Reply, Key); + return; + } + } + if (_stricmp(NodeURL, "/Mail/WPSave") == 0) + { + SaveWP(Session, input, Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/Mail/MsgInfo.txt") == 0) + { + int n, len = 0; + char * FF = "", *FT = "", *FB = "", *FV = ""; + char * param, * ptr1, *ptr2; + struct MsgInfo * Msg; + char UCto[80]; + char UCfrom[80]; + char UCvia[80]; + char UCbid[80]; + + // Get filter string + + param = strstr(input, "\r\n\r\n"); // End of headers + + + if (param) + { + ptr1 = param + 4; + ptr2 = strchr(ptr1, '|'); + if (ptr2){*(ptr2++) = 0; FF = ptr1; ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0; FT = ptr1;ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0; FV = ptr1;ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0; FB = ptr1;ptr1 = ptr2;} + } + + if (FT[0]) + _strupr(FT); + if (FF[0]) + _strupr(FF); + if (FV[0]) + _strupr(FV); + if (FB[0]) + _strupr(FB); + + for (n = NumberofMessages; n >= 1; n--) + { + Msg = MsgHddrPtr[n]; + + strcpy(UCto, Msg->to); + strcpy(UCfrom, Msg->from); + strcpy(UCvia, Msg->via); + strcpy(UCbid, Msg->bid); + + _strupr(UCto); + _strupr(UCfrom); + _strupr(UCvia); + _strupr(UCbid); + + if ((!FT[0] || strstr(UCto, FT)) && + (!FF[0] || strstr(UCfrom, FF)) && + (!FB[0] || strstr(UCbid, FB)) && + (!FV[0] || strstr(UCvia, FV))) + { + len += sprintf(&Reply[len], "%d|", Msg->number); + } + } + *RLen = len; + return; + } + + if (_stricmp(NodeURL, "/Mail/UserList.txt") == 0) + { + SendUserSelectPage(Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/Mail/FwdList.txt") == 0) + { + SendFwdSelectPage(Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/Mail/FwdDetails") == 0) + { + char * param; + + param = strstr(input, "\r\n\r\n"); // End of headers + + if (param) + { + Session->User = LookupCall(param+4); + if (Session->User) + { + SendFwdDetails(Session->User, Reply, RLen, Key); + return; + } + } + } + + if (_stricmp(NodeURL, "/Mail/FWDSave") == 0) + { + SaveFwdDetails(Session, input, Reply, RLen, Key); + return ; + } + + // End of POST section + } + + if (strstr(NodeURL, "webscript.js")) + { + if (jsTemplate) + free(jsTemplate); + + jsTemplate = GetTemplateFromFile(1, "webscript.js"); + + ReplyLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Cache-Control: max-age=900\r\nContent-Type: text/javascript\r\n\r\n%s", (int)strlen(jsTemplate), jsTemplate); + *RLen = ReplyLen; + return; + } + + + if (_stricmp(NodeURL, "/Mail/Header") == 0) + { + *RLen = SendHeader(Reply, Session->Key); + return; + } + + if (_stricmp(NodeURL, "/Mail/all.html") == 0) + { + *RLen = SendHeader(Reply, Session->Key); + return; + } + + if (_stricmp(NodeURL, "/Mail/Status") == 0 || + _stricmp(NodeURL, "/Mail/DisSession") == 0) // Sent as POST by refresh timer for some reason + { + SendStatusPage(Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/Mail/Conf") == 0) + { + if (ConfigTemplate) + free(ConfigTemplate); + + ConfigTemplate = GetTemplateFromFile(7, "MainConfig.txt"); + + SendConfigPage(Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/Mail/FWD") == 0) + { + if (FwdTemplate) + free(FwdTemplate); + + FwdTemplate = GetTemplateFromFile(4, "FwdPage.txt"); + + if (FwdDetailTemplate) + free(FwdDetailTemplate); + + FwdDetailTemplate = GetTemplateFromFile(3, "FwdDetail.txt"); + + SendFwdMainPage(Reply, RLen, Key); + return; + } + if (_stricmp(NodeURL, "/Mail/Wel") == 0) + { + SendWelcomePage(Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/Mail/Users") == 0) + { + if (UserListTemplate) + free(UserListTemplate); + + UserListTemplate = GetTemplateFromFile(4, "UserPage.txt"); + + if (UserDetailTemplate) + free(UserDetailTemplate); + + UserDetailTemplate = GetTemplateFromFile(4, "UserDetail.txt"); + + *RLen = sprintf(Reply, UserListTemplate, Key, Key, BBSName, + Key, Key, Key, Key, Key, Key, Key, Key); + + return; + } + + if (_stricmp(NodeURL, "/Mail/SaveMessage") == 0) + { + struct MsgInfo * Msg = Session->Msg; + char * MailBuffer; + + int Files = 0; + int BodyLen; + char * ptr; + int WriteLen=0; + char Hddr[1000]; + char FullTo[100]; + + MailBuffer = ReadMessageFile(Msg->number); + BodyLen = Msg->length; + + ptr = MailBuffer; + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + sprintf(Hddr, "From: %s%s\r\nTo: %s\r\nType/Status: %c%c\r\nDate/Time: %s\r\nBid: %s\r\nTitle: %s\r\n\r\n", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); + + if (Msg->B2Flags & B2Msg) + { + // Remove B2 Headers (up to the File: Line) + + char * bptr; + bptr = strstr(ptr, "Body:"); + if (bptr) + { + BodyLen = atoi(bptr + 5); + bptr = strstr(bptr, "\r\n\r\n"); + + if (bptr) + ptr = bptr+4; + } + } + + ptr[BodyLen] = 0; + + sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Disposition: attachment; filename=\"SavedMsg%05d.txt\" \r\n\r\n", + (int)(strlen(Hddr) + strlen(ptr)), Msg->number); + strcat(Reply, Hddr); + strcat(Reply, ptr); + + *RLen = (int)strlen(Reply); + + free(MailBuffer); + return; + } + + if (_stricmp(NodeURL, "/Mail/SaveAttachment") == 0) + { + struct MsgInfo * Msg = Session->Msg; + char * MailBuffer; + + int Files = 0, i; + int BodyLen; + char * ptr; + int WriteLen=0; + char FileName[100][MAX_PATH] = {""}; + int FileLen[100]; + char Noatt[] = "Message has no attachments"; + + + MailBuffer = ReadMessageFile(Msg->number); + BodyLen = Msg->length; + + if ((Msg->B2Flags & Attachments) == 0) + { + sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s", + (int)strlen(Noatt), Noatt); + *RLen = (int)strlen(Reply); + + free(MailBuffer); + return; + } + + ptr = MailBuffer; + + while(ptr && *ptr != 13) + { + char * ptr2 = strchr(ptr, 10); // Find CR + + if (memcmp(ptr, "Body: ", 6) == 0) + { + BodyLen = atoi(&ptr[6]); + } + + if (memcmp(ptr, "File: ", 6) == 0) + { + char * ptr1 = strchr(&ptr[6], ' '); // Find Space + + FileLen[Files] = atoi(&ptr[6]); + + memcpy(FileName[Files++], &ptr1[1], (ptr2-ptr1 - 2)); + } + + ptr = ptr2; + ptr++; + } + + ptr += 4; // Over Blank Line and Separator + ptr += BodyLen; // to first file + + if (Files == 0) + { + sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s", + (int)strlen(Noatt), Noatt); + *RLen = (int)strlen(Reply); + free(MailBuffer); + return; + } + + *RLen = 0; + + // For now only handle first + + i = 0; + +// for (i = 0; i < Files; i++) + { + int Len = sprintf(&Reply[*RLen], "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Disposition: attachment; filename=\"%s\" \r\n\r\n", + FileLen[i], FileName[i]); + + memcpy(&Reply[Len + *RLen], ptr, FileLen[i]); + + *RLen += (Len + FileLen[i]); + + ptr += FileLen[i]; + ptr +=2; // Over separator - I don't think there should be one + } + + free(MailBuffer); + return; + } + + + if (_stricmp(NodeURL, "/Mail/Msgs") == 0) + { + struct UserInfo * USER = NULL; + int PageLen; + + if (MsgEditTemplate) + free(MsgEditTemplate); + + MsgEditTemplate = GetTemplateFromFile(2, "MsgPage.txt"); + + // Refresh BBS No to BBS list + + MaxBBS = 0; + + for (USER = BBSChain; USER; USER = USER->BBSNext) + { + int n = USER->BBSNumber; + BBSLIST[n] = USER; + if (n > MaxBBS) + MaxBBS = n; + } + + PageLen = 334 + (MaxBBS / 8) * 24; + + if (MsgEditTemplate) + { + int len =sprintf(Reply, MsgEditTemplate, PageLen, PageLen, PageLen - 97, Key, Key, Key, Key, Key, + BBSName, Key, Key, Key, Key, Key, Key, Key, Key); + *RLen = len; + return; + } + + + + + } + + if (_stricmp(NodeURL, "/Mail/EditM") == 0) + { + // Edit Message + + char * MsgBytes; + + MsgBytes = ReadMessageFile(Session->Msg->number); + + // See if Multipart + +// if (Msg->B2Flags & Attachments) +// EnableWindow(GetDlgItem(hDlg, IDC_SAVEATTACHMENTS), TRUE); + + if (MsgBytes) + { + *RLen = sprintf(Reply, MsgEditPage, Key, MsgBytes); + free (MsgBytes); + } + else + *RLen = sprintf(Reply, MsgEditPage, Key, "Message Not Found"); + + return; + } + + if (_stricmp(NodeURL, "/Mail/HK") == 0) + { + if (HousekeepingTemplate) + free(HousekeepingTemplate); + + HousekeepingTemplate = GetTemplateFromFile(2, "Housekeeping.txt"); + + SendHouseKeeping(Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/Mail/WP") == 0) + { + if (WPTemplate) + free(WPTemplate); + + WPTemplate = GetTemplateFromFile(1, "WP.txt"); + + if (WPTemplate) + { + int len =sprintf(Reply, WPTemplate, Key, Key, Key, Key, + BBSName, Key, Key, Key, Key, Key, Key, Key, Key); + *RLen = len; + return; + } + + return; + } + + if (_stricmp(NodeURL, "/Mail/WPInfo.txt") == 0) + { + int i = 0, n, len = 0; + WPRec * WP[10000]; + + // Get array of addresses + + for (n = 1; n <= NumberofWPrecs; n++) + { + WP[i++] = WPRecPtr[n]; + if (i > 9999) break; + } + + qsort((void *)WP, i, sizeof(void *), compare); + + for (i=0; i < NumberofWPrecs; i++) + { + len += sprintf(&Reply[len], "%s|", WP[i]->callsign); + } + + *RLen = len; + return; + } + + + ReplyLen = sprintf(Reply, MailSignon, BBSName, BBSName); + *RLen = ReplyLen; + +} + +int SendWPDetails(WPRec * WP, char * Reply, char * Key) +{ + int len = 0; + char D1[80], D2[80]; + + if (WP) + { + strcpy(D1, FormatDateAndTime(WP->last_modif, FALSE)); + strcpy(D2, FormatDateAndTime(WP->last_seen, FALSE)); + + len = sprintf(Reply, WPDetail, Key, WP->callsign, WP->name, + WP->first_homebbs, WP->secnd_homebbs, + WP->first_qth, WP->secnd_qth, + WP->first_zip, WP->secnd_zip, D1, D2, + WP->Type, + WP->changed, + WP->seen); + } + return(len); +} +VOID SaveWP(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key) +{ + WPRec * WP = Session->WP; + char * input, * ptr1, * ptr2; + int n; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "Cancel=Cancel")) + { + *RLen = SendHeader(Reply, Session->Key); + return; + } + + if (strcmp(input + 4, "Delete") == 0) + { + for (n = 1; n <= NumberofWPrecs; n++) + { + if (Session->WP == WPRecPtr[n]) + break; + } + + if (n <= NumberofWPrecs) + { + WP = Session->WP; + + for (n = n; n < NumberofWPrecs; n++) + { + WPRecPtr[n] = WPRecPtr[n+1]; // move down all following entries + } + + NumberofWPrecs--; + + free(WP); + + SaveWPDatabase(); + + Session->WP = WPRecPtr[1]; + } + *RLen = SendWPDetails(Session->WP, Reply, Session->Key); + return; + } + } + if (input && WP) + { + ptr1 = input + 4; + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;if (strlen(ptr1) > 12) ptr1[12] = 0;strcpy(WP->name, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;if (strlen(ptr1) > 40) ptr1[40] = 0;strcpy(WP->first_homebbs, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;if (strlen(ptr1) > 40) ptr1[40] = 0;strcpy(WP->secnd_homebbs, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;if (strlen(ptr1) > 30) ptr1[30] = 0;strcpy(WP->first_qth, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;if (strlen(ptr1) > 30) ptr1[30] = 0;strcpy(WP->secnd_qth, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;if (strlen(ptr1) > 8) ptr1[8] = 0;strcpy(WP->first_zip, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;if (strlen(ptr1) > 8) ptr1[8] = 0;strcpy(WP->secnd_zip, ptr1);ptr1 = ptr2;} + + // GetParam(input, "BBSCall=", BBSName); + + +/* + GetDlgItemText(hDlg, IDC_WPNAME, WP->name, 13); + GetDlgItemText(hDlg, IDC_HOMEBBS1, WP->first_homebbs, 41); + GetDlgItemText(hDlg, IDC_HOMEBBS2, WP->first_homebbs, 41); + GetDlgItemText(hDlg, IDC_QTH1, WP->first_qth, 31); + GetDlgItemText(hDlg, IDC_QTH2, WP->secnd_qth, 31); + GetDlgItemText(hDlg, IDC_ZIP1, WP->first_zip, 31); + GetDlgItemText(hDlg, IDC_ZIP2, WP->secnd_zip, 31); + WP->seen = GetDlgItemInt(hDlg, IDC_SEEN, &OK1, FALSE); +*/ + + WP->last_modif = time(NULL); + WP->Type = 'U'; + WP->changed = 1; + + SaveWPDatabase(); + + *RLen = SendWPDetails(WP, Reply, Key); + } +} + + +int SendMessageDetails(struct MsgInfo * Msg, char * Reply, char * Key) +{ + int BBSNo = 1, x, y, len = 0; + char D1[80], D2[80], D3[80]; + struct UserInfo * USER; + int i = 0, n; + struct UserInfo * bbs[NBBBS+2] = {0}; + + if (Msg) + { + char EmailFromLine[256] = ""; + + strcpy(D1, FormatDateAndTime((time_t)Msg->datecreated, FALSE)); + strcpy(D2, FormatDateAndTime((time_t)Msg->datereceived, FALSE)); + strcpy(D3, FormatDateAndTime((time_t)Msg->datechanged, FALSE)); + +// if (Msg->emailfrom[0]) + sprintf(EmailFromLine, "Email From
", Msg->emailfrom); + + len = sprintf(Reply, MailDetailPage, Msg->number, Key, + Msg->from, D1, + (Msg->type == 'B')?sel:"", + (Msg->type == 'P')?sel:"", + (Msg->type == 'T')?sel:"", + Msg->to, D2, + (Msg->status == 'N')?sel:"", + (Msg->status == 'Y')?sel:"", + (Msg->status == 'F')?sel:"", + (Msg->status == 'K')?sel:"", + (Msg->status == 'H')?sel:"", + (Msg->status == 'D')?sel:"", + (Msg->status == '$')?sel:"", + Msg->bid, D3, Msg->length, EmailFromLine, Msg->via, Msg->title, + Key, Msg->number, Key, Key, + (Msg->B2Flags & Attachments)?"":"disabled"); + + // Get a sorted list of BBS records + + for (n = 1; n <= NumberofUsers; n++) + { + USER = UserRecPtr[n]; + + if ((USER->flags & F_BBS)) + if (USER->BBSNumber) + bbs[i++] = USER; + } + + qsort((void *)bbs, i, sizeof(void *), compare ); + + n = 0; + + for (y = 0; y < NBBBS/8; y++) + { + len += sprintf(&Reply[len],""); + for (x= 0; x < 8; x++) + { + char * Colour = NotThisOne; + + if (bbs[n]) + { + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + if (check_fwd_bit(Msg->fbbs, bbs[n]->BBSNumber)) + Colour = ToSend; + if (memcmp(Msg->forw, zeros, NBMASK) != 0) + if (check_fwd_bit(Msg->forw, bbs[n]->BBSNumber)) + Colour = Sent; + + len += sprintf(&Reply[len],"%s", + Colour, bbs[n]->BBSNumber, bbs[n]->Call); + } + else + len += sprintf(&Reply[len], " "); + + n++; + } + len += sprintf(&Reply[len],""); + if (n > i) + break; + } + len += sprintf(&Reply[len], "%s", MailDetailTail); + } + return(len); +} + +char ** GetMultiStringInput(char * input, char * key) +{ + char MultiString[16384] = ""; + + GetParam(input, key, MultiString); + + if (MultiString[0] == 0) + return NULL; + + return SeparateMultiString(MultiString, TRUE); +} + +char ** SeparateMultiString(char * MultiString, BOOL NoToUpper) +{ + char * ptr1 = MultiString; + char * ptr2 = NULL; + char * DecodedString; + char ** Value; + int Count = 0; + char c; + char * ptr; + + ptr2 = zalloc(strlen(MultiString) + 1); + DecodedString = ptr2; + + // Input has crlf or lf - replace with | + + while (*ptr1) + { + c = *(ptr1++); + + if (c == 13) + continue; + + if (c == 10) + { + *ptr2++ = '|'; + } + else + *(ptr2++) = c; + } + + // Convert to string array + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + ptr = DecodedString; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + if (strlen(ptr)) + { + Value = realloc(Value, (Count+2) * sizeof(void *)); + if (_memicmp(ptr, "file ", 5) == 0 || NoToUpper) + Value[Count++] = _strdup(ptr); + else + Value[Count++] = _strupr(_strdup(ptr)); + } + ptr = ptr1; + } + + Value[Count] = NULL; + return Value; +} + +VOID GetMallocedParam(char * input, char * key, char ** value) +{ + char Param[32768] = ""; + + GetParam(input, key, Param); + + if (Param[0]) + { + free(*value); + *value = _strdup(Param); + } +} + +VOID GetParam(char * input, char * key, char * value) +{ + char * ptr = strstr(input, key); + char Param[32768]; + char * ptr1, * ptr2; + char c; + + if (ptr) + { + ptr2 = strchr(ptr, '&'); + if (ptr2) *ptr2 = 0; + strcpy(Param, ptr + strlen(key)); + if (ptr2) *ptr2 = '&'; // Restore string + + // Undo any % transparency + + ptr1 = Param; + ptr2 = Param; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + *(ptr2++) = m * 16 + n; + } + else if (c == '+') + *(ptr2++) = ' '; + else + *(ptr2++) = c; + + c = *(ptr1++); + } + + *(ptr2++) = 0; + + strcpy(value, Param); + } +} + +VOID GetCheckBox(char * input, char * key, int * value) +{ + char * ptr = strstr(input, key); + if (ptr) + *value = 1; + else + *value = 0; +} + + +VOID * GetOverrideFromString(char * input) +{ + char * ptr1; + char * MultiString = NULL; + char * ptr = input; + int Count = 0; + struct Override ** Value; + char * Val; + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, 13); + + if (ptr1) + { + *(ptr1) = 0; + ptr1 += 2; + } + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count] = zalloc(sizeof(struct Override)); + Val = strlop(ptr, ','); + if (Val == NULL) + break; + + Value[Count]->Call = _strupr(_strdup(ptr)); + Value[Count++]->Days = atoi(Val); + ptr = ptr1; + } + + Value[Count] = NULL; + return Value; +} + + + + +VOID SaveHousekeeping(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key) +{ + int ReplyLen = 0; + char * input; + char Temp[80]; + struct tm *tm; + time_t now; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "RunNow=")) + { + DoHouseKeeping(FALSE); + SendHouseKeeping(Reply, RLen, Key); + return; + } + if (strstr(input, "Cancel=Cancel")) + { + SendHouseKeeping(Reply, RLen, Key); + return; + } + + GetParam(input, "MTTime=", Temp); + MaintTime = atoi(Temp); + GetParam(input, "MTInt=", Temp); + MaintInterval = atoi(Temp); + GetParam(input, "MAXMSG=", Temp); + MaxMsgno = atoi(Temp); + GetParam(input, "BIDLife=", Temp); + BidLifetime= atoi(Temp); + GetParam(input, "MaxAge=", Temp); + MaxAge = atoi(Temp); + GetParam(input, "LogLife=", Temp); + LogAge = atoi(Temp); + GetParam(input, "UserLife=", Temp); + UserLifetime= atoi(Temp); + + GetCheckBox(input, "Deltobin=", &DeletetoRecycleBin); + GetCheckBox(input, "SendND=", &SendNonDeliveryMsgs); + GetCheckBox(input, "NoMail=", &SuppressMaintEmail); + GetCheckBox(input, "GenTraffic=", &GenerateTrafficReport); + GetCheckBox(input, "OvUnsent=", &OverrideUnsent); + + GetParam(input, "PR=", Temp); + PR = atof(Temp); + GetParam(input, "PUR=", Temp); + PUR = atof(Temp); + GetParam(input, "PF=", Temp); + PF = atof(Temp); + GetParam(input, "PUF=", Temp); + PNF = atof(Temp); + GetParam(input, "BF=", Temp); + BF = atoi(Temp); + GetParam(input, "BUF=", Temp); + BNF = atoi(Temp); + + GetParam(input, "NTSD=", Temp); + NTSD = atoi(Temp); + + GetParam(input, "NTSF=", Temp); + NTSF = atoi(Temp); + + GetParam(input, "NTSU=", Temp); + NTSU = atoi(Temp); + + GetParam(input, "From=", LTFROMString); + LTFROM = GetOverrideFromString(LTFROMString); + + GetParam(input, "To=", LTTOString); + LTTO = GetOverrideFromString(LTTOString); + + GetParam(input, "At=", LTATString); + LTAT = GetOverrideFromString(LTATString); + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + // Calulate time to run Housekeeping + + now = time(NULL); + + tm = gmtime(&now); + + tm->tm_hour = MaintTime / 100; + tm->tm_min = MaintTime % 100; + tm->tm_sec = 0; + +// MaintClock = _mkgmtime(tm); + MaintClock = mktime(tm) - (time_t)_MYTIMEZONE; + + while (MaintClock < now) + MaintClock += MaintInterval * 3600; + + Debugprintf("Maint Clock %d NOW %d Time to HouseKeeping %d", MaintClock, now, MaintClock - now); + } + SendHouseKeeping(Reply, RLen, Key); + return; +} + + + + + + + +VOID SaveWelcome(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key) +{ + int ReplyLen = 0; + char * input; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "Cancel=Cancel")) + { + *RLen = SendHeader(Reply, Session->Key); + return; + } + + GetMallocedParam(input, "NUWelcome=", &WelcomeMsg); + GetMallocedParam(input, "NewWelcome=", &NewWelcomeMsg); + GetMallocedParam(input, "ExWelcome=", &ExpertWelcomeMsg); + + TidyWelcomeMsg(&WelcomeMsg); + TidyWelcomeMsg(&NewWelcomeMsg); + TidyWelcomeMsg(&ExpertWelcomeMsg); + + GetMallocedParam(input, "NUPrompt=", &Prompt); + GetMallocedParam(input, "NewPrompt=", &NewPrompt); + GetMallocedParam(input, "ExPrompt=", &ExpertPrompt); + TidyPrompts(); + + GetParam(input, "Bye=", &SignoffMsg[0]); + if (SignoffMsg[0]) + { + if (SignoffMsg[strlen(SignoffMsg) - 1] == 10) + SignoffMsg[strlen(SignoffMsg) - 1] = 0; + + if (SignoffMsg[strlen(SignoffMsg) - 1] != 13) + strcat(SignoffMsg, "\r"); + } + + if (SignoffMsg[0] == 13) + SignoffMsg[0] = 0; + } + + SendWelcomePage(Reply, RLen, Key); + return; +} + +VOID ProcessConfUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key) +{ + int ReplyLen = 0; + char * input; + struct UserInfo * USER = NULL; + char Temp[80]; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "Cancel=Cancel")) + { + *RLen = SendHeader(Reply, Session->Key); + return; + } + + if (strstr(input, "ConfigUI=Config+UI")) + { + SendUIPage(Reply, RLen, Key); + return; + } + + GetParam(input, "BBSCall=", BBSName); + _strupr(BBSName); + strlop(BBSName, '-'); + GetParam(input, "SYSOPCall=", SYSOPCall); + _strupr(SYSOPCall); + strlop(SYSOPCall, '-'); + GetParam(input, "HRoute=", HRoute); + _strupr(HRoute); + GetParam(input, "ApplNum=", Temp); + BBSApplNum = atoi(Temp); + GetParam(input, "Streams=", Temp); + MaxStreams = atoi(Temp); + + GetCheckBox(input, "SysToSYSOP=", &SendSYStoSYSOPCall); + GetCheckBox(input, "BBSToSYSOP=", &SendBBStoSYSOPCall); + GetCheckBox(input, "RefuseBulls=", &RefuseBulls); + GetCheckBox(input, "EnUI=", &EnableUI); + + GetParam(input, "UIInterval=", Temp); + MailForInterval = atoi(Temp); + + GetCheckBox(input, "DontHold=", &DontHoldNewUsers); + GetCheckBox(input, "DefaultNoWinlink=", &DefaultNoWINLINK); + GetCheckBox(input, "DontNeedName=", &AllowAnon); + GetCheckBox(input, "DontNeedHomeBBS=", &DontNeedHomeBBS); + GetCheckBox(input, "DontCheckFromCall=", &DontCheckFromCall); + GetCheckBox(input, "UserCantKillT=", &UserCantKillT); + UserCantKillT = !UserCantKillT; // Reverse Logic + GetCheckBox(input, "FWDtoMe=", &ForwardToMe); + GetCheckBox(input, "OnlyKnown=", &OnlyKnown); + GetCheckBox(input, "Events=", &reportMailEvents); + + GetParam(input, "POP3Port=", Temp); + POP3InPort = atoi(Temp); + + GetParam(input, "SMTPPort=", Temp); + SMTPInPort = atoi(Temp); + + GetParam(input, "NNTPPort=", Temp); + NNTPInPort = atoi(Temp); + + GetCheckBox(input, "EnRemote=", &RemoteEmail); + + GetCheckBox(input, "EnISP=", &ISP_Gateway_Enabled); + GetCheckBox(input, "SendAMPR=", &SendAMPRDirect); + + GetParam(input, "AMPRDomain=", AMPRDomain); + + GetParam(input, "ISPDomain=", MyDomain); + GetParam(input, "SMTPServer=", ISPSMTPName); + GetParam(input, "ISPEHLOName=", ISPEHLOName); + + GetParam(input, "ISPSMTPPort=", Temp); + ISPSMTPPort = atoi(Temp); + + GetParam(input, "POP3Server=", ISPPOP3Name); + + GetParam(input, "ISPPOP3Port=", Temp); + ISPPOP3Port = atoi(Temp); + + GetParam(input, "ISPAccount=", ISPAccountName); + + GetParam(input, "ISPPassword=", ISPAccountPass); + EncryptedPassLen = EncryptPass(ISPAccountPass, EncryptedISPAccountPass); + + GetParam(input, "PollInterval=", Temp); + ISPPOP3Interval = atoi(Temp); + + GetCheckBox(input, "ISPAuth=", &SMTPAuthNeeded); + + GetCheckBox(input, "EnWP=", &SendWP); + GetCheckBox(input, "RejWFBulls=", &FilterWPBulls); + + if (strstr(input, "Type=TypeB")) + SendWPType = 0; + + if (strstr(input, "Type=TypeP")) + SendWPType = 1; + + SendWPAddrs = GetMultiStringInput(input, "WPTO="); + + RejFrom = GetMultiStringInput(input, "Rfrom="); + RejTo = GetMultiStringInput(input, "Rto="); + RejAt = GetMultiStringInput(input, "Rat="); + RejBID = GetMultiStringInput(input, "RBID="); + HoldFrom = GetMultiStringInput(input, "Hfrom="); + HoldTo = GetMultiStringInput(input, "Hto="); + HoldAt = GetMultiStringInput(input, "Hat="); + HoldBID = GetMultiStringInput(input, "HBID="); + + // Look for fbb style filters + + input = strstr(input, "&Action="); + + // delete old list + + while(Filters && Filters->Next) + { + FBBFilter * next = Filters->Next; + free(Filters); + Filters = next; + } + + free(Filters); + Filters = NULL; + + while (input) + { + // extract and validate before saving + + FBBFilter Filter; + FBBFilter * PFilter; + + memset(&Filter, 0, sizeof(FBBFilter)); + + Filter.Action = toupper(input[8]); + + input = strstr(input, "&Type="); + + if (Filter.Action == 'H' || Filter.Action == 'R') + { + Filter.Type = toupper(input[6]); + input = strstr(input, "&From="); + memcpy(Filter.From, &input[6], 10); + input = strstr(input, "&TO="); + strlop(Filter.From, '&'); + _strupr(Filter.From); + memcpy(Filter.TO, &input[4], 10); + input = strstr(input, "&AT="); + strlop(Filter.TO, '&'); + _strupr(Filter.TO); + memcpy(Filter.AT, &input[4], 10); + input = strstr(input, "&BID="); + strlop(Filter.AT, '&'); + _strupr(Filter.AT); + memcpy(Filter.BID, &input[5], 10); + input = strstr(input, "&MaxLen="); + strlop(Filter.BID, '&'); + _strupr(Filter.BID); + Filter.MaxLen = atoi(&input[8]); + + if (Filter.Type == '&') Filter.Type = '*'; + if (Filter.From[0] == 0) strcpy(Filter.From, "*"); + if (Filter.TO[0] == 0) strcpy(Filter.TO, "*"); + if (Filter.AT[0] == 0) strcpy(Filter.AT, "*"); + if (Filter.BID[0] == 0) strcpy(Filter.BID, "*"); + + // add to list + + PFilter = zalloc(sizeof(FBBFilter)); + + memcpy(PFilter, &Filter, sizeof(FBBFilter)); + + if (Filters == 0) + Filters = PFilter; + else + { + FBBFilter * p = Filters; + + while (p->Next) + p = p->Next; + + p->Next = PFilter; + } + } + + input = strstr(input, "&Action="); + } + + SaveConfig(ConfigName); + GetConfig(ConfigName); + } + + SendConfigPage(Reply, RLen, Key); + return; +} + + + +VOID ProcessUIUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key) +{ + int ReplyLen = 0, i; + char * input; + struct UserInfo * USER = NULL; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "Cancel=Cancel")) + { + *RLen = SendHeader(Reply, Session->Key); + return; + } + + GetParam(input, "MailFor=", &MailForText[0]); + + for (i = 1; i <= GetNumberofPorts(); i++) + { + char EnKey[10]; + char DigiKey[10]; + char MFKey[12]; + char HDDRKey[12]; + char NullKey[12]; + char Temp[100]; + + sprintf(EnKey, "En%d=", i); + sprintf(DigiKey, "Path%d=", i); + sprintf(MFKey, "SndMF%d=", i); + sprintf(HDDRKey, "SndHDDR%d=", i); + sprintf(NullKey, "SndNull%d=", i); + + GetCheckBox(input, EnKey, &UIEnabled[i]); + GetParam(input, DigiKey, Temp); + if (UIDigi[i]) + free (UIDigi[i]); + UIDigi[i] = _strdup(Temp); + GetCheckBox(input, MFKey, &UIMF[i]); + GetCheckBox(input, HDDRKey, &UIHDDR[i]); + GetCheckBox(input, NullKey, &UINull[i]); + } + + SaveConfig(ConfigName); + GetConfig(ConfigName); + } + + SendUIPage(Reply, RLen, Key); + return; +} + +VOID ProcessDisUser(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + char * input; + char * ptr; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + ptr = strstr(input, "call="); + if (ptr) + { + int Stream = atoi(ptr + 5); + Disconnect(Stream); + } + } + SendStatusPage(Reply, RLen, Rest); +} + + + +VOID SaveFwdCommon(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + int ReplyLen = 0; + char * input; + struct UserInfo * USER = NULL; + + char Temp[80]; + int Mask = 0; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + int n; + GetParam(input, "MaxTX=", Temp); + MaxTXSize = atoi(Temp); + GetParam(input, "MaxRX=", Temp); + MaxRXSize = atoi(Temp); + GetParam(input, "MaxAge=", Temp); + MaxAge = atoi(Temp); + GetCheckBox(input, "WarnNoRoute=", &WarnNoRoute); + GetCheckBox(input, "LocalTime=", &Localtime); + GetCheckBox(input, "SendPtoMultiple=", &SendPtoMultiple); + GetCheckBox(input, "FourCharCont=", &FOURCHARCONT); + + // Reinitialise Aliases + + n = 0; + + if (Aliases) + { + while(Aliases[n]) + { + free(Aliases[n]->Dest); + free(Aliases[n]); + n++; + } + + free(Aliases); + Aliases = NULL; + FreeList(AliasText); + } + + AliasText = GetMultiStringInput(input, "Aliases="); + + if (AliasText) + { + n = 0; + + while (AliasText[n]) + { + _strupr(AliasText[n]); + n++; + } + } + SetupFwdAliases(); + } + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + SendFwdMainPage(Reply, RLen, Session->Key); +} + +char * GetNextParam(char ** next) +{ + char * ptr1 = *next; + char * ptr2 = strchr(ptr1, '|'); + if (ptr2) + { + *(ptr2++) = 0; + *next = ptr2; + } + return ptr1; +} + +VOID SaveFwdDetails(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + int ReplyLen = 0; + char * input; + struct UserInfo * USER = Session->User; + struct BBSForwardingInfo * FWDInfo = USER->ForwardingInfo; + char * ptr1, *ptr2; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "StartForward")) + { + StartForwarding(USER->BBSNumber, NULL); + SendFwdDetails(Session->User, Reply, RLen, Session->Key); + return; + } + + if (strstr(input, "CopyForward")) + { + struct UserInfo * OldBBS; + + // Get call to copy from + + ptr2 = input + 4; + ptr1 = GetNextParam(&ptr2); // Call + _strupr(ptr2); + + OldBBS = FindBBS(ptr2); + + if (OldBBS == NULL) + { + + *RLen = sprintf(Reply, "

Copy From BBS %s not found

", ptr2); + return; + } + + // Set current info from OldBBS +// +// SetForwardingPage(hDlg, OldBBS); // moved to separate routine as also called from copy config + + SendFwdDetails(OldBBS, Reply, RLen, Session->Key); + return; + } + // Fwd update + + ptr2 = input + 4; + ptr1 = GetNextParam(&ptr2); // TO + FWDInfo->TOCalls = SeparateMultiString(ptr1, FALSE); + + ptr1 = GetNextParam(&ptr2); // AT + FWDInfo->ATCalls = SeparateMultiString(ptr1, FALSE); + + ptr1 = GetNextParam(&ptr2); // TIMES + FWDInfo->FWDTimes = SeparateMultiString(ptr1, FALSE); + + ptr1 = GetNextParam(&ptr2); // FWD SCRIPT + FWDInfo->ConnectScript = SeparateMultiString(ptr1, TRUE); + + ptr1 = GetNextParam(&ptr2); // HRB + FWDInfo->Haddresses = SeparateMultiString(ptr1, FALSE); + + ptr1 = GetNextParam(&ptr2); // HRP + FWDInfo->HaddressesP = SeparateMultiString(ptr1, FALSE); + + ptr1 = GetNextParam(&ptr2); // BBSHA + if (FWDInfo->BBSHA) + free(FWDInfo->BBSHA); + + FWDInfo->BBSHA = _strdup(_strupr(ptr1)); + + ptr1 = GetNextParam(&ptr2); // EnF + if (strcmp(ptr1, "true") == 0) FWDInfo->Enabled = TRUE; else FWDInfo->Enabled = FALSE; + + ptr1 = GetNextParam(&ptr2); // Interval + FWDInfo->FwdInterval = atoi(ptr1); + + ptr1 = GetNextParam(&ptr2); // EnR + if (strcmp(ptr1, "true") == 0) FWDInfo->ReverseFlag = TRUE; else FWDInfo->ReverseFlag = FALSE; + + ptr1 = GetNextParam(&ptr2); // RInterval + FWDInfo->RevFwdInterval = atoi(ptr1); + + ptr1 = GetNextParam(&ptr2); // No Wait + if (strcmp(ptr1, "true") == 0) FWDInfo->SendNew = TRUE; else FWDInfo->SendNew = FALSE; + + ptr1 = GetNextParam(&ptr2); // Blocked + if (strcmp(ptr1, "true") == 0) FWDInfo->AllowBlocked = TRUE; else FWDInfo->AllowBlocked = FALSE; + + ptr1 = GetNextParam(&ptr2); // FBB Block + FWDInfo->MaxFBBBlockSize = atoi(ptr1); + + ptr1 = GetNextParam(&ptr2); // Personals + if (strcmp(ptr1, "true") == 0) FWDInfo->PersonalOnly = TRUE; else FWDInfo->PersonalOnly = FALSE; + ptr1 = GetNextParam(&ptr2); // Binary + if (strcmp(ptr1, "true") == 0) FWDInfo->AllowCompressed = TRUE; else FWDInfo->AllowCompressed = FALSE; + ptr1 = GetNextParam(&ptr2); // B1 + if (strcmp(ptr1, "true") == 0) FWDInfo->AllowB1 = TRUE; else FWDInfo->AllowB1 = FALSE; + ptr1 = GetNextParam(&ptr2); // B2 + if (strcmp(ptr1, "true") == 0) FWDInfo->AllowB2 = TRUE; else FWDInfo->AllowB2 = FALSE; + ptr1 = GetNextParam(&ptr2); // CTRLZ + if (strcmp(ptr1, "true") == 0) FWDInfo->SendCTRLZ = TRUE; else FWDInfo->SendCTRLZ = FALSE; + ptr1 = GetNextParam(&ptr2); // Connect Timeout + FWDInfo->ConTimeout = atoi(ptr1); + + SaveConfig(ConfigName); + GetConfig(ConfigName); + + ReinitializeFWDStruct(Session->User); + + SendFwdDetails(Session->User, Reply, RLen, Session->Key); + } +} + + + +VOID ProcessUserUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + int ReplyLen = 0; + char * input; + struct UserInfo * USER = Session->User; + int SSID, Mask = 0; + char * ptr1, *ptr2; + int skipRMSExUser = 0; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "Cancel")) + { + *RLen = SendHeader(Reply, Session->Key); + return; + } + + if (strstr(input, "Delete")) + { + int n; + + for (n = 1; n <= NumberofUsers; n++) + { + if (Session->User == UserRecPtr[n]) + break; + } + + if (n <= NumberofUsers) + { + USER = Session->User; + + for (n = n; n < NumberofUsers; n++) + { + UserRecPtr[n] = UserRecPtr[n+1]; // move down all following entries + } + + NumberofUsers--; + + if (USER->flags & F_BBS) // was a BBS? + DeleteBBS(USER); + + free(USER); + + SaveUserDatabase(); + + Session->User = UserRecPtr[1]; + + SendUserSelectPage(Reply, RLen, Session->Key); + return; + } + } + + if (strstr(input, "Add=")) + { + char * Call; + + Call = input + 8; + strlop(Call, '-'); + + if (strlen(Call) > 6) + Call[6] = 0; + + _strupr(Call); + + if (Call[0] == 0 || LookupCall(Call)) + { + // Null or exists + + SendUserSelectPage(Reply, RLen, Session->Key); + return; + } + + USER = AllocateUserRecord(Call); + USER->Temp = zalloc(sizeof (struct TempUserInfo)); + + SendUserSelectPage(Reply, RLen, Session->Key); + return; + + } + + // User update + + ptr2 = input + 4; + ptr1 = GetNextParam(&ptr2); // BBS + + // If BBS Flag has changed, must set up or delete forwarding info + + if (strcmp(ptr1, "true") == 0) + { + if ((USER->flags & F_BBS) == 0) + { + // New BBS + + if(SetupNewBBS(USER)) + { + USER->flags |= F_BBS; + USER->flags &= ~F_Temp_B2_BBS; // Clear RMS Express User + skipRMSExUser = 1; // Dont read old value + } + else + { + // Failed - too many bbs's defined + + //sprintf(InfoBoxText, "Cannot set user to be a BBS - you already have 80 BBS's defined"); + //DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + USER->flags &= ~F_BBS; + //CheckDlgButton(hDlg, IDC_BBSFLAG, (user->flags & F_BBS)); + } + } + } + else + { + if (USER->flags & F_BBS) + { + //was a BBS + + USER->flags &= ~F_BBS; + DeleteBBS(USER); + } + } + + ptr1 = GetNextParam(&ptr2); // Permit Email + if (strcmp(ptr1, "true") == 0) USER->flags |= F_EMAIL; else USER->flags &= ~F_EMAIL; + + ptr1 = GetNextParam(&ptr2); // PMS + if (strcmp(ptr1, "true") == 0) USER->flags |= F_PMS; else USER->flags &= ~F_PMS; + + ptr1 = GetNextParam(&ptr2); // RMS EX User + if (strcmp(ptr1, "true") == 0 && !skipRMSExUser) USER->flags |= F_Temp_B2_BBS; else USER->flags &= ~F_Temp_B2_BBS; + ptr1 = GetNextParam(&ptr2); // SYSOP + if (strcmp(ptr1, "true") == 0) USER->flags |= F_SYSOP; else USER->flags &= ~F_SYSOP; + ptr1 = GetNextParam(&ptr2); // PollRMS + if (strcmp(ptr1, "true") == 0) USER->flags |= F_POLLRMS; else USER->flags &= ~F_POLLRMS; + ptr1 = GetNextParam(&ptr2); // Expert + if (strcmp(ptr1, "true") == 0) USER->flags |= F_Expert; else USER->flags &= ~F_Expert; + + ptr1 = GetNextParam(&ptr2); // SSID1 + SSID = atoi(ptr1); + Mask |= (1 << SSID); + ptr1 = GetNextParam(&ptr2); // SSID2 + SSID = atoi(ptr1); + Mask |= (1 << SSID); + ptr1 = GetNextParam(&ptr2); // SSID3 + SSID = atoi(ptr1); + Mask |= (1 << SSID); + ptr1 = GetNextParam(&ptr2); // SSID4 + SSID = atoi(ptr1); + Mask |= (1 << SSID); + Session->User->RMSSSIDBits = Mask; + + ptr1 = GetNextParam(&ptr2); // Excluded + if (strcmp(ptr1, "true") == 0) USER->flags |= F_Excluded; else USER->flags &= ~F_Excluded; + ptr1 = GetNextParam(&ptr2); // Hold + if (strcmp(ptr1, "true") == 0) USER->flags |= F_HOLDMAIL; else USER->flags &= ~F_HOLDMAIL; + ptr1 = GetNextParam(&ptr2); // SYSOP gets LM + if (strcmp(ptr1, "true") == 0) USER->flags |= F_SYSOP_IN_LM; else USER->flags &= ~F_SYSOP_IN_LM; + ptr1 = GetNextParam(&ptr2); // Dont add winlink.org + if (strcmp(ptr1, "true") == 0) USER->flags |= F_NOWINLINK; else USER->flags &= ~F_NOWINLINK; + ptr1 = GetNextParam(&ptr2); // Allow Bulls + if (strcmp(ptr1, "true") == 0) USER->flags &= ~F_NOBULLS; else USER->flags |= F_NOBULLS; // Inverted flag + ptr1 = GetNextParam(&ptr2); // NTS Message Pickup Station + if (strcmp(ptr1, "true") == 0) USER->flags |= F_NTSMPS; else USER->flags &= ~F_NTSMPS; + ptr1 = GetNextParam(&ptr2); // APRS Mail For + if (strcmp(ptr1, "true") == 0) USER->flags |= F_RMSREDIRECT; else USER->flags &= ~F_RMSREDIRECT; + ptr1 = GetNextParam(&ptr2); // Redirect to RMS + + if (strcmp(ptr1, "true") == 0) USER->flags |= F_APRSMFOR; else USER->flags &= ~F_APRSMFOR; + + ptr1 = GetNextParam(&ptr2); // APRS SSID + SSID = atoi(ptr1); + SSID &= 15; + USER->flags &= 0x0fffffff; + USER->flags |= (SSID << 28); + + + ptr1 = GetNextParam(&ptr2); // Last Listed + USER->lastmsg = atoi(ptr1); + ptr1 = GetNextParam(&ptr2); // Name + memcpy(USER->Name, ptr1, 17); + ptr1 = GetNextParam(&ptr2); // Pass + memcpy(USER->pass, ptr1, 12); + ptr1 = GetNextParam(&ptr2); // CMS Pass + if (memcmp("****************", ptr1, strlen(ptr1) != 0)) + { + memcpy(USER->CMSPass, ptr1, 15); + } + + ptr1 = GetNextParam(&ptr2); // QTH + memcpy(USER->Address, ptr1, 60); + ptr1 = GetNextParam(&ptr2); // ZIP + memcpy(USER->ZIP, ptr1, 8); + ptr1 = GetNextParam(&ptr2); // HomeBBS + memcpy(USER->HomeBBS, ptr1, 40); + _strupr(USER->HomeBBS); + + SaveUserDatabase(); + UpdateWPWithUserInfo(USER); + + *RLen = SendUserDetails(Session, Reply, Session->Key); + } +} + +VOID ProcessMsgAction(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + int ReplyLen = 0; + char * input; + int BBSNumber = 0; + struct MsgInfo * Msg = Session->Msg; + char * ptr1; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input && Msg) + { + ptr1 = input + 4; + *RLen = SendMessageDetails(Msg, Reply, Session->Key); + } +} + +VOID SaveMessageText(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + int ReplyLen = 0; + struct MsgInfo * Msg = Session->Msg; + char * ptr, * ptr1, * ptr2, *input; + char c; + int MsgLen, WriteLen; + char MsgFile[256]; + FILE * hFile; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "Cancel=Cancel")) + { + *RLen = sprintf(Reply, "%s", ""); + return; + } + + ptr = strstr(input, "&Save="); + + if (ptr) + { + *ptr = 0; + + // Undo any % transparency + + ptr1 = ptr2 = input + 8; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + *(ptr2++) = m * 16 + n; + } + else if (c == '+') + *(ptr2++) = ' '; + else + *(ptr2++) = c; + + c = *(ptr1++); + } + + *(ptr2++) = 0; + + MsgLen = (int)strlen(input + 8); + + Msg->datechanged = time(NULL); + Msg->length = MsgLen; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = (int)fwrite(input + 8, 1, Msg->length, hFile); + fclose(hFile); + } + + if (WriteLen != Msg->length) + { + char Mess[80]; + sprintf_s(Mess, sizeof(Mess), "Failed to create Message File\r"); + CriticalErrorHandler(Mess); + + return; + } + + SaveMessageDatabase(); + + *RLen = sprintf(Reply, "%s", ""); + + } + } + return; + +} + + +VOID ProcessMsgUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + int ReplyLen = 0; + char * input; + int BBSNumber = 0; + struct MsgInfo * Msg = Session->Msg; + char * ptr1, * ptr2; + char OldStatus = Msg->status; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input && Msg) + { + ptr1 = input + 4; + ptr2 = strchr(ptr1, '|'); + if (ptr2) + { + *(ptr2++) = 0; + strcpy(Msg->from, ptr1); + ptr1 = ptr2; + } + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;strcpy(Msg->to, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;strcpy(Msg->bid, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;strcpy(Msg->emailfrom, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;strcpy(Msg->via, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;strcpy(Msg->title, ptr1);ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;Msg->type = *ptr1;ptr1 = ptr2;} + ptr2 = strchr(ptr1, '|');if (ptr2){*(ptr2++) = 0;Msg->status = *ptr1;ptr1 = ptr2;} + + if (Msg->status != OldStatus) + { + // Need to take action if killing message + + if (Msg->status == 'K') + FlagAsKilled(Msg, FALSE); // Clear forwarding bits + } + + Msg->datechanged = time(NULL); + SaveMessageDatabase(); + } + + *RLen = SendMessageDetails(Msg, Reply, Session->Key); +} + + + + +VOID ProcessMsgFwdUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest) +{ + int ReplyLen = 0; + char * input; + int BBSNumber = 0; + struct UserInfo * User; + struct MsgInfo * Msg = Session->Msg; + BOOL toforward, forwarded; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input && Msg) + { + BBSNumber = atoi(input + 4); + User = BBSLIST[BBSNumber]; + + if (User == NULL) + return; + + toforward = check_fwd_bit(Msg->fbbs, BBSNumber); + forwarded = check_fwd_bit(Msg->forw, BBSNumber); + + if (forwarded) + { + // Changing to not this BBS + + clear_fwd_bit(Msg->forw, BBSNumber); + } + else if (toforward) + { + // Change to Forwarded + + clear_fwd_bit(Msg->fbbs, BBSNumber); + User->ForwardingInfo->MsgCount--; + set_fwd_bit(Msg->forw, BBSNumber); + } + else + { + // Change to to forward + + set_fwd_bit(Msg->fbbs, BBSNumber); + User->ForwardingInfo->MsgCount++; + clear_fwd_bit(Msg->forw, BBSNumber); + if (FirstMessageIndextoForward > Msg->number) + FirstMessageIndextoForward = Msg->number; + + } + *RLen = SendMessageDetails(Msg, Reply, Session->Key); + } + SaveMessageDatabase(); +} + + + + +VOID SetMultiStringValue(char ** values, char * Multi) +{ + char ** Calls; + char * ptr = &Multi[0]; + + *ptr = 0; + + if (values) + { + Calls = values; + + while(Calls[0]) + { + strcpy(ptr, Calls[0]); + ptr += strlen(Calls[0]); + *(ptr++) = '\r'; + *(ptr++) = '\n'; + Calls++; + } + *(ptr) = 0; + } +} + + + +VOID SendFwdDetails(struct UserInfo * User, char * Reply, int * ReplyLen, char * Key) +{ + int Len; + struct BBSForwardingInfo * FWDInfo = User->ForwardingInfo; + char TO[2048] = ""; + char AT[2048] = ""; + char TIMES[2048] = ""; + char FWD[100000] = ""; + char HRB[2048] = ""; + char HRP[2048] = ""; + + SetMultiStringValue(FWDInfo->TOCalls, TO); + SetMultiStringValue(FWDInfo->ATCalls, AT); + SetMultiStringValue(FWDInfo->FWDTimes, TIMES); + SetMultiStringValue(FWDInfo->ConnectScript, FWD); + SetMultiStringValue(FWDInfo->Haddresses, HRB); + SetMultiStringValue(FWDInfo->HaddressesP, HRP); + + if (FwdDetailTemplate == NULL) + FwdDetailTemplate = GetTemplateFromFile(3, "FwdDetail.txt"); + + Len = sprintf(Reply, FwdDetailTemplate, User->Call, + CountMessagestoForward (User), Key, + TO, AT, TIMES , FWD, HRB, HRP, + (FWDInfo->BBSHA) ? FWDInfo->BBSHA : "", + (FWDInfo->Enabled) ? CHKD : UNC, + FWDInfo->FwdInterval, + (FWDInfo->ReverseFlag) ? CHKD : UNC, + FWDInfo->RevFwdInterval, + (FWDInfo->SendNew) ? CHKD : UNC, + (FWDInfo->AllowBlocked) ? CHKD : UNC, + FWDInfo->MaxFBBBlockSize, + (FWDInfo->PersonalOnly) ? CHKD : UNC, + (FWDInfo->AllowCompressed) ? CHKD : UNC, + (FWDInfo->AllowB1) ? CHKD : UNC, + (FWDInfo->AllowB2) ? CHKD : UNC, + (FWDInfo->SendCTRLZ) ? CHKD : UNC, + FWDInfo->ConTimeout); + + *ReplyLen = Len; + +} + +VOID SendConfigPage(char * Reply, int * ReplyLen, char * Key) +{ + int Len, i; + + char HF[2048] = ""; + char HT[2048] = ""; + char HA[2048] = ""; + char HB[2048] = ""; + char RF[2048] = ""; + char RT[2048] = ""; + char RA[2048] = ""; + char RB[2048] = ""; + char WPTO[10000] = ""; + + char FBBFilters[100000] = ""; + + + char * ptr = FBBFilters; + FBBFilter * Filter = Filters; + + SetMultiStringValue(RejFrom, RF); + SetMultiStringValue(RejTo, RT); + SetMultiStringValue(RejAt, RA); + SetMultiStringValue(RejBID, RB); + SetMultiStringValue(HoldFrom, HF); + SetMultiStringValue(HoldTo, HT); + SetMultiStringValue(HoldAt, HA); + SetMultiStringValue(HoldBID, HB); + SetMultiStringValue(SendWPAddrs, WPTO); + + // set up FB style fiters + + ptr += sprintf(ptr, + ""); + + while(Filter) + { + ptr += sprintf(ptr, "" + "" + "" + "" + "" + "" + "" + "", + Filter->Action, Filter->Type, Filter->From, Filter->TO, Filter->AT, Filter->BID, Filter->MaxLen); + + Filter = Filter->Next; + } + + // Add a few blank entries for input + + for (i = 0; i < 5; i++) + { + ptr += sprintf(ptr, "" + "" + "" + "" + "" + "" + "" + "", ' ', ' ', "", "", "", "", 0); + } + + ptr += sprintf(ptr, "
ActionTypeFromTo@BBSBidMax Size
"); + + Debugprintf("%d", strlen(FBBFilters)); + + Len = sprintf(Reply, ConfigTemplate, + BBSName, Key, Key, Key, Key, Key, Key, Key, Key, Key, + BBSName, SYSOPCall, HRoute, + (SendBBStoSYSOPCall) ? CHKD : UNC, + BBSApplNum, MaxStreams, + (SendSYStoSYSOPCall) ? CHKD : UNC, + (RefuseBulls) ? CHKD : UNC, + (EnableUI) ? CHKD : UNC, + MailForInterval, + (DontHoldNewUsers) ? CHKD : UNC, + (DefaultNoWINLINK) ? CHKD : UNC, + (AllowAnon) ? CHKD : UNC, + (DontNeedHomeBBS) ? CHKD : UNC, + (DontCheckFromCall) ? CHKD : UNC, + (UserCantKillT) ? UNC : CHKD, // Reverse logic + (ForwardToMe) ? CHKD : UNC, + (OnlyKnown) ? CHKD : UNC, + (reportMailEvents) ? CHKD : UNC, + POP3InPort, SMTPInPort, NNTPInPort, + (RemoteEmail) ? CHKD : UNC, + AMPRDomain, + (SendAMPRDirect) ? CHKD : UNC, + (ISP_Gateway_Enabled) ? CHKD : UNC, + MyDomain, ISPSMTPName, ISPSMTPPort, ISPEHLOName, ISPPOP3Name, ISPPOP3Port, + ISPAccountName, ISPAccountPass, ISPPOP3Interval, + (SMTPAuthNeeded) ? CHKD : UNC, + (SendWP) ? CHKD : UNC, + (FilterWPBulls) ? CHKD : UNC, + (SendWPType == 0) ? CHKD : UNC, + (SendWPType == 1) ? CHKD : UNC, + WPTO, + RF, RT, RA, RB, HF, HT, HA, HB, FBBFilters); + + *ReplyLen = Len; +} +VOID SendHouseKeeping(char * Reply, int * ReplyLen, char * Key) +{ + char FromList[1000]= "", ToList[1000]= "", AtList[1000] = ""; + char Line[80]; + struct Override ** Call; + + if (LTFROM) + { + Call = LTFROM; + while(Call[0]) + { + sprintf(Line, "%s, %d\r\n", Call[0]->Call, Call[0]->Days); + strcat(FromList, Line); + Call++; + } + } + if (LTTO) + { + Call = LTTO; + while(Call[0]) + { + sprintf(Line, "%s, %d\r\n", Call[0]->Call, Call[0]->Days); + strcat(ToList, Line); + Call++; + } + } + + if (LTAT) + { + Call = LTAT; + while(Call[0]) + { + sprintf(Line, "%s, %d\r\n", Call[0]->Call, Call[0]->Days); + strcat(AtList, Line); + Call++; + } + } + + *ReplyLen = sprintf(Reply, HousekeepingTemplate, + BBSName, Key, Key, Key, Key, Key, Key, Key, Key, Key, + MaintTime, MaintInterval, MaxMsgno, BidLifetime, LogAge, UserLifetime, + (DeletetoRecycleBin) ? CHKD : UNC, + (SendNonDeliveryMsgs) ? CHKD : UNC, + (SuppressMaintEmail) ? CHKD : UNC, + (GenerateTrafficReport) ? CHKD : UNC, + PR, PUR, PF, PNF, BF, BNF, NTSD, NTSF, NTSU, + FromList, ToList, AtList, + (OverrideUnsent) ? CHKD : UNC); + + return; + +} + + +VOID SendWelcomePage(char * Reply, int * ReplyLen, char * Key) +{ + int Len; + + Len = SendHeader(Reply, Key); + + Len += sprintf(&Reply[Len], Welcome, Key, WelcomeMsg, NewWelcomeMsg, ExpertWelcomeMsg, + Prompt, NewPrompt, ExpertPrompt, SignoffMsg); + *ReplyLen = Len; +} + +VOID SendFwdMainPage(char * Reply, int * RLen, char * Key) +{ + char ALIASES[16384]; + + SetMultiStringValue(AliasText, ALIASES); + + *RLen = sprintf(Reply, FwdTemplate, Key, Key, BBSName, + Key, Key, Key, Key, Key, Key, Key, Key, + Key, MaxTXSize, MaxRXSize, MaxAge, + (WarnNoRoute) ? CHKD : UNC, + (Localtime) ? CHKD : UNC, + (SendPtoMultiple) ? CHKD : UNC, + (FOURCHARCONT) ? CHKD : UNC, + ALIASES); +} + + +char TenSpaces[] = "          "; + +VOID SendUIPage(char * Reply, int * ReplyLen, char * Key) +{ + int Len, i; + + Len = SendHeader(Reply, Key); + Len += sprintf(&Reply[Len], UIHddr, Key, MailForText); + + for (i = 1; i <= GetNumberofPorts(); i++) + { + char PortNo[512]; + char PortDesc[31]; + int n; + + // Only allow UI on ax.25 ports + + struct _EXTPORTDATA * PORTVEC; + + PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromSlot(i); + + if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL + if (PORTVEC->PORTCONTROL.PROTOCOL == 10) // Pactor/WINMOR + if (PORTVEC->PORTCONTROL.UICAPABLE == 0) + continue; + + + GetPortDescription(i, PortDesc); + n = sprintf(PortNo, "Port %2d %s", GetPortNumber(i), PortDesc); + + while (PortNo[--n] == ' '); + + PortNo[n + 1] = 0; + + while (n++ < 38) + strcat(PortNo, " "); + + Len += sprintf(&Reply[Len], UILine, + (UIEnabled[i])?CHKD:UNC, i, + PortNo, + (UIDigi[i])?UIDigi[i]:"", i, + (UIMF[i])?CHKD:UNC, i, + (UIHDDR[i])?CHKD:UNC, i, + (UINull[i])?CHKD:UNC, i); + } + + Len += sprintf(&Reply[Len], UITail, Key); + + *ReplyLen = Len; +} + +VOID SendStatusPage(char * Reply, int * ReplyLen, char * Key) +{ + int Len; + char msg[1024]; + CIRCUIT * conn; + int i,n, SYSOPMsgs = 0, HeldMsgs = 0; + char Name[80]; + + SMTPMsgs = 0; + + Len = sprintf(Reply, RefreshMainPage, BBSName, BBSName, Key, Key, Key, Key, Key, Key, Key, Key); + + Len += sprintf(&Reply[Len], StatusPage, Key); + + for (n = 0; n < NumberofStreams; n++) + { + conn=&Connections[n]; + + if (!conn->Active) + { + strcpy(msg,"Idle          " + "         " + "         \r\n"); + } + else + { + { + if (conn->UserPointer == 0) + strcpy(msg,"Logging in\r\n"); + else + { + strcpy(Name, conn->UserPointer->Name); + Name[9] = 0; + + i=sprintf_s(msg, sizeof(msg), "%s%s%s%s%2d %5d\r\n", + Name, + &TenSpaces[strlen(Name) * 6], + conn->UserPointer->Call, + &TenSpaces[strlen(conn->UserPointer->Call) * 6], + conn->BPQStream, + conn->OutputQueueLength - conn->OutputGetPointer); + } + } + } + Len += sprintf(&Reply[Len], StatusLine, conn->BPQStream, msg); + } + + n = 0; + + for (i=1; i <= NumberofMessages; i++) + { + if (MsgHddrPtr[i]->status == 'N') + { + if (_stricmp(MsgHddrPtr[i]->to, SYSOPCall) == 0 || _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0) + SYSOPMsgs++; + else + if (MsgHddrPtr[i]->to[0] == 0) + SMTPMsgs++; + } + else + { + if (MsgHddrPtr[i]->status == 'H') + HeldMsgs++; + } + } + + Len += sprintf(&Reply[Len], StreamEnd, + NumberofMessages, SYSOPMsgs, HeldMsgs, SMTPMsgs); + + // If there are any active multicast transfers, display them. + + Len += MulticastStatusHTML(&Reply[Len]); + + Len += sprintf(&Reply[Len], StatusTail, + NumberofMessages, SYSOPMsgs, HeldMsgs, SMTPMsgs); + + *ReplyLen = Len; +} + +VOID SendFwdSelectPage(char * Reply, int * ReplyLen, char * Key) +{ + struct UserInfo * USER; + int i = 0; + int Len = 0; + + for (USER = BBSChain; USER; USER = USER->BBSNext) + { + Len += sprintf(&Reply[Len], "%s|", USER->Call); + } + + *ReplyLen = Len; +} + +VOID SendUserSelectPage(char * Reply, int * ReplyLen, char * Key) +{ + struct UserInfo * USER; + int i = 0, n; + int Len = 0; + struct UserInfo * users[10000]; + + // Get array of addresses + + for (n = 1; n <= NumberofUsers; n++) + { + users[i++] = UserRecPtr[n]; + if (i > 9999) break; + } + + qsort((void *)users, i, sizeof(void *), compare ); + + for (n = 0; n < NumberofUsers; n++) + { + USER = users[n]; + Len += sprintf(&Reply[Len], "%s|", USER->Call); + } + *ReplyLen = Len; +} + +int SendUserDetails(struct HTTPConnectionInfo * Session, char * Reply, char * Key) +{ + char SSID[16][16] = {""}; + char ASSID[16]; + int i, n, s, Len; + struct UserInfo * User = Session->User; + unsigned int flags = User->flags; + int RMSSSIDBits = Session->User->RMSSSIDBits; + char HiddenPass[20] = ""; + + int ConnectsIn; + int ConnectsOut; + int MsgsReceived; + int MsgsSent; + int MsgsRejectedIn; + int MsgsRejectedOut; + int BytesForwardedIn; + int BytesForwardedOut; +// char MsgsIn[80]; +// char MsgsOut[80]; +// char BytesIn[80]; +// char BytesOut[80]; +// char RejIn[80]; +// char RejOut[80]; + + i = 0; + + ConnectsIn = User->Total.ConnectsIn - User->Last.ConnectsIn; + ConnectsOut = User->Total.ConnectsOut - User->Last.ConnectsOut; + + MsgsReceived = MsgsSent = MsgsRejectedIn = MsgsRejectedOut = BytesForwardedIn = BytesForwardedOut = 0; + + for (n = 0; n < 4; n++) + { + MsgsReceived += User->Total.MsgsReceived[n] - User->Last.MsgsReceived[n]; + MsgsSent += User->Total.MsgsSent[n] - User->Last.MsgsSent[n]; + BytesForwardedIn += User->Total.BytesForwardedIn[n] - User->Last.BytesForwardedIn[n]; + BytesForwardedOut += User->Total.BytesForwardedOut[n] - User->Last.BytesForwardedOut[n]; + MsgsRejectedIn += User->Total.MsgsRejectedIn[n] - User->Last.MsgsRejectedIn[n]; + MsgsRejectedOut += User->Total.MsgsRejectedOut[n] - User->Last.MsgsRejectedOut[n]; + } + + + for (s = 0; s < 16; s++) + { + if (RMSSSIDBits & (1 << s)) + { + if (s) + sprintf(&SSID[i++][0], "%d", s); + else + SSID[i++][0] = 0; + } + } + + memset(HiddenPass, '*', strlen(User->CMSPass)); + + i = (flags >> 28); + sprintf(ASSID, "%d", i); + + if (i == 0) + ASSID[0] = 0; + + if (!UserDetailTemplate) + UserDetailTemplate = GetTemplateFromFile(4, "UserDetail.txt"); + + Len = sprintf(Reply, UserDetailTemplate, Key, User->Call, + (flags & F_BBS)?CHKD:UNC, + (flags & F_EMAIL)?CHKD:UNC, + (flags & F_PMS)?CHKD:UNC, + (flags & F_Temp_B2_BBS)?CHKD:UNC, + (flags & F_SYSOP)?CHKD:UNC, + (flags & F_POLLRMS)?CHKD:UNC, + (flags & F_Expert)?CHKD:UNC, + SSID[0], SSID[1], SSID[2], SSID[3], + (flags & F_Excluded)?CHKD:UNC, + (flags & F_HOLDMAIL)?CHKD:UNC, + (flags & F_SYSOP_IN_LM)?CHKD:UNC, + (flags & F_NOWINLINK)?CHKD:UNC, + (flags & F_NOBULLS)?UNC:CHKD, // Inverted flag + (flags & F_NTSMPS)?CHKD:UNC, + (flags & F_RMSREDIRECT)?CHKD:UNC, + (flags & F_APRSMFOR)?CHKD:UNC, ASSID, + + ConnectsIn, MsgsReceived, MsgsRejectedIn, + ConnectsOut, MsgsSent, MsgsRejectedOut, + BytesForwardedIn, FormatDateAndTime((time_t)User->TimeLastConnected, FALSE), + BytesForwardedOut, User->lastmsg, + User->Name, + User->pass, + HiddenPass, + User->Address, + User->ZIP, + User->HomeBBS); + + return Len; +} + +#ifdef WIN32 + +int ProcessWebmailWebSock(char * MsgPtr, char * OutBuffer); + +static char PipeFileName[] = "\\\\.\\pipe\\BPQMailWebPipe"; + +// Constants + +static DWORD WINAPI InstanceThread(LPVOID lpvParam) + +// This routine is a thread processing function to read from and reply to a client +// via the open pipe connection passed from the main loop. Note this allows +// the main loop to continue executing, potentially creating more threads of +// of this procedure to run concurrently, depending on the number of incoming +// client connections. +{ + DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0; + BOOL fSuccess = FALSE; + HANDLE hPipe = NULL; + char Buffer[250000]; + char OutBuffer[250000]; + char * MsgPtr; + int InputLen = 0; + int OutputLen = 0; + struct HTTPConnectionInfo Session; + char URL[100001]; + char * Context, * Method; + int n; + char token[16]= ""; + + char * ptr; + + // The thread's parameter is a handle to a pipe object instance. + + hPipe = (HANDLE) lpvParam; + + // First block is the HTTPConnectionInfo record, rest is request + + n = ReadFile(hPipe, &Session, sizeof (struct HTTPConnectionInfo), &n, NULL); + + // Get the data + + fSuccess = ReadFile(hPipe, Buffer, 250000, &InputLen, NULL); + + if (!fSuccess || InputLen == 0) + { + if (GetLastError() == ERROR_BROKEN_PIPE) + Debugprintf("InstanceThread: client disconnected.", GetLastError()); + else + Debugprintf("InstanceThread ReadFile failed, GLE=%d.", GetLastError()); + + return 1; + } + + Buffer[InputLen] = 0; + + MsgPtr = &Buffer[0]; + + if (memcmp(MsgPtr, "WMRefresh", 9) == 0) + { + OutputLen = ProcessWebmailWebSock(MsgPtr, OutBuffer); + } + else + { + // look for auth header + + const char * auth_header = "Authorization: Bearer "; + char * token_begin = strstr(MsgPtr, auth_header); + int Flags = 0; + + // Node Flags isn't currently used + + if (token_begin) + { + // Using Auth Header + + // Extract the token from the request (assuming it's present in the request headers) + + token_begin += strlen(auth_header); // Move to the beginning of the token + strncpy(token, token_begin, 13); + token[13] = '\0'; // Null-terminate the token + } + } + + strcpy(URL, MsgPtr); + + + + ptr = strstr(URL, " HTTP"); + + if (ptr) + *ptr = 0; + + Method = strtok_s(URL, " ", &Context); + + ProcessMailHTTPMessage(&Session, Method, Context, MsgPtr, OutBuffer, &OutputLen, InputLen, token); + + + WriteFile(hPipe, &Session, sizeof (struct HTTPConnectionInfo), &n, NULL); + WriteFile(hPipe, OutBuffer, OutputLen, &cbWritten, NULL); + + FlushFileBuffers(hPipe); + DisconnectNamedPipe(hPipe); + CloseHandle(hPipe); + + return 1; +} + +static DWORD WINAPI PipeThreadProc(LPVOID lpvParam) +{ + BOOL fConnected = FALSE; + DWORD dwThreadId = 0; + HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL; + +// The main loop creates an instance of the named pipe and +// then waits for a client to connect to it. When the client +// connects, a thread is created to handle communications +// with that client, and this loop is free to wait for the +// next client connect request. It is an infinite loop. + + for (;;) + { + hPipe = CreateNamedPipe( + PipeFileName, // pipe name + PIPE_ACCESS_DUPLEX, // read/write access + PIPE_TYPE_BYTE | // message type pipe + PIPE_WAIT, // blocking mode + PIPE_UNLIMITED_INSTANCES, // max. instances + 4096, // output buffer size + 4096, // input buffer size + 0, // client time-out + NULL); // default security attribute + + if (hPipe == INVALID_HANDLE_VALUE) + { + Debugprintf("CreateNamedPipe failed, GLE=%d.\n", GetLastError()); + return -1; + } + + // Wait for the client to connect; if it succeeds, + // the function returns a nonzero value. If the function + // returns zero, GetLastError returns ERROR_PIPE_CONNECTED. + + fConnected = ConnectNamedPipe(hPipe, NULL) ? + TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); + + if (fConnected) + { + // Create a thread for this client. + + hThread = CreateThread( + NULL, // no security attribute + 0, // default stack size + InstanceThread, // thread proc + (LPVOID) hPipe, // thread parameter + 0, // not suspended + &dwThreadId); // returns thread ID + + if (hThread == NULL) + { + Debugprintf("CreateThread failed, GLE=%d.\n", GetLastError()); + return -1; + } + else CloseHandle(hThread); + } + else + // The client could not connect, so close the pipe. + CloseHandle(hPipe); + } + + return 0; +} + +BOOL CreatePipeThread() +{ + DWORD ThreadId; + CreateThread(NULL, 0, PipeThreadProc, 0, 0, &ThreadId); + return TRUE; +} + +#endif + +char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + +VOID FormatTime(char * Time, time_t cTime) +{ + struct tm * TM; + TM = gmtime(&cTime); + + sprintf(Time, "%s, %02d %s %3d %02d:%02d:%02d GMT", dat[TM->tm_wday], TM->tm_mday, month[TM->tm_mon], + TM->tm_year + 1900, TM->tm_hour, TM->tm_min, TM->tm_sec); +} + + + + + + + diff --git a/.svn/pristine/2f/2fe79676747e7365bac8d7147933abf30a627270.svn-base b/.svn/pristine/2f/2fe79676747e7365bac8d7147933abf30a627270.svn-base new file mode 100644 index 0000000..54f17d3 --- /dev/null +++ b/.svn/pristine/2f/2fe79676747e7365bac8d7147933abf30a627270.svn-base @@ -0,0 +1,673 @@ +/* +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 + + +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); +} + + + + diff --git a/.svn/pristine/31/310f3819c63e4ced75d5879b2622bb44addc8eba.svn-base b/.svn/pristine/31/310f3819c63e4ced75d5879b2622bb44addc8eba.svn-base new file mode 100644 index 0000000..dea9d1e --- /dev/null +++ b/.svn/pristine/31/310f3819c63e4ced75d5879b2622bb44addc8eba.svn-base @@ -0,0 +1,6433 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// Interface to allow G8BPQ switch to use ARDOP Virtual TNC + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifdef WIN32 +//#include +#else + +// For serial over i2c support + +#ifdef MACBPQ +#define NOI2C +#endif + +#ifdef FREEBSD +#define NOI2C +#endif + +#ifndef NOI2C +#include "i2c-dev.h" +#endif +#endif + +#include "cheaders.h" + + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +int (WINAPI FAR *EnumProcessesPtr)(); + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#define APMaxStreams 10 // First is used for ARDOP, even though ARDOP uses channel 31 + +#include "bpq32.h" + +#include "tncinfo.h" + + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +static int Socket_Data(int sock, int error, int eventcode); + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +int KillPopups(struct TNCINFO * TNC); +VOID MoveWindows(struct TNCINFO * TNC); +int SendReporttoWL2K(struct TNCINFO * TNC); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +BOOL KillOldTNC(char * Path); +int ARDOPSendData(struct TNCINFO * TNC, char * Buff, int Len); +VOID ARDOPSendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue); +VOID ARDOPSendPktCommand(struct TNCINFO * TNC, int Stream, char * Buff); +VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +VOID ARDOPProcessDataPacket(struct TNCINFO * TNC, UCHAR * Type, UCHAR * Data, int Length); +void ARDOPSCSCheckRX(struct TNCINFO * TNC); +VOID ARDOPSCSPoll(struct TNCINFO * TNC); +VOID ARDOPDoTNCReinit(struct TNCINFO * TNC); +int SerialGetTCPMessage(struct TNCINFO * TNC, unsigned char * Buffer, int Len); +int SerialConnecttoTCP(struct TNCINFO * TNC); +int ARDOPWriteCommBlock(struct TNCINFO * TNC); +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); +int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len); +BOOL WriteCommBlock(struct TNCINFO * TNC); +VOID AddVirtualKISSPort(struct TNCINFO * TNC, int Port, char * buf); +int ConfigVirtualKISSPort(struct TNCINFO * TNC, char * Cmd); +void ProcessKISSBytes(struct TNCINFO * TNC, UCHAR * Data, int Len); +void ProcessKISSPacket(struct TNCINFO * TNC, UCHAR * KISSBuffer, int Len); +int ARDOPProcessDEDFrame(struct TNCINFO * TNC, UCHAR * Msg, int framelen); +int ConnecttoARDOP(struct TNCINFO * TNC); +int standardParams(struct TNCINFO * TNC, char * buf); + +#ifndef LINBPQ +BOOL CALLBACK EnumARDOPWindowsProc(HWND hwnd, LPARAM lParam); +#endif + +static char ClassName[]="ARDOPSTATUS"; +static char WindowTitle[] = "ARDOP"; +static int RigControlRow = 165; + +#define WINMOR +#define NARROWMODE 21 +#define WIDEMODE 22 + +#ifndef LINBPQ +#include +#endif + +extern int SemHeldByAPI; + +static RECT Rect; + +static int ProcessLine(char * buf, int Port); + + +BOOL ARDOPStopPort(struct PORTCONTROL * PORT) +{ + // Disable Port - close TCP Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + TNC->Streams[0].Connecting = 0; + TNC->Streams[0].Connected = 0; + TNC->Streams[0].Attached = 0; + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + if (TNC->hDevice) + { + CloseCOMPort(TNC->hDevice); + TNC->hDevice = 0; + } + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Stopped"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return TRUE; +} + +BOOL ARDOPStartPort(struct PORTCONTROL * PORT) +{ + // Restart Port - Open Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + if (TNC->ARDOPCommsMode == 'T') + { + ConnecttoARDOP(TNC); + TNC->lasttime = time(NULL);; + } + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Restarted"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + return TRUE; +} + + +int GenCRC16(unsigned char * Data, unsigned short length) +{ + // For CRC-16-CCITT = x^16 + x^12 +x^5 + 1 intPoly = 1021 Init FFFF + // intSeed is the seed value for the shift register and must be in the range 0-&HFFFF + + int intRegister = 0xffff; //intSeed + int i,j; + int Bit; + int intPoly = 0x8810; // This implements the CRC polynomial x^16 + x^12 +x^5 + 1 + + for (j = 0; j < (length); j++) + { + int Mask = 0x80; // Top bit first + + for (i = 0; i < 8; i++) // for each bit processing MS bit first + { + Bit = Data[j] & Mask; + Mask >>= 1; + + if (intRegister & 0x8000) // Then ' the MSB of the register is set + { + // Shift left, place data bit as LSB, then divide + // Register := shiftRegister left shift 1 + // Register := shiftRegister xor polynomial + + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + + intRegister = intRegister ^ intPoly; + } + else + { + // the MSB is not set + // Register is not divisible by polynomial yet. + // Just shift left and bring current data bit onto LSB of shiftRegister + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + } + } + } + + return intRegister; +} + +BOOL checkcrc16(unsigned char * Data, unsigned short length) +{ + int intRegister = 0xffff; //intSeed + int i,j; + int Bit; + int intPoly = 0x8810; // This implements the CRC polynomial x^16 + x^12 +x^5 + 1 + + for (j = 0; j < (length - 2); j++) // ' 2 bytes short of data length + { + int Mask = 0x80; // Top bit first + + for (i = 0; i < 8; i++) // for each bit processing MS bit first + { + Bit = Data[j] & Mask; + Mask >>= 1; + + if (intRegister & 0x8000) // Then ' the MSB of the register is set + { + // Shift left, place data bit as LSB, then divide + // Register := shiftRegister left shift 1 + // Register := shiftRegister xor polynomial + + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + + intRegister = intRegister ^ intPoly; + } + else + { + // the MSB is not set + // Register is not divisible by polynomial yet. + // Just shift left and bring current data bit onto LSB of shiftRegister + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + } + } + } + + if (Data[length - 2] == intRegister >> 8) + if (Data[length - 1] == (intRegister & 0xFF)) + return TRUE; + + return FALSE; +} + + +// Logging Interface. Used for Log over Serial Mode + +BOOL ARDOPOpenLogFiles(struct TNCINFO * TNC) +{ + UCHAR FN[MAX_PATH]; + + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + strlop(TNC->LogPath, 13); + strlop(TNC->LogPath, 32); + + sprintf(FN,"%s/ARDOPDebugLog_%02d%02d_%d.txt", TNC->LogPath, tm->tm_mon + 1, tm->tm_mday, TNC->Port); + TNC->DebugHandle = fopen(FN, "ab"); + sprintf(FN,"%s/ARDOPLog_%02d%02d_%d.txt", TNC->LogPath, tm->tm_mon + 1, tm->tm_mday, TNC->Port); + TNC->LogHandle = fopen(FN, "ab"); + + return (TNC->LogHandle != NULL); +} + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +void SendARDOPorPacketData(struct TNCINFO * TNC, int Stream, UCHAR * Buff, int txlen) +{ + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + + if (Stream == 0) + { + ARDOPSendData(TNC, Buff, txlen); + STREAM->bytesTXed += txlen; + WritetoTrace(TNC, Buff, txlen); + } + else + { + // Packet. Only works over Serial + + PMSGWITHLEN buffptr; + UCHAR * buffp; + + if (TNC->ARDOPCommsMode == 'T') + return; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = txlen + 1; + + buffp = &buffptr->Data[0]; + buffp[0] = 0; // CMD/Data Flag on front + + memcpy(buffp +1, Buff, txlen); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + STREAM->FramesQueued++; + + if (TNC->Timeout == 0) // if link idle can send now + ARDOPSCSPoll(TNC); + } +} + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + char * PktPort = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + // Must start ADDR or SERIAL + + ptr = strtok(NULL, " \t\n\r"); + BPQport = Port; + p_ipad = ptr; + + if (_stricmp(buf, "ADDR") == 0 || _stricmp(buf, "TCPSERIAL") == 0) + { + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + PktPort = strlop(p_port, '/'); + + if (PktPort) + TNC->PacketPort = atoi(PktPort); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + if (buf[0] == 'A') + TNC->ARDOPCommsMode = 'T'; // TCP + else + TNC->ARDOPCommsMode = 'E'; // TCPSERIAL (ESP01) + } + else if (_stricmp(buf, "SERIAL") == 0) + { + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->ARDOPSerialPort = _strdup(p_ipad); + TNC->ARDOPSerialSpeed = atoi(p_port); + + TNC->ARDOPCommsMode = 'S'; + } + else if (_stricmp(buf, "I2C") == 0) + { + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->ARDOPSerialPort = _strdup(p_ipad); + TNC->ARDOPSerialSpeed = atoi(p_port); + + TNC->ARDOPCommsMode = 'I'; + } + else + return FALSE; // Must start with ADDR + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + TNC->MaxConReq = 10; // Default + TNC->OldMode = FALSE; // Default + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if ((_memicmp(buf, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) + {} // Ignore + else +/* + if (_memicmp(buf, "PATH", 4) == 0) + { + char * Context; + p_cmd = strtok_s(&buf[5], "\n\r", &Context); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + else +*/ + + if (_memicmp(buf, "PACKETCHANNELS", 14) == 0) // Packet Channels + TNC->PacketChannels = atoi(&buf[14]); + else + if (_memicmp(buf, "MAXCONREQ", 9) == 0) // Hold Time for Busy Detect + TNC->MaxConReq = atoi(&buf[9]); + + else + if (_memicmp(buf, "STARTINROBUST", 13) == 0) + TNC->StartInRobust = TRUE; + + else + if (_memicmp(buf, "ROBUST", 6) == 0) + { + if (_memicmp(&buf[7], "TRUE", 4) == 0) + TNC->Robust = TRUE; + + strcat (TNC->InitScript, buf); + } + else + if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + else + if (_memicmp(buf, "ENABLEPACKET", 12) == 0) + { + if (TNC->PacketChannels == 0) + TNC->PacketChannels = 5; + // AddVirtualKISSPort(TNC, Port, buf); + } + +// else if (_memicmp(buf, "PAC ", 4) == 0 && _memicmp(buf, "PAC MODE", 8) != 0) +// { + // PAC MODE goes to TNC, others are parsed locally +// +// ConfigVirtualKISSPort(TNC, buf); +// } + else if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + } + + + return (TRUE); +} + + +void ARDOPThread(struct TNCINFO * TNC); +VOID ARDOPProcessDataSocketData(int port); +int ConnecttoARDOP(struct TNCINFO * TNC); +static VOID ARDOPProcessReceivedData(struct TNCINFO * TNC); +static VOID ARDOPProcessReceivedControl(struct TNCINFO * TNC); +int V4ProcessReceivedData(struct TNCINFO * TNC); +VOID ARDOPReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + + +static time_t ltime; + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + +unsigned short int compute_crc(unsigned char *buf,int len); + +VOID ARDOPSendPktCommand(struct TNCINFO * TNC, int Stream, char * Buff) +{ + // Encode and send to TNC. May be TCP or Serial + + int EncLen; + UCHAR Encoded[256]; + + if (Stream == 0) + { + if (Buff[0] == 0) // Terminal Keepalive? + return; + } + else + { + if (Buff[1] == 0) // Terminal Keepalive? + return; + } + + if (TNC->PacketSock) // Packet Data over separate TCP Connectoion? + { + // Chan, Cmd/Data, Len-1 + + int SentLen; + + EncLen = sprintf(Encoded, "%c%c%c%s\r", Buff[0], 1, (int)strlen(Buff) -2, &Buff[1]); + SentLen = send(TNC->PacketSock, Encoded, EncLen, 0); + + if (SentLen != EncLen) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Pkt Write Failed for port %d - error code = %d\r\n", TNC->PacketPort, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->PacketSock); + TNC->PacketSock = 0; + } + return; + + } + + EncLen = sprintf(Encoded, "%s\r", Buff); + SendToTNC(TNC, Stream, Encoded, EncLen); + + return; +} + + +VOID ARDOPSendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue) +{ + // Encode and send to TNC. May be TCP or Serial + + // Command Formst is C:TEXT + + int i, EncLen; + UCHAR Encoded[260]; // could get 256 byte packet + + if (Buff[0] == 0) // Terminal Keepalive? + return; + + if (memcmp(Buff, "LISTEN ", 7) == 0) + { + strcpy(TNC->WEB_MODE, &Buff[7]); + MySetWindowText(TNC->xIDC_MODE, &Buff[7]); + } + + EncLen = sprintf(Encoded, "%s\r", Buff); + + // it is possible for binary data to be dumped into the command + // handler if a session disconnects in middle of transfer + + for (i = 0; i < EncLen; i++) + { + if (Encoded[i] > 0x7f) + return; + } + + SendToTNC(TNC, 12, Encoded, EncLen); // Use streams 12 aad 13 for Host Mode Schannels 32 and 33 + return; +} + +VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + int SentLen; + + if (TNC->hDevice || (TNC->ARDOPCommsMode == 'E' && TNC->TCPSock)) + { + // Serial mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + + if (TNC->Timeout == 0) // if link idle can send now + ARDOPSCSPoll(TNC); + + return; + } + + if(TNC->ARDOPCommsMode == 'T' && TNC->TCPSock) + { + SentLen = send(TNC->TCPSock, Encoded, EncLen, 0); + + if (SentLen != EncLen) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTED = FALSE; + return; + } + } +} + +VOID SendDataToTNC(struct TNCINFO * TNC, int Streem , UCHAR * Encoded, int EncLen) +{ + int SentLen; + + if (TNC->hDevice || (TNC->ARDOPCommsMode == 'E' && TNC->TCPSock)) + { + // Serial mode. Queue to Hostmode driver + + int Stream = 13; // use 12 and 13 for scs channels 32 and 33 + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + if (TNC->Timeout == 0) // if link idle can send now + ARDOPSCSPoll(TNC); + + return; + } + + if(TNC->TCPDataSock) + { + SentLen = send(TNC->TCPDataSock, Encoded, EncLen, 0); + + if (SentLen != EncLen) + { + // WINMOR doesn't seem to recover from a blocked write. For now just reset + +// if (bytes == SOCKET_ERROR) +// { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + +// if (winerr != WSAEWOULDBLOCK) +// { + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + TNC->CONNECTED = FALSE; + return; + } + } +} + +int ARDOPSenPktdData(struct TNCINFO * TNC, int Stream, char * Buff, int Len) +{ + // Encode and send to TNC. May be TCP or Serial + + int EncLen; + + UCHAR Msg[400]; + UCHAR * Encoded = Msg; + + *(Encoded++) = Len >> 8; + *(Encoded++) = Len & 0xff; + + memcpy(Encoded, Buff, Len); + EncLen = Len + 2; + + SendDataToTNC(TNC, Stream, Msg, EncLen); + return Len; +} + + + +int ARDOPSendData(struct TNCINFO * TNC, char * Buff, int Len) +{ + // Encode and send to TNC. May be TCP or Serial + + int EncLen; + + UCHAR Msg[400]; + UCHAR * Encoded = Msg; + + *(Encoded++) = Len >> 8; + *(Encoded++) = Len & 0xff; + + memcpy(Encoded, Buff, Len); + + EncLen = Len + 2; + + SendDataToTNC(TNC, 13, Msg, EncLen); + return Len; +} + + + +VOID ARDOPChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + +// ARDOPSendCommand(TNC, "CODEC FALSE"); + + datalen = sprintf(TXMsg, "MYCALL %s", Call); + ARDOPSendCommand(TNC, TXMsg, TRUE); + +// ARDOPSendCommand(TNC, "CODEC TRUE"); +// TNC->StartSent = TRUE; + +// ARDOPSendCommand(TNC, "MYCALL", TRUE); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int bytes,txlen=0; + size_t Param; + int Stream = 0; + HKEY hKey=0; + + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + if (TNC->CONNECTED == 0) + { + // clear Q if not connected + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = (PMSGWITHLEN)Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + } + + + switch (fn) + { + case 7: + + // approx 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + + // Check session limit timer + + if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) + { + if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) + { + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); + STREAM->ReportDISC = 1; + STREAM->AttachTime = 0; + } + } + + // Check ATTACH time limit + + if (STREAM->Attached) + { + if (STREAM->AttachTime && TNC->AttachTimeLimit && time(NULL) > (TNC->AttachTimeLimit + STREAM->AttachTime)) + { + STREAM->ReportDISC = 1; + STREAM->AttachTime = 0; + } + } + + if (TNC->ARDOPCommsMode != 'T') // S I or E + { + ARDOPSCSCheckRX(TNC); + ARDOPSCSPoll(TNC); + } + + return 0; + + case 1: // poll + + // If not using serial interface, Rig Contol Frames are sent as + // ARDOP COmmand Frames. These are hex encoded + + if (TNC->ARDOPCommsMode == 'T' && TNC->BPQtoRadio_Q) + { + PMSGWITHLEN buffptr; + + buffptr = (PMSGWITHLEN)Q_REM(&TNC->BPQtoRadio_Q); + + if (TNC->CONNECTED) + { + int len = (int)buffptr->Len; + UCHAR * ptr = &buffptr->Data[0]; + char RigCommand[256] = "RADIOHEX "; + char * ptr2 = &RigCommand[9] ; + int i, j; + + if (len < 120) + { + while (len--) + { + i = *(ptr++); + j = i >>4; + j += '0'; // ascii + if (j > '9') + j += 7; + *(ptr2++) = j; + + j = i & 0xf; + j += '0'; // ascii + if (j > '9') + j += 7; + *(ptr2++) = j; + } + ARDOPSendCommand(TNC, RigCommand, FALSE); + } + } + ReleaseBuffer(buffptr); + + } + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + int CallLen; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + if (TNC->CONNECTED == 0 || + TNC->Streams[0].Connecting || + TNC->Streams[0].Connected) + { + // discard if TNC not connected or session active + + ReleaseBuffer(buffptr); + continue; + } + + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + *ptr++ = '^'; // delimit frame with ^ + + // Frame has ax.25 format header. Convert to Text + + CallLen = ConvFromAX25(Buffer + 7, Call); // Origin + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + *ptr++ = '>'; + + CallLen = ConvFromAX25(Buffer, Call); // Dest + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + Buffer += 14; // TO Digis + datalen -= 14; + + while ((Buffer[-1] & 1) == 0) + { + *ptr++ = ','; + CallLen = ConvFromAX25(Buffer, Call); + memcpy(ptr, Call, CallLen); + ptr += CallLen; + Buffer += 7; // End of addr + datalen -= 7; + } + + *ptr++ = '|'; // delimit calls + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + } + + memcpy(ptr, Buffer, datalen); + ptr += datalen; + *ptr++ = '^'; // delimit frame with ^ + + ARDOPSendData(TNC, FECMsg, (int)(ptr - FECMsg)); + TNC->FECPending = 1; + + ReleaseBuffer((UINT *)buffptr); + } + + if (TNC->Busy) // Count down to clear + { + if ((TNC->BusyFlags & CDBusy) == 0) // TNC Has reported not busy + { + TNC->Busy--; + if (TNC->Busy == 0) + MySetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } + } + + if (TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send + +// ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); // !!!! Temp bug workaround !!!! + + ARDOPSendCommand(TNC, TNC->ConnectCmd, TRUE); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[8], (int)strlen(TNC->ConnectCmd)-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + free(TNC->ConnectCmd); + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 39; + memcpy(&buffptr->Data[0], "Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + if (TNC->HeartBeat++ > 600 || (TNC->Streams[0].Connected && TNC->HeartBeat > 50)) // Every Minute unless connected + { + TNC->HeartBeat = 0; + + if (TNC->CONNECTED) + { + // Probe link + + if (TNC->Streams[0].Connecting || TNC->Streams[0].Connected) + fn =fn; //ARDOPSendCommand(TNC, "MODE", TRUE); + else + { +// if (time(NULL) - TNC->WinmorRestartCodecTimer > 300) // 5 mins +// { +// ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); +// ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); +// } +// else + ARDOPSendCommand(TNC, "STATE", TRUE); + } + } + } + + if (TNC->FECMode) + { + if (TNC->FECIDTimer++ > 6000) // ID every 10 Mins + { + if (!TNC->Busy) + { + TNC->FECIDTimer = 0; + ARDOPSendCommand(TNC, "SENDID", TRUE); + } + } + } + + // FECPending can be set if not in FEC Mode (eg beacon) + + if (TNC->FECPending) // Check if FEC Send needed + { + if (TNC->Streams[0].BytesOutstanding) //Wait for data to be queued (async data session) + { + if (TNC->Busy == 0 && TNC->GavePermission == 0) + { + TNC->FECPending = 0; + ARDOPSendCommand(TNC,"FECSEND TRUE", TRUE); + } + } + } + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + + if (TNC->PID) + KillTNC(TNC); + + RestartTNC(TNC); + } + } + + if (TNC->TimeSinceLast++ > 800) // Allow 10 secs for Keepalive + { + // Restart TNC + + if (TNC->ProgramPath && TNC->CONNECTED && 0) + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); + + KillTNC(TNC); + RestartTNC(TNC); + + TNC->TimeSinceLast = 0; + } + } + + for (Stream = 0; Stream <= APMaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + if (Stream == 0) + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); + else + { + char Cmd[32]; + sprintf(Cmd, "%cDISCONNECT", Stream); + ARDOPSendPktCommand(TNC, Stream, Cmd); + } + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("ARDOP New Attach Stream %d DEDStream %d", Stream, STREAM->DEDStream); + + STREAM->Attached = TRUE; + STREAM->AttachTime = time(NULL); + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + if (Stream == 0) + { + // If Pactor, stop scanning and take out of listen mode. + // if (Stream == 0) + // STREAM->DEDStream = 31; // Pactor + + // Stop Listening, and set MYCALL to user's call + + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + ARDOPChangeMYC(TNC, TNC->Streams[0].MyCall); + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + } + else + { + // Packet Connect + + } + } + + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + if (TNC->ARDOPCommsMode == 'T' && TNC->PortRecord->PORTCONTROL.PortStopped == 0) + ConnecttoARDOP(TNC); + TNC->lasttime = ltime; + } + } + + // See if any frames for this port + + for (Stream = 0; Stream <= APMaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->ARDOPCommsMode == 'T') + { + // For serial mode packets are taken from the queue by ARDOPSCSPoll + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + STREAM->bytesTXed += txlen; + + if (Stream == 0) + { + bytes=ARDOPSendData(TNC, data, txlen); + WritetoTrace(TNC, data, txlen); + } + else + { + if (TNC->PacketSock) + { + // Using Packet over TCP) + // Chan, Cmd/Data, Len-1 + + UCHAR Encoded[280]; + int EncLen; + int SentLen; + + EncLen = sprintf(Encoded, "%c%c%c%s\r", Stream, 0, txlen - 1, data); + SentLen = send(TNC->PacketSock, Encoded, EncLen, 0); + + if (SentLen != EncLen) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Pkt Write Failed for port %d - error code = %d\r\n", TNC->PacketPort, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->PacketSock); + TNC->PacketSock = 0; + } + } + } + ReleaseBuffer(buffptr); + } + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "No Connection to ARDOP TNC\r"); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4); + + if (STREAM->Connected) + { + STREAM->PacketsSent++; + + if (Stream == 0) + { + bytes=ARDOPSendData(TNC, &buff->L2DATA[0], txlen); + TNC->Streams[Stream].BytesOutstanding += bytes; // So flow control works - will be updated by BUFFER response + STREAM->bytesTXed += bytes; + WritetoTrace(TNC, &buff->L2DATA[0], txlen); + } + else + { + // Packet. Only works over Serial + + PMSGWITHLEN buffptr; + UCHAR * buffp; + + if (TNC->PacketSock) + { + // Using Packet over TCP) + // Chan, Cmd/Data, Len-1 + + UCHAR Encoded[280]; + int EncLen; + int SentLen; + + Encoded[0] = Stream; + Encoded[1] = 0; // Data + Encoded[2] = txlen - 1; + + memcpy(&Encoded[3], &buff->L2DATA[0], txlen); + + EncLen = txlen + 3; + SentLen = send(TNC->PacketSock, Encoded, EncLen, 0); + + // We should increment outstanding here as TCP interface can fill buffer + // very quickly + + TNC->Streams[Stream].BytesOutstanding += txlen; + + if (SentLen != EncLen) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Pkt Write Failed for port %d - error code = %d\r\n", TNC->PacketPort, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->PacketSock); + TNC->PacketSock = 0; + } + return 0; + } + + if (TNC->ARDOPCommsMode == 'T') + return 0; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 0; // No buffers, so ignore + + buffptr->Len = txlen + 1; + buffp = &buffptr->Data[0]; + + buffp[0] = 0; // CMD/Data Flag on front + + memcpy(buffp + 1, &buff->L2DATA[0], txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + STREAM->FramesQueued++; + + if (TNC->Timeout == 0) // if link idle can send now + ARDOPSCSPoll(TNC); + + return 0; + } + } + else + { + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + if (TNC->FECMode) + { + char Buffer[300]; + int len; + + // Send FEC Data + + buff->L2DATA[txlen] = 0; + len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, &buff->L2DATA[0]); + + ARDOPSendData(TNC, Buffer, len); + TNC->FECPending = 1; + + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + +// if (_memicmp(&buff[8], "PAC ", 4) == 0 && _memicmp(&buff[8], "PAC MODE", 8) != 0) +// { + // PAC MODE goes to TNC, others are parsed locally + +// buff[7 + txlen] = 0; +// ConfigVirtualKISSPort(TNC, &buff[8]); +// return 1; +// } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + + if (_memicmp(&buff->L2DATA[0], "MAXCONREQ", 9) == 0) + { + if (buff->L2DATA[9] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + // Limit connects + + int tries = atoi(&buff->L2DATA[10]); + if (tries > 10) tries = 10; + + TNC->MaxConReq = tries; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + } + + if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0) + { + if (buff->L2DATA[16] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + } + + if (_memicmp(&buff->L2DATA[0], "ARQBW ", 6) == 0) + TNC->WinmorCurrentMode = 0; // So scanner will set next value + + if (_memicmp(&buff->L2DATA[0], "CODEC TRUE", 9) == 0) + TNC->StartSent = TRUE; + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "FEC\r", 4) == 0 || _memicmp(&buff->L2DATA[0], "FEC ", 4) == 0) + { + TNC->FECMode = TRUE; + TNC->FECIDTimer = 0; +// ARDOPSendCommand(TNC,"FECRCV TRUE"); + + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "PING ", 5) == 0) + { + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, reject + + if (TNC->OverrideBusy == 0) + { + // Reject + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Ping blocked by Busy\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + } + TNC->OverrideBusy = FALSE; + } + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + if (Stream == 0) + { + sprintf(Connect, "ARQCALL %s %d", &buff->L2DATA[2], TNC->MaxConReq); + + ARDOPChangeMYC(TNC, TNC->Streams[0].MyCall); + + hookL4SessionAttempt(STREAM, &buff->L2DATA[2], TNC->Streams[0].MyCall); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + +// ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); // !!!! Temp bug workaround !!!! + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + ARDOPSendCommand(TNC, Connect, TRUE); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else + { + // Packet Connect + + sprintf(Connect, "%cPKTCALL %s %s", Stream, &buff->L2DATA[2], STREAM->MyCall); + ARDOPSendPktCommand(TNC, Stream, Connect); + } + + STREAM->Connecting = TRUE; + STREAM->ConnectTime = time(NULL); + return 0; + + } + buff->L2DATA[txlen - 1] = 0; // Remove CR + ARDOPSendCommand(TNC, &buff->L2DATA[0], TRUE); + } + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // I think we should check buffer space for all comms modes + + //if (!(TNC->ARDOPCommsMode == 'T')) + //{ + // // if serial mode must check buffer space + + { + int Queued; + int Outstanding = TNC->Streams[Stream].BytesOutstanding; + + if (Stream == 0) + Queued = TNC->Streams[13].FramesQueued; // ARDOP Native Mode Send Queue + else + Queued = TNC->Streams[Stream].FramesQueued; + + if (TNC->Mode == 'O') // OFDM version has more buffer space + { + if (Queued > 4 || Outstanding > 8500) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + else if (TNC->Mode == '3') // ARDOP3 has a bit more buffer space + { + if (Queued > 4 || Outstanding > 5000) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + else + { + if (Queued > 4 || Outstanding > 2000) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + + } + if (TNC->Streams[Stream].Attached == 0) + return TNC->CONNECTED << 8 | 1; + + return (TNC->CONNECTED << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + + case 4: // reinit7 + + return 0; + + case 5: // Close + + if (TNC->CONNECTED) + { + if (TNC->WeStartedTNC) + { + GetSemaphore(&Semaphore, 52); + ARDOPSendCommand(TNC, "CLOSE", FALSE); + FreeSemaphore(&Semaphore); + Sleep(100); + } + } + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on ARDOP"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (TNC->ARDOPCommsMode == 'T') // TCP Mode + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + } + else + { + // Serial Modes + + if (!TNC->HostMode) + return 0; // No connection so no interlock + } + + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + if (Scan->ARDOPMode[0] == 0) + { + // Not specified, so no change from previous + + return 0; + } + + if (strcmp(Scan->ARDOPMode, TNC->ARDOPCurrentMode) != 0) + { + // Mode changed + + char CMD[32]; + + if (TNC->ARDOPCurrentMode[0] == 'S') // Skip + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + +// Debugprintf("ARDOPMODE %s", Scan->ARDOPMode); + + memcpy(TNC->ARDOPCurrentMode, Scan->ARDOPMode, 6); + + if (Scan->ARDOPMode[0] == 'S') // SKIP - Dont Allow Connects + { + if (TNC->ARDOPCurrentMode[0] != 'S') + { + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + TNC->ARDOPCurrentMode[0] = 'S'; + } + + TNC->WL2KMode = 0; + return 0; + } + + if (strchr(Scan->ARDOPMode, 'F')) + sprintf(CMD, "ARQBW %sORCED", Scan->ARDOPMode); + else if (strchr(Scan->ARDOPMode, 'M')) + sprintf(CMD, "ARQBW %sAX", Scan->ARDOPMode); + else + sprintf(CMD, "ARQBW %s", Scan->ARDOPMode); // ARDOPOFDM doesn't use MAX/FORCED + + ARDOPSendCommand(TNC, CMD, TRUE); + + return 0; + } + return 0; + } + return 0; +} + +VOID ARDOPReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + ARDOPChangeMYC(TNC, TNC->NodeCall); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + + // Start Scanner + + if (TNC->DefaultRadioCmd) + { + sprintf(TXMsg, "%d %s", TNC->Port, TNC->DefaultRadioCmd); + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + } + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID ARDOPSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ + TNC->PortRecord->PORTCONTROL.PortSuspended = TRUE; + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Interlocked"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +} + +VOID ARDOPReleasePort(struct TNCINFO * TNC) +{ + TNC->PortRecord->PORTCONTROL.PortSuspended = FALSE; + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); +} + +extern char WebProcTemplate[]; +extern char sliderBit[]; + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, WebProcTemplate, TNC->Port, TNC->Port, "ARDOP Status", "ARDOP Status"); + + if (TNC->TXFreq) + Len += sprintf(&Buff[Len], sliderBit, TNC->TXOffset, TNC->TXOffset); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE, TNC->WEB_LEVELS); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Listen%s
Channel State%s   %s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +VOID * ARDOPExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + APPLCALLS * APPL; + struct TNCINFO * TNC; + char Aux[100] = "MYAUX "; + char Appl[11]; + char * TempScript; + int Stream; + + // + // Will be called once for each WINMOR port + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->PortRecord = PortEntry; + + if (TNC->LogPath) + ARDOPOpenLogFiles(TNC); + + TNC->ARDOPBuffer = malloc(8192); + TNC->ARDOPDataBuffer = malloc(16384); + TNC->ARDOPAPRS = zalloc(512); + TNC->ARDOPAPRSLen = 0; + + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_ARDOP; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + for (Stream = 1; Stream <= APMaxStreams; Stream++) + { + TNC->Streams[Stream].DEDStream = Stream; + } + + if (TNC->PacketChannels > APMaxStreams) + TNC->PacketChannels = APMaxStreams; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = ARDOPSuspendPort; + TNC->ReleasePortProc = ARDOPReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = ARDOPStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = ARDOPStopPort; + + TNC->ModemCentre = 1500; // ARDOP is always 1500 Offset + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = malloc(1000); + + strcpy(TempScript, "INITIALIZE\r"); + strcat(TempScript, "VERSION\r"); + strcat(TempScript, "CWID False\r"); + strcat(TempScript, "PROTOCOLMODE ARQ\r"); + strcat(TempScript, "ARQTIMEOUT 90\r"); +// strcat(TempScript, "ROBUST False\r"); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Set MYCALL + +// strcat(TNC->InitScript,"FECRCV True\r"); +// strcat(TNC->InitScript,"AUTOBREAK True\r"); + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); +// strcat(TNC->InitScript,"PROCESSID\r"); +// strcat(TNC->InitScript,"CODEC TRUE\r"); +// strcat(TNC->InitScript,"LISTEN TRUE\r"); + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + char * ptr; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + { + *ptr++ = ','; + *ptr = 0; + } + strcat(Aux, Appl); + } + } + + if (strlen(Aux) > 8) + { + Aux[strlen(Aux) - 1] = '\r'; + strcat(TNC->InitScript, Aux); + } + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->HostName=malloc(10); + + if (TNC->HostName != NULL) + strcpy(TNC->HostName,"127.0.0.1"); + + } + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + TNC->WEB_LEVELS =zalloc(32); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Listen", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,72,82,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_LEVELS = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 200,72,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,120,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,120,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,120,138,40,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill ARDOP TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart ARDOP TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + MoveWindows(TNC); +#endif + + if (TNC->ARDOPCommsMode == 'T') + { + Consoleprintf("ARDOP Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port)); + ConnecttoARDOP(TNC); + } + + else if (TNC->ARDOPCommsMode == 'E') + { + Consoleprintf("ARDOP TCPSerial %s:%d", TNC->HostName, htons(TNC->destaddr.sin_port)); + SerialConnecttoTCP(TNC); + } + + else if (TNC->ARDOPCommsMode == 'S') + { + TNC->PortRecord->PORTCONTROL.SerialPortName = _strdup(TNC->ARDOPSerialPort); // for common routines + Consoleprintf("ARDOP Serial %s", TNC->ARDOPSerialPort); + OpenCOMMPort(TNC, TNC->ARDOPSerialPort, TNC->ARDOPSerialSpeed, FALSE); + } + else if (TNC->ARDOPCommsMode == 'I') + { +#ifdef WIN32 + sprintf(Msg,"ARDOP I2C is not supported on WIN32 systems\n"); + WritetoConsoleLocal(Msg); +#else +#ifdef NOI2C + sprintf(Msg,"I2C is not supported on this systems\n"); + WritetoConsoleLocal(Msg); +#else + char i2cname[30]; + int fd; + int retval; + + if (strlen(TNC->ARDOPSerialPort) < 3) + { + sprintf(i2cname, "/dev/i2c-%s", TNC->ARDOPSerialPort); + TNC->ARDOPSerialPort = _strdup(i2cname); + } + else + strcpy(i2cname, TNC->ARDOPSerialPort); + + TNC->PortRecord->PORTCONTROL.SerialPortName = _strdup(i2cname); // for common routines + + Consoleprintf("ARDOP I2C Bus %s Addr %d ", i2cname, TNC->ARDOPSerialSpeed); + + // Open and configure the i2c interface + + fd = TNC->hDevice = open(i2cname, O_RDWR); + + if (fd < 0) + printf("Cannot find i2c bus %s \n", i2cname); + else + { + retval = ioctl(TNC->hDevice, I2C_SLAVE, TNC->ARDOPSerialSpeed); + + if(retval == -1) + printf("Cannot open i2c device %x\n", TNC->ARDOPSerialSpeed); + + ioctl(TNC->hDevice, I2C_TIMEOUT, 10); //100 mS + } +#endif +#endif + } + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + +VOID TNCLost(struct TNCINFO * TNC) +{ + int Stream = 0; + struct STREAMINFO * STREAM; + + if (TNC->TCPSock) + closesocket(TNC->TCPSock); + if (TNC->TCPDataSock) + closesocket(TNC->TCPDataSock); + if (TNC->PacketSock) + closesocket(TNC->PacketSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->PacketSock = 0; + TNC->CONNECTED = FALSE; + + for (Stream = 0; Stream <= APMaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + STREAM->BytesOutstanding = 0; + + if (Stream == 0) + { + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->bytesTXed - STREAM->BytesOutstanding, STREAM->bytesRXed, STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + } + + if (STREAM->Attached) + STREAM->ReportDISC = TRUE; + + STREAM->Connected = FALSE; + STREAM->Connecting = FALSE; + + } +} + + +int ConnecttoARDOP(struct TNCINFO * TNC) +{ + _beginthread(ARDOPThread, 0, (void *)TNC); + + return 0; +} + +VOID ARDOPThread(struct TNCINFO * TNC) +{ + // Opens sockets and looks for data on control and data sockets. + + // Socket may be TCP/IP or Serial + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + char * ptr1, * ptr2; + PMSGWITHLEN buffptr; + char Cmd[64]; + + if (TNC->HostName == NULL) + return; + + TNC->BusyFlags = 0; + + TNC->CONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +#ifdef WIN32 + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + if (TNC->PID == 0) + { + TNC->CONNECTING = FALSE; + return; // Not listening so no point trying to connect + } + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + HANDLE hProc; + char ExeName[256] = ""; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + + } +#endif + + +// // If we started the TNC make sure it is still running. + +// if (!IsProcess(TNC->PID)) +// { +// RestartTNC(TNC); +// Sleep(3000); +// } + + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + } + + if (TNC->TCPSock) + { + Debugprintf("ARDOP Closing Sock %d", TNC->TCPSock); + closesocket(TNC->TCPSock); + } + if (TNC->TCPDataSock) + { + Debugprintf("ARDOP Closing Sock %d", TNC->TCPDataSock); + closesocket(TNC->TCPDataSock); + } + + if (TNC->PacketSock) + { + Debugprintf("ARDOP Closing Sock %d", TNC->PacketSock); + closesocket(TNC->PacketSock); + } + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->PacketSock = 0; + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); +// setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + sprintf(Msg, "Connect Failed for ARDOP socket - error code = %d Port %d\n", + WSAGetLastError(), htons(TNC->destaddr.sin_port)); + + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + // Connect Data Port + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for ARDOP Data socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + if (TNC->PacketPort) + { + struct sockaddr_in destaddr; + + TNC->PacketSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->PacketSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP Packet socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + TNC->PacketSock = 0; + } + else + { + setsockopt(TNC->PacketSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + // setsockopt(TNC->PacketSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(TNC->PacketPort); + + if (connect(TNC->PacketSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) + { + // Connected successful + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for ARDOP Packet socket - error code = %d\r\n", err); + WritetoConsole(Msg); + TNC->Alerted = TRUE; + } + closesocket(TNC->PacketSock); + TNC->PacketSock = 0; + } + } + } + + +#ifndef LINBPQ +// FreeSemaphore(&Semaphore); + EnumWindows(EnumARDOPWindowsProc, (LPARAM)TNC); +// GetSemaphore(&Semaphore, 52); +#endif + Sleep(1000); + + TNC->LastFreq = 0; // so V4 display will be updated + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + + // Send INIT script + + // ARDOP needs each command in a separate send + + ptr1 = &TNC->InitScript[0]; + + // We should wait for first RDY. Cheat by queueing a null command + + GetSemaphore(&Semaphore, 52); + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = (PMSGWITHLEN)Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + + buffptr = (PMSGWITHLEN)GetBuff(); + buffptr->Len = 0; + C_Q_ADD(&TNC->BPQtoWINMOR_Q, buffptr); + + while (ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, 13); + if (ptr2) + *(ptr2) = 0; + + // if Date or Time command add current time + + if (_memicmp(ptr1, "DATETIME", 4) == 0) + { + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Cmd, "DATETIME %02d %02d %02d %02d %02d %02d", + tm->tm_mday, tm->tm_mon + 1, tm->tm_year - 100, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + ptr1 = Cmd; + } + + ARDOPSendCommand(TNC, ptr1, TRUE); + + if (ptr2) + *(ptr2++) = 13; // Put CR back for next time + + ptr1 = ptr2; + } + + TNC->Alerted = TRUE; + + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + + sprintf(TNC->WEB_COMMSSTATE, "Connected to ARDOP TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + FreeSemaphore(&Semaphore); + + sprintf(Msg, "Connected to ARDOP TNC Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + while (TNC->CONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TNC->TCPSock,&readfs); + FD_SET(TNC->TCPSock,&errorfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + if (TNC->PacketSock) + { + FD_SET(TNC->PacketSock,&errorfs); + FD_SET(TNC->PacketSock,&readfs); + } + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + timeout.tv_sec = 600; + timeout.tv_usec = 0; // We should get messages more frequently that this + + if (TNC->PacketSock) + ret = select((int)TNC->PacketSock + 1, &readfs, NULL, &errorfs, &timeout); + else + ret = select((int)TNC->TCPDataSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("ARDOP Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + ARDOPProcessReceivedControl(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + ARDOPProcessReceivedData(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->PacketSock, &readfs)) + { + int InputLen, Used; + UCHAR Buffer[4096]; + + InputLen = recv(TNC->PacketSock, Buffer, 4096, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + sprintf(Msg, "ARDOP Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + TNCLost(TNC); + return; + } + + // Could be more than one frame in buffer + + while (InputLen > 0) + { + GetSemaphore(&Semaphore, 52); + Used = ARDOPProcessDEDFrame(TNC, Buffer, InputLen); + FreeSemaphore(&Semaphore); + + if (Used == 0) + break; // need to check + + InputLen -= Used; + + if (InputLen > 0) + memmove(Buffer, &Buffer[Used], InputLen); + + } + + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { +Lost: + sprintf(Msg, "ARDOP Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + return; + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { + sprintf(Msg, "ARDOP Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + return; + } + + if (FD_ISSET(TNC->PacketSock, &errorfs)) + { + sprintf(Msg, "ARDOP Packet Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + return; + } + + + continue; + } + else + { + // 60 secs without data. Shouldn't happen + + sprintf(Msg, "ARDOP No Data Timeout Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + +// sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); +// GetSemaphore(&Semaphore, 52); +// MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); +// FreeSemaphore(&Semaphore); + + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + GetSemaphore(&Semaphore, 52); + ARDOPSendCommand(TNC, "CODEC FALSE", FALSE); + FreeSemaphore(&Semaphore); + + shutdown(TNC->TCPSock, SD_BOTH); + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + if (TNC->PID && TNC->WeStartedTNC) + { +// KillTNC(TNC); + } + return; + } + } + sprintf(Msg, "ARDOP Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + +#ifndef LINBPQ + +BOOL CALLBACK EnumARDOPWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[100]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowText(hwnd,wtext,99); + + if (memcmp(wtext,"ARDOP_Win ", 10) == 0) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + if (TNC->PID == ProcessId) + { + // Our Process + + sprintf (wtext, "ARDOP Virtual TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + SetWindowText(hwnd, wtext); + return FALSE; + } + } + + return (TRUE); +} +#endif + +VOID ARDOPProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + Buffer[MsgLen - 1] = 0; // Remove CR + + if (_memicmp(Buffer, "RDY", 3) == 0) + return; // RDY not used now + + if (_memicmp(Buffer, "RADIOHEX ", 9) == 0) + { + // Parameter is block to send to radio, in hex + + char c; + int val; + char * ptr1 = &Buffer[9]; + UCHAR * ptr2 = Buffer; + PMSGWITHLEN buffptr; + int Len; + + // if not configured to use PTC Rig Control, Ignore + + if (TNC->RIG->PORT == NULL || TNC->RIG->PORT->PTC == NULL) + return; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == NULL) + return; + + while (c = *(ptr1++)) + { + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(ptr2++) = val; + } + + *(ptr2) = 0; + + Len = (int)(ptr2 - Buffer); + + buffptr->Len = Len; + memcpy(&buffptr->Data[0], Buffer, Len); + C_Q_ADD(&TNC->RadiotoBPQ_Q, buffptr); + +// WriteCOMBlock(hRIGDevice, ptrParams, ptr2 - ptrParams); + return; + + } + + + if (_memicmp(Buffer, "INPUTPEAKS", 10) == 0) + { + sscanf(&Buffer[10], "%i %i", &TNC->InputLevelMin, &TNC->InputLevelMax); + sprintf(TNC->WEB_LEVELS, "Input peaks %s", &Buffer[10]); + MySetWindowText(TNC->xIDC_LEVELS, TNC->WEB_LEVELS); + return; // Response shouldn't go to user + } + + if (_memicmp(Buffer, "LISTEN NOW", 10) == 0) + return; // Response shouldn't go to user + + if (_memicmp(Buffer, "ARQCALL ", 7) == 0) + return; // Response shouldn't go to user + + if (_memicmp(Buffer, "FAULT failure to Restart Sound card", 20) == 0) + { + Debugprintf(Buffer); + + // Force a restart + + ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); + ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); + } + else + { + TNC->TimeSinceLast = 0; + } + + + if (_memicmp(Buffer, "STATE ", 6) == 0) + { + if (_memicmp(&Buffer[6], "OFFLINE", 7) == 0) + { + // Force a restart + + ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); + ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); + } + return; + } + + if (_memicmp(Buffer, "PTT T", 5) == 0) + { + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + TNC->PTTState = TRUE; + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + + TNC->PTTonTime = GetTickCount(); + + // Cancel Busy timer (stats include ptt on time in port active + + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } + return; + } + if (_memicmp(Buffer, "PTT F", 5) == 0) + { + TNC->PTTState = FALSE; + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + + if (TNC->PTTonTime) + { + TNC->PTTActivemS += (GetTickCount() - TNC->PTTonTime); + TNC->PTTonTime = 0; + } + + return; + } + + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + TNC->BusyonTime = GetTickCount(); + + MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); + + return; + } + + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->Busy) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } + + MySetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + if (_memicmp(Buffer, "TARGET", 6) == 0) + { + TNC->ConnectPending = 6; // This comes before Pending + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 1); + memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "OFFSET", 6) == 0) + { +// WritetoTrace(TNC, Buffer, MsgLen - 5); + return; + } + + if (_memicmp(Buffer, "BUFFER", 6) == 0) + { + sscanf(&Buffer[7], "%d", &STREAM->BytesOutstanding); + + if (STREAM->BytesOutstanding == 0) + { + // all sent + + if (STREAM->Disconnecting) // Disconnect when all sent + { + if (STREAM->NeedDisc == 0) + STREAM->NeedDisc = 1; + } + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->bytesTXed - STREAM->BytesOutstanding, STREAM->bytesRXed, STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + return; + } + + if (_memicmp(Buffer, "CONNECTED ", 10) == 0) + { + char Call[11]; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + int Speed = 0; + + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 1); + + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->PacketsSent = 0; + + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + // Get Speed + + ptr = strchr(&Buffer[10], ' '); + if (ptr) + { + Speed = atoi(ptr); + + if (Speed == 200) + TNC->WL2KMode = 40; + else if (Speed == 500) + TNC->WL2KMode = 41; + else if (Speed == 1000) + TNC->WL2KMode = 42; + else if (Speed == 2000) + TNC->WL2KMode = 43; + } + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + TRANSPORTENTRY * SESS; + + // Incoming Connect + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + ProcessIncommingConnectEx(TNC, Call, 0, TRUE, TRUE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + SESS->Mode = TNC->WL2KMode; + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Status); + Debugprintf("ARDOP Call from %s rejected", Call); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(SESS->L4USER, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Status); + Debugprintf("ARDOP Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12); + AppName[12] = 0; + + if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(&buffptr->Data[0], Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + // Send CTEXT First + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + int txlen = (int)(buffptr->Len); + SendARDOPorPacketData(TNC, 0, data, txlen); + } + + SendARDOPorPacketData(TNC, 0, Msg, (int)strlen(Msg)); + STREAM->NeedDisc = 100; // 10 secs + } + } + strcpy(STREAM->MyCall, TNC->TargetCall); + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + ReplyLen = sprintf(Reply, "*** Connected to %s\r", Call); + + buffptr->Len = ReplyLen; + memcpy(&buffptr->Data[0], Reply, ReplyLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + if (TNC->RIG && TNC->RIG->Valchar[0]) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, Call, '+', 'O'); + return; + } + } + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0 + || _memicmp(Buffer, "STATUS CONNECT TO", 17) == 0 + || _memicmp(Buffer, "STATUS END ARQ CALL", 19) == 0 + || _memicmp(Buffer, "STATUS ARQ TIMEOUT FROM PROTOCOL STATE", 24) == 0 +// || _memicmp(Buffer, "NEWSTATE DISC", 13) == 0 + || _memicmp(Buffer, "ABORT", 5) == 0) + + { + TNC->ConnectPending = FALSE; // Cancel Scan Lock + + if (TNC->FECMode) + return; + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "*** Failure with %s\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + if (TNC->RestartAfterFailure) + { + if (TNC->PID) + KillTNC(TNC); + + RestartTNC(TNC); + } + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + } + + WritetoTrace(TNC, Buffer, MsgLen - 1); + + // Release Session + + if (TNC->Streams[0].Connected) + { + // Create a traffic record + + hookL4SessionDeleted(TNC, STREAM); + + } + + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + if (TNC->Streams[0].Disconnecting) // + ARDOPReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + + return; + } + + if (_memicmp(Buffer, "MODE", 4) == 0) + { + strcpy(TNC->WEB_MODE, &Buffer[5]); + MySetWindowText(TNC->xIDC_MODE, &Buffer[5]); + return; + } + + if (_memicmp(Buffer, "STATUS ", 7) == 0) + { + return; + } + + if (_memicmp(Buffer, "RADIOMODELS", 11) == 0) + return; + + if (_memicmp(&Buffer[0], "PENDING", 7) == 0) // Save Pending state for scan control + { + TNC->ConnectPending = 6; // Time out after 6 Scanintervals + return; + } + + // REJECTEDBW and REJECTEDBUSY are sent to both calling and called + + if (_memicmp(&Buffer[0], "REJECTEDBUSY", 12) == 0) + { + TNC->ConnectPending = FALSE; + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Connection to %s Rejected - Channel Busy\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + } + } + + if (_memicmp(&Buffer[0], "REJECTEDBW", 10) == 0) + { + TNC->ConnectPending = FALSE; + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Connection to %s Rejected - Incompatible Bandwidth\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + } + } + + + + if (_memicmp(&Buffer[0], "CANCELPENDING", 13) == 0 + || _memicmp(&Buffer[0], "REJECTEDB", 9) == 0) //REJECTEDBUSY or REJECTEDBW + { + TNC->ConnectPending = FALSE; + return; + } + + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 1); +// return; + } + + if (_memicmp(Buffer, "NEWSTATE", 8) == 0) + { + TNC->WinmorRestartCodecTimer = time(NULL); + + MySetWindowText(TNC->xIDC_PROTOSTATE, &Buffer[9]); + strcpy(TNC->WEB_PROTOSTATE, &Buffer[9]); + + if (_memicmp(&Buffer[9], "DISC", 4) == 0) + { + TNC->DiscPending = FALSE; + TNC->ConnectPending = FALSE; + + return; + } + + if (strcmp(&Buffer[9], "ISS") == 0) // Save Pending state for scan control + TNC->TXRXState = 'S'; + else if (strcmp(&Buffer[9], "IRS") == 0) + TNC->TXRXState = 'R'; + + return; + } + + if ((_memicmp(Buffer, "FAULT Not from state FEC", 24) == 0) || (_memicmp(Buffer, "FAULT Blocked by Busy Lock", 24) == 0)) + { + if (TNC->FECMode) + { + Sleep(1000); + +// if (TNC->FEC1600) +// ARDOPSendCommand(TNC,"FECSEND 1600"); +// else +// ARDOPSendCommand(TNC,"FECSEND 500"); + return; + } + } + + if (_memicmp(Buffer, "PLAYBACKDEVICES", 15) == 0) + { + TNC->PlaybackDevices = _strdup(&Buffer[16]); + } + // Others should be responses to commands + + if (_memicmp(Buffer, "BLOCKED", 6) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 1); + return; + } + + if (_memicmp(Buffer, "OVER", 4) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 1); + return; + } + + if (_memicmp(Buffer, "PING ", 5) == 0) + { + char Call[32]; + + // Make sure not Echoed PING + + // c:ping gm8bpq-1 5 + // c:PING GM8BPQ>GM8BPQ-1 15 98 + + if (strchr(Buffer, '>') == 0) // Echoed + return; + + WritetoTrace(TNC, Buffer, MsgLen - 1); + + // Release scanlock after another interval (to allow time for response to be sent) + // ?? use cancelpending TNC->ConnectPending = 1; + + + memcpy(Call, &Buffer[5], 20); + strlop(Call, '>'); + UpdateMH(TNC, Call, '!', 'I'); + + return; + } + + if (_memicmp(Buffer, "VERSION ", 8) == 0) + { + // If contains "OFDM" or "ARDOP3" increase data session busy level + + if (strstr(&Buffer[8], "OFDM")) + TNC->Mode = 'O'; + else if (strstr(&Buffer[8], "TNC_3")) + TNC->Mode = '3'; + else + TNC->Mode = 0; + } + + if (_memicmp(Buffer, "PINGACK ", 8) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 1); + // Drop through to return to user + } + + if (_memicmp(Buffer, "CQ ", 3) == 0 && MsgLen > 10) + { + char Call[32]; + char * Loc; + + WritetoTrace(TNC, Buffer, MsgLen - 1); + + // Update MH + { + memcpy(Call, &Buffer[3], 32); + Loc = strlop(Call, ' '); + strlop(Loc, ']'); + UpdateMHEx(TNC, Call, '!', 'I', &Loc[1], TRUE); + } + // Drop through to go to user if attached but not connected + + } + // Return others to user (if attached but not connected) + + if (TNC->Streams[0].Attached == 0) + return; + + if (TNC->Streams[0].Connected || TNC->Streams[0].Connecting) + return; + + if (MsgLen > 200) + MsgLen = 200; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} %s\r", Buffer); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); +} + +static VOID ARDOPProcessReceivedData(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + + // May get several messages per packet + // May get message split over packets + + // Data has a length field + // ARQ|FEC|ERR|, 2 byte count (Hex 0001 – FFFF), binary data” + // New standard doesnt have d: + + if (TNC->DataInputLen > 16000) // Shouldnt have packets longer than this + TNC->DataInputLen=0; + + // OFDM can return large packets (up to 10160) + + // in serial mode, data has been put in input buffer by comms code + + if (TNC->ARDOPCommsMode == 'T') + { + InputLen=recv(TNC->TCPDataSock, &TNC->ARDOPDataBuffer[TNC->DataInputLen], 16384 - TNC->DataInputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + TNCLost(TNC); + return; + } + + TNC->DataInputLen += InputLen; + } +loop: + + if (TNC->OldMode) + goto OldRX; + + else + { // No D: + + // Data = check we have it all + + int DataLen = (TNC->ARDOPDataBuffer[0] << 8) + TNC->ARDOPDataBuffer[1]; // HI First + UCHAR DataType[4]; + UCHAR * Data; + + if (TNC->DataInputLen < DataLen + 2) + return; // Wait for more + + MsgLen = DataLen + 2; // Len + + memcpy(DataType, &TNC->ARDOPDataBuffer[2] , 3); + DataType[3] = 0; + + Data = &TNC->ARDOPDataBuffer[5]; + DataLen -= 3; + + ARDOPProcessDataPacket(TNC, DataType, Data, DataLen); + + // See if anything else in buffer + + TNC->DataInputLen -= MsgLen; + + if (TNC->DataInputLen == 0) + return; + + memmove(TNC->ARDOPDataBuffer, &TNC->ARDOPDataBuffer[MsgLen], TNC->DataInputLen); + goto loop; + } + +OldRX: + + if (TNC->DataInputLen < 8) + return; // Wait for more to arrive (?? timeout??) + + if (TNC->ARDOPDataBuffer[1] = ':') // At least message looks reasonable + { + if (TNC->ARDOPDataBuffer[0] == 'd') + { + // Data = check we have it all + + int DataLen = (TNC->ARDOPDataBuffer[2] << 8) + TNC->ARDOPDataBuffer[3]; // HI First +// unsigned short CRC; + UCHAR DataType[4]; + UCHAR * Data; + + if (TNC->DataInputLen < DataLen + 4) + return; // Wait for more + + MsgLen = DataLen + 4; // d: Len CRC + + memcpy(DataType, &TNC->ARDOPDataBuffer[4] , 3); + DataType[3] = 0; + Data = &TNC->ARDOPDataBuffer[7]; + DataLen -= 3; + + ARDOPProcessDataPacket(TNC, DataType, Data, DataLen); + + // See if anything else in buffer + + TNC->DataInputLen -= MsgLen; + + if (TNC->DataInputLen == 0) + return; + + memmove(TNC->ARDOPDataBuffer, &TNC->ARDOPDataBuffer[MsgLen], TNC->DataInputLen); + goto loop; + } + else + // Duff - clear input buffer + TNC->DataInputLen = 0; + + } + return; +} + + + +static VOID ARDOPProcessReceivedControl(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[4096]; + + // May get several messages per packet + // May get message split over packets + + // Commands end with CR. + + if (TNC->InputLen > 8000) // Shouldnt have packets longer than this + TNC->InputLen=0; + + // in serial mode, data has been put in input buffer by comms code + + if (TNC->ARDOPCommsMode == 'T') + { + // I don't think it likely we will get packets this long, but be aware... + + InputLen=recv(TNC->TCPSock, &TNC->ARDOPBuffer[TNC->InputLen], 8192 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + TNCLost(TNC); + return; + } + + TNC->InputLen += InputLen; + } + +loop: + + ptr = memchr(TNC->ARDOPBuffer, '\r', TNC->InputLen); + + if (ptr == 0) // CR in buffer + return; // Wait for it + + ptr2 = &TNC->ARDOPBuffer[TNC->InputLen]; + + if ((ptr2 - ptr) == 1) // CR (no CRC in new version) + { + // Usual Case - single meg in buffer + + ARDOPProcessResponse(TNC, TNC->ARDOPBuffer, TNC->InputLen); + TNC->InputLen=0; + return; + } + else + { + // buffer contains more that 1 message + + + MsgLen = TNC->InputLen - (int)(ptr2-ptr) + 1; // Include CR + + memcpy(Buffer, TNC->ARDOPBuffer, MsgLen); + + ARDOPProcessResponse(TNC, Buffer, MsgLen); + + if (TNC->InputLen < MsgLen) + { + TNC->InputLen = 0; + return; + } + memmove(TNC->ARDOPBuffer, ptr + 1, TNC->InputLen-MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + return; +} + + + +VOID ARDOPProcessDataPacket(struct TNCINFO * TNC, UCHAR * Type, UCHAR * Data, int Length) +{ + // Info on Data Socket - just packetize and send on + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int PacLen = 236; + PMSGWITHLEN buffptr; + + TNC->TimeSinceLast = 0; + + if (strcmp(Type, "IDF") == 0) + { + // Place ID frames in Monitor Window and MH + + char Call[20]; + char * Loc; + +// GM8BPQ-2:[IO68VL] +//ID:GM8BPQ-2 IO68VL : +// GM8BPQ-2:[IO68VL] +//ID:GM8BPQ-2 [IO68vl]: +//ID:HB9AVK JN47HG : + +// TX BPQ IDF GM8BPQ-2:[IO68VL] +// RX Rick IDF ID:GM8BPQ-2 [IO68vl]: + +// TX Rick IDF GM8BPQ-2:[IO68VL] +// RX BPQ IDF ID:GM8BPQ-2 IO68VL : + +//ID:GM8BPQ-2 [IO68vl] : + + Data[Length] = 0; + WritetoTrace(TNC, Data, Length); + + Debugprintf("ARDOP IDF %s", Data); + + // Loos like transmitted ID doesnt have ID: + + if (memcmp(Data, "ID:", 3) == 0) // These seem to be received ID's + { + memcpy(Call, &Data[3], 20); + Loc = strlop(Call, ' '); + strlop(Loc, ']'); + UpdateMHEx(TNC, Call, '!', 'I', &Loc[1], TRUE); + } + return; + } + + STREAM->bytesRXed += Length; + + Data[Length] = 0; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->bytesTXed - STREAM->BytesOutstanding, STREAM->bytesRXed, STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + + if (TNC->FECMode) + { + Length = (int)strlen(Data); + if (Data[Length - 1] == 10) + Data[Length - 1] = 13; + + } + + if (strcmp(Type, "FEC") == 0) + { + // May be an APRS Message + // These are delimired with ^ characters + // As they are likely to be split across + // FEC blocks they need to be recombined + + char * ptr = Data; + char * ptr1; + char * ptr2; + char c; + int Len = Length; + char Call[10] = ""; + + Debugprintf(Data); + + if (*ptr == '^' || TNC->ARDOPAPRSLen) + { + // New Packet or continuation + + while (Len--) + { + c = *(ptr++); + if (c == '^') + { + // may be start or end + + Debugprintf("Start/end of beacon Len = %d", TNC->ARDOPAPRSLen); + + if (TNC->ARDOPAPRSLen == 0) + continue; // Start + + // Validate and Process Block + + Debugprintf("beacon %s", TNC->ARDOPAPRS); + + ptr1 = TNC->ARDOPAPRS; + ptr2 = strchr(ptr1, '>'); + + if (ptr2 && (ptr2 - ptr1) < 10) + { + // Could be APRS + +// if ((memcmp(ptr2 + 1, "AP", 2) == 0) || (memcmp(ptr2 + 1, "BE", 2) == 0)) + if (1) // People using other dests + { + int APLen; + + // assume it is + + char * ptr3 = strchr(ptr2, '|'); + struct _MESSAGE * buffptr; + + if (ptr3 == 0) + { + TNC->ARDOPAPRSLen = 0; + Debugprintf("no |"); + continue; + } + + buffptr = GetBuff(); + *(ptr3++) = 0; // Terminate TO call + + APLen = TNC->ARDOPAPRSLen - (int)(ptr3 - ptr1); + + TNC->ARDOPAPRSLen = 0; + + Debugprintf("Good APRS %d Left", Len); + + // Convert to ax.25 format + + if (buffptr == 0) + continue; // No buffers, so ignore + + buffptr->PORT = TNC->Port; + + ConvToAX25(ptr1, buffptr->ORIGIN); + ConvToAX25(ptr2 + 1, buffptr->DEST); + buffptr->ORIGIN[6] |= 1; // Set end of address + buffptr->CTL = 3; + buffptr->PID = 0xF0; + memcpy(buffptr->L2DATA, ptr3, APLen); + buffptr->LENGTH = 16 + MSGHDDRLEN + APLen; + time(&buffptr->Timestamp); + + memcpy(Call,ptr1, 9); + strlop(Call, '>'); + UpdateMH(TNC, Call, '!', 'I'); + + BPQTRACE((MESSAGE *)buffptr, TRUE); + + ReleaseBuffer(buffptr); + + } + else + { + Debugprintf("Not APRS"); + TNC->ARDOPAPRSLen = 0; // in case not aprs + } + } + else + { + Debugprintf("cant be APRS"); + TNC->ARDOPAPRSLen = 0; // in case not aprs + } + continue; + } + + // Normal Char + + TNC->ARDOPAPRS[TNC->ARDOPAPRSLen++] = c; + if (TNC->ARDOPAPRSLen == 512) + TNC->ARDOPAPRSLen = 0; + } + // End of packet. + + Debugprintf("End of Packet Len %d", TNC->ARDOPAPRSLen); + } + + // FEC but not APRS. Discard if connected + + if (TNC->Streams[0].Connected) + return; + } + + WritetoTrace(TNC, Data, Length); + + // We can get messages of form ARQ [ConReq2000M: GM8BPQ-2 > OE3FQU] + // Noe (V2) [ConReq2500 > G8XXX] + + // when not connected. + + if (TNC->Streams[0].Connected == FALSE) + { + if (strcmp(Type, "ARQ") == 0) + { + if (Data[1] == '[') + { + // Log to MH + + char Call[20]; + char * ptr; + + // Add a Newline for monitoring + + Data[Length++] = 13; + Data[Length] = 0; + + ptr = strchr(Data, ':'); + + if (ptr) + { + memcpy(Call, &ptr[2], 20); + strlop(Call, ' '); + UpdateMH(TNC, Call, '!', 'I'); + } + } + } + } + + if (TNC->Streams[0].Attached == 0) + return; + + // May need to fragment + + while (Length) + { + int Fraglen = Length; + + if (Length > PACLEN) + Fraglen = PACLEN; + + Length -= Fraglen; + + buffptr = GetBuff(); + + if (buffptr == 0) + return; // No buffers, so ignore + + memcpy(&buffptr->Data[0], Data, Fraglen); + + Data += Fraglen; + + buffptr->Len = Fraglen; + + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + + return; +} + +/* +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Cmd = LOWORD(wParam); + + switch (message) + { + case WM_INITDIALOG: + { + struct TNCINFO * TNC = (struct TNCINFO * )lParam; + char * ptr1, *ptr2; + int ptr3 = 0; + char Line[1000]; + int len; + + ptr1 = TNC->CaptureDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_CAPTURE, Line); + + ptr3 = 0; + + ptr1 = TNC->PlaybackDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_PLAYBACK, Line); + + SendDlgItemMessage(hDlg, IDC_PLAYBACK, EM_SETSEL, -1, 0); + +// KillTNC(TNC); + + return TRUE; + } + + case WM_SIZING: + { + return TRUE; + } + + case WM_ACTIVATE: + +// SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + break; + + + case WM_COMMAND: + + + if (Cmd == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} +*/ + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[Stream].BytesOutstanding == 0) + { + if (Stream == 0) + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); + else + { + char Cmd[32]; + sprintf(Cmd, "%cDISCONNECT", 1); + ARDOPSendPktCommand(TNC, Stream, Cmd); + } + } +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + ARDOPSendCommand(TNC, "ABORT", TRUE); + else + { + char Cmd[32]; + sprintf(Cmd, "%cDISCONNECT", Stream); + ARDOPSendPktCommand(TNC, Stream, Cmd); + } +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + ARDOPReleaseTNC(TNC); + + if (TNC->FECMode) + { + TNC->FECMode = FALSE; + ARDOPSendCommand(TNC, "SENDID", TRUE); + } + } +} + +VOID ARDOPAbort(struct TNCINFO * TNC) +{ + ARDOPSendCommand(TNC, "ABORT", TRUE); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID ARDOPCRCStuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len) +{ + unsigned short int crc; + UCHAR StuffedMsg[500]; + int i, j; + + Msg[3] |= TNC->Toggle; + +// Debugprintf("ARDOP TX Toggle %x", TNC->Toggle); + + crc = compute_crc(&Msg[2], Len-2); + crc ^= 0xffff; + + Msg[Len++] = (crc&0xff); + Msg[Len++] = (crc>>8); + + for (i = j = 2; i < Len; i++) + { + StuffedMsg[j++] = Msg[i]; + if (Msg[i] == 170) + { + StuffedMsg[j++] = 0; + } + } + + if (j != i) + { + Len = j; + memcpy(Msg, StuffedMsg, j); + } + + TNC->TXLen = Len; + + Msg[0] = 170; + Msg[1] = 170; + + ARDOPWriteCommBlock(TNC); + + TNC->Retries = 5; +} + +VOID ARDOPExitHost(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + // Try to exit Host Mode + + TNC->TXBuffer[2] = 31; + TNC->TXBuffer[3] = 0x41; + TNC->TXBuffer[4] = 0x5; + memcpy(&TNC->TXBuffer[5], "JHOST0", 6); + + ARDOPCRCStuffAndSend(TNC, Poll, 11); + return; +} + + +VOID ARDOPDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + ARDOPExitHost(TNC); + TNC->Retries = 1; + + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + ARDOPDoTNCReinit(TNC); // See if worked + return; + } + + if (TNC->ReinitState == 3) + { + // Entering Host Mode + + // Assume ok + + TNC->HostMode = TRUE; + TNC->IntCmdDelay = 10; + + return; + } +} + + +VOID ARDOPDoTNCReinit(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + // Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode + + Poll[0] = 13; + Poll[1] = 0x1B; + TNC->TXLen = 2; + +// Debugprintf("Sending CR ESC, Mode %c", TNC->ARDOPCommsMode); + + if (TNC->ARDOPCommsMode == 'E') + { + if (TNC->TCPCONNECTED) + { + int SentLen = send(TNC->TCPSock, TNC->TXBuffer, TNC->TXLen, 0); + + if (SentLen != TNC->TXLen) + { + // ARDOP doesn't seem to recover from a blocked write. For now just reset + + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->TCPCONNECTED = FALSE; + } + } + + TNC->TNCOK = FALSE; + sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->ARDOPSerialPort); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Timeout = 20; // 2 secs + TNC->Retries = 1; + return; + } + + if (TNC->hDevice == 0) // Dont try to init if device not open + { + if (TNC->PortRecord->PORTCONTROL.PortStopped == 0) + OpenCOMMPort(TNC, TNC->ARDOPSerialPort, TNC->ARDOPSerialSpeed, TRUE); + + TNC->Timeout = 100; // 10 secs + TNC->Retries = 1; + return; + } + + TNC->TNCOK = FALSE; + sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->ARDOPSerialPort); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + + if (ARDOPWriteCommBlock(TNC) == FALSE) + { + if (TNC->hDevice) + { + Debugprintf("ARDOPWriteCommBlock Failed Mode %c", TNC->ARDOPCommsMode); + CloseCOMPort(TNC->hDevice); + } + if (TNC->ARDOPCommsMode == 'S') + { + OpenCOMMPort(TNC, TNC->ARDOPSerialPort, TNC->ARDOPSerialSpeed, TRUE); + } + else + { +#ifdef WIN32 +#else +#ifdef NOI2C +#else + char i2cname[30]; + int fd; + int retval; + + // Open and configure the i2c interface + + if (strlen(TNC->ARDOPSerialPort) < 3) + sprintf(i2cname, "/dev/i2c-%s", TNC->ARDOPSerialPort); + else + strcpy(i2cname, TNC->ARDOPSerialPort); + + fd = TNC->hDevice = open(i2cname, O_RDWR); + + if (fd < 0) + printf("Cannot find i2c bus %s\n", i2cname); + else + { + retval = ioctl(fd, I2C_SLAVE, TNC->ARDOPSerialSpeed); + + if(retval == -1) + printf("Cannot open i2c device %x\n", TNC->ARDOPSerialSpeed); + + ioctl(fd, I2C_TIMEOUT, 10); + } +#endif +#endif + } + } + TNC->Retries = 1; + } + + if (TNC->ReinitState == 1) // Forcing back to Term + TNC->ReinitState = 0; + + if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands + { + Debugprintf("DOTNCReinit %d Complete - Entering Hostmode", TNC->Port); + + TNC->TXBuffer[2] = 0; + TNC->Toggle = 0; + + memcpy(Poll, "JHOST4\r", 7); + + TNC->TXLen = 7; + ARDOPWriteCommBlock(TNC); + + // Timeout will enter host mode + + TNC->Timeout = 1; + TNC->Retries = 1; + TNC->Toggle = 0; + TNC->ReinitState = 3; // Set toggle force bit + TNC->OKToChangeFreq = 1; // In case failed whilst waiting for permission + TNC->CONNECTED = TRUE; + + return; + } +} + +VOID ARDOPProcessTermModeResponse(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + char * ptr1, * ptr2; + int len; + + if (TNC->ReinitState == 0) + { + // Testing if in Term Mode. It is, so can now send Init Commands + + TNC->InitPtr = TNC->InitScript; + TNC->ReinitState = 2; + + // Send ARDOP to make sure TNC is in a known state + + strcpy(Poll, "ARDOP\r"); + +// OpenLogFile(TNC->Port); +// WriteLogLine(TNC->Port, Poll, 7); +// CloseLogFile(TNC->Port); + + TNC->TXLen = 6; + ARDOPWriteCommBlock(TNC); + + TNC->Timeout = 60; // 6 secs + + return; + } + if (TNC->ReinitState == 2) + { + // Sending Init Commands + + // Send INIT script + + ptr1 = &TNC->InitScript[0]; + +/* GetSemaphore(&Semaphore, 52); + + while(TNC->BPQtoWINMOR_Q) + { + void ** buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + + FreeSemaphore(&Semaphore, 52); +*/ + while (ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, 13); + + if (ptr2 == 0) + break; + + len = (int)(ptr2 - ptr1) + 1; + + memcpy(Poll, ptr1, len); + + // if Date or Time command add current time + + if (_memicmp(ptr1, "DATETIME", 4) == 0) + { + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + len = sprintf(Poll, "DATETIME %02d %02d %02d %02d %02d %02d\r", + tm->tm_mday, tm->tm_mon + 1, tm->tm_year - 100, + tm->tm_hour, tm->tm_min, tm->tm_sec); + } + + // if RADIOPTTON ? or RADIOPTTOFF ? replace ? + // with correct string + + if (_memicmp(ptr1, "RADIOPTTOFF ?", 13) == 0) + { + int Len = TNC->RIG->PTTOffLen; + UCHAR * Cmd = TNC->RIG->PTTOff; + char Hex[256]; + char * hexptr = Hex; + int i, j; + while(Len--) + { + i = *(Cmd++); + j = i >>4; + j += '0'; // ascii + if (j > '9') + j += 7; + *(hexptr++) = j; + + j = i & 0xf; + j += '0'; // ascii + if (j > '9') + j += 7; + *(hexptr++) = j; + } + *(hexptr++) = 0; + + len = sprintf(Poll, "RADIOPTTOFF %s\r", Hex); + } + + if (_memicmp(ptr1, "RADIOPTTON ?", 12) == 0) + { + int Len = TNC->RIG->PTTOnLen; + UCHAR * Cmd = TNC->RIG->PTTOn; + char Hex[256]; + char * hexptr = Hex; + int i, j; + while(Len--) + { + i = *(Cmd++); + j = i >>4; + j += '0'; // ascii + if (j > '9') + j += 7; + *(hexptr++) = j; + + j = i & 0xf; + j += '0'; // ascii + if (j > '9') + j += 7; + *(hexptr++) = j; + } + *(hexptr++) = 0; + + len = sprintf(Poll, "RADIOPTTON %s\r", Hex); + } + + TNC->TXLen = len; + ARDOPWriteCommBlock(TNC); + + Sleep(50); + + TNC->Timeout = 60; // 6 secs + + if (ptr2) + *(ptr2++) = 13; // Put CR back for next time + + ptr1 = ptr2; + } + + + + // All Sent - enter Host Mode + + ARDOPDoTNCReinit(TNC); // Send Next Command + return; + } +} + +int ARDOPProcessDEDFrame(struct TNCINFO * TNC, UCHAR * Msg, int framelen) +{ + PMSGWITHLEN buffptr; + UCHAR * Buffer; // Data portion of frame + unsigned int Stream = 0, RealStream; + + if (Msg[0] == 255 && Msg[1] == 255) + { + goto tcpHostFrame; + } + + if (TNC->HostMode == 0) + return framelen; + + // Check toggle + +// Debugprintf("ARDOP RX Toggle = %x MSG[3] = %x", TNC->Toggle, Msg[3]); + + if (TNC->Toggle != (Msg[3] & 0x80)) + { + Debugprintf("ARDOP PTC Seq Error"); + return framelen; // should check if retrying + } + + // Any valid frame is an ACK + + TNC->Toggle ^= 0x80; // update toggle + + TNC->Timeout = 0; + + Msg[3] &= 0x7f; // remove toggle + + if (TNC->TNCOK == FALSE) + { + // Just come up + + TNC->TNCOK = TRUE; + sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->ARDOPSerialPort); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + +tcpHostFrame: + + Stream = RealStream = Msg[2]; + + // See if Poll Reply or Data + + if (Msg[3] == 1 && Stream > 0 && Stream <= APMaxStreams) + { + // Ardop Packet Data. Probably Buffer Status + + int Len = (int)strlen(&Msg[4]); + + if (memcmp(&Msg[4], "Queued ", 7) == 0) + { + int Count = atoi(&Msg[11]); + TNC->Streams[Stream].BytesOutstanding = Count; + } + + return Len + 5; + } + + if (Stream == 32) // Native Mode Command Response + { + if (Msg[3] == 1) // Null terminated response + { + int Len = (int)strlen(&Msg[4]) + 1; + ARDOPProcessResponse(TNC, &Msg[4], Len); + return Len + 5; + } + if (Msg[3] == 0) // Success, no response + return 5; + + if (Msg[3] == 7) // Status Reports + { + int Len = Msg[4] + 1; + + // may have more than one message in buffer, + // so pass to unpack + + memcpy(&TNC->ARDOPBuffer[TNC->InputLen],&Msg[5], Len); + TNC->InputLen += Len; + + ARDOPProcessReceivedControl(TNC); + return Len + 5; + } + return 0; + } + if (Stream == 33) // Native Mode Data + { + // May be connected, FEC or ID + + if (Msg[3] == 1) // Null terminated response + return 0; + + if (Msg[3] == 0) // Success, no response + return 0; + + if (Msg[3] == 7) // Data + { + int Len = Msg[4] + 1; + + // may have more than one message in buffer, + // so pass to unpack + + memcpy(&TNC->ARDOPDataBuffer[TNC->DataInputLen],&Msg[5], Len); + TNC->DataInputLen += Len; + + ARDOPProcessReceivedData(TNC); + return 0; + } + return 0; + + } + + if (Stream == 34) // Native Mode Log + { + int Len = Msg[4] + 1; + char timebuf[32]; + char Line[256]; + char * ptr, * ptr2; +#ifdef WIN32 + SYSTEMTIME st; +#else + struct timespec tp; + int hh; + int mm; + int ss; +#endif + if (TNC->LogHandle == 0 && TNC->DebugHandle == 0) + return 0; + +#ifdef WIN32 + GetSystemTime(&st); + sprintf(timebuf, "%02d:%02d:%02d.%03d ", + st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); +#else + clock_gettime(CLOCK_REALTIME, &tp); + ss = tp.tv_sec % 86400; // Secs int day + hh = ss / 3600; + mm = (ss - (hh * 3600)) / 60; + ss = ss % 60; + + sprintf(timebuf, "%02d:%02d:%02d.%03d ", + hh, mm, ss, (int)tp.tv_nsec/1000000); +#endif + // Messages may be blocked with top bit of first byte set + + ptr = &Msg[5]; + ptr2 = Line; + + while(Len--) + { + int c = (*ptr++); + + if (c & 0x80) + { + // New Message + + c &= 0x7f; + + *ptr2 = 0; + fputs(Line, TNC->DebugHandle); // rest of last line + if (TNC->LastLogType < '7') + fputs(Line, TNC->LogHandle); + + TNC->LastLogType = c; + + // Timestamp new message and add type + + fputs(timebuf, TNC->DebugHandle); + fputc(c, TNC->DebugHandle); + fputc(' ', TNC->DebugHandle); + if (TNC->LastLogType < '7') + { + fputs(timebuf, TNC->LogHandle); + fputc(c, TNC->LogHandle); + fputc(' ', TNC->LogHandle); + } + ptr2 = Line; + } + else + *(ptr2++) = c; + } + *ptr2 = 0; + + fputs(Line, TNC->DebugHandle); // rest of last line + fflush(TNC->DebugHandle); + + if (TNC->LastLogType < '7') + { + fputs(Line, TNC->LogHandle); + fflush(TNC->LogHandle); + } + + return 0; + } + + if (Msg[3] == 4 || Msg[3] == 5) + { + MESSAGE Monframe; + + // Packet Monitor Data. + // DED Host uses 4 and 5 as Null Terminated ascii encoded header + // and 6 byte count format info. + + // In ARDOP Native mode I pass both header and data + // in byte count raw format, as there is no point + // in ascii coding then converting back to pass to + // monitor code + + // The First byte is TX/RX Flag + + int Len = Msg[4]; // Would be +1 but first is Flag + + memset(&Monframe, 0, sizeof(Monframe)); + + memcpy(Monframe.DEST, &Msg[6], Len); + Monframe.LENGTH = Len + MSGHDDRLEN; + Monframe.PORT = TNC->Port | Msg[5]; // or in TX Flag + + time(&Monframe.Timestamp); + + if (Msg[3] == 5) // More to come + { + // Save the header till the data arrives + + if (TNC->Monframe) + free(TNC->Monframe); + + TNC->Monframe = malloc(sizeof(MESSAGE)); + + if (TNC->Monframe) + memcpy(TNC->Monframe, &Monframe, sizeof(MESSAGE)); + + return Len + 6; + } + + BPQTRACE((MESSAGE *)&Monframe, TRUE); + return Len + 6; + } + + if (Msg[3] == 6) + { + // Second part of I or UI + + int Len = Msg[4] + 1; + + MESSAGE Monframe; + UCHAR * ptr = (UCHAR *)&Monframe; + + memset(&Monframe, 0, sizeof(Monframe)); + + if (TNC->Monframe) + { + memcpy(&Monframe, TNC->Monframe, TNC->Monframe->LENGTH); + memcpy(&ptr[TNC->Monframe->LENGTH], &Msg[5], Len); + + Monframe.LENGTH += Len; + + time(&Monframe.Timestamp); + BPQTRACE((MESSAGE *)&Monframe, TRUE); + + free(TNC->Monframe); + TNC->Monframe = NULL; + } + return Len + 6; + } + + if (Msg[3] == 0) + { + // Success - Nothing Follows + + if (Stream < 32) + if (TNC->Streams[Stream].CmdSet) + return 4; // Response to Command Set + + if ((TNC->TXBuffer[3] & 1) == 0) // Data + return 4; + + if (Stream > 0 && Stream <= APMaxStreams) + { + // Packet Response + + return 4; + } + + // If the response to a Command, then we should convert to a text OK" for forward scripts, etc + + if (TNC->TXBuffer[5] == 'G') // Poll + return 4; + + if (TNC->TXBuffer[5] == 'C') // Connect - reply we need is async + return 4; + + if (TNC->TXBuffer[5] == 'L') // Shouldnt happen! + return 4; + + if (TNC->TXBuffer[5] == '#') // Shouldnt happen! + return 4; + + if (TNC->TXBuffer[5] == '%' && TNC->TXBuffer[6] == 'W') // Scan Control - Response to W1 + if (TNC->InternalCmd) + return 4; // Just Ignore + + if (TNC->TXBuffer[5] == 'J') // JHOST + { + if (TNC->TXBuffer[10] == '0') // JHOST0 + { + TNC->Timeout = 1; // + return 4; + } + } + + if (TNC->Streams[Stream].Connected) + return 4; + + buffptr = GetBuff(); + + if (buffptr == NULL) return 4; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0],"ARDOP} Ok\r"); + + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); +// C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 4; + } + + if (Msg[3] > 0 && Msg[3] < 6) + { + // Success with message - null terminated + + UCHAR * ptr; + int len; + + if (Msg[2] == 0xff) // General Poll Response + { + UCHAR * Poll = TNC->TXBuffer; + UCHAR Chan = Msg[4] - 1; + + if (Chan == 255) // Nothing doing + return 0; + + if (Msg[5] != 0) + { + // More than one to poll - save the list of channels to poll + + strcpy(TNC->NexttoPoll, &Msg[5]); + } + + // Poll the channel that had data + + Poll[2] = Chan; // Channel + Poll[3] = 0x1; // Command + + if (Chan == 254) // Status - Send Extended Status (G3) + { + Poll[4] = 1; // Len-1 + Poll[5] = 'G'; // Extended Status Poll + Poll[6] = '3'; + } + else + { + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + } + + ARDOPCRCStuffAndSend(TNC, Poll, Poll[4] + 6); + TNC->InternalCmd = FALSE; + + return 0; + } + + Buffer = &Msg[4]; + + ptr = strchr(Buffer, 0); + + if (ptr == 0) + return 0; + + *(ptr++) = 13; + *(ptr) = 0; + + len = (int)(ptr - Buffer); + + if (len > 256) + return 0; + + if (Stream > 0 && Stream <= APMaxStreams) + { + // Packet Mode Response. Could be command response or status. + + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + PMSGWITHLEN buffptr; + + if (strstr(Buffer, "Incoming")) + { + // incoming call. Check which application it is for + + char Call[11]; + char TargetCall[11] = ""; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + TRANSPORTENTRY * SESS; + + Buffer[len-1] = 0; + WritetoTrace(TNC, Buffer, len); + + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->PacketsSent = 0; + + memcpy(Call, &Buffer[19], 10); + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + ptr = strstr(&Buffer[19], " to "); + if (ptr) + { + memcpy(TargetCall, ptr + 4, 10); + ptr = strchr(TargetCall, 13); + if (ptr) + *ptr = 0; + } + + ProcessIncommingConnectEx(TNC, Call, Stream, TRUE, FALSE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + TidyClose(TNC, 0); + Debugprintf("ARDOP Packet Call from %s rejected", Call); + return 0; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(SESS->L4USER, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + TidyClose(TNC, 0); + Debugprintf("ARDOP Packet Call from %s not in ValidCalls - rejected", Call); + return 0; + } + } + } + + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + char ApplCmd[80]; + int Len = sprintf(ApplCmd, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return len + 5; // No buffers, so ignore + } + + buffptr->Len = Len; + memcpy(&buffptr->Data[0], ApplCmd, Len); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + // Send CTEXT First + + if (STREAM->BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + int txlen = (int)buffptr->Len; + SendARDOPorPacketData(TNC, Stream, data, txlen); + ReleaseBuffer(buffptr); + } + + SendARDOPorPacketData(TNC, Stream, Msg, (int)strlen(Msg)); + STREAM->NeedDisc = 100; // 10 secs + } + } + + STREAM->Connected = TRUE; + return len + 5; + } + + // Send to host + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) + return len + 5; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", Buffer); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + // Unless Connected response close session + + STREAM->Connecting = FALSE; + + if (strstr(Buffer, "Connected")) + STREAM->Connected = TRUE; + else + if (strstr(Buffer, "Failure with")) + STREAM->ReportDISC = 10; // Gives time for failure message to display + else + if (strstr(Buffer, "Busy from")) + STREAM->ReportDISC = 10; // Gives time for failure message to display + else + if (strstr(Buffer, "Disconnected from")) + { + if (STREAM->Disconnecting) // We requested disconnect + { + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->Disconnecting = FALSE; + } + else + { + STREAM->Connected = 0; + STREAM->ReportDISC = 10; + } + } + else + STREAM->NeedDisc = 10; + + return len + 5; + } + + // See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc + + if (Msg[3] < 3) // 1 or 2 - Success or Fail + { + return 0; + } + + if (Msg[3] == 3) // Status + { + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + return 0; + } + + // 1, 2, 4, 5 - pass to Appl + + return 0; + } + + if (Msg[3] == 6) + { + return 0; + } + + if (Msg[3] == 7) // Data + { + if (Stream > 0 && Stream <= APMaxStreams) + { + // Packet Response + + int len = Msg[4] + 1; + + if (TNC->Streams[Stream].Connected == 0) + return len + 5; + + buffptr = GetBuff(); + + if (buffptr == NULL) return 0; // No buffers, so ignore + + buffptr->Len = len; + memcpy((UCHAR *)&buffptr->Data[0], &Msg[5], buffptr->Len); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return len + 5; + } + + if (Stream == 32) // Command string + { + int Len = Msg[4] + 1; + + ARDOPProcessResponse(TNC, &Msg[5], Len); + + return 0; + } + + if (Msg[2] == 0xfe) // Status Poll Response + { + return 0; + } + + if (Msg[2] == 248) // Log Message + { + // Monitor Data - Length format + // first 4 bytes contain a 32 bits long timestamp. + // That timestamp holds the number of seconds that elapsed since date 01.01.2000 at 00:00:00. + // The MS byte is sent first. The timestamp can be corrected to the usual C timestamp (seconds + //since 01.01.1970, 00:00:00) simply by adding 946684800 (seconds) to it. + // Teminated with LF + + int datalen = Msg[4] + 1; + time_t timestamp = (Msg[5] << 24) + (Msg[6] << 16) + + (Msg[7] << 8) + Msg[8] + 946684800; + char c; + char timebuf[32] = "HH:MM:SS.MMM"; + struct tm * tm; + + if (TNC->LogHandle == 0 || TNC->DebugHandle == 0) + return 0; + + tm = gmtime(×tamp); + + sprintf(timebuf, "%02d:%02d:%02d. ", + tm->tm_hour, tm->tm_min, tm->tm_sec); + + // ARDOP Messages have a millisec time in first 3 bytes + // and a log type in 4th + + c = Msg[12]; // Type + + memcpy(&timebuf[9], &Msg[9], 3); // copy millisecs + fputs(timebuf, TNC->DebugHandle); + fputc(c, TNC->DebugHandle); + fputc(' ', TNC->DebugHandle); + fwrite(&Msg[13], 1, datalen - 8, TNC->DebugHandle); + fflush(TNC->DebugHandle); + + if (c < '7') + { + // All types below debug go to log file + + fputs(timebuf, TNC->LogHandle); + fputc(c, TNC->LogHandle); + fputc(' ', TNC->LogHandle); + fwrite(&Msg[13], 1, datalen - 8, TNC->LogHandle); + fflush(TNC->LogHandle); + } + return 0; + } + + if (Msg[2] == 253) // Rig Port Response + { + // Queue for Rig Control Driver + + int datalen = Msg[4] + 1; + PMSGWITHLEN buffptr; + + // if not configured to use PTC Rig Control, Ignore + + if (TNC->RIG->PORT == NULL || TNC->RIG->PORT->PTC == NULL) + return 0; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = datalen; + memcpy(&buffptr->Data[0], &Msg[5], datalen); + C_Q_ADD(&TNC->RadiotoBPQ_Q, buffptr); + } + return 0; + } + + if (Msg[2] == 250) // KISS + { + // Pass to KISS Code + + int datalen = Msg[4] + 1; + void ** buffptr = NULL; + + ProcessKISSBytes(TNC, &Msg[5], datalen); + return datalen + 5; + } + + return 0; + } + return 0; +} + +void ARDOPSCSCheckRX(struct TNCINFO * TNC) +{ + int Length, Len = 0; + unsigned short crc; + char UnstuffBuffer[500]; + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + if (TNC->ARDOPCommsMode == 'I') + { + unsigned char Buffer[33]; + BOOL Error; + int gotThisTime = 0, i2clen; + + // i2c mode always returns as much as requested or error + // First two bytes of block are length + +// if (TNC->hDevice < 0) +// return; + + while ((TNC->RXLen + Len) < 460) + { + i2clen = ReadCOMBlockEx(TNC->hDevice, Buffer, 33, &Error); + + if (i2clen < 33 || i2clen == 5) + return; + + if (Error) + { + Debugprintf("ARDOP i2c returned %d bytes Error %d", i2clen, Error); + return; + } + gotThisTime = Buffer[0]; + + + if (gotThisTime == 0) + { + if (Len) + break; // Something to process + + return; // No More + } + +// if (gotThisTime != 7) +// Debugprintf("ARDOP i2c Len %d RXL %d %x %x %x %x %x %x %x %x %x %x %x %x", +// gotThisTime, TNC->RXLen + Len, +// Buffer[0], Buffer[1], Buffer[2], Buffer[3], +// Buffer[4], Buffer[5], Buffer[6], Buffer[7], +// Buffer[8], Buffer[9], Buffer[10], Buffer[11]); + + memcpy(&TNC->RXBuffer[TNC->RXLen + Len], &Buffer[1], gotThisTime); + + Len += gotThisTime; + + if (Buffer[0] < 32) + break; // no more + } + } + + else if (TNC->ARDOPCommsMode =='E') //Serial over TCP + Len = SerialGetTCPMessage(TNC, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + else + if (TNC->hDevice) + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + // DED mode doesn't have an end of frame delimiter. We need to know if we have a full frame + + // Fortunately this is a polled protocol, so we only get one frame at a time + + // If first char != 170, then probably a Terminal Mode Frame. Wait for CR on end + + // If first char is 170, we could check rhe length field, but that could be corrupt, as + // we haen't checked CRC. All I can think of is to check the CRC and if it is ok, assume frame is + // complete. If CRC is duff, we will eventually time out and get a retry. The retry code + // can clear the RC buffer + + if (TNC->RXBuffer[0] != 170) + { + // Char Mode Frame I think we need to see cmd: on end + + // If we think we are in host mode, then to could be noise - just discard. + + if (TNC->HostMode) + { + TNC->RXLen = 0; // Ready for next frame + return; + } + + TNC->RXBuffer[TNC->RXLen] = 0; + +// if (TNC->Streams[Stream].RXBuffer[TNC->Streams[Stream].RXLen-2] != ':') + + if (strlen(TNC->RXBuffer) < TNC->RXLen) + TNC->RXLen = 0; + + if ((strstr(TNC->RXBuffer, "cmd: ") == 0) && (strstr(TNC->RXBuffer, "pac: ") == 0)) + + return; // Wait for rest of frame + + // Complete Char Mode Frame + +// OpenLogFile(TNC->Port); +// WriteLogLine(TNC->Port, TNC->RXBuffer, (int)strlen(TNC->RXBuffer)); +// CloseLogFile(TNC->Port); + + TNC->RXLen = 0; // Ready for next frame + + if (TNC->HostMode == 0) + { + // We think TNC is in Terminal Mode + ARDOPProcessTermModeResponse(TNC); + return; + } + // We thought it was in Host Mode, but are wrong. + + TNC->HostMode = FALSE; + return; + } + + if (TNC->HostMode == FALSE) + { + TNC->RXLen = 0; // clear input and wait for char mode response + return; + } + + + // Receiving a Host Mode frame + + if (Length < 6) // Minimum Frame Sise + return; + + if (TNC->RXBuffer[2] == 170) + { + // Retransmit Request + + TNC->RXLen = 0; + return; // Ignore for now + } + + // Can't unstuff into same buffer - fails if partial msg received, and we unstuff twice + + + Length = Unstuff(&TNC->RXBuffer[2], &UnstuffBuffer[2], Length - 2); + + if (Length == -1) + { + // Unstuff returned an errors (170 not followed by 0) + + TNC->RXLen = 0; + return; // Ignore for now + } + + crc = compute_crc(&UnstuffBuffer[2], Length); + + if (crc == 0xf0b8) // Good CRC + { + TNC->RXLen = 0; // Ready for next frame + UnstuffBuffer[0] = 0; // Make sure not seen as TCP Frame + ARDOPProcessDEDFrame(TNC, UnstuffBuffer, Length); + + // If there are more channels to poll (more than 1 entry in general poll response, + // and link is not active, poll the next one + + if (TNC->Timeout == 0) + { + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->NexttoPoll[0]) + { + UCHAR Chan = TNC->NexttoPoll[0] - 1; + + memmove(&TNC->NexttoPoll[0], &TNC->NexttoPoll[1], 19); + + Poll[2] = Chan; // Channel + Poll[3] = 0x1; // Command + + if (Chan == 254) // Status - Send Extended Status (G3) + { + Poll[4] = 1; // Len-1 + Poll[5] = 'G'; // Extended Status Poll + Poll[6] = '3'; + } + else + { + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + } + + ARDOPCRCStuffAndSend(TNC, Poll, Poll[4] + 6); + TNC->InternalCmd = FALSE; + + return; + } + else + { + // if last message wasn't a general poll, send one now + + if (TNC->PollSent) + return; + + TNC->PollSent = TRUE; + + // Use General Poll (255) + + Poll[2] = 255 ; // Channel + Poll[3] = 0x1; // Command + + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + + ARDOPCRCStuffAndSend(TNC, Poll, 6); + TNC->InternalCmd = FALSE; + } + } + return; + } + + // Bad CRC - assume incomplete frame, and wait for rest. If it was a full bad frame, timeout and retry will recover link. + + return; +} + +VOID ARDOPSCSPoll(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + int Stream = 0; + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + TNC->Retries--; + + if(TNC->Retries >= 0) + { + if (TNC->HostMode) + Debugprintf("ARDOP Timeout - Retransmit PTC Block"); + ARDOPWriteCommBlock(TNC); // Retransmit Block + return; + } + + // Retried out. + + if (TNC->HostMode == 0) + { + ARDOPDoTermModeTimeout(TNC); + return; + } + + // Retried out in host mode - Clear any connection and reinit the TNC + + Debugprintf("ARDOP - Link to TNC Lost"); + TNC->TNCOK = FALSE; + + sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // Clear anything from UI_Q + + while (TNC->PortRecord->UI_Q) + { + void ** buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + + TNC->HostMode = 0; + TNC->ReinitState = 0; + TNC->CONNECTED = FALSE; + + // Disconenct any attached sessions + + + for (Stream = 0; Stream <= APMaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected + { + TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + } + } + } + + if (TNC->Timeout) + return; // We've sent something + + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (!TNC->HostMode) + { + ARDOPDoTNCReinit(TNC); + return; + } + + TNC->PollSent = FALSE; + + if (TNC->TNCOK && TNC->BPQtoRadio_Q) + { + int datalen; + PMSGWITHLEN buffptr; + + buffptr = (PMSGWITHLEN)Q_REM(&TNC->BPQtoRadio_Q); + datalen = (int)buffptr->Len; + Poll[2] = 253; // Radio Channel + Poll[3] = 0; // Data? + Poll[4] = datalen - 1; + memcpy(&Poll[5], &buffptr->Data[0], datalen); + + ReleaseBuffer(buffptr); + ARDOPCRCStuffAndSend(TNC, Poll, datalen + 5); + return; + } + + for (Stream = 0; Stream < 14; Stream++) // Priority to commands + { + if (TNC->TNCOK && TNC->Streams[Stream].BPQtoPACTOR_Q) + { + int datalen; + PMSGWITHLEN buffptr; + char * Buffer; + + buffptr=Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + TNC->Streams[Stream].FramesQueued--; + + datalen = (int)buffptr->Len; + Buffer = &buffptr->Data[0]; // Data portion of frame + + Poll[3]= 0; + + if (Stream > 11) + Poll[2] = Stream + 20; // 12 and 13 to Channels 32 and 33 + else + if (Stream == 0) + Poll[2] = 33; + else + { + // Packet Frame + + Poll[2] = Stream; + Poll[3] = Buffer[0]; // First Byte is CMD/Data FLag + datalen--; + Buffer++; + } + + Poll[4] = datalen - 1; + + memcpy(&Poll[5], Buffer, datalen); + + ReleaseBuffer(buffptr); + + ARDOPCRCStuffAndSend(TNC, Poll, datalen + 5); + return; + } + } + +//0x04421CB0 aa aa 21 00 07 00 06 48 65 6c 6c 6f 0d c8 3e 38 42 50 51 2d 32 20 35 0d 4a 8a 4d 38 42 50 51 ªª!....Hello.È>8BPQ-2 5.JŠM8BPQ +//0x04421CCF 2d 31 30 2c 47 4d 38 42 50 51 2d 35 2c 47 4d 38 42 50 51 2c 47 4d 38 42 50 51 2d 31 35 0d 00 -10,GM8BPQ-5,GM8BPQ,GM8BPQ-15.. +//0x04421CEE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ............................... + + + if (TNC->TNCOK && TNC->KISSTX_Q) + { + int datalen; + PMSGWITHLEN buffptr; + + buffptr = (PMSGWITHLEN)Q_REM(&TNC->KISSTX_Q); + datalen = (int)buffptr->Len; + Poll[2] = 250; // KISS Channel + Poll[3] = 0; // Data + Poll[4] = datalen - 1; + memcpy(&Poll[5], &buffptr->Data[0], datalen); + + ReleaseBuffer(buffptr); + ARDOPCRCStuffAndSend(TNC, Poll, datalen + 5); + return; + } + + TNC->PollSent = TRUE; + + // Use General Poll (255) + + Poll[2] = 255 ; // Channel + Poll[3] = 0x1; // Command + + if (TNC->ReinitState == 3) + { + TNC->ReinitState = 0; + Poll[3] = 0x41; + } + + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + + ARDOPCRCStuffAndSend(TNC, Poll, 6); + TNC->InternalCmd = FALSE; + + return; + +} + +// ARDOP Serial over TCP Routines + +// Probably only for Teensy with ESP01. Runs SCS Emulator over a TCP Link + + +VOID SerialConnecttoTCPThread(struct TNCINFO * TNC); + +int SerialConnecttoTCP(struct TNCINFO * TNC) +{ + _beginthread(SerialConnecttoTCPThread, 0, (void *)TNC); + + return 0; +} + +VOID SerialConnecttoTCPThread(struct TNCINFO * TNC) +{ + char Msg[255]; + int i; + u_long param = 1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + SOCKADDR_IN sinx; + int addrlen=sizeof(sinx); + + if (TNC->HostName == NULL) + return; + + Sleep(5000); // Allow init to complete + + while(1) + { + if (TNC->TCPCONNECTED) + { + Sleep(57000); + continue; + } + + + TNC->BusyFlags = 0; + + TNC->CONNECTING = TRUE; + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + Sleep (57000); + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + if (TNC->TCPSock) + { + Debugprintf("ARDOP Closing Sock %d", TNC->TCPSock); + closesocket(TNC->TCPSock); + } + + TNC->TCPSock = 0; + TNC->TCPSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); +// setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + + TNC->TCPCONNECTED = TRUE; + ioctl(TNC->TCPSock, FIONBIO, ¶m); + Debugprintf("ARDOP TCPSerial connected, Socket %d", TNC->TCPSock); + } + else + { + if (TNC->Alerted == FALSE) + { + sprintf(Msg, "Connect Failed for ARDOP socket - error code = %d Port %d\n", + WSAGetLastError(), htons(TNC->destaddr.sin_port)); + + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + Sleep (57000); // 1 Mins + } + } +} + +int SerialGetTCPMessage(struct TNCINFO * TNC, unsigned char * Buffer, int Len) +{ + int index=0; + ULONG param = 1; + + if (TNC->TCPCONNECTED) + { + int InputLen; + + // Poll TCP COnnection for data + + // May have several messages per packet, or message split over packets + + InputLen = recv(TNC->TCPSock, Buffer, Len, 0); + + if (InputLen < 0) + { + int err = WSAGetLastError(); + + if (err == 10035 || err == 11) + { + InputLen = 0; + return 0; + } + Debugprintf("ARDOP Serial TCP RX Error %d received for socket %d", err, TNC->TCPSock); + + TNC->TCPCONNECTED = 0; + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + return 0; + } + + if (InputLen > 0) + return InputLen; + else + { + Debugprintf("ARDOP Serial TCP Close received for socket %d", TNC->TCPSock); + + TNC->TCPCONNECTED = 0; + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + return 0; + } + } + + return 0; +} + +int ARDOPWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->ARDOPCommsMode == 'E') + { + if (TNC->TCPCONNECTED) + { + int SentLen = send(TNC->TCPSock, TNC->TXBuffer, TNC->TXLen, 0); + + if (SentLen != TNC->TXLen) + { + // ARDOP doesn't seem to recover from a blocked write. For now just reset + + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->TCPCONNECTED = FALSE; + } + } + TNC->Timeout = 20; // 2 secs + return 1; + } + if (TNC->hDevice) + return (WriteCommBlock(TNC)); + else + { + TNC->Timeout = 20; // 2 secs + return 0; + } +} + +// Teensy Combined ARDOP/AX.25 Support + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD +/* + +VOID ARAXINIT(struct PORTCONTROL * PORT) +{ + char Msg[80] = ""; + + memcpy(Msg, PORT->PORTDESCRIPTION, 30); + sprintf(Msg, "%s\n", Msg); + + WritetoConsoleLocal(Msg); +} + +VOID ARAXTX(struct PORTCONTROL * PORT, PMESSAGE Buffer) +{ + // KISS Encode and Queue to Host Mode KISS Queue + + UINT * TXMsg = NULL; // KISS Message to queue to Hostmode KISS Queue + UCHAR * TXPtr; + int TXLen = 0; + struct _LINKTABLE * ACKWORD = Buffer->Linkptr; + struct _MESSAGE * Message = (struct _MESSAGE *)Buffer; + UCHAR c; + + char * ptr1; + int Len; + + struct TNCINFO * TNC = PORT->TNC; + + if (TNC && TNC->CONNECTED) // Have a Host Session + TXMsg = GetBuff(); // KISS Message to queue to Hostmode KISS Queue + + if (TXMsg == NULL) // No Session or No buffers + { + // Reset any ACKMODE Timer and release buffer C_Q_ADD(&TRACE_Q, Buffer); + + struct _LINKTABLE * LINK = Buffer->Linkptr; + + if (LINK) + { + if (LINK->L2TIMER) + LINK->L2TIMER = LINK->L2TIME; + + Buffer->Linkptr = 0; // CLEAR FLAG FROM BUFFER + } + C_Q_ADD(&TRACE_Q, Buffer); + return; + } + + TXPtr = (UCHAR *)&TXMsg[2]; + + ptr1 = &Message->DEST[0]; + Len = Message->LENGTH - 7; + *(TXPtr++) = FEND; + + if (ACKWORD) // Frame Needs ACK + { + *TXPtr++ = 0x0c; // ACK OPCODE + ACKWORD -= (UINT)LINKS; // Con only send 16 bits, so use offset into LINKS + *TXPtr++ = ACKWORD & 0xff; + *TXPtr++ = (ACKWORD >> 8) &0xff; + + // have to reset flag so trace doesnt clear it + + Buffer->Linkptr = 0; + TXLen = 4;struct _LINKTABLE * + } + else + { + *TXPtr++ = 0; + TXLen = 2; + } + + while (Len--) + { + c = *(ptr1++); + + switch (c) + { + case FEND: + (*TXPtr++) = FESC; + (*TXPtr++) = TFEND; + TXLen += 2; + break; + + case FESC: + (*TXPtr++) = FESC; + (*TXPtr++) = TFESC; + TXLen += 2; + break; + + default: + (*TXPtr++) = c; + TXLen++; + } + if (TXLen > 250) + { + // Queue frame to KISS Channel and get another buffer + // can take up to 256, but sometimes add 2 at a time + TXMsg[1] = (int)(TXPtr - (UCHAR *)&TXMsg[2]); + } + } + + (*TXPtr++) = FEND; + TXLen++; + + TXMsg[1] = TXLen; + + C_Q_ADD(&TNC->KISSTX_Q, TXMsg); + + // Pass buffer to trace routines + + C_Q_ADD(&TRACE_Q, Buffer); + +} + + +VOID ARAXRX() +{ +} + + +VOID ARAXTIMER() +{ +} + +VOID ARAXCLOSE() +{ +} + +BOOL ARAXTXCHECK() +{ + return 0; +} + + +#define DATABYTES 400000 // WAS 320000 +extern UCHAR * NEXTFREEDATA; // ADDRESS OF NEXT FREE BYTE in shared memory +extern UCHAR DATAAREA[DATABYTES]; + + + +VOID AddVirtualKISSPort(struct TNCINFO * TNC, int ARDOPPort, char * buf) +{ + // Adds a Virtual KISS port for simultaneous ARDOP and Packet on Teensy TNC + // or ARDOP_PTC ovet a single host mode port. + + // Not needed if using TCP interface as that uses KISS over TCP + + struct PORTCONTROL * PORTVEC=PORTTABLE; + struct PORTCONTROL * PORT; + int pl = sizeof(struct PORTCONTROL); + int mh = MHENTRIES * sizeof(MHSTRUC); + int space = (int)(&DATAAREA[DATABYTES] - NEXTFREEDATA); + char Msg[64]; + unsigned char * ptr3; + unsigned int3; + int newPortNumber = 0; + + if (TNC->ARDOPCommsMode == 'T') // TCP + return; + + if (buf[12] == '=') + newPortNumber = atoi(&buf[13]); + + if (space < (pl + mh)) + { + WritetoConsoleLocal("Insufficient space to add ARDOP/Packet Port\n"); + return; + } + + + while (PORTVEC->PORTPOINTER) + { + PORTVEC=PORTVEC->PORTPOINTER; + } + + // PORTVEC is now last port in chain + + ptr3 = NEXTFREEDATA; + + PORT = (struct PORTCONTROL *)ptr3; + + ptr3 += sizeof (struct PORTCONTROL); + + // Round to word boundary (for ARM5 etc) + + int3 = (int)ptr3; + int3 += 3; + int3 &= 0xfffffffc; + ptr3 = (UCHAR *)int3; + + PORTVEC->PORTPOINTER = PORT; // Chain to previous last port + + if (newPortNumber == 0) + newPortNumber = 32;; + + if (GetPortTableEntryFromPortNum(newPortNumber)) + { + // Number in use + + // If user specified search up, if default search down + + if (newPortNumber == 32) + while(newPortNumber && GetPortTableEntryFromPortNum(newPortNumber)) // Try next lower + newPortNumber--; + else + while(newPortNumber < 32 && GetPortTableEntryFromPortNum(newPortNumber)) // Try next highest + newPortNumber++; + + } + + if (newPortNumber == 0 || newPortNumber > 32) + { + WritetoConsoleLocal("No free Port Number to add ARDOP/Packet Port\n"); + return; + } + + + + NUMBEROFPORTS++; + + PORT->PORTNUMBER = newPortNumber; + PORT->PortSlot = PORTVEC->PortSlot + 1; + + sprintf(Msg, "Packet Port for ARDOP Port %d ", ARDOPPort); + memcpy(PORT->PORTDESCRIPTION, Msg, 30); + + PORT->TNC = TNC; + TNC->VirtualPORT = PORT; // Link TNC and PORT both ways + PORT->PORTINITCODE = ARAXINIT; + PORT->PORTTIMERCODE = ARAXTIMER; + PORT->PORTRXROUTINE = ARAXRX; + PORT->PORTTXROUTINE = ARAXTX; + PORT->PORTCLOSECODE = ARAXCLOSE; + PORT->PORTTXCHECKCODE = ARAXTXCHECK; + + // Default L2 Params + + PORT->PORTN2 = 5; + PORT->PORTT1 = 5000/333; // FRACK 5 secs + + // As we use IPoll we can set RESPTIME very long and FRACK short + + PORT->PORTT2 = 20000/333; // RESPTIME + PORT->PORTPACLEN = 128; + PORT->PORTWINDOW = 1; + + // ADD MH AREA IF NEEDED + + NEEDMH = 1; // Include MH in Command List + + PORT->PORTMHEARD = (PMHSTRUC)ptr3; + + ptr3 += MHENTRIES * sizeof(MHSTRUC); + + // Round to word boundary (for ARM5 etc) + + int3 = (int)ptr3; + int3 += 3; + int3 &= 0xfffffffc; + ptr3 = (UCHAR *)int3; + + NEXTFREEDATA = ptr3; + + return; +} + +int ConfigVirtualKISSPort(struct TNCINFO * TNC, char * Cmd) +{ + struct PORTCONTROL * PORT = TNC->VirtualPORT; + void ** buffptr = GetBuff(); + char * Context; + char * Param; + char * Command; + int Value; + int Stream = 0; + if (buffptr == NULL) + return 0; + + if (PORT == NULL) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Packet Mode nor Enabled\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + + _strupr(Cmd); + + Command = strtok_s(&Cmd[4], " \n\r", &Context); + Param = strtok_s(NULL, " \n\r", &Context); + + if (Param) + Value = atoi(Param); + + if (strcmp(Command, "PACLEN") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d\r", Command, PORT->PORTPACLEN); + else + { + if (Value > 0 && Value <= 256) + PORT->PORTPACLEN = Value; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d\r", Command, PORT->PORTPACLEN); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else if (strcmp(Command, "RETRIES") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d\r", Command, PORT->PORTN2); + else + { + if (Value > 0 && Value <= 16) + PORT->PORTN2 = Value; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d\r", Command, PORT->PORTN2); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else if (strcmp(Command, "WINDOW") == 0 || strcmp(Command, "MAXFRAME") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d\r", Command, PORT->PORTWINDOW); + else + { + if (Value > 0 && Value <= 7) + PORT->PORTWINDOW = Value; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d\r", Command, PORT->PORTWINDOW); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else if (strcmp(Command, "FRACK") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d mS\r", Command, PORT->PORTT1 * 333); + else + { + if (Value > 0 && Value <= 20000) + PORT->PORTT1 = Value / 333; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d mS\r", Command, PORT->PORTT1 * 333); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else if (strcmp(Command, "RESPTIME") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d mS\r", Command, PORT->PORTT2 * 333); + else + { + if (Value > 0 && Value <= 20000) + PORT->PORTT2 = Value / 333; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d mS\r", Command, PORT->PORTT2 * 333); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Invalid Command %s\r", Cmd); + + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + + +} +*/ +void ProcessKISSBytes(struct TNCINFO * TNC, UCHAR * Data, int Len) +{ + // Kiss data received from TNC but not necessarrily a full packet + // and could be multiple packets + + // The TNC record is for the ARDOP Port, but we need to queue data + // to the linked Virtual Packet Port + + struct PORTCONTROL * PORT = TNC->VirtualPORT; + UCHAR * KISSBuffer = TNC->KISSBuffer; + UCHAR c; + UCHAR * inptr = Data; + int outptr = TNC->KISSInputLen; + + if (PORT == NULL) + return; + + while(Len--) + { + c = *(inptr++); + + if (TNC->ESCFLAG) + { + // + // FESC received - next should be TFESC or TFEND + + TNC->ESCFLAG = FALSE; + + if (c == TFESC) + c = FESC; + + if (c == TFEND) + c = FEND; + } + else + { + switch (c) + { + case FEND: + + // + // Either start of message or message complete + // + + if (outptr == 0) + { + // Start of Message. If polling, extend timeout + + continue; + } + + ProcessKISSPacket(TNC, KISSBuffer, outptr); + outptr = 0; + return; + + case FESC: + + TNC->ESCFLAG = TRUE; + continue; + + } + } + + // + // Ok, a normal char + // + + KISSBuffer[outptr++] = c; + } + + if (outptr > 510) + outptr = 0; // Protect Buffer + + TNC->KISSInputLen = outptr; + + return; +} + +void ProcessKISSPacket(struct TNCINFO * TNC, UCHAR * KISSBuffer, int Len) +{ + if (KISSBuffer[0] == 0x0c) // ACK Frame + { + // ACK FRAME - reset link timer + + struct _LINKTABLE * LINK; + UINT ACKWORD = KISSBuffer[1] | KISSBuffer[2] << 8; + + LINK = LINKS + ACKWORD; + + if (LINK->L2TIMER) + LINK->L2TIMER = LINK->L2TIME; + + return; + } + if (KISSBuffer[0] == 0) // Data Frame + { + PDATAMESSAGE Buffer = (PDATAMESSAGE)GetBuff(); + + if (Buffer) + { + memcpy(&Buffer->PID, &KISSBuffer[1], --Len); + Len += sizeof(void *) + 3; + + PutLengthinBuffer(Buffer, Len); + + C_Q_ADD(&TNC->VirtualPORT->PORTRX_Q, (UINT *)Buffer); + } + } +} diff --git a/.svn/pristine/31/31511abc0f944271559fe515c79887edfbf4e58f.svn-base b/.svn/pristine/31/31511abc0f944271559fe515c79887edfbf4e58f.svn-base new file mode 100644 index 0000000..463aa40 --- /dev/null +++ b/.svn/pristine/31/31511abc0f944271559fe515c79887edfbf4e58f.svn-base @@ -0,0 +1,1731 @@ +// Mail and Chat Server for BPQ32 Packet Switch +// +// Console Window Module + +#include "BPQChat.h" + +extern BOOL WINE; + +char ClassName[]="CONSOLEWINDOW"; + + +struct UserInfo * user; + +struct ConsoleInfo BBSConsole; +struct ConsoleInfo ChatConsole; +struct ConsoleInfo * ConsHeader[2] = {&BBSConsole, &ChatConsole}; + +struct ConsoleInfo * InitHeader; + +HWND hConsole; + +int AutoColours[20] = {0, 4, 9, 11, 13, 16, 17, 42, 45, 50, 61, 64, 66, 72, 81, 84, 85, 86, 87, 89}; + +COLORREF Colours[256] = {0, + RGB(0,0,0), RGB(0,0,128), RGB(0,0,192), RGB(0,0,255), // 1 - 4 + RGB(0,64,0), RGB(0,64,128), RGB(0,64,192), RGB(0,64,255), // 5 - 8 + RGB(0,128,0), RGB(0,128,128), RGB(0,128,192), RGB(0,128,255), // 9 - 12 + RGB(0,192,0), RGB(0,192,128), RGB(0,192,192), RGB(0,192,255), // 13 - 16 + RGB(0,255,0), RGB(0,255,128), RGB(0,255,192), RGB(0,255,255), // 17 - 20 + + RGB(64,0,0), RGB(64,0,128), RGB(64,0,192), RGB(0,0,255), // 21 + RGB(64,64,0), RGB(64,64,128), RGB(64,64,192), RGB(64,64,255), + RGB(64,128,0), RGB(64,128,128), RGB(64,128,192), RGB(64,128,255), + RGB(64,192,0), RGB(64,192,128), RGB(64,192,192), RGB(64,192,255), + RGB(64,255,0), RGB(64,255,128), RGB(64,255,192), RGB(64,255,255), + + RGB(128,0,0), RGB(128,0,128), RGB(128,0,192), RGB(128,0,255), // 41 + RGB(128,64,0), RGB(128,64,128), RGB(128,64,192), RGB(128,64,255), + RGB(128,128,0), RGB(128,128,128), RGB(128,128,192), RGB(128,128,255), + RGB(128,192,0), RGB(128,192,128), RGB(128,192,192), RGB(128,192,255), + RGB(128,255,0), RGB(128,255,128), RGB(128,255,192), RGB(128,255,255), + + RGB(192,0,0), RGB(192,0,128), RGB(192,0,192), RGB(192,0,255), // 61 + RGB(192,64,0), RGB(192,64,128), RGB(192,64,192), RGB(192,64,255), + RGB(192,128,0), RGB(192,128,128), RGB(192,128,192), RGB(192,128,255), + RGB(192,192,0), RGB(192,192,128), RGB(192,192,192), RGB(192,192,255), + RGB(192,255,0), RGB(192,255,128), RGB(192,255,192), RGB(192,255,255), + + RGB(255,0,0), RGB(255,0,128), RGB(255,0,192), RGB(255,0,255), // 81 + RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,64,255), + RGB(255,128,0), RGB(255,128,128), RGB(255,128,192), RGB(255,128,255), + RGB(255,192,0), RGB(255,192,128), RGB(255,192,192), RGB(255,192,255), + RGB(255,255,0), RGB(255,255,128), RGB(255,255,192), RGB(255,255,255) // 100 ? +}; + + +#define InputBoxHeight 25 +static LRESULT CALLBACK ConsWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +static LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY MonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +void MoveWindows(struct ConsoleInfo * Cinfo); +VOID CloseConsoleSupport(struct ConsoleInfo * Cinfo); +VOID AddLinetoWindow(struct ConsoleInfo * Cinfo, WCHAR * Line); +VOID DoRefresh(struct ConsoleInfo * Cinfo); + +void ChatFlush(ChatCIRCUIT * conn); +char * lookupuser(char * call); + +VOID SaveIntValue(char * Key, int Value); +VOID SaveStringValue(char * Key, char * Value); +int GetIntValue(char * Key, int Default); +VOID GetStringValue(char * Key, char * Value, int Len); +int ChatIsUTF8(unsigned char *ptr, int len); + + +#define BGCOLOUR RGB(236,233,216) + +extern int Bells, FlashOnBell, StripLF, WarnWrap, WrapInput, FlashOnConnect, CloseWindowOnBye; + +char Version[32]; +char ConsoleSize[32]; +char MonitorSize[32]; +char DebugSize[32]; +char WindowSize[32]; + +RECT ConsoleRect; + + +HMENU trayMenu = 0; + +BOOL CreateConsole(int Stream) +{ + WNDCLASS wc = {0}; + HBRUSH bgBrush; + HMENU hMenu; + char Size[80] = ""; + char RTFColours[3000]; + struct ConsoleInfo * Cinfo; + int i, n; + char Text[80]; + + if (Stream == -1) + Cinfo = &BBSConsole; + else + Cinfo = &ChatConsole; + + InitHeader = Cinfo; + + if (Cinfo->hConsole) + { + ShowWindow(Cinfo->hConsole, SW_SHOWNORMAL); + SetForegroundWindow(Cinfo->hConsole); + return FALSE; // Already open + } + + memset(Cinfo, 0, sizeof(struct ConsoleInfo)); + + if (BBSConsole.next == NULL) BBSConsole.next = &ChatConsole; + + Cinfo->BPQStream = Stream; + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = ConsWndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + + hConsole = CreateDialog(hInst,ClassName,0,NULL); + + if (!hConsole) + return (FALSE); + + wsprintf(Text, "Chat %s Console", Session); + SetWindowText(hConsole, Text); + + Cinfo->readbuff = zalloc(2000); + Cinfo->readbufflen = 1000; + + hMenu=GetMenu(hConsole); + Cinfo->hMenu = hMenu; + + CheckMenuItem(hMenu,BPQBELLS, (Bells) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,BPQFLASHONBELL, (FlashOnBell) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,BPQStripLF, (StripLF) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_WARNINPUT, (WarnWrap) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_WRAPTEXT, (WrapInput) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_Flash, (FlashOnConnect) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_CLOSEWINDOW, (CloseWindowOnBye) ? MF_CHECKED : MF_UNCHECKED); + + DrawMenuBar(hWnd); + + if (trayMenu == 0) + { + trayMenu = CreatePopupMenu(); + AppendMenu(trayMenu,MF_STRING,40000,"Copy"); + } + + // Set up RTF Header, including Colours String; + + memcpy(RTFColours, "{\\colortbl ;", 12); + n = 12; + + for (i = 1; i < 100; i++) + { + COLORREF Colour = Colours[i]; + n += wsprintf(&RTFColours[n], "\\red%d\\green%d\\blue%d;", GetRValue(Colour), GetGValue(Colour),GetBValue(Colour)); + } + + RTFColours[n++] = '}'; + RTFColours[n] = 0; + + strcpy(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fprq1 FixedSys;}}"); +// strcpy(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fmodern\\fcharset204\\fprq1 FixedSys;}}"); + strcat(RTFHeader, RTFColours); + strcat(RTFHeader, "\\viewkind4\\uc1\\pard\\f0"); + + sprintf(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fprq1\\cpg%d\\fcharset%d %s;}}", 0, 0, "Courier New"); + sprintf(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fmodern\\fprq1;}}"); + strcat(RTFHeader, RTFColours); + strcat(RTFHeader, "\\viewkind4\\uc1\\pard\\f0\\fs20\\uc0"); + + + RTFHddrLen = strlen(RTFHeader); + + // Create a Rich Text Control + + Cinfo->SendHeader = TRUE; + Cinfo->Finished = TRUE; + Cinfo->CurrentColour = 1; + + LoadLibrary("riched20.dll"); + + Cinfo->hwndOutput = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, "", + WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_NOHIDESEL | WS_VSCROLL | ES_READONLY, + 6,145,290,130, hConsole, NULL, hInst, NULL); + + // Register for Mouse Events for Copy/Paste + + SendMessage(Cinfo->hwndOutput, EM_SETEVENTMASK, (WPARAM)0, (LPARAM)ENM_MOUSEEVENTS | ENM_SCROLLEVENTS | ENM_KEYEVENTS); + SendMessage(Cinfo->hwndOutput, EM_EXLIMITTEXT, 0, MAXLINES * LINELEN); + + Cinfo->hwndInput = GetDlgItem(hConsole, 118); + + // Set our own WndProcs for the controls. + + Cinfo->wpOrigInputProc = (WNDPROC) SetWindowLong(Cinfo->hwndInput, GWL_WNDPROC, (LONG) InputProc); + + if (cfgMinToTray) + { + char Text[80]; + wsprintf(Text, "Chat %s Console", Session); + AddTrayMenuItem(hConsole, Text); + } + + ShowWindow(hConsole, SW_SHOWNORMAL); + + if (ConsoleRect.right < 100 || ConsoleRect.bottom < 100) + { + GetWindowRect(hConsole, &ConsoleRect); + } + + MoveWindow(hConsole, ConsoleRect.left, ConsoleRect.top, + ConsoleRect.right-ConsoleRect.left, + ConsoleRect.bottom-ConsoleRect.top, TRUE); + + Cinfo->hConsole = hConsole; + + MoveWindows(Cinfo); + + Cinfo->Console = zalloc(sizeof(ChatCIRCUIT)); + + Cinfo->Console->Active = TRUE; + Cinfo->Console->BPQStream = Stream; + + strcpy(Cinfo->Console->Callsign, ChatSYSOPCall); + + user = zalloc(sizeof(struct UserInfo)); + + strcpy(user->Call, ChatSYSOPCall); + + Cinfo->Console->UserPointer = user; + Cinfo->Console->paclen=236; + Cinfo->Console->sysop = TRUE; + + if (user->Name[0] == 0) + { + char * Name = lookupuser(user->Call); + + if (Name) + { + if (strlen(Name) > 17) + Name[17] = 0; + + strcpy(user->Name, Name); + free(Name); + } + else + { + Cinfo->Console->Flags |= GETTINGUSER; + SendUnbuffered(-2, NewUserPrompt, strlen(NewUserPrompt)); + return TRUE; + } + } + + if (rtloginu (Cinfo->Console, TRUE)) + Cinfo->Console->Flags |= CHATMODE; + + return TRUE; +} + + +VOID CloseConsole(int Stream) +{ + struct ConsoleInfo * Cinfo; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->Console) + { + if (Cinfo->BPQStream == Stream) + { + CloseConsoleSupport(Cinfo); + return; + } + } + } +} + + + +VOID CloseConsoleSupport(struct ConsoleInfo * Cinfo) +{ + if (Cinfo->Console->Flags & CHATMODE) + { + __try + { + logout(Cinfo->Console); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } + + Cinfo->Console->Flags = 0; + } + + + if (CloseWindowOnBye) + { +// PostMessage(hConsole, WM_DESTROY, 0, 0); + DestroyWindow(Cinfo->hConsole); + } +} + +void MoveWindows(struct ConsoleInfo * Cinfo) +{ + RECT rcClient; + int ClientWidth; + + GetClientRect(Cinfo->hConsole, &rcClient); + + if (rcClient.bottom == 0) // Minimised + return; + + Cinfo->ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + MoveWindow(Cinfo->hwndOutput,2, 2, ClientWidth-4, Cinfo->ClientHeight-InputBoxHeight-4, TRUE); + MoveWindow(Cinfo->hwndInput,2, Cinfo->ClientHeight-InputBoxHeight-2, ClientWidth-4, InputBoxHeight, TRUE); + + GetClientRect(Cinfo->hwndOutput, &rcClient); + + Cinfo->ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + Cinfo->WarnLen = ClientWidth/8 - 1; + Cinfo->WrapLen = Cinfo->WarnLen; + Cinfo->maxlinelen = Cinfo->WarnLen; + +} + + +INT_PTR CALLBACK ChatColourDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + TEXTMETRIC tm; + int y; + LPMEASUREITEMSTRUCT lpmis; + LPDRAWITEMSTRUCT lpdis; + + + switch (message) + { + int Colour; + USER *user; + char Call[100]; + int Sel; + char ColourString[100]; + + case WM_INITDIALOG: + + for (user = user_hd; user; user = user->next) + { + SendDlgItemMessage(hDlg, IDC_CHATCALLS, CB_ADDSTRING, 0, (LPARAM) user->call); + } + + for (Colour = 0; Colour < 100; Colour++) + { + SendDlgItemMessage(hDlg, IDC_CHATCOLOURS, CB_ADDSTRING, 0, (LPARAM) Colours [Colour]); + } + return TRUE; + + + case WM_MEASUREITEM: + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + + // Set the height of the list box items. + + lpmis->itemHeight = 15; + return TRUE; + + case WM_DRAWITEM: + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + // If there are no list box items, skip this message. + + if (lpdis->itemID == -1) + { + break; + } + + switch (lpdis->itemAction) + { + case ODA_SELECT: + case ODA_DRAWENTIRE: + + // if Chat Console, and message has a colour eacape, action it + + GetTextMetrics(lpdis->hDC, &tm); + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2; + + SetTextColor(lpdis->hDC, Colours[lpdis->itemID]); + +// SetBkColor(lpdis->hDC, 0); + + wsprintf(ColourString, "XXXXXX %06X", Colours[lpdis->itemID]); + + TextOut(lpdis->hDC, + 6, + y, + ColourString, + 13); + + // SetTextColor(lpdis->hDC, OldColour); + + break; + } + + + case WM_COMMAND: + + switch LOWORD(wParam) + { + case IDC_CHATCALLS: + + if (HIWORD(wParam) == CBN_SELCHANGE) + { + Sel = SendDlgItemMessage(hDlg, IDC_CHATCALLS, CB_GETCURSEL, 0, 0); + + SendDlgItemMessage(hDlg, IDC_CHATCALLS, CB_GETLBTEXT, Sel, (LPARAM)(LPCTSTR)&Call); + + user = user_find(Call, NULL); + + if (user) + SendDlgItemMessage(hDlg, IDC_CHATCOLOURS, CB_SETCURSEL, user->Colour - 10, 0); + } + + break; + + case IDOK: + + Sel = SendDlgItemMessage(hDlg, IDC_CHATCALLS, CB_GETCURSEL, 0, 0); + + SendDlgItemMessage(hDlg, IDC_CHATCALLS, CB_GETLBTEXT, Sel, (LPARAM)(LPCTSTR)&Call); + + Sel = SendDlgItemMessage(hDlg, IDC_CHATCOLOURS, CB_GETCURSEL, 0, 0); + + user = user_find(Call, NULL); + + if (user) + { + user->Colour = Sel + 10; + upduser(user); + } + break; + + + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return TRUE; + + } + } + return FALSE; +} + + + +LRESULT CALLBACK ConsWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + int i; + struct ConsoleInfo * Cinfo; + UCHAR tchBuffer[100000]; + UCHAR * buf = tchBuffer; + TEXTMETRIC tm; + int y; + LPMEASUREITEMSTRUCT lpmis; + LPDRAWITEMSTRUCT lpdis; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->hConsole == hWnd) + break; + } + + if (Cinfo == NULL) + Cinfo = InitHeader; + + switch (message) { + + case WM_CTLCOLOREDIT: + + if (Cinfo->Scrolled) + { + HDC hdcStatic = (HDC)wParam; + SetBkMode(hdcStatic, TRANSPARENT); + + return (LONG)GetStockObject(LTGRAY_BRUSH); + } + return (DefWindowProc(hWnd, message, wParam, lParam)); + + + case WM_VSCROLL: + break; + + case WM_NOTIFY: + { + const MSGFILTER * pF = (MSGFILTER *)lParam; + POINT pos; + CHARRANGE Range; + + if(pF->nmhdr.hwndFrom == Cinfo->hwndOutput) + { + if(pF->msg == WM_VSCROLL) + { +// int Command = LOWORD(pF->wParam); +// int Pos = HIWORD(pF->wParam); + +// Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + + DoRefresh(Cinfo); + break; + } + + if(pF->msg == WM_KEYUP) + { + if (pF->wParam == VK_PRIOR || pF->wParam == VK_NEXT) + { +// Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + DoRefresh(Cinfo); + } + } + + if(pF->msg == WM_RBUTTONDOWN) + { + // Only allow popup if something is selected + + SendMessage(Cinfo->hwndOutput, EM_EXGETSEL , 0, (WPARAM)&Range); + if (Range.cpMin == Range.cpMax) + return TRUE; + + GetCursorPos(&pos); + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); + return TRUE; + } + } + break; + } + + case WM_MEASUREITEM: + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + + // Set the height of the list box items. + + lpmis->itemHeight = 15; + return TRUE; + + case WM_DRAWITEM: + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + // If there are no list box items, skip this message. + + if (lpdis->itemID == -1) + { + return TRUE; + } + + switch (lpdis->itemAction) + { + case ODA_SELECT: + case ODA_DRAWENTIRE: + + // if Chat Console, and message has a colour eacape, action it + + SendMessage(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID, (LPARAM) tchBuffer); + + GetTextMetrics(lpdis->hDC, &tm); + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2; + + if ((Cinfo->BPQStream == -2) && (tchBuffer[0] == 0x1b)) + { + SetTextColor(lpdis->hDC, Colours[tchBuffer[1] - 10]); + buf += 2; + } +// SetBkColor(lpdis->hDC, 0); + + TextOut(lpdis->hDC, + 6, + y, + buf, + strlen(buf)); + + // SetTextColor(lpdis->hDC, OldColour); + + break; + } + + return TRUE; + + + case WM_ACTIVATE: + + SetFocus(Cinfo->hwndInput); + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case 40000: + { + int len=0; + HGLOBAL hMem; + char * ptr; + CHARRANGE Range; + + // Copy Rich Text Selection to Clipboard + + SendMessage(Cinfo->hwndOutput, EM_EXGETSEL , 0, (WPARAM)&Range); + + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, Range.cpMax - Range.cpMin + 1); + + if (hMem != 0) + { + ptr=GlobalLock(hMem); + + if (OpenClipboard(Cinfo->hConsole)) + { + len = SendMessage(Cinfo->hwndOutput, EM_GETSELTEXT , 0, (WPARAM)ptr); + + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + } + else + GlobalFree(hMem); + + SetFocus(Cinfo->hwndInput); + } + + + + + case BPQBELLS: + + ToggleParam(Cinfo->hMenu, hWnd, &Bells, BPQBELLS); + break; + + case BPQFLASHONBELL: + + ToggleParam(Cinfo->hMenu, hWnd, &FlashOnBell, BPQFLASHONBELL); + break; + + case BPQStripLF: + + ToggleParam(Cinfo->hMenu, hWnd, &StripLF, BPQStripLF); + break; + + case IDM_WARNINPUT: + + ToggleParam(Cinfo->hMenu, hWnd, &WarnWrap, IDM_WARNINPUT); + break; + + + case IDM_WRAPTEXT: + + ToggleParam(Cinfo->hMenu, hWnd, &WrapInput, IDM_WRAPTEXT); + break; + + case IDM_Flash: + + ToggleParam(Cinfo->hMenu, hWnd, &FlashOnConnect, IDM_Flash); + break; + + case IDM_CLOSEWINDOW: + + ToggleParam(Cinfo->hMenu, hWnd, &CloseWindowOnBye, IDM_CLOSEWINDOW); + break; + + case BPQCLEAROUT: + + for (i = 0; i < MAXLINES; i++) + { + Cinfo->OutputScreen[i][0] = 0; + } + + Cinfo->CurrentLine = 0; + DoRefresh(Cinfo); + break; + + + SendMessage(Cinfo->hwndOutput,LB_RESETCONTENT, 0, 0); + break; + + case BPQCOPYOUT: + + CopyRichTextToClipboard(Cinfo->hwndOutput); + break; + + + case IDM_EDITCHATCOLOURS: + DialogBox(hInst, MAKEINTRESOURCE(IDD_CHATCOLCONFIG), hWnd, ChatColourDialogProc); + break; + + + //case BPQHELP: + + // HtmlHelp(hWnd,"BPQTerminal.chm",HH_HELP_FINDER,0); + // break; + + default: + + return 0; + + } + + case WM_SYSCOMMAND: + + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case SC_MINIMIZE: + + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Cinfo->Height = lprc->bottom-lprc->top; + Cinfo->Width = lprc->right-lprc->left; + + MoveWindows(Cinfo); + + return TRUE; + + case WM_SIZE: + + MoveWindows(Cinfo); + return TRUE; + + case WM_CLOSE: + + CloseConsoleSupport(Cinfo); + + return (DefWindowProc(hWnd, message, wParam, lParam)); + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &ConsoleRect); // For save soutine + + SetWindowLong(Cinfo->hwndInput, GWL_WNDPROC, + (LONG) Cinfo->wpOrigInputProc); + + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + + if (Cinfo->Console && Cinfo->Console->Active) + { + ChatClearQueue(Cinfo->Console); + + Cinfo->Console->Active = FALSE; + RefreshMainWindow(); + logout(Cinfo->Console); + } + + // Free Scrollback + + for (i = 0; i < MAXSTACK ; i++) + { + if (Cinfo->KbdStack[i]) + { + free(Cinfo->KbdStack[i]); + Cinfo->KbdStack[i] = NULL; + } + } + + Sleep(500); + + free(Cinfo->readbuff); + Cinfo->readbufflen = 0; + + free(Cinfo->Console); + Cinfo->Console = 0; + Cinfo->hConsole = NULL; + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + +LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + int i; + unsigned int TextLen; + struct ConsoleInfo * Cinfo; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->hwndInput == hwnd) + break; + } + + if (Cinfo == NULL) + Cinfo = InitHeader; + + + if (uMsg == WM_KEYUP) + { + unsigned int i; +// Debugprintf("5%x", LOBYTE(HIWORD(lParam))); + + if (LOBYTE(HIWORD(lParam)) == 0x48 && wParam == 0x26) + { + // Scroll up + + if (Cinfo->KbdStack[Cinfo->StackIndex] == NULL) + return TRUE; + + SendMessage(Cinfo->hwndInput, WM_SETTEXT,0,(LPARAM)(LPCSTR) Cinfo->KbdStack[Cinfo->StackIndex]); + + for (i = 0; i < strlen(Cinfo->KbdStack[Cinfo->StackIndex]); i++) + { + SendMessage(Cinfo->hwndInput, WM_KEYDOWN, VK_RIGHT, 0); + SendMessage(Cinfo->hwndInput, WM_KEYUP, VK_RIGHT, 0); + } + + Cinfo->StackIndex++; + if (Cinfo->StackIndex == 20) + Cinfo->StackIndex = 19; + + return TRUE; + } + + if (LOBYTE(HIWORD(lParam)) == 0x50 && wParam == 0x28) + { + // Scroll up + + Cinfo->StackIndex--; + if (Cinfo->StackIndex < 0) + Cinfo->StackIndex = 0; + + if (Cinfo->KbdStack[Cinfo->StackIndex] == NULL) + return TRUE; + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) Cinfo->KbdStack[Cinfo->StackIndex]); + + for (i = 0; i < strlen(Cinfo->KbdStack[Cinfo->StackIndex]); i++) + { + SendMessage(Cinfo->hwndInput, WM_KEYDOWN, VK_RIGHT, 0); + SendMessage(Cinfo->hwndInput, WM_KEYUP, VK_RIGHT, 0); + } + + return TRUE; + } + } + + + if (uMsg == WM_CHAR) + { + TextLen = SendMessage(Cinfo->hwndInput,WM_GETTEXTLENGTH, 0, 0); + + if (TextLen > INPUTLEN-10) Beep(220, 150); + + if(WarnWrap || WrapInput) + { + TextLen = SendMessage(Cinfo->hwndInput,WM_GETTEXTLENGTH, 0, 0); + + if (WarnWrap) + if (TextLen == Cinfo->WarnLen) Beep(220, 150); + + if (WrapInput) + if ((wParam == 0x20) && (TextLen > Cinfo->WrapLen)) + wParam = 13; // Replace space with Enter + + } + + if (wParam == 13) + { + Cinfo->kbptr=SendMessage(Cinfo->hwndInput, WM_GETTEXT, INPUTLEN-1, + (LPARAM) (LPCSTR)Cinfo->kbbuf); + + Cinfo->StackIndex = 0; + + // Stack it + + if (Cinfo->KbdStack[19]) + free(Cinfo->KbdStack[19]); + + for (i = 18; i >= 0; i--) + { + Cinfo->KbdStack[i+1] = Cinfo->KbdStack[i]; + } + + Cinfo->KbdStack[0] = _strdup(Cinfo->kbbuf); + + Cinfo->kbbuf[Cinfo->kbptr]=13; + + // Echo + + if (Cinfo->BPQStream == -2) + { + UCHAR Msg[INPUTLEN * 2]; // Should be plenty + Msg[0] = 0x1b; + Msg[1] = 11; + + memcpy(&Msg[2], Cinfo->kbbuf, Cinfo->kbptr+1); + WritetoConsoleWindow(Cinfo->BPQStream, Msg, Cinfo->kbptr+3); + } + else + WritetoConsoleWindow(Cinfo->BPQStream, Cinfo->kbbuf, Cinfo->kbptr+1); + + if (Cinfo->Scrolled) + { + POINT Point; + Point.x = 0; + Point.y = 25000; // Should be plenty for any font + + SendMessage(Cinfo->hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + Cinfo->Scrolled = FALSE; + } + + DoRefresh(Cinfo); + ProcessLine(Cinfo->Console, user, &Cinfo->kbbuf[0], Cinfo->kbptr+1); + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) ""); + + return 0; + } + if (wParam == 0x1a) // Ctrl/Z + { + + Cinfo->kbbuf[0]=0x1a; + Cinfo->kbbuf[1]=13; + + ProcessLine(Cinfo->Console, user, &Cinfo->kbbuf[0], 2); + + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) ""); + + return 0; + } + + } + + return CallWindowProc(Cinfo->wpOrigInputProc, hwnd, uMsg, wParam, lParam); +} + + + +int WritetoConsoleWindowSupport(struct ConsoleInfo * Cinfo, WCHAR * Msg, int len); + +int WritetoConsoleWindow(int Stream, UCHAR * Msg, int len) +{ + struct ConsoleInfo * Cinfo; + WCHAR BufferW[65536]; + + int wlen; + + if (ChatIsUTF8(Msg, len)) + wlen = MultiByteToWideChar(CP_UTF8, 0, Msg, len, BufferW, 65536); + else + wlen = MultiByteToWideChar(CP_ACP, 0, Msg, len, BufferW, 65536); + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->Console) + { + if (Cinfo->BPQStream == Stream) + { + WritetoConsoleWindowSupport(Cinfo, BufferW, wlen); + DoRefresh(Cinfo); + return 0; + } + } + } + return 0; +} + +int WritetoConsoleWindowSupport(struct ConsoleInfo * Cinfo, WCHAR * Msg, int len) +{ + WCHAR * ptr1, * ptr2; + + if (len + Cinfo->PartLinePtr > Cinfo->readbufflen) + { + Cinfo->readbufflen += len + Cinfo->PartLinePtr; + Cinfo->readbuff = realloc(Cinfo->readbuff, Cinfo->readbufflen * 2); + } + + if (Cinfo->PartLinePtr != 0) + { + Cinfo->CurrentLine--; // Overwrite part line in buffer + if (Cinfo->CurrentLine < 0) + Cinfo->CurrentLine = MAXLINES - 1; + + if (Msg[0] == 0x1b && len > 1) + { + Msg += 2; // Remove Colour Escape + len -= 2; + } + } + + memcpy(&Cinfo->readbuff[Cinfo->PartLinePtr], Msg, len * 2); + + len=len+Cinfo->PartLinePtr; + + ptr1=&Cinfo->readbuff[0]; + Cinfo->readbuff[len]=0; + + if (Bells) + { + do { + + ptr2=memchr(ptr1,7 , len * 2); + + if (ptr2) + { + *(ptr2)=32; + + if (FlashOnBell) + FlashWindow(Cinfo->hConsole, TRUE); + else + Beep(440,250); + } + + } while (ptr2); + } + +lineloop: + + if (len > 0) + { + // copy text to control a line at a time + + ptr2=memchr(ptr1, 13, len * 2); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + Cinfo->PartLinePtr = len; + memmove(Cinfo->readbuff, ptr1, len * 2); + AddLinetoWindow(Cinfo, ptr1); +// InvalidateRect(Cinfo->hwndOutput, NULL, FALSE); + + return (0); + } + + *(ptr2++)=0; + + // If len is greater that screen with, fold + + if ((ptr2 - ptr1) > Cinfo->maxlinelen) + { + WCHAR * ptr3; + WCHAR * saveptr1 = ptr1; + int linelen = ptr2 - ptr1; + int foldlen; + WCHAR save; + + foldloop: + + ptr3 = ptr1 + Cinfo->maxlinelen; + + while(*ptr3!= 0x20 && ptr3 > ptr1) + { + ptr3--; + } + + foldlen = ptr3 - ptr1 ; + + if (foldlen == 0) + { + // No space before, so split at width + + foldlen = Cinfo->maxlinelen; + ptr3 = ptr1 + Cinfo->maxlinelen; + + } + else + { + ptr3++ ; // Omit space + linelen--; + } + save = ptr1[foldlen]; + ptr1[foldlen] = 0; + AddLinetoWindow(Cinfo, ptr1); + ptr1[foldlen] = save; + linelen -= foldlen; + ptr1 = ptr3; + + if (linelen > Cinfo->maxlinelen) + goto foldloop; + + AddLinetoWindow(Cinfo, ptr1); + + ptr1 = saveptr1; + } + else + AddLinetoWindow(Cinfo, ptr1); + + Cinfo->PartLinePtr=0; + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + if ((len > 0) && StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + + goto lineloop; + } + + + return (0); +} + +int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item) +{ + *Param = !(*Param); + + CheckMenuItem(hMenu,Item, (*Param) ? MF_CHECKED : MF_UNCHECKED); + + return (0); +} + +void CopyRichTextToClipboard(HWND hWnd) +{ + int len=0; + HGLOBAL hMem; + char * ptr; + + // Copy Rich Text to Clipboard + + len = SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0); + + hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len + 1); + + if (hMem != 0) + { + ptr=GlobalLock(hMem); + + if (OpenClipboard(MainWnd)) + { + len = SendMessage(hWnd, WM_GETTEXT , len, (LPARAM)ptr); + + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + } + else + GlobalFree(hMem); +} + + +void CopyToClipboard(HWND hWnd) +{ + int i,n, len=0; + HGLOBAL hMem; + char * ptr; + // + // Copy List Box to clipboard + // + + n = SendMessage(hWnd, LB_GETCOUNT, 0, 0); + + for (i=0; i 127 || val < -128 ) + ptr2 += sprintf(ptr2, "\\u%d ", val); + else + *(ptr2++) = val; + } + val = *ptr1++; + } + *ptr2 = 0; +} + + +DWORD CALLBACK EditStreamCallback(struct ConsoleInfo * Cinfo, LPBYTE lpBuff, LONG cb, PLONG pcb) +{ + int ReqLen = cb; + int i; + int Line; + char LineB[4096]; + +// if (cb != 4092) +// return 0; + + if (Cinfo->SendHeader) + { + // Return header + + memcpy(lpBuff, RTFHeader, RTFHddrLen); + *pcb = RTFHddrLen; + Cinfo->SendHeader = FALSE; + Cinfo->Finished = FALSE; + Cinfo->Index = 0; + return 0; + } + + if (Cinfo->Finished) + { + *pcb = 0; + return 0; + } + +/* + if (BufferLen > cb) + { + memcpy(lpBuff, &Buffer[Offset], cb); + BufferLen -= cb; + Offset += cb; + *pcb = cb; + return 0; + } + + memcpy(lpBuff, &Buffer[Offset], BufferLen); + + *pcb = BufferLen; +*/ + + // Return 10 line at a time + + for (i = 0; i < 10; i++); + { + Line = Cinfo->Index++ + Cinfo->CurrentLine - MAXLINES; + + if (Line <0) + Line = Line + MAXLINES; + + wcstoRTF(&LineB[0], Cinfo->OutputScreen[Line]); + + sprintf(lpBuff, "\\cf%d ", Cinfo->Colourvalue[Line]); + strcat(lpBuff, LineB); + strcat(lpBuff, "\\line"); + + if (Cinfo->Index == MAXLINES) + { + Cinfo->Finished = TRUE; + strcat(lpBuff, "}"); + i = 10; + } + } + *pcb = strlen(lpBuff); + return 0; +} + +VOID DoRefresh(struct ConsoleInfo * Cinfo) +{ + EDITSTREAM es = {0}; + int Min, Max, Pos; + POINT Point; + SCROLLINFO ScrollInfo; + int LoopTrap = 0; + HWND hwndOutput = Cinfo->hwndOutput; + + if(WINE) + Cinfo->Thumb = 30000; + else + Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + + Pos = Cinfo->Thumb + Cinfo->ClientHeight; + + if ((Cinfo->Thumb + Cinfo->ClientHeight) > Cinfo->RTFHeight - 10) // Don't bother writing to screen if scrolled back + { + es.pfnCallback = (EDITSTREAMCALLBACK)EditStreamCallback; + es.dwCookie = (DWORD_PTR)Cinfo; + Cinfo->SendHeader = TRUE; + SendMessage(hwndOutput, EM_STREAMIN, SF_RTF, (LPARAM)&es); + } + + GetScrollRange(hwndOutput, SB_VERT, &Min, &Max); + ScrollInfo.cbSize = sizeof(ScrollInfo); + ScrollInfo.fMask = SIF_ALL; + + GetScrollInfo(hwndOutput, SB_VERT, &ScrollInfo); + +// Debugprintf("Pos %d Max %d Min %d nMax %d ClientH %d", Pos, Min, Max, ScrollInfo.nMax, Cinfo->ClientHeight); + + if (Cinfo->FirstTime == FALSE) + { + // RTF Controls don't immediately scroll to end - don't know why. + + Cinfo->FirstTime = TRUE; + Point.x = 0; + Point.y = 25000; // Should be plenty for any font + + while (LoopTrap++ < 20) + { + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + } + + GetScrollRange(hwndOutput, SB_VERT, &Min, &Max); // Get Actual Height + Cinfo->RTFHeight = Max; + Point.x = 0; + Point.y = Cinfo->RTFHeight - ScrollInfo.nPage; + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + Cinfo->Thumb = SendMessage(hwndOutput, EM_GETTHUMB, 0, 0); + } + + Point.x = 0; + Point.y = Cinfo->RTFHeight - ScrollInfo.nPage; + + if (Cinfo->Thumb > (Point.y - 10)) // Don't Scroll if user has scrolled back + { + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + + if (Cinfo->Scrolled) + { + Cinfo->Scrolled = FALSE; + InvalidateRect(Cinfo->hwndInput, NULL, TRUE); + } + return; + } + + if (!Cinfo->Scrolled) + { + Cinfo->Scrolled = TRUE; + InvalidateRect(Cinfo->hwndInput, NULL, TRUE); + } +} + +VOID AddLinetoWindow(struct ConsoleInfo * Cinfo, WCHAR * Line) +{ + int Len = wcslen(Line); + WCHAR * ptr1 = Line; + WCHAR * ptr2; + int l, Index; + WCHAR LineCopy[LINELEN * 2]; + + if (Line[0] == 0x1b && Len > 1) + { + // Save Colour Char + + Cinfo->CurrentColour = Line[1] - 10; + ptr1 +=2; + Len -= 2; + } + + wcscpy(Cinfo->OutputScreen[Cinfo->CurrentLine], ptr1); + + // Look for chars we need to escape (\ { }) + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = wcschr(ptr1, L'\\'); // Look for Backslash first, as we may add some later + + if (ptr2) + { + while (ptr2) + { + l = ++ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l * 2); // Copy Including found char + Index += l; + LineCopy[Index++] = '\\'; + Len++; + ptr1 = ptr2; + ptr2 = wcschr(ptr1, '\\'); + } + wcscpy(&LineCopy[Index], ptr1); // Copy in rest + wcscpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = wcschr(ptr1, '{'); + + if (ptr2) + { + while (ptr2) + { + l = ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l * 2); + Index += l; + LineCopy[Index++] = '\\'; + LineCopy[Index++] = '{'; + Len++; + ptr1 = ++ptr2; + ptr2 = wcschr(ptr1, '{'); + } + wcscpy(&LineCopy[Index], ptr1); // Copy in rest + wcscpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = wcschr(ptr1, '}'); // Look for Backslash first, as we may add some later + + if (ptr2) + { + while (ptr2) + { + l = ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l * 2); // Copy + Index += l; + LineCopy[Index++] = '\\'; + LineCopy[Index++] = '}'; + Len++; + ptr1 = ++ptr2; + ptr2 = wcschr(ptr1, '}'); + } + wcscpy(&LineCopy[Index], ptr1); // Copy in rest + wcscpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + + Cinfo->Colourvalue[Cinfo->CurrentLine] = Cinfo->CurrentColour; + Cinfo->LineLen[Cinfo->CurrentLine++] = Len; + if (Cinfo->CurrentLine >= MAXLINES) Cinfo->CurrentLine = 0; +} + + +/* +#define XBITMAP 80 +#define YBITMAP 20 + +#define BUFFER MAX_PATH + +HBITMAP hbmpPencil, hbmpCrayon, hbmpMarker, hbmpPen, hbmpFork; +HBITMAP hbmpPicture, hbmpOld; + +void AddItem(HWND hwnd, LPSTR lpstr, HBITMAP hbmp) +{ + int nItem; + + nItem = SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)lpstr); + SendMessage(hwnd, LB_SETITEMDATA, (WPARAM)nItem, (LPARAM)hbmp); +} + +DWORD APIENTRY DlgDrawProc( + HWND hDlg, // window handle to dialog box + UINT message, // type of message + UINT wParam, // message-specific information + LONG lParam) +{ + int nItem; + TCHAR tchBuffer[BUFFER]; + HBITMAP hbmp; + HWND hListBox; + TEXTMETRIC tm; + int y; + HDC hdcMem; + LPMEASUREITEMSTRUCT lpmis; + LPDRAWITEMSTRUCT lpdis; + RECT rcBitmap; + HRESULT hr; + size_t * pcch; + + switch (message) + { + + case WM_INITDIALOG: + + // Load bitmaps. + + hbmpPencil = LoadBitmap(hinst, MAKEINTRESOURCE(700)); + hbmpCrayon = LoadBitmap(hinst, MAKEINTRESOURCE(701)); + hbmpMarker = LoadBitmap(hinst, MAKEINTRESOURCE(702)); + hbmpPen = LoadBitmap(hinst, MAKEINTRESOURCE(703)); + hbmpFork = LoadBitmap(hinst, MAKEINTRESOURCE(704)); + + // Retrieve list box handle. + + hListBox = GetDlgItem(hDlg, IDL_STUFF); + + // Initialize the list box text and associate a bitmap + // with each list box item. + + AddItem(hListBox, "pencil", hbmpPencil); + AddItem(hListBox, "crayon", hbmpCrayon); + AddItem(hListBox, "marker", hbmpMarker); + AddItem(hListBox, "pen", hbmpPen); + AddItem(hListBox, "fork", hbmpFork); + + SetFocus(hListBox); + SendMessage(hListBox, LB_SETCURSEL, 0, 0); + return TRUE; + + case WM_MEASUREITEM: + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + + // Set the height of the list box items. + + lpmis->itemHeight = 20; + return TRUE; + + case WM_DRAWITEM: + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + // If there are no list box items, skip this message. + + if (lpdis->itemID == -1) + { + break; + } + + // Draw the bitmap and text for the list box item. Draw a + // rectangle around the bitmap if it is selected. + + switch (lpdis->itemAction) + { + case ODA_SELECT: + case ODA_DRAWENTIRE: + + // Display the bitmap associated with the item. + + hbmpPicture =(HBITMAP)SendMessage(lpdis->hwndItem, + LB_GETITEMDATA, lpdis->itemID, (LPARAM) 0); + + hdcMem = CreateCompatibleDC(lpdis->hDC); + hbmpOld = SelectObject(hdcMem, hbmpPicture); + + BitBlt(lpdis->hDC, + lpdis->rcItem.left, lpdis->rcItem.top, + lpdis->rcItem.right - lpdis->rcItem.left, + lpdis->rcItem.bottom - lpdis->rcItem.top, + hdcMem, 0, 0, SRCCOPY); + + // Display the text associated with the item. + + SendMessage(lpdis->hwndItem, LB_GETTEXT, + lpdis->itemID, (LPARAM) tchBuffer); + + GetTextMetrics(lpdis->hDC, &tm); + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - + tm.tmHeight) / 2; + + hr = StringCchLength(tchBuffer, BUFFER, pcch); + if (FAILED(hr)) + { + // TODO: Handle error. + } + + TextOut(lpdis->hDC, + XBITMAP + 6, + y, + tchBuffer, + pcch); + + SelectObject(hdcMem, hbmpOld); + DeleteDC(hdcMem); + + // Is the item selected? + + if (lpdis->itemState & ODS_SELECTED) + { + // Set RECT coordinates to surround only the + // bitmap. + + rcBitmap.left = lpdis->rcItem.left; + rcBitmap.top = lpdis->rcItem.top; + rcBitmap.right = lpdis->rcItem.left + XBITMAP; + rcBitmap.bottom = lpdis->rcItem.top + YBITMAP; + + // Draw a rectangle around bitmap to indicate + // the selection. + + DrawFocusRect(lpdis->hDC, &rcBitmap); + } + break; + + case ODA_FOCUS: + + // Do not process focus changes. The focus caret + // (outline rectangle) indicates the selection. + // The IDOK button indicates the final + // selection. + + break; + } + return TRUE; + + case WM_COMMAND: + + switch (LOWORD(wParam)) + { + case IDOK: + // Get the selected item's text. + + nItem = SendMessage(GetDlgItem(hDlg, IDL_STUFF), + LB_GETCURSEL, 0, (LPARAM) 0); + hbmp = SendMessage(GetDlgItem(hDlg, IDL_STUFF), + LB_GETITEMDATA, nItem, 0); + + // If the item is not the correct answer, tell the + // user to try again. + // + // If the item is the correct answer, congratulate + // the user and destroy the dialog box. + + if (hbmp != hbmpFork) + { + MessageBox(hDlg, "Try again!", "Oops", MB_OK); + return FALSE; + } + else + { + MessageBox(hDlg, "You're right!", + "Congratulations.", MB_OK); + + // Fall through. + } + + case IDCANCEL: + + // Destroy the dialog box. + + EndDialog(hDlg, TRUE); + return TRUE; + + default: + + return FALSE; + } + + case WM_DESTROY: + + // Free any resources used by the bitmaps. + + DeleteObject(hbmpPencil); + DeleteObject(hbmpCrayon); + DeleteObject(hbmpMarker); + DeleteObject(hbmpPen); + DeleteObject(hbmpFork); + + return TRUE; + + default: + return FALSE; + + } + return FALSE; +} +*/ diff --git a/.svn/pristine/31/31865b8533e0ceec265db1cfb113f0a2c9ff72ca.svn-base b/.svn/pristine/31/31865b8533e0ceec265db1cfb113f0a2c9ff72ca.svn-base new file mode 100644 index 0000000..2ad04ed --- /dev/null +++ b/.svn/pristine/31/31865b8533e0ceec265db1cfb113f0a2c9ff72ca.svn-base @@ -0,0 +1,750 @@ +// This is a part of the Microsoft Foundation Classes C++ library. +// Copyright (C) 1992-1995 Microsoft Corporation +// All rights reserved. +// +// This source code is only intended as a supplement to the +// Microsoft Foundation Classes Reference and related +// electronic documentation provided with the library. +// See these sources for detailed information regarding the +// Microsoft Foundation Classes product. + +#ifndef __AFXRES_H__ +#define __AFXRES_H__ + +#ifdef REZ // Mac resource compiler (mrc) defines REZ +#define RC_INVOKED +#endif + +#ifdef RC_INVOKED +#ifndef _INC_WINDOWS +#define _INC_WINDOWS + #include "..\include\winres.h" // extract from windows header +#endif +#endif + +#ifdef _AFX_MINREBUILD +#pragma component(minrebuild, off) +#endif + +#ifdef APSTUDIO_INVOKED +#define APSTUDIO_HIDDEN_SYMBOLS +#endif + +///////////////////////////////////////////////////////////////////////////// +// MFC resource types (see Technical note TN024 for implementation details) + +#ifndef RC_INVOKED +#define RT_DLGINIT MAKEINTRESOURCE(240) +#define RT_TOOLBAR MAKEINTRESOURCE(241) +#endif + +///////////////////////////////////////////////////////////////////////////// + +#ifdef APSTUDIO_INVOKED +#undef APSTUDIO_HIDDEN_SYMBOLS +#endif + +///////////////////////////////////////////////////////////////////////////// +// General style bits etc + +// Tab Control styles +#ifndef TCS_MULTILINE // new in later versions of Win32 +#define TCS_MULTILINE 0x0200 +#endif + +// ControlBar styles +#define CBRS_ALIGN_LEFT 0x1000L +#define CBRS_ALIGN_TOP 0x2000L +#define CBRS_ALIGN_RIGHT 0x4000L +#define CBRS_ALIGN_BOTTOM 0x8000L +#define CBRS_ALIGN_ANY 0xF000L + +#define CBRS_BORDER_LEFT 0x0100L +#define CBRS_BORDER_TOP 0x0200L +#define CBRS_BORDER_RIGHT 0x0400L +#define CBRS_BORDER_BOTTOM 0x0800L +#define CBRS_BORDER_ANY 0x0F00L + +#define CBRS_TOOLTIPS 0x0010L +#define CBRS_FLYBY 0x0020L +#define CBRS_FLOAT_MULTI 0x0040L +#define CBRS_BORDER_3D 0x0080L +#define CBRS_HIDE_INPLACE 0x0008L +#define CBRS_SIZE_DYNAMIC 0x0004L +#define CBRS_SIZE_FIXED 0x0002L +#define CBRS_FLOATING 0x0001L + +#define CBRS_ORIENT_HORZ (CBRS_ALIGN_TOP|CBRS_ALIGN_BOTTOM) +#define CBRS_ORIENT_VERT (CBRS_ALIGN_LEFT|CBRS_ALIGN_RIGHT) +#define CBRS_ORIENT_ANY (CBRS_ORIENT_HORZ|CBRS_ORIENT_VERT) + +#define CBRS_ALL 0xFFFFL + + +// the CBRS_ style is made up of an alignment style and a draw border style +// the alignment styles are mutually exclusive +// the draw border styles may be combined +#define CBRS_NOALIGN 0x00000000L +#define CBRS_LEFT (CBRS_ALIGN_LEFT|CBRS_BORDER_RIGHT) +#define CBRS_TOP (CBRS_ALIGN_TOP|CBRS_BORDER_BOTTOM) +#define CBRS_RIGHT (CBRS_ALIGN_RIGHT|CBRS_BORDER_LEFT) +#define CBRS_BOTTOM (CBRS_ALIGN_BOTTOM|CBRS_BORDER_TOP) + +///////////////////////////////////////////////////////////////////////////// +// Standard window components + +// Mode indicators in status bar - these are routed like commands +#define ID_INDICATOR_EXT 0xE700 // extended selection indicator +#define ID_INDICATOR_CAPS 0xE701 // cap lock indicator +#define ID_INDICATOR_NUM 0xE702 // num lock indicator +#define ID_INDICATOR_SCRL 0xE703 // scroll lock indicator +#define ID_INDICATOR_OVR 0xE704 // overtype mode indicator +#define ID_INDICATOR_REC 0xE705 // record mode indicator +#define ID_INDICATOR_KANA 0xE706 // kana lock indicator + +#define ID_SEPARATOR 0 // special separator value + +#ifndef RC_INVOKED // code only +// Standard control bars (IDW = window ID) +#define AFX_IDW_CONTROLBAR_FIRST 0xE800 +#define AFX_IDW_CONTROLBAR_LAST 0xE8FF + +#define AFX_IDW_TOOLBAR 0xE800 // main Toolbar for window +#define AFX_IDW_STATUS_BAR 0xE801 // Status bar window +#define AFX_IDW_PREVIEW_BAR 0xE802 // PrintPreview Dialog Bar +#define AFX_IDW_RESIZE_BAR 0xE803 // OLE in-place resize bar + +// Note: If your application supports docking toolbars, you should +// not use the following IDs for your own toolbars. The IDs chosen +// are at the top of the first 32 such that the bars will be hidden +// while in print preview mode, and are not likely to conflict with +// IDs your application may have used succesfully in the past. + +#define AFX_IDW_DOCKBAR_TOP 0xE81B +#define AFX_IDW_DOCKBAR_LEFT 0xE81C +#define AFX_IDW_DOCKBAR_RIGHT 0xE81D +#define AFX_IDW_DOCKBAR_BOTTOM 0xE81E +#define AFX_IDW_DOCKBAR_FLOAT 0xE81F + +// Macro for mapping standard control bars to bitmask (limit of 32) +#define AFX_CONTROLBAR_MASK(nIDC) (1L << (nIDC - AFX_IDW_CONTROLBAR_FIRST)) + +// parts of Main Frame +#define AFX_IDW_PANE_FIRST 0xE900 // first pane (256 max) +#define AFX_IDW_PANE_LAST 0xE9ff +#define AFX_IDW_HSCROLL_FIRST 0xEA00 // first Horz scrollbar (16 max) +#define AFX_IDW_VSCROLL_FIRST 0xEA10 // first Vert scrollbar (16 max) + +#define AFX_IDW_SIZE_BOX 0xEA20 // size box for splitters +#define AFX_IDW_PANE_SAVE 0xEA21 // to shift AFX_IDW_PANE_FIRST +#endif //!RC_INVOKED + +#ifndef APSTUDIO_INVOKED + +// common style for form views +#define AFX_WS_DEFAULT_VIEW (WS_CHILD | WS_VISIBLE | WS_BORDER) + +#endif //!APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// Standard app configurable strings + +// for application title (defaults to EXE name or name in constructor) +#define AFX_IDS_APP_TITLE 0xE000 +// idle message bar line +#define AFX_IDS_IDLEMESSAGE 0xE001 +// message bar line when in shift-F1 help mode +#define AFX_IDS_HELPMODEMESSAGE 0xE002 +// document title when editing OLE embedding +#define AFX_IDS_APP_TITLE_EMBEDDING 0xE003 +// company name +#define AFX_IDS_COMPANY_NAME 0xE004 +// object name when server is inplace +#define AFX_IDS_OBJ_TITLE_INPLACE 0xE005 + +///////////////////////////////////////////////////////////////////////////// +// Standard Commands + +// File commands +#define ID_FILE_NEW 0xE100 +#define ID_FILE_OPEN 0xE101 +#define ID_FILE_CLOSE 0xE102 +#define ID_FILE_SAVE 0xE103 +#define ID_FILE_SAVE_AS 0xE104 +#define ID_FILE_PAGE_SETUP 0xE105 +#define ID_FILE_PRINT_SETUP 0xE106 +#define ID_FILE_PRINT 0xE107 +#define ID_FILE_PRINT_DIRECT 0xE108 +#define ID_FILE_PRINT_PREVIEW 0xE109 +#define ID_FILE_UPDATE 0xE10A +#define ID_FILE_SAVE_COPY_AS 0xE10B +#define ID_FILE_SEND_MAIL 0xE10C + +#define ID_FILE_MRU_FIRST 0xE110 +#define ID_FILE_MRU_FILE1 0xE110 // range - 16 max +#define ID_FILE_MRU_FILE2 0xE111 +#define ID_FILE_MRU_FILE3 0xE112 +#define ID_FILE_MRU_FILE4 0xE113 +#define ID_FILE_MRU_FILE5 0xE114 +#define ID_FILE_MRU_FILE6 0xE115 +#define ID_FILE_MRU_FILE7 0xE116 +#define ID_FILE_MRU_FILE8 0xE117 +#define ID_FILE_MRU_FILE9 0xE118 +#define ID_FILE_MRU_FILE10 0xE119 +#define ID_FILE_MRU_FILE11 0xE11A +#define ID_FILE_MRU_FILE12 0xE11B +#define ID_FILE_MRU_FILE13 0xE11C +#define ID_FILE_MRU_FILE14 0xE11D +#define ID_FILE_MRU_FILE15 0xE11E +#define ID_FILE_MRU_FILE16 0xE11F +#define ID_FILE_MRU_LAST 0xE11F + +// Edit commands +#define ID_EDIT_CLEAR 0xE120 +#define ID_EDIT_CLEAR_ALL 0xE121 +#define ID_EDIT_COPY 0xE122 +#define ID_EDIT_CUT 0xE123 +#define ID_EDIT_FIND 0xE124 +#define ID_EDIT_PASTE 0xE125 +#define ID_EDIT_PASTE_LINK 0xE126 +#define ID_EDIT_PASTE_SPECIAL 0xE127 +#define ID_EDIT_REPEAT 0xE128 +#define ID_EDIT_REPLACE 0xE129 +#define ID_EDIT_SELECT_ALL 0xE12A +#define ID_EDIT_UNDO 0xE12B +#define ID_EDIT_REDO 0xE12C + +// Window commands +#define ID_WINDOW_NEW 0xE130 +#define ID_WINDOW_ARRANGE 0xE131 +#define ID_WINDOW_CASCADE 0xE132 +#define ID_WINDOW_TILE_HORZ 0xE133 +#define ID_WINDOW_TILE_VERT 0xE134 +#define ID_WINDOW_SPLIT 0xE135 +#ifndef RC_INVOKED // code only +#define AFX_IDM_WINDOW_FIRST 0xE130 +#define AFX_IDM_WINDOW_LAST 0xE13F +#define AFX_IDM_FIRST_MDICHILD 0xFF00 // window list starts here +#endif //!RC_INVOKED + +// Help and App commands +#define ID_APP_ABOUT 0xE140 +#define ID_APP_EXIT 0xE141 +#define ID_HELP_INDEX 0xE142 +#define ID_HELP_FINDER 0xE143 +#define ID_HELP_USING 0xE144 +#define ID_CONTEXT_HELP 0xE145 // shift-F1 +// special commands for processing help +#define ID_HELP 0xE146 // first attempt for F1 +#define ID_DEFAULT_HELP 0xE147 // last attempt + +// Misc +#define ID_NEXT_PANE 0xE150 +#define ID_PREV_PANE 0xE151 + +// Format +#define ID_FORMAT_FONT 0xE160 + +// OLE commands +#define ID_OLE_INSERT_NEW 0xE200 +#define ID_OLE_EDIT_LINKS 0xE201 +#define ID_OLE_EDIT_CONVERT 0xE202 +#define ID_OLE_EDIT_CHANGE_ICON 0xE203 +#define ID_OLE_EDIT_PROPERTIES 0xE204 +#define ID_OLE_VERB_FIRST 0xE210 // range - 16 max +#ifndef RC_INVOKED // code only +#define ID_OLE_VERB_LAST 0xE21F +#endif //!RC_INVOKED + +// for print preview dialog bar +#define AFX_ID_PREVIEW_CLOSE 0xE300 +#define AFX_ID_PREVIEW_NUMPAGE 0xE301 // One/Two Page button +#define AFX_ID_PREVIEW_NEXT 0xE302 +#define AFX_ID_PREVIEW_PREV 0xE303 +#define AFX_ID_PREVIEW_PRINT 0xE304 +#define AFX_ID_PREVIEW_ZOOMIN 0xE305 +#define AFX_ID_PREVIEW_ZOOMOUT 0xE306 + +// View commands (same number used as IDW used for control bar) +#define ID_VIEW_TOOLBAR 0xE800 +#define ID_VIEW_STATUS_BAR 0xE801 + // -> E8FF reserved for other control bar commands + +// RecordForm commands +#define ID_RECORD_FIRST 0xE900 +#define ID_RECORD_LAST 0xE901 +#define ID_RECORD_NEXT 0xE902 +#define ID_RECORD_PREV 0xE903 + +///////////////////////////////////////////////////////////////////////////// +// Standard control IDs + +#ifdef IDC_STATIC +#undef IDC_STATIC +#endif +#define IDC_STATIC (-1) // all static controls + +///////////////////////////////////////////////////////////////////////////// +// Standard string error/warnings + +#ifndef RC_INVOKED // code only +#define AFX_IDS_SCFIRST 0xEF00 +#endif //!RC_INVOKED + +#define AFX_IDS_SCSIZE 0xEF00 +#define AFX_IDS_SCMOVE 0xEF01 +#define AFX_IDS_SCMINIMIZE 0xEF02 +#define AFX_IDS_SCMAXIMIZE 0xEF03 +#define AFX_IDS_SCNEXTWINDOW 0xEF04 +#define AFX_IDS_SCPREVWINDOW 0xEF05 +#define AFX_IDS_SCCLOSE 0xEF06 +#define AFX_IDS_SCRESTORE 0xEF12 +#define AFX_IDS_SCTASKLIST 0xEF13 + +#define AFX_IDS_MDICHILD 0xEF1F + +#define AFX_IDS_DESKACCESSORY 0xEFDA + +// General strings +#define AFX_IDS_OPENFILE 0xF000 +#define AFX_IDS_SAVEFILE 0xF001 +#define AFX_IDS_ALLFILTER 0xF002 +#define AFX_IDS_UNTITLED 0xF003 +#define AFX_IDS_SAVEFILECOPY 0xF004 +#define AFX_IDS_PREVIEW_CLOSE 0xF005 +#define AFX_IDS_UNNAMED_FILE 0xF006 +#ifdef _MAC +#define AFX_IDS_ABOUT 0xF010 +#endif +#define AFX_IDS_HIDE 0xF011 + +// MFC Standard Exception Error messages +#define AFX_IDP_NO_ERROR_AVAILABLE 0xF020 +#define AFX_IDS_NOT_SUPPORTED_EXCEPTION 0xF021 +#define AFX_IDS_RESOURCE_EXCEPTION 0xF022 +#define AFX_IDS_MEMORY_EXCEPTION 0xF023 +#define AFX_IDS_USER_EXCEPTION 0xF024 + +// Printing and print preview strings +#define AFX_IDS_PRINTONPORT 0xF040 +#define AFX_IDS_ONEPAGE 0xF041 +#define AFX_IDS_TWOPAGE 0xF042 +#define AFX_IDS_PRINTPAGENUM 0xF043 +#define AFX_IDS_PREVIEWPAGEDESC 0xF044 +#define AFX_IDS_PRINTDEFAULTEXT 0xF045 +#define AFX_IDS_PRINTDEFAULT 0xF046 +#define AFX_IDS_PRINTFILTER 0xF047 +#define AFX_IDS_PRINTCAPTION 0xF048 +#define AFX_IDS_PRINTTOFILE 0xF049 + + +// OLE strings +#define AFX_IDS_OBJECT_MENUITEM 0xF080 +#define AFX_IDS_EDIT_VERB 0xF081 +#define AFX_IDS_ACTIVATE_VERB 0xF082 +#define AFX_IDS_CHANGE_LINK 0xF083 +#define AFX_IDS_AUTO 0xF084 +#define AFX_IDS_MANUAL 0xF085 +#define AFX_IDS_FROZEN 0xF086 +#define AFX_IDS_ALL_FILES 0xF087 +// dynamically changing menu items +#define AFX_IDS_SAVE_MENU 0xF088 +#define AFX_IDS_UPDATE_MENU 0xF089 +#define AFX_IDS_SAVE_AS_MENU 0xF08A +#define AFX_IDS_SAVE_COPY_AS_MENU 0xF08B +#define AFX_IDS_EXIT_MENU 0xF08C +#define AFX_IDS_UPDATING_ITEMS 0xF08D +// COlePasteSpecialDialog defines +#define AFX_IDS_METAFILE_FORMAT 0xF08E +#define AFX_IDS_DIB_FORMAT 0xF08F +#define AFX_IDS_BITMAP_FORMAT 0xF090 +#define AFX_IDS_LINKSOURCE_FORMAT 0xF091 +#define AFX_IDS_EMBED_FORMAT 0xF092 +// other OLE utility strings +#define AFX_IDS_PASTELINKEDTYPE 0xF094 +#define AFX_IDS_UNKNOWNTYPE 0xF095 +#define AFX_IDS_RTF_FORMAT 0xF096 +#define AFX_IDS_TEXT_FORMAT 0xF097 +// OLE datatype format error strings +#define AFX_IDS_INVALID_CURRENCY 0xF098 +#define AFX_IDS_INVALID_DATETIME 0xF099 +#define AFX_IDS_INVALID_DATETIMESPAN 0xF09A + +// General error / prompt strings +#define AFX_IDP_INVALID_FILENAME 0xF100 +#define AFX_IDP_FAILED_TO_OPEN_DOC 0xF101 +#define AFX_IDP_FAILED_TO_SAVE_DOC 0xF102 +#define AFX_IDP_ASK_TO_SAVE 0xF103 +#define AFX_IDP_FAILED_TO_CREATE_DOC 0xF104 +#define AFX_IDP_FILE_TOO_LARGE 0xF105 +#define AFX_IDP_FAILED_TO_START_PRINT 0xF106 +#define AFX_IDP_FAILED_TO_LAUNCH_HELP 0xF107 +#define AFX_IDP_INTERNAL_FAILURE 0xF108 // general failure +#define AFX_IDP_COMMAND_FAILURE 0xF109 // command failure +#define AFX_IDP_FAILED_MEMORY_ALLOC 0xF10A + +// DDV parse errors +#define AFX_IDP_PARSE_INT 0xF110 +#define AFX_IDP_PARSE_REAL 0xF111 +#define AFX_IDP_PARSE_INT_RANGE 0xF112 +#define AFX_IDP_PARSE_REAL_RANGE 0xF113 +#define AFX_IDP_PARSE_STRING_SIZE 0xF114 +#define AFX_IDP_PARSE_RADIO_BUTTON 0xF115 +#define AFX_IDP_PARSE_BYTE 0xF116 +#define AFX_IDP_PARSE_UINT 0xF117 +#define AFX_IDP_PARSE_DATETIME 0xF118 +#define AFX_IDP_PARSE_CURRENCY 0xF119 + +// CFile/CArchive error strings for user failure +#define AFX_IDP_FAILED_INVALID_FORMAT 0xF120 +#define AFX_IDP_FAILED_INVALID_PATH 0xF121 +#define AFX_IDP_FAILED_DISK_FULL 0xF122 +#define AFX_IDP_FAILED_ACCESS_READ 0xF123 +#define AFX_IDP_FAILED_ACCESS_WRITE 0xF124 +#define AFX_IDP_FAILED_IO_ERROR_READ 0xF125 +#define AFX_IDP_FAILED_IO_ERROR_WRITE 0xF126 + +// OLE errors / prompt strings +#define AFX_IDP_STATIC_OBJECT 0xF180 +#define AFX_IDP_FAILED_TO_CONNECT 0xF181 +#define AFX_IDP_SERVER_BUSY 0xF182 +#define AFX_IDP_BAD_VERB 0xF183 +#define AFX_IDP_FAILED_TO_NOTIFY 0xF185 +#define AFX_IDP_FAILED_TO_LAUNCH 0xF186 +#define AFX_IDP_ASK_TO_UPDATE 0xF187 +#define AFX_IDP_FAILED_TO_UPDATE 0xF188 +#define AFX_IDP_FAILED_TO_REGISTER 0xF189 +#define AFX_IDP_FAILED_TO_AUTO_REGISTER 0xF18A +#define AFX_IDP_FAILED_TO_CONVERT 0xF18B +#define AFX_IDP_GET_NOT_SUPPORTED 0xF18C +#define AFX_IDP_SET_NOT_SUPPORTED 0xF18D +#define AFX_IDP_ASK_TO_DISCARD 0xF18E +#define AFX_IDP_FAILED_TO_CREATE 0xF18F + +// MAPI errors / prompt strings +#define AFX_IDP_FAILED_MAPI_LOAD 0xF190 +#define AFX_IDP_INVALID_MAPI_DLL 0xF191 +#define AFX_IDP_FAILED_MAPI_SEND 0xF192 + +#define AFX_IDP_FILE_NONE 0xF1A0 +#define AFX_IDP_FILE_GENERIC 0xF1A1 +#define AFX_IDP_FILE_NOT_FOUND 0xF1A2 +#define AFX_IDP_FILE_BAD_PATH 0xF1A3 +#define AFX_IDP_FILE_TOO_MANY_OPEN 0xF1A4 +#define AFX_IDP_FILE_ACCESS_DENIED 0xF1A5 +#define AFX_IDP_FILE_INVALID_FILE 0xF1A6 +#define AFX_IDP_FILE_REMOVE_CURRENT 0xF1A7 +#define AFX_IDP_FILE_DIR_FULL 0xF1A8 +#define AFX_IDP_FILE_BAD_SEEK 0xF1A9 +#define AFX_IDP_FILE_HARD_IO 0xF1AA +#define AFX_IDP_FILE_SHARING 0xF1AB +#define AFX_IDP_FILE_LOCKING 0xF1AC +#define AFX_IDP_FILE_DISKFULL 0xF1AD +#define AFX_IDP_FILE_EOF 0xF1AE + +#define AFX_IDP_ARCH_NONE 0xF1B0 +#define AFX_IDP_ARCH_GENERIC 0xF1B1 +#define AFX_IDP_ARCH_READONLY 0xF1B2 +#define AFX_IDP_ARCH_ENDOFFILE 0xF1B3 +#define AFX_IDP_ARCH_WRITEONLY 0xF1B4 +#define AFX_IDP_ARCH_BADINDEX 0xF1B5 +#define AFX_IDP_ARCH_BADCLASS 0xF1B6 +#define AFX_IDP_ARCH_BADSCHEMA 0xF1B7 + +#define AFX_IDS_OCC_SCALEUNITS_PIXELS 0xF1C0 + +// 0xf200-0xf20f reserved + +// font names and point sizes +#define AFX_IDS_STATUS_FONT 0xF230 +#define AFX_IDS_TOOLTIP_FONT 0xF231 +#define AFX_IDS_UNICODE_FONT 0xF232 +#define AFX_IDS_MINI_FONT 0xF233 + +// ODBC Database errors / prompt strings +#ifndef RC_INVOKED // code only +#define AFX_IDP_SQL_FIRST 0xF280 +#endif //!RC_INVOKED +#define AFX_IDP_SQL_CONNECT_FAIL 0xF281 +#define AFX_IDP_SQL_RECORDSET_FORWARD_ONLY 0xF282 +#define AFX_IDP_SQL_EMPTY_COLUMN_LIST 0xF283 +#define AFX_IDP_SQL_FIELD_SCHEMA_MISMATCH 0xF284 +#define AFX_IDP_SQL_ILLEGAL_MODE 0xF285 +#define AFX_IDP_SQL_MULTIPLE_ROWS_AFFECTED 0xF286 +#define AFX_IDP_SQL_NO_CURRENT_RECORD 0xF287 +#define AFX_IDP_SQL_NO_ROWS_AFFECTED 0xF288 +#define AFX_IDP_SQL_RECORDSET_READONLY 0xF289 +#define AFX_IDP_SQL_SQL_NO_TOTAL 0xF28A +#define AFX_IDP_SQL_ODBC_LOAD_FAILED 0xF28B +#define AFX_IDP_SQL_DYNASET_NOT_SUPPORTED 0xF28C +#define AFX_IDP_SQL_SNAPSHOT_NOT_SUPPORTED 0xF28D +#define AFX_IDP_SQL_API_CONFORMANCE 0xF28E +#define AFX_IDP_SQL_SQL_CONFORMANCE 0xF28F +#define AFX_IDP_SQL_NO_DATA_FOUND 0xF290 +#define AFX_IDP_SQL_ROW_UPDATE_NOT_SUPPORTED 0xF291 +#define AFX_IDP_SQL_ODBC_V2_REQUIRED 0xF292 +#define AFX_IDP_SQL_NO_POSITIONED_UPDATES 0xF293 +#define AFX_IDP_SQL_LOCK_MODE_NOT_SUPPORTED 0xF294 +#define AFX_IDP_SQL_DATA_TRUNCATED 0xF295 +#define AFX_IDP_SQL_ROW_FETCH 0xF296 +#define AFX_IDP_SQL_INCORRECT_ODBC 0xF297 +#define AFX_IDP_SQL_UPDATE_DELETE_FAILED 0xF298 +#define AFX_IDP_SQL_DYNAMIC_CURSOR_NOT_SUPPORTED 0xF299 + +// DAO Database errors / prompt strings +#ifndef RC_INVOKED // code only +#define AFX_IDP_DAO_FIRST 0xF2A0 +#endif //!RC_INVOKED +#define AFX_IDP_DAO_ENGINE_INITIALIZATION 0xF2A0 +#define AFX_IDP_DAO_DFX_BIND 0xF2A1 +#define AFX_IDP_DAO_OBJECT_NOT_OPEN 0xF2A2 + +// ICDAORecordset::GetRows Errors +// These are not placed in DAO Errors collection +// and must be handled directly by MFC. +#define AFX_IDP_DAO_ROWTOOSHORT 0xF2A3 +#define AFX_IDP_DAO_BADBINDINFO 0xF2A4 +#define AFX_IDP_DAO_COLUMNUNAVAILABLE 0xF2A5 + +///////////////////////////////////////////////////////////////////////////// +// AFX implementation - control IDs (AFX_IDC) + +// Parts of dialogs +#define AFX_IDC_LISTBOX 100 +#define AFX_IDC_CHANGE 101 + +// for print dialog +#define AFX_IDC_PRINT_DOCNAME 201 +#define AFX_IDC_PRINT_PRINTERNAME 202 +#define AFX_IDC_PRINT_PORTNAME 203 +#define AFX_IDC_PRINT_PAGENUM 204 + +// Property Sheet control id's (determined with Spy++) +#define ID_APPLY_NOW 0x3021 +#define ID_WIZBACK 0x3023 +#define ID_WIZNEXT 0x3024 +#define ID_WIZFINISH 0x3025 +#define AFX_IDC_TAB_CONTROL 0x3020 + +///////////////////////////////////////////////////////////////////////////// +// IDRs for standard components + +#ifndef RC_INVOKED // code only +// These are really COMMDLG dialogs, so there usually isn't a resource +// for them, but these IDs are used as help IDs. +#define AFX_IDD_FILEOPEN 28676 +#define AFX_IDD_FILESAVE 28677 +#define AFX_IDD_FONT 28678 +#define AFX_IDD_COLOR 28679 +#define AFX_IDD_PRINT 28680 +#define AFX_IDD_PRINTSETUP 28681 +#define AFX_IDD_FIND 28682 +#define AFX_IDD_REPLACE 28683 +#endif //!RC_INVOKED + +// Standard dialogs app should leave alone (0x7801->) +#define AFX_IDD_NEWTYPEDLG 30721 +#define AFX_IDD_PRINTDLG 30722 +#define AFX_IDD_PREVIEW_TOOLBAR 30723 +#ifdef _MAC +#define AFX_IDD_PREVIEW_SHORTTOOLBAR 30731 +#endif + +// Dialogs defined for OLE2UI library +#define AFX_IDD_INSERTOBJECT 30724 +#define AFX_IDD_CHANGEICON 30725 +#define AFX_IDD_CONVERT 30726 +#define AFX_IDD_PASTESPECIAL 30727 +#define AFX_IDD_EDITLINKS 30728 +#define AFX_IDD_FILEBROWSE 30729 +#define AFX_IDD_BUSY 30730 + +#define AFX_IDD_OBJECTPROPERTIES 30732 +#define AFX_IDD_CHANGESOURCE 30733 + +// Standard cursors (0x7901->) + // AFX_IDC = Cursor resources +#define AFX_IDC_CONTEXTHELP 30977 // context sensitive help +#define AFX_IDC_MAGNIFY 30978 // print preview zoom +#define AFX_IDC_SMALLARROWS 30979 // splitter +#define AFX_IDC_HSPLITBAR 30980 // splitter +#define AFX_IDC_VSPLITBAR 30981 // splitter +#define AFX_IDC_NODROPCRSR 30982 // No Drop Cursor +#define AFX_IDC_TRACKNWSE 30983 // tracker +#define AFX_IDC_TRACKNESW 30984 // tracker +#define AFX_IDC_TRACKNS 30985 // tracker +#define AFX_IDC_TRACKWE 30986 // tracker +#define AFX_IDC_TRACK4WAY 30987 // tracker +#define AFX_IDC_MOVE4WAY 30988 // resize bar (server only) + +// Mini frame window bitmap ID +#define AFX_IDB_MINIFRAME_MENU 30994 + +// CheckListBox checks bitmap ID +#define AFX_IDB_CHECKLISTBOX_NT 30995 +#define AFX_IDB_CHECKLISTBOX_95 30996 + +// AFX standard accelerator resources +#define AFX_IDR_PREVIEW_ACCEL 30997 + +// AFX standard ICON IDs (for MFC V1 apps) (0x7A01->) +#define AFX_IDI_STD_MDIFRAME 31233 +#define AFX_IDI_STD_FRAME 31234 + +///////////////////////////////////////////////////////////////////////////// +// AFX OLE control implementation - control IDs (AFX_IDC) + +// Font property page +#define AFX_IDC_FONTPROP 1000 +#define AFX_IDC_FONTNAMES 1001 +#define AFX_IDC_FONTSTYLES 1002 +#define AFX_IDC_FONTSIZES 1003 +#define AFX_IDC_STRIKEOUT 1004 +#define AFX_IDC_UNDERLINE 1005 +#define AFX_IDC_SAMPLEBOX 1006 + +// Color property page +#define AFX_IDC_COLOR_BLACK 1100 +#define AFX_IDC_COLOR_WHITE 1101 +#define AFX_IDC_COLOR_RED 1102 +#define AFX_IDC_COLOR_GREEN 1103 +#define AFX_IDC_COLOR_BLUE 1104 +#define AFX_IDC_COLOR_YELLOW 1105 +#define AFX_IDC_COLOR_MAGENTA 1106 +#define AFX_IDC_COLOR_CYAN 1107 +#define AFX_IDC_COLOR_GRAY 1108 +#define AFX_IDC_COLOR_LIGHTGRAY 1109 +#define AFX_IDC_COLOR_DARKRED 1110 +#define AFX_IDC_COLOR_DARKGREEN 1111 +#define AFX_IDC_COLOR_DARKBLUE 1112 +#define AFX_IDC_COLOR_LIGHTBROWN 1113 +#define AFX_IDC_COLOR_DARKMAGENTA 1114 +#define AFX_IDC_COLOR_DARKCYAN 1115 +#define AFX_IDC_COLORPROP 1116 +#define AFX_IDC_SYSTEMCOLORS 1117 + +// Picture porperty page +#define AFX_IDC_PROPNAME 1201 +#define AFX_IDC_PICTURE 1202 +#define AFX_IDC_BROWSE 1203 +#define AFX_IDC_CLEAR 1204 + +///////////////////////////////////////////////////////////////////////////// +// IDRs for OLE control standard components + +// Standard propery page dialogs app should leave alone (0x7E01->) +#define AFX_IDD_PROPPAGE_COLOR 32257 +#define AFX_IDD_PROPPAGE_FONT 32258 +#define AFX_IDD_PROPPAGE_PICTURE 32259 + +#define AFX_IDB_TRUETYPE 32384 + +///////////////////////////////////////////////////////////////////////////// +// Standard OLE control strings + +// OLE Control page strings +#define AFX_IDS_PROPPAGE_UNKNOWN 0xFE01 +#define AFX_IDS_COLOR_DESKTOP 0xFE04 +#define AFX_IDS_COLOR_APPWORKSPACE 0xFE05 +#define AFX_IDS_COLOR_WNDBACKGND 0xFE06 +#define AFX_IDS_COLOR_WNDTEXT 0xFE07 +#define AFX_IDS_COLOR_MENUBAR 0xFE08 +#define AFX_IDS_COLOR_MENUTEXT 0xFE09 +#define AFX_IDS_COLOR_ACTIVEBAR 0xFE0A +#define AFX_IDS_COLOR_INACTIVEBAR 0xFE0B +#define AFX_IDS_COLOR_ACTIVETEXT 0xFE0C +#define AFX_IDS_COLOR_INACTIVETEXT 0xFE0D +#define AFX_IDS_COLOR_ACTIVEBORDER 0xFE0E +#define AFX_IDS_COLOR_INACTIVEBORDER 0xFE0F +#define AFX_IDS_COLOR_WNDFRAME 0xFE10 +#define AFX_IDS_COLOR_SCROLLBARS 0xFE11 +#define AFX_IDS_COLOR_BTNFACE 0xFE12 +#define AFX_IDS_COLOR_BTNSHADOW 0xFE13 +#define AFX_IDS_COLOR_BTNTEXT 0xFE14 +#define AFX_IDS_COLOR_BTNHIGHLIGHT 0xFE15 +#define AFX_IDS_COLOR_DISABLEDTEXT 0xFE16 +#define AFX_IDS_COLOR_HIGHLIGHT 0xFE17 +#define AFX_IDS_COLOR_HIGHLIGHTTEXT 0xFE18 +#define AFX_IDS_REGULAR 0xFE19 +#define AFX_IDS_BOLD 0xFE1A +#define AFX_IDS_ITALIC 0xFE1B +#define AFX_IDS_BOLDITALIC 0xFE1C +#define AFX_IDS_SAMPLETEXT 0xFE1D +#define AFX_IDS_DISPLAYSTRING_FONT 0xFE1E +#define AFX_IDS_DISPLAYSTRING_COLOR 0xFE1F +#define AFX_IDS_DISPLAYSTRING_PICTURE 0xFE20 +#define AFX_IDS_PICTUREFILTER 0xFE21 +#define AFX_IDS_PICTYPE_UNKNOWN 0xFE22 +#define AFX_IDS_PICTYPE_NONE 0xFE23 +#define AFX_IDS_PICTYPE_BITMAP 0xFE24 +#define AFX_IDS_PICTYPE_METAFILE 0xFE25 +#define AFX_IDS_PICTYPE_ICON 0xFE26 +#define AFX_IDS_COLOR_PPG 0xFE28 +#define AFX_IDS_COLOR_PPG_CAPTION 0xFE29 +#define AFX_IDS_FONT_PPG 0xFE2A +#define AFX_IDS_FONT_PPG_CAPTION 0xFE2B +#define AFX_IDS_PICTURE_PPG 0xFE2C +#define AFX_IDS_PICTURE_PPG_CAPTION 0xFE2D +#define AFX_IDS_PICTUREBROWSETITLE 0xFE30 +#define AFX_IDS_BORDERSTYLE_0 0xFE31 +#define AFX_IDS_BORDERSTYLE_1 0xFE32 + +// OLE Control verb names +#define AFX_IDS_VERB_EDIT 0xFE40 +#define AFX_IDS_VERB_PROPERTIES 0xFE41 + +// OLE Control internal error messages +#define AFX_IDP_PICTURECANTOPEN 0xFE83 +#define AFX_IDP_PICTURECANTLOAD 0xFE84 +#define AFX_IDP_PICTURETOOLARGE 0xFE85 +#define AFX_IDP_PICTUREREADFAILED 0xFE86 + +// Standard OLE Control error strings +#define AFX_IDP_E_ILLEGALFUNCTIONCALL 0xFEA0 +#define AFX_IDP_E_OVERFLOW 0xFEA1 +#define AFX_IDP_E_OUTOFMEMORY 0xFEA2 +#define AFX_IDP_E_DIVISIONBYZERO 0xFEA3 +#define AFX_IDP_E_OUTOFSTRINGSPACE 0xFEA4 +#define AFX_IDP_E_OUTOFSTACKSPACE 0xFEA5 +#define AFX_IDP_E_BADFILENAMEORNUMBER 0xFEA6 +#define AFX_IDP_E_FILENOTFOUND 0xFEA7 +#define AFX_IDP_E_BADFILEMODE 0xFEA8 +#define AFX_IDP_E_FILEALREADYOPEN 0xFEA9 +#define AFX_IDP_E_DEVICEIOERROR 0xFEAA +#define AFX_IDP_E_FILEALREADYEXISTS 0xFEAB +#define AFX_IDP_E_BADRECORDLENGTH 0xFEAC +#define AFX_IDP_E_DISKFULL 0xFEAD +#define AFX_IDP_E_BADRECORDNUMBER 0xFEAE +#define AFX_IDP_E_BADFILENAME 0xFEAF +#define AFX_IDP_E_TOOMANYFILES 0xFEB0 +#define AFX_IDP_E_DEVICEUNAVAILABLE 0xFEB1 +#define AFX_IDP_E_PERMISSIONDENIED 0xFEB2 +#define AFX_IDP_E_DISKNOTREADY 0xFEB3 +#define AFX_IDP_E_PATHFILEACCESSERROR 0xFEB4 +#define AFX_IDP_E_PATHNOTFOUND 0xFEB5 +#define AFX_IDP_E_INVALIDPATTERNSTRING 0xFEB6 +#define AFX_IDP_E_INVALIDUSEOFNULL 0xFEB7 +#define AFX_IDP_E_INVALIDFILEFORMAT 0xFEB8 +#define AFX_IDP_E_INVALIDPROPERTYVALUE 0xFEB9 +#define AFX_IDP_E_INVALIDPROPERTYARRAYINDEX 0xFEBA +#define AFX_IDP_E_SETNOTSUPPORTEDATRUNTIME 0xFEBB +#define AFX_IDP_E_SETNOTSUPPORTED 0xFEBC +#define AFX_IDP_E_NEEDPROPERTYARRAYINDEX 0xFEBD +#define AFX_IDP_E_SETNOTPERMITTED 0xFEBE +#define AFX_IDP_E_GETNOTSUPPORTEDATRUNTIME 0xFEBF +#define AFX_IDP_E_GETNOTSUPPORTED 0xFEC0 +#define AFX_IDP_E_PROPERTYNOTFOUND 0xFEC1 +#define AFX_IDP_E_INVALIDCLIPBOARDFORMAT 0xFEC2 +#define AFX_IDP_E_INVALIDPICTURE 0xFEC3 +#define AFX_IDP_E_PRINTERERROR 0xFEC4 +#define AFX_IDP_E_CANTSAVEFILETOTEMP 0xFEC5 +#define AFX_IDP_E_SEARCHTEXTNOTFOUND 0xFEC6 +#define AFX_IDP_E_REPLACEMENTSTOOLONG 0xFEC7 + +#ifdef _AFX_MINREBUILD +#pragma component(minrebuild, on) +#endif + +#endif //__AFXRES_H__ + +///////////////////////////////////////////////////////////////////////////// diff --git a/.svn/pristine/31/319684bea156cd928626feb7046dab8dae8bb9b7.svn-base b/.svn/pristine/31/319684bea156cd928626feb7046dab8dae8bb9b7.svn-base new file mode 100644 index 0000000..a37e562 --- /dev/null +++ b/.svn/pristine/31/319684bea156cd928626feb7046dab8dae8bb9b7.svn-base @@ -0,0 +1,432 @@ +// Mail and Chat Server for BPQ32 Packet Switch +// +// Debug Window(s) Module + +#include "BPQChat.h" + +static char ClassName[]="BPQDEBUGWINDOW"; + + +static WNDPROC wpOrigInputProc; +static WNDPROC wpOrigOutputProc; + +HWND hDebug; +static HWND hwndInput; +static HWND hwndOutput; + +static HMENU hMenu; // handle of menu + +#define InputBoxHeight 25 + +RECT DebugRect; + + +int Height, Width, LastY; + +static char readbuff[1024]; + +static BOOL Bells = TRUE; +static BOOL StripLF = TRUE; +static BOOL MonBBS = TRUE; +static BOOL MonCHAT = TRUE; +static BOOL MonTCP = TRUE; + +static int PartLinePtr=0; +static int PartLineIndex=0; // Listbox index of (last) incomplete line + + +static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +static LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY MonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY SplitProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static void MoveWindows(); + +#define BGCOLOUR RGB(236,233,216) + +extern char DebugSize[32]; + +BOOL CreateDebugWindow() +{ + WNDCLASS wc; + HBRUSH bgBrush; + char Text[80]; + + if (hDebug) + { + ShowWindow(hDebug, SW_SHOWNORMAL); + SetForegroundWindow(hDebug); + return FALSE; // Alreaqy open + } + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = MonWndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + + hDebug=CreateDialog(hInst,ClassName,0,NULL); + + if (!hDebug) + return (FALSE); + + wsprintf(Text, "Chat %s Debug", Session); + SetWindowText(hDebug, Text); + + hMenu=GetMenu(hDebug); + + if (Bells & 1) + CheckMenuItem(hMenu,BPQBELLS, MF_CHECKED); + else + CheckMenuItem(hMenu,BPQBELLS, MF_UNCHECKED); + + if (StripLF & 1) + CheckMenuItem(hMenu,BPQStripLF, MF_CHECKED); + else + CheckMenuItem(hMenu,BPQStripLF, MF_UNCHECKED); + + CheckMenuItem(hMenu,MONBBS, MonBBS ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,MONCHAT, MonCHAT ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,MONTCP, MonTCP ? MF_CHECKED : MF_UNCHECKED); + + DrawMenuBar(hWnd); + + // Retrieve the handlse to the edit controls. + + hwndOutput = GetDlgItem(hDebug, 122); + + // Set our own WndProcs for the controls. + + wpOrigOutputProc = (WNDPROC)SetWindowLong(hwndOutput, GWL_WNDPROC, (LONG)OutputProc); + + if (cfgMinToTray) + { + AddTrayMenuItem(hDebug, Text); + } + ShowWindow(hDebug, SW_SHOWNORMAL); + + if (DebugRect.right < 100 || DebugRect.bottom < 100) + { + GetWindowRect(hDebug, &DebugRect); + } + + MoveWindow(hDebug,DebugRect.left,DebugRect.top, DebugRect.right-DebugRect.left, DebugRect.bottom-DebugRect.top, TRUE); + + MoveWindows(); + + return TRUE; + +} + + +static void MoveWindows() +{ + RECT rcMain, rcClient; + int ClientHeight, ClientWidth; + + GetWindowRect(hDebug, &rcMain); + GetClientRect(hDebug, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + +// MoveWindow(hwndMon,2, 0, ClientWidth-4, SplitPos, TRUE); + MoveWindow(hwndOutput,2, 2, ClientWidth-4, ClientHeight-4, TRUE); +// MoveWindow(hwndSplit,0, SplitPos, ClientWidth, SplitBarHeight, TRUE); +} + + +static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + + switch (message) { + + case WM_ACTIVATE: + + SetFocus(hwndInput); + break; + + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case MONBBS: + + ToggleParam(hMenu, hWnd, &MonBBS, MONBBS); + break; + + case MONCHAT: + + ToggleParam(hMenu, hWnd, &MonCHAT, MONCHAT); + break; + + case MONTCP: + + ToggleParam(hMenu, hWnd, &MonTCP, MONTCP); + break; + + + case BPQCLEAROUT: + + SendMessage(hwndOutput,LB_RESETCONTENT, 0, 0); + break; + + case BPQCOPYOUT: + + CopyToClipboard(hwndOutput); + break; + + + + //case BPQHELP: + + // HtmlHelp(hWnd,"BPQTerminal.chm",HH_HELP_FINDER,0); + // break; + + default: + + return 0; + + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case SC_MINIMIZE: + + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Height = lprc->bottom-lprc->top; + Width = lprc->right-lprc->left; + + MoveWindows(); + + return TRUE; + + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &DebugRect); // For save soutine + + SetWindowLong(hwndInput, GWL_WNDPROC, + (LONG) wpOrigInputProc); + + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + + hDebug = NULL; + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + + +LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + + // Trap mouse messages, so we cant select stuff in output and mon windows, + // otherwise scrolling doesnt work. + + if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) + return TRUE; + + return CallWindowProc(wpOrigOutputProc, hwnd, uMsg, wParam, lParam); +} + +VOID ClearDebugWindow() +{ + SendMessage(hwndOutput,LB_RESETCONTENT, 0, 0); +} + +VOID WritetoDebugWindow(char * Msg, int len) +{ + char * ptr1, * ptr2; + int index; + + if (len ==0) + return; + + + if (PartLinePtr != 0) + SendMessage(hwndOutput,LB_DELETESTRING,PartLineIndex,(LPARAM)(LPCTSTR) 0 ); + + memcpy(&readbuff[PartLinePtr], Msg, len); + + len=len+PartLinePtr; + + ptr1=&readbuff[0]; + readbuff[len]=0; + + if (Bells) + { + do { + + ptr2=memchr(ptr1,7,len); + + if (ptr2) + { + *(ptr2)=32; + Beep(440,250); + } + + } while (ptr2); + + } + +lineloop: + + if (PartLinePtr > 300) + PartLinePtr = 0; + + if (len > 0) + { + // copy text to control a line at a time + + ptr2=memchr(ptr1,13,len); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + PartLinePtr=len; + memmove(readbuff,ptr1,len); + PartLineIndex=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) PartLineIndex, MAKELPARAM(FALSE, 0)); + + return; + + } + + *(ptr2++)=0; + + index=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + + // if (LogOutput) WriteMonitorLine(ptr1, ptr2 - ptr1); + + PartLinePtr=0; + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + if ((len > 0) && StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + + if (index > 1200) + + do{ + + index=SendMessage(hwndOutput,LB_DELETESTRING, 0, 0); + + } while (index > 1000); + + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); + + goto lineloop; + } + return; +} + +/*static int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item) +{ + *Param = !(*Param); + + CheckMenuItem(hMenu,Item, (*Param) ? MF_CHECKED : MF_UNCHECKED); + + return (0); +} +*/ +static void CopyToClipboard(HWND hWnd) +{ + int i,n, len=0; + HGLOBAL hMem; + char * ptr; + // + // Copy List Box to clipboard + // + + n = SendMessage(hWnd, LB_GETCOUNT, 0, 0); + + for (i=0; i +#include "cheaders.h" +#include "bpq32.h" +#include +#include "kernelresource.h" + +#include "tncinfo.h" + +#include "bpqaprs.h" + +#ifndef WIN32 + +#include +#include +#include + +int sfd; +struct sockaddr_un my_addr, peer_addr; +socklen_t peer_addr_size; + + +#endif + + +#define MAXAGE 3600 * 12 // 12 Hours +#define MAXCALLS 20 // Max Flood, Trace and Digi +#define GATETIMELIMIT 40 * 60 // Don't gate to RF if station not heard for this time (40 mins) + +static BOOL APIENTRY GETSENDNETFRAMEADDR(); +static VOID DoSecTimer(); +static VOID DoMinTimer(); +static int APRSProcessLine(char * buf); +static BOOL APRSReadConfigFile(); +VOID APRSISThread(void * Report); +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Consoleprintf(const char * format, ...); +BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port); +VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); +int APRSDecodeFrame(char * msg, char * buffer, time_t Stamp, uint64_t Mask); // Unsemaphored DecodeFrame +APRSHEARDRECORD * UpdateHeard(UCHAR * Call, int Port); +BOOL CheckforDups(char * Call, char * Msg, int Len); +VOID ProcessQuery(char * Query); +VOID ProcessSpecificQuery(char * Query, int Port, char * Origin, char * DestPlusDigis); +VOID CheckandDigi(DIGIMESSAGE * Msg, int Port, int FirstUnused, int Digis, int Len); +VOID SendBeacon(int toPort, char * Msg, BOOL SendISStatus, BOOL SendSOGCOG); +Dll BOOL APIENTRY PutAPRSMessage(char * Frame, int Len); +VOID ProcessAPRSISMsg(char * APRSMsg); +static VOID SendtoDigiPorts(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); +APRSHEARDRECORD * FindStationInMH(char * call); +BOOL OpenGPSPort(); +void PollGPSIn(); +int CountLocalStations(); +BOOL SendAPPLAPRSMessage(char * Frame); +VOID SendAPRSMessage(char * Message, int toPort); +static VOID TCPConnect(void * unuxed); +struct STATIONRECORD * DecodeAPRSISMsg(char * msg); +struct STATIONRECORD * ProcessRFFrame(char * buffer, int len, int * ourMessage); +VOID APRSSecTimer(); +double myDistance(double laa, double loa, BOOL KM); +struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFound); +int DecodeAPRSPayload(char * Payload, struct STATIONRECORD * Station); +BOOL KillOldTNC(char * Path); + +BOOL ToLOC(double Lat, double Lon , char * Locator); +BOOL InternalSendAPRSMessage(char * Text, char * Call); +void UndoTransparency(char * input); +char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...); +char * GetStandardPage(char * FN, int * Len); +VOID WriteMiniDump(); +BOOL ProcessConfig(); +int ProcessAISMessage(char * msg, int len); +int read_png(unsigned char *bytes); +VOID sendandcheck(SOCKET sock, const char * Buffer, int Len); +void SaveAPRSMessage(struct APRSMESSAGE * ptr); +void ClearSavedMessages(); +void GetSavedAPRSMessages(); +static VOID GPSDConnect(void * unused); +int CanPortDigi(int Port); +int FromLOC(char * Locator, double * pLat, double * pLon); + +extern int SemHeldByAPI; +extern int APRSMONDECODE(); +extern struct ConsoleInfo MonWindow; +extern char VersionString[]; + +BOOL SaveAPRSMsgs = 0; + +BOOL LogAPRSIS = FALSE; + +// All data should be initialised to force into shared segment + +static char ConfigClassName[]="CONFIG"; + +extern BPQVECSTRUC * APRSMONVECPTR; + +extern int MONDECODE(); +extern VOID * zalloc(int len); +extern BOOL StartMinimized; + +extern char TextVerstring[]; + +extern HWND hConsWnd; +extern HKEY REGTREE; + +extern char LOCATOR[80]; +extern char LOC[7]; + +static int SecTimer = 10; +static int MinTimer = 60; + +BOOL APRSApplConnected = FALSE; +BOOL APRSWeb = FALSE; + +void * APPL_Q = 0; // Queue of frames for APRS Appl +void * APPLTX_Q = 0; // Queue of frames from APRS Appl +uint64_t APRSPortMask = 0; + +char APRSCall[10] = ""; +char APRSDest[10] = "APBPQ1"; + +char WXCall[10]; + +UCHAR AXCall[7] = ""; + +char CallPadded[10] = " "; + +char GPSPort[80] = ""; +int GPSSpeed = 0; +char GPSRelay[80] = ""; + +BOOL GateLocal = FALSE; +double GateLocalDistance = 0.0; + +int MaxDigisforIS = 7; // Dont send to IS if more digis uued to reach us + +char WXFileName[MAX_PATH]; +char WXComment[80]; +BOOL SendWX = FALSE; +int WXInterval = 30; +int WXCounter = 29 * 60; + +char APRSCall[10]; +char LoppedAPRSCall[10]; + +BOOL WXPort[MaxBPQPortNo + 1]; // Ports to send WX to + +BOOL GPSOK = 0; + +char LAT[] = "0000.00N"; // in standard APRS Format +char LON[] = "00000.00W"; //in standard APRS Format + +char HostName[80]; // for BlueNMEA +int HostPort = 4352; + +char GPSDHost[80]; +int GPSDPort = 2947; + + +extern int ADSBPort; +extern char ADSBHost[]; + +BOOL BlueNMEAOK = FALSE; +int BlueNMEATimer = 0; + +BOOL GPSDOK = FALSE; +int GPSDTimer = 0; + + +BOOL GPSSetsLocator = 0; // Update Map Location from GPS + +double SOG, COG; // From GPS + +double Lat = 0.0; +double Lon = 0.0; + +BOOL PosnSet = FALSE; +/* +The null position should be include the \. symbol (unknown/indeterminate +position). For example, a Position Report for a station with unknown position +will contain the coordinates …0000.00N\00000.00W.… +*/ +char * FloodCalls = 0; // Calls to relay using N-n without tracing +char * TraceCalls = 0; // Calls to relay using N-n with tracing +char * DigiCalls = 0; // Calls for normal relaying + +UCHAR FloodAX[MAXCALLS][7] = {0}; +UCHAR TraceAX[MAXCALLS][7] = {0}; +UCHAR DigiAX[MAXCALLS][7] = {0}; + +int FloodLen[MAXCALLS]; +int TraceLen[MAXCALLS]; +int DigiLen[MAXCALLS]; + +int ISPort = 0; +char ISHost[256] = ""; +int ISPasscode = 0; +char NodeFilter[1000] = "m/50"; // Filter when the isn't an application +char ISFilter[1000] = "m/50"; // Current Filter +char APPLFilter[1000] = ""; // Filter when an Applcation is running + +extern BOOL IGateEnabled; + +char StatusMsg[256] = ""; // Must be in shared segment +int StatusMsgLen = 0; + +char * BeaconPath[65] = {0}; + +char CrossPortMap[65][65] = {0}; +char APRSBridgeMap[65][65] = {0}; + +UCHAR BeaconHeader[65][10][7] = {""}; // Dest, Source and up to 8 digis +int BeaconHddrLen[65] = {0}; // Actual Length used + +UCHAR GatedHeader[65][10][7] = {""}; // Dest, Source and up to 8 digis for messages gated from IS +int GatedHddrLen[65] = {0}; // Actual Length used + + +char CFGSYMBOL = 'a'; +char CFGSYMSET = 'B'; + +char SYMBOL = '='; // Unknown Locaton +char SYMSET = '/'; + +char * PHG = 0; // Optional PHG (Power-Height-Gain) string for beacon + +BOOL TraceDigi = FALSE; // Add Trace to packets relayed on Digi Calls +BOOL SATGate = FALSE; // Delay Gating to IS directly heard packets +BOOL RXOnly = FALSE; // Run as RX only IGATE, ie don't gate anything to RF + +BOOL DefaultLocalTime = FALSE; +BOOL DefaultDistKM = FALSE; + +int multiple = 0; // Allows multiple copies of LinBPQ/APRS on one machine + +extern BOOL needAIS; + +extern unsigned long long IconData[]; // Symbols as a png image. + +typedef struct _ISDELAY +{ + struct _ISDELAY * Next; + char * ISMSG; + time_t SendTIme; +} ISDELAY; + +ISDELAY * SatISQueue = NULL; + +int MaxTraceHops = 2; +int MaxFloodHops = 2; + +int BeaconInterval = 0; +int MobileBeaconInterval = 0; +time_t LastMobileBeacon = 0; +int BeaconCounter = 0; +int IStatusCounter = 3600; // Used to send ?ISTATUS? Responses +//int StatusCounter = 0; // Used to send Status Messages + +char RunProgram[128] = ""; // Program to start + +BOOL APRSISOpen = FALSE; +BOOL BeacontoIS = TRUE; + +int ISDelayTimer = 0; // Time before trying to reopen APRS-IS link + +char APRSDESTS[][7] = {"AIR*", "ALL*", "AP*", "BEACON", "CQ*", "GPS*", "DF*", "DGPS*", "DRILL*", + "DX*", "ID*", "JAVA*", "MAIL*", "MICE*", "QST*", "QTH*", "RTCM*", "SKY*", + "SPACE*", "SPC*", "SYM*", "TEL*", "TEST*", "TLM*", "WX*", "ZIP"}; + +UCHAR AXDESTS[30][7] = {""}; +int AXDESTLEN[30] = {0}; + +UCHAR axTCPIP[7]; +UCHAR axRFONLY[7]; +UCHAR axNOGATE[7]; + +int MessageCount = 0; + +struct PortInfo +{ + int Index; + int ComPort; + char PortType[2]; + BOOL NewVCOM; // Using User Mode Virtual COM Driver + int ReopenTimer; // Retry if open failed delay + int RTS; + int CTS; + int DCD; + int DTR; + int DSR; + char Params[20]; // Init Params (eg 9600,n,8) + char PortLabel[20]; + HANDLE hDevice; + BOOL Created; + BOOL PortEnabled; + int FLOWCTRL; + int gpsinptr; +#ifdef WIN32 + OVERLAPPED Overlapped; + OVERLAPPED OverlappedRead; +#endif + char GPSinMsg[160]; + int GPSTypeFlag; // GPS Source flags + BOOL RMCOnly; // Only send RMC msgs to this port +}; + + + +struct PortInfo InPorts[1] = {0}; + +// Heard Station info + +#define MAXHEARD 1000 + +int HEARDENTRIES = 0; +int MAXHEARDENTRIES = 0; +int MHLEN = sizeof(APRSHEARDRECORD); + +// Area is allocated as needed + +APRSHEARDRECORD MHTABLE[MAXHEARD] = {0}; + +APRSHEARDRECORD * MHDATA = &MHTABLE[0]; + +static SOCKET sock = 0; + +//Duplicate suppression Code + +#define MAXDUPS 100 // Number to keep +#define DUPSECONDS 28 // Time to Keep + +struct DUPINFO +{ + time_t DupTime; + int DupLen; + char DupUser[8]; // Call in ax.35 format + char DupText[100]; +}; + +struct DUPINFO DupInfo[MAXDUPS]; + +struct OBJECT +{ + struct OBJECT * Next; + UCHAR Path[10][7]; // Dest, Source and up to 8 digis + int PathLen; // Actual Length used + char Message[81]; + char PortMap[MaxBPQPortNo + 1]; + int Interval; + int Timer; +}; + +struct OBJECT * ObjectList; // List of objects to send; + +int ObjectCount = 0; + +#include + +#define M_PI 3.14159265358979323846 + +int RetryCount = 4; +int RetryTimer = 45; +int ExpireTime = 120; +int TrackExpireTime = 1440; +BOOL SuppressNullPosn = FALSE; +BOOL DefaultNoTracks = FALSE; + +int MaxStations = 1000; + +int SharedMemorySize = 0; + + +RECT Rect, MsgRect, StnRect; + +char Key[80]; + +// function prototypes + +VOID RefreshMessages(); + +// a few global variables + +char APRSDir[MAX_PATH] = "BPQAPRS"; +char DF[MAX_PATH]; + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +int StationCount = 0; + +UCHAR NextSeq = 1; + +// Stationrecords are stored in a shared memory segment. based at APRSStationMemory (normally 0x43000000) + +// A pointer to the first is placed at the start of this + +struct STATIONRECORD ** StationRecords = NULL; +struct STATIONRECORD * StationRecordPool = NULL; +struct APRSMESSAGE * MessageRecordPool = NULL; + +struct SharedMem * SMEM; + +UCHAR * Shared; +UCHAR * StnRecordBase; + +VOID SendObject(struct OBJECT * Object); +VOID MonitorAPRSIS(char * Msg, int MsgLen, BOOL TX); + +#ifndef WIN32 +#define WSAEWOULDBLOCK 11 +#endif + +HANDLE hMapFile; + +// Logging + +static int LogAge = 14; + +#ifdef WIN32 + +int DeleteAPRSLogFiles() +{ + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + char File[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + DWORD dwError=0; + LARGE_INTEGER ft; + time_t now = time(NULL); + int Age; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetLogDirectory()); + strcat(szDir, "/logs/APRS*.log"); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + return dwError; + + // Walk directory + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugString(ffd.cFileName); + } + else + { + ft.HighPart = ffd.ftCreationTime.dwHighDateTime; + ft.LowPart = ffd.ftCreationTime.dwLowDateTime; + + ft.QuadPart -= 116444736000000000; + ft.QuadPart /= 10000000; + + Age = (int)((now - ft.LowPart) / 86400); + + if (Age > LogAge) + { + sprintf(File, "%s/logs/%s%c", GetLogDirectory(), ffd.cFileName, 0); + Debugprintf("Deleting %s", File); + DeleteFile(File); + } + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + return dwError; +} + +#else + +#include + +int APRSFilter(const struct dirent * dir) +{ + return (memcmp(dir->d_name, "APRS", 4) == 0 && strstr(dir->d_name, ".log")); +} + +int DeleteAPRSLogFiles() +{ + struct dirent **namelist; + int n; + struct stat STAT; + time_t now = time(NULL); + int Age = 0, res; + char FN[256]; + + n = scandir("logs", &namelist, APRSFilter, alphasort); + + if (n < 0) + perror("scandir"); + else + { + while(n--) + { + sprintf(FN, "logs/%s", namelist[n]->d_name); + if (stat(FN, &STAT) == 0) + { + Age = (now - STAT.st_mtime) / 86400; + + if (Age > LogAge) + { + Debugprintf("Deleting %s\n", FN); + unlink(FN); + } + } + free(namelist[n]); + } + free(namelist); + } + return 0; +} +#endif + +int APRSWriteLog(char * msg) +{ + FILE *file; + UCHAR Value[MAX_PATH]; + time_t T; + struct tm * tm; + int n; + + + if (LogAPRSIS == 0) + return 0; + + if (strchr(msg, '\n') == 0) + strcat(msg, "\r\n"); + + T = time(NULL); + tm = gmtime(&T); + + if (GetLogDirectory()[0] == 0) + { + strcpy(Value, "logs/APRS_"); + } + else + { + strcpy(Value, GetLogDirectory()); + strcat(Value, "/"); + strcat(Value, "logs/APRS_"); + } + + n = strlen(Value); + + sprintf(&Value[n], "%02d%02d%02d.log", tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + + if ((file = fopen(Value, "ab")) == NULL) + return FALSE; + + fputs(msg, file); + fclose(file); + return 0; +} + + +int ISSend(SOCKET sock, char * Msg, int Len, int flags) +{ + int Loops = 0; + int Sent; + + MonitorAPRSIS(Msg, Len, TRUE); + + Sent = send(sock, Msg, Len, flags); + + while (Sent != Len && Loops++ < 300) // 10 secs max + { + if ((Sent == SOCKET_ERROR) && (WSAGetLastError() != WSAEWOULDBLOCK)) + return SOCKET_ERROR; + + if (Sent > 0) // something sent + { + Len -= Sent; + memmove(Msg, &Msg[Sent], Len); + } + + Sleep(30); + Sent = send(sock, Msg, Len, flags); + } + + return Sent; +} + +void * endofStations; + +Dll BOOL APIENTRY Init_APRS() +{ + int i; + char * DCall; + +#ifdef WIN32 + HKEY hKey=0; + int retCode, Vallen, Type; +#else + int fd; + char RX_SOCK_PATH[] = "BPQAPRSrxsock"; + char TX_SOCK_PATH[] = "BPQAPRStxsock"; + char SharedName[256]; + char * ptr1; +#endif + struct STATIONRECORD * Stn1, * Stn2; + struct APRSMESSAGE * Msg1, * Msg2; + + // Clear tables in case a restart + + StationRecords = NULL; + + StationCount = 0; + HEARDENTRIES = 0; + MAXHEARDENTRIES = 0; + MobileBeaconInterval = 0; + BeaconInterval = 0; + + DeleteAPRSLogFiles(); + + memset(MHTABLE, 0, sizeof(MHTABLE)); + + ConvToAX25(MYNODECALL, MYCALL); + + ConvToAX25("TCPIP", axTCPIP); + ConvToAX25("RFONLY", axRFONLY); + ConvToAX25("NOGATE", axNOGATE); + + memset(&FloodAX[0][0], 0, sizeof(FloodAX)); + memset(&TraceAX[0][0], 0, sizeof(TraceAX)); + memset(&DigiAX[0][0], 0, sizeof(DigiAX)); + + APRSPortMask = 0; + + memset(BeaconPath, sizeof(BeaconPath), 0); + + memset(&CrossPortMap[0][0], 0, sizeof(CrossPortMap)); + memset(&APRSBridgeMap[0][0], 0, sizeof(APRSBridgeMap)); + + for (i = 1; i <= MaxBPQPortNo; i++) + { + if (CanPortDigi(i)) + CrossPortMap[i][i] = TRUE; // Set Defaults - Same Port + CrossPortMap[i][0] = TRUE; // and APRS-IS + } + + PosnSet = 0; + ObjectList = NULL; + ObjectCount = 0; + + ISPort = ISHost[0] = ISPasscode = 0; + + if (APRSReadConfigFile() == 0) + return FALSE; + + if (APRSCall[0] == 0) + { + strcpy(APRSCall, MYNODECALL); + strlop(APRSCall, ' '); + strcpy(LoppedAPRSCall, APRSCall); + memcpy(CallPadded, APRSCall, (int)strlen(APRSCall)); // Call Padded to 9 chars for APRS Messaging + ConvToAX25(APRSCall, AXCall); + } + + if (WXCall[0] == 0) + strcpy(WXCall, APRSCall); + + // Caluclate size of Shared Segment + + SharedMemorySize = sizeof(struct STATIONRECORD) * (MaxStations + 4) + + sizeof(struct APRSMESSAGE) * (MAXMESSAGES + 4) + 32; // 32 for header + + +#ifndef WIN32 + + // Create a Shared Memory Object + + Shared = NULL; + + // Append last bit of current directory to shared name + + ptr1 = BPQDirectory; + + while (strchr(ptr1, '/')) + { + ptr1 = strchr(ptr1, '/'); + ptr1++; + } + + if (multiple) + sprintf(SharedName, "/BPQAPRSSharedMem%s", ptr1); + else + strcpy(SharedName, "/BPQAPRSSharedMem"); + + printf("Using Shared Memory %s\n", SharedName); + +#ifndef WIN32 + + fd = shm_open(SharedName, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + if (fd == -1) + { + perror("Create Shared Memory"); + printf("Create APRS Shared Memory Failed\n"); + } + else + { + if (ftruncate(fd, SharedMemorySize)) + { + perror("Extend Shared Memory"); + printf("Extend APRS Shared Memory Failed\n"); + } + else + { + // Map shared memory object + + Shared = mmap((void *)APRSSHAREDMEMORYBASE, + SharedMemorySize, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if (Shared == MAP_FAILED) + { + perror("Map Shared Memory"); + printf("Map APRS Shared Memory Failed\n"); + Shared = NULL; + } + + if (Shared != (void *)APRSSHAREDMEMORYBASE) + { + printf("Map APRS Shared Memory Allocated at %x\n", Shared); + Shared = NULL; + } + + } + } + +#endif + + printf("Map APRS Shared Memory Allocated at %p\n", Shared); + + if (Shared == NULL) + { + printf("APRS not using shared memory\n"); + Shared = malloc(SharedMemorySize); + printf("APRS Non-Shared Memory Allocated at %x\n", Shared); + } + +#else + +#ifndef LINBPQ + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen = 4; + retCode = RegQueryValueEx(hKey, "IGateEnabled", 0, &Type, (UCHAR *)&IGateEnabled, &Vallen); + } + +#endif + + // Create Memory Mapping for Station List + + hMapFile = CreateFileMapping( + INVALID_HANDLE_VALUE, // use paging file + NULL, // default security + PAGE_READWRITE, // read/write access + 0, // maximum object size (high-order DWORD) + SharedMemorySize, // maximum object size (low-order DWORD) + "BPQAPRSStationsMappingObject");// name of mapping object + + if (hMapFile == NULL) + { + Consoleprintf("Could not create file mapping object (%d).\n", GetLastError()); + return 0; + } + + UnmapViewOfFile((void *)APRSSHAREDMEMORYBASE); + + + Shared = (LPTSTR) MapViewOfFileEx(hMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + SharedMemorySize, + (void *)APRSSHAREDMEMORYBASE); + + if (Shared == NULL) + { + Consoleprintf("Could not map view of file (%d).\n", GetLastError()); + CloseHandle(hMapFile); + return 0; + } + +#endif + + // First record has pointer to table + + memset(Shared, 0, SharedMemorySize); + + StnRecordBase = Shared + 32; + SMEM = (struct SharedMem *)Shared; + + SMEM->Version = 1; + SMEM->SharedMemLen = SharedMemorySize; + SMEM->NeedRefresh = TRUE; + SMEM->Arch = sizeof(void *); + SMEM->SubVersion = 1; + + Stn1 = (struct STATIONRECORD *)StnRecordBase; + + StationRecords = (struct STATIONRECORD **)Stn1; + + Stn1++; + + StationRecordPool = Stn1; + + for (i = 1; i < MaxStations; i++) // Already have first + { + Stn2 = Stn1; + Stn2++; + Stn1->Next = Stn2; + + Stn1 = Stn2; + } + + Debugprintf("End of Stations %p", Stn1); + endofStations = Stn1; + + Stn1 += 2; // Try to fix corruption of messages. + + // Build Message Record Pool + + Msg1 = (struct APRSMESSAGE *)Stn1; + + MessageRecordPool = Msg1; + + for (i = 1; i < MAXMESSAGES; i++) // Already have first + { + Msg2 = Msg1; + Msg2++; + Msg1->Next = Msg2; + + Msg1 = Msg2; + } + + if (PosnSet == 0) + { + SYMBOL = '.'; + SYMSET = '\\'; // Undefined Posn Symbol + } + else + { + // Convert posn to floating degrees + + char LatDeg[3], LonDeg[4]; + memcpy(LatDeg, LAT, 2); + LatDeg[2]=0; + Lat=atof(LatDeg) + (atof(LAT+2)/60); + + if (LAT[7] == 'S') Lat=-Lat; + + memcpy(LonDeg, LON, 3); + LonDeg[3]=0; + Lon=atof(LonDeg) + (atof(LON+3)/60); + + if (LON[8]== 'W') Lon=-Lon; + + SYMBOL = CFGSYMBOL; + SYMSET = CFGSYMSET; + } + + // First record has control info for APRS Mapping App + + Stn1 = (struct STATIONRECORD *)StnRecordBase; + memcpy(Stn1->Callsign, APRSCall, 10); + Stn1->Lat = Lat; + Stn1->Lon = Lon; + Stn1->LastPort = MaxStations; + +#ifndef WIN32 + + // Open unix socket for messaging app + + sfd = socket(AF_UNIX, SOCK_DGRAM, 0); + + if (sfd == -1) + { + perror("Socket"); + } + else + { + u_long param=1; + ioctl(sfd, FIONBIO, ¶m); // Set non-blocking + + memset(&my_addr, 0, sizeof(struct sockaddr_un)); + my_addr.sun_family = AF_UNIX; + strncpy(my_addr.sun_path, TX_SOCK_PATH, sizeof(my_addr.sun_path) - 1); + + memset(&peer_addr, 0, sizeof(struct sockaddr_un)); + peer_addr.sun_family = AF_UNIX; + strncpy(peer_addr.sun_path, RX_SOCK_PATH, sizeof(peer_addr.sun_path) - 1); + + unlink(TX_SOCK_PATH); + + if (bind(sfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_un)) == -1) + perror("bind"); + } +#endif + + // Convert Dest ADDRS to AX.25 + + for (i = 0; i < 26; i++) + { + DCall = &APRSDESTS[i][0]; + if (strchr(DCall, '*')) + AXDESTLEN[i] = (int)strlen(DCall) - 1; + else + AXDESTLEN[i] = 6; + + ConvToAX25(DCall, &AXDESTS[i][0]); + } + + // Process any Object Definitions + + // Setup Heard Data Area + + HEARDENTRIES = 0; + MAXHEARDENTRIES = MAXHEARD; + + APRSMONVECPTR->HOSTAPPLFLAGS = 0x80; // Request Monitoring + + if (ISPort && IGateEnabled) + { + _beginthread(APRSISThread, 0, (VOID *) TRUE); + } + + if (GPSPort[0]) + OpenGPSPort(); + + WritetoConsole("APRS Digi/Gateway Enabled\n"); + + APRSWeb = TRUE; + + read_png((unsigned char *)IconData); + + // Reload saved messages + + if (SaveAPRSMsgs) + GetSavedAPRSMessages(); + + // If a Run parameter was supplied, run the program + + if (RunProgram[0] == 0) + return TRUE; + + #ifndef WIN32 + { + char * arg_list[] = {NULL, NULL}; + pid_t child_pid; + + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + // Fork and Exec program + + printf("Trying to start %s\n", RunProgram); + + arg_list[0] = RunProgram; + + // Duplicate this process. + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("APRS fork() Failed\n"); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + // The execvp function returns only if an error occurs. + + printf ("Failed to run %s\n", RunProgram); + exit(0); // Kill the new process + } + } +#else + { + int n = 0; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + while (KillOldTNC(RunProgram) && n++ < 100) + { + Sleep(100); + } + + if (!CreateProcess(RunProgram, NULL, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo)) + Debugprintf("Failed to Start %s Error %d ", RunProgram, GetLastError()); + } +#endif + + return TRUE; +} + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +BOOL APRSActive; + +VOID APRSClose() +{ + APRSActive = FALSE; + + if (sock) + { + shutdown(sock, SD_BOTH); + Sleep(50); + + closesocket(sock); + } +#ifdef WIN32 + if (InPorts[0].hDevice) + CloseHandle(InPorts[0].hDevice); +#endif +} + +time_t lastSecTimer = 0; + + +Dll VOID APIENTRY Poll_APRS() +{ + time_t Now = time(NULL); + + if (lastSecTimer != Now) + { + lastSecTimer = Now; + + DoSecTimer(); + + MinTimer--; + + if (MinTimer == 0) + { + MinTimer = 60; + DoMinTimer(); + } + } + + if (SMEM->ClearRX) + { + // Clear Received Messages request from GUI + + struct APRSMESSAGE * ptr = SMEM->Messages; + + // Move Message Queue to Free Queue + + if (ptr) + { + while (ptr->Next) // Find end of chain + { + ptr = ptr->Next; + } + + // ptr is end of chain - chain free pool to it + + ptr->Next = MessageRecordPool; + + MessageRecordPool = SMEM->Messages; + MessageCount = 0; + } + + SMEM->Messages = NULL; + SMEM->ClearRX = 0; + SMEM->NeedRefresh = TRUE; + + ClearSavedMessages(); + } + + if (SMEM->ClearTX) + { + // Clear Sent Messages )request from GUI + + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + + // Move Message Queue to Free Queue + + if (ptr) + { + while (ptr->Next) // Find end of chain + { + ptr = ptr->Next; + } + + // ptr is end of chain - chain free pool to it + + ptr->Next = MessageRecordPool; + + MessageRecordPool = SMEM->OutstandingMsgs; + MessageCount = 0; + } + + SMEM->OutstandingMsgs = NULL; + SMEM->ClearTX = 0; + SMEM->NeedRefresh = TRUE; + } +#ifdef LINBPQ +#ifndef WIN32 + { + char Msg[256]; + int numBytes; + + // Look for messages from App + + numBytes = recvfrom(sfd, Msg, 256, 0, NULL, NULL); + + if (numBytes > 0) + { + InternalSendAPRSMessage(&Msg[10], &Msg[0]); + } + } +#endif +#endif + + if (GPSPort[0]) + PollGPSIn(); + + if (APPLTX_Q) + { + PMSGWITHLEN buffptr = Q_REM(&APPLTX_Q); + + InternalSendAPRSMessage(&buffptr->Data[10], &buffptr->Data[0]); + ReleaseBuffer(buffptr); + } + + while (APRSMONVECPTR->HOSTTRACEQ) + { + time_t stamp; + int len; + BOOL MonitorNODES = FALSE; + PMESSAGE monbuff; + UCHAR * monchars; + MESSAGE * Orig; + int Digis = 0; + MESSAGE * AdjBuff; // Adjusted for digis + BOOL FirstUnused = FALSE; + int DigisUsed = 0; // Digis used to reach us + DIGIMESSAGE Msg = {0}; + int Port; + unsigned char buffer[1024]; + char ISMsg[500]; + char * ptr1; + char * Payload; + char * ptr3; + char * ptr4; + BOOL ThirdParty = FALSE; + BOOL NoGate = FALSE; + APRSHEARDRECORD * MH; + char MsgCopy[500]; + int toPort; + struct STATIONRECORD * Station; + int ourMessage = 0; + +#ifdef WIN32 + struct _EXCEPTION_POINTERS exinfo; + char EXCEPTMSG[80] = ""; +#endif + monbuff = Q_REM((VOID **)&APRSMONVECPTR->HOSTTRACEQ); + + monchars = (UCHAR *)monbuff; + AdjBuff = Orig = (MESSAGE *)monchars; // Adjusted for digis + + Port = Orig->PORT; + + if (Port & 0x80) // TX + { + ReleaseBuffer(monbuff); + continue; + } + + if ((APRSPortMask & ((uint64_t)1 << (Port - 1))) == 0)// Port in use for APRS? + { + ReleaseBuffer(monbuff); + continue; + } + + stamp = monbuff->Timestamp; + + if ((UCHAR)monchars[4] & 0x80) // TX + { + ReleaseBuffer(monbuff); + continue; + } + + // See if digipeaters present. + + while ((AdjBuff->ORIGIN[6] & 1) == 0 && Digis < 9) + { + UCHAR * temp = (UCHAR *)AdjBuff; + temp += 7; + AdjBuff = (MESSAGE *)temp; + + // If we have already digi'ed it or if we sent it, + // ignore (Dup Check my fail on slow links) + + if (AdjBuff->ORIGIN[6] & 0x80) + { + // Used Digi + + if (memcmp(AdjBuff->ORIGIN, AXCall, 7) == 0) + { + ReleaseBuffer(monbuff); + return; + } + DigisUsed++; + } + + if (memcmp(AdjBuff->ORIGIN, axTCPIP, 6) == 0) + ThirdParty = TRUE; + + Digis ++; + + if (FirstUnused == FALSE && (AdjBuff->ORIGIN[6] & 0x80) == 0) + { + // Unused Digi - see if we should digi it + + FirstUnused = Digis; + // CheckDigi(buff, AdjBuff->ORIGIN); + } + } + + if (Digis > 8) + { + ReleaseBuffer(monbuff); + continue; // Corrupt + } + + if (Digis) + { + if (memcmp(AdjBuff->ORIGIN, axNOGATE, 6) == 0 + || memcmp(AdjBuff->ORIGIN, axRFONLY, 6) == 0 + || DigisUsed > MaxDigisforIS) + + // Too many digis or Last digis is NOGATE or RFONLY - dont send to IS + + NoGate = TRUE; + } + if (AdjBuff->CTL != 3 || AdjBuff->PID != 0xf0) // Only UI + { + ReleaseBuffer(monbuff); + continue; + } + + // Bridge if requested + + for (toPort = 1; toPort <= MaxBPQPortNo; toPort++) + { + if (APRSBridgeMap[Port][toPort]) + { + MESSAGE * Buffer = GetBuff(); + struct PORTCONTROL * PORT; + + if (Buffer) + { + memcpy(Buffer, Orig, Orig->LENGTH); + Buffer->PORT = toPort; + PORT = GetPortTableEntryFromPortNum(toPort); + + if (PORT) + { + if (PORT->SmartIDInterval && PORT->SmartIDNeeded == 0) + { + // Using Smart ID, but none scheduled + + PORT->SmartIDNeeded = time(NULL) + PORT->SmartIDInterval; + } + PUT_ON_PORT_Q(PORT, Buffer); + } + else + ReleaseBuffer(Buffer); + } + } + } + + // Used to check for dups here but according to "Notes to iGate developers" IS should be sent dups, and dup + // check only applied to digi'ing + +// if (SATGate == 0) +// { +// if (CheckforDups(Orig->ORIGIN, AdjBuff->L2DATA, Orig->LENGTH - Digis * 7 - (19 + sizeof(void *))) +// { +// ReleaseBuffer(monbuff); +// continue; +// } +// } + // Decode Frame to TNC2 Monitor Format + + len = APRSDecodeFrame((char *)monchars, buffer, stamp, APRSPortMask); + + if (len == 0) + { + // Couldn't Decode + + ReleaseBuffer(monbuff); + Debugprintf("APRS discarded frame - decode failed\n"); + continue; + } + + buffer[len] = 0; + + memcpy(MsgCopy, buffer, len); + MsgCopy[len] = 0; + + // Do internal Decode + +#ifdef WIN32 + + strcpy(EXCEPTMSG, "ProcessRFFrame"); + + __try + { + + Station = ProcessRFFrame(MsgCopy, len, &ourMessage); + } + #include "StdExcept.c" + + } +#else + Station = ProcessRFFrame(MsgCopy, len, &ourMessage); +#endif + + if (Station == NULL) + { + ReleaseBuffer(monbuff); + continue; + } + + memcpy(MsgCopy, buffer, len); // Process RF Frame may have changed it + MsgCopy[len] = 0; + + buffer[len++] = 10; + buffer[len] = 0; + ptr1 = &buffer[10]; // Skip Timestamp + Payload = strchr(ptr1, ':') + 2; // Start of Payload + ptr3 = strchr(ptr1, ' '); // End of addresses + *ptr3 = 0; + + // We should send path to IS unchanged, so create IS + // message before chopping path. We won't decide if + // we will actually send it to IS till later + + len = sprintf(ISMsg, "%s,qAR,%s:%s", ptr1, APRSCall, Payload); + + + // if digis, remove any unactioned ones + + if (Digis) + { + ptr4 = strchr(ptr1, '*'); // Last Used Digi + + if (ptr4) + { + // We need header up to ptr4 + + *(ptr4) = 0; + } + else + { + // No digis actioned - remove them all + + ptr4 = strchr(ptr1, ','); // End of Dest + if (ptr4) + *ptr4 = 0; + } + } + + ptr4 = strchr(ptr1, '>'); // End of Source + *ptr4++ = 0; + + MH = UpdateHeard(ptr1, Port); + + MH->Station = Station; + + if (ThirdParty) + { +// Debugprintf("Setting Igate Flag - %s", MsgCopy); + MH->IGate = TRUE; // if we've seen msgs to TCPIP, it must be an Igate + } + + if (NoGate || RXOnly) + goto NoIS; + + // I think all PID F0 UI frames go to APRS-IS, + // Except General Queries, Frames Gated from IS to RF, and Messages Addressed to us + + // or should we process Query frames locally ?? + + if (Payload[0] == '}') + goto NoIS; + + if (Payload[0] == '?') + { + // General Query + + ProcessQuery(&Payload[1]); + + // ?? Should we pass addressed Queries to IS ?? + + goto NoIS; + } + + if (Payload[0] == ':' && memcmp(&Payload[1], CallPadded, 9) == 0) + { + // Message for us + + if (Payload[11] == '?') // Only queries - the node doesnt do messaging + ProcessSpecificQuery(&Payload[12], Port, ptr1, ptr4); + + goto NoIS; + } + + if (APRSISOpen && CrossPortMap[Port][0]) // No point if not open + { + // was done above len = sprintf(ISMsg, "%s>%s,qAR,%s:%s", ptr1, ptr4, APRSCall, Payload); + + if (BeacontoIS == 0) + { + // Don't send anything we've received as an echo + + char SaveCall[7]; + memcpy(SaveCall, &monbuff->ORIGIN, 7); + SaveCall[6] &= 0x7e; // Mask End of address bit + + if (memcmp(SaveCall, AXCall, 7) == 0) // We sent it + { + // Should we check for being received via digi? - not for now + + goto NoIS; + } + } + + if (SATGate && (DigisUsed == 0)) + { + // If in Satgate mode delay directly heard to IGate + + ISDELAY * SatISEntry = malloc(sizeof(ISDELAY)); + SatISEntry->Next = NULL; + SatISEntry->ISMSG = _strdup(ISMsg); + SatISEntry->SendTIme = time(NULL) + 10; // Delay 10 seconds + + if (SatISQueue) + SatISEntry->Next = SatISQueue; // Chain + + SatISQueue = SatISEntry; + goto NoIS; + } + + ISSend(sock, ISMsg, len, 0); + + ptr1 = strchr(ISMsg, 13); + if (ptr1) *ptr1 = 0; +// Debugprintf(">%s", ISMsg); + } + + NoIS: + + // We skipped DUP check for SATGate Mode, so apply it here + + // Now we don't dup check earlier so always check here + +// if (SATGate) +// { + if (CheckforDups(Orig->ORIGIN, AdjBuff->L2DATA, Orig->LENGTH - Digis * 7 - (19 + sizeof(void *)))) + { + ReleaseBuffer(monbuff); + continue; + } +// } + + // See if it is an APRS frame + + // If MIC-E, we need to process, whatever the destination + + // Now process any dest + +/* + DEST = &Orig->DEST[0]; + + for (i = 0; i < 26; i++) + { + if (memcmp(DEST, &AXDESTS[i][0], AXDESTLEN[i]) == 0) + goto OK; + } + + switch(AdjBuff->L2DATA[0]) + { + case '`': + case 0x27: // ' + case 0x1c: + case 0x1d: // MIC-E + + break; + // default: + + // Not to an APRS Destination + +// ReleaseBuffer(monbuff); +// continue; + } + +OK: +*/ + + // If there are unused digis, we may need to digi it. + + if (ourMessage) + { + // A message addressed to us, so no point in digi'ing it + + ReleaseBuffer(monbuff); + continue; + } + + if (Digis == 0 || FirstUnused == 0) + { + // No Digis, so finished + + ReleaseBuffer(monbuff); + continue; + } + + if (memcmp(monbuff->ORIGIN, AXCall, 7) == 0) // We sent it + { + ReleaseBuffer(monbuff); + continue; + } + + // Copy frame to a DIGIMessage Struct + + memcpy(&Msg, monbuff, MSGHDDRLEN + 14 + (7 * Digis)); // Header, Dest, Source, Addresses and Digis + + len = Msg.LENGTH - (MSGHDDRLEN + 14) - (7 * Digis); // Payload Length (including CTL and PID + + memcpy(&Msg.CTL, &AdjBuff->CTL, len); + + // Pass to Digi Code + + CheckandDigi(&Msg, Port, FirstUnused, Digis, len); // Digi if necessary + + ReleaseBuffer(monbuff); + } + + return; +} + +VOID CheckandDigi(DIGIMESSAGE * Msg, int Port, int FirstUnused, int Digis, int Len) +{ + UCHAR * Digi = &Msg->DIGIS[--FirstUnused][0]; + UCHAR * Call; + int Index = 0; + int SSID; + + // Check ordinary digi first + + Call = &DigiAX[0][0]; + SSID = Digi[6] & 0x1e; + + while (*Call) + { + if ((memcmp(Digi, Call, 6) == 0) && ((Call[6] & 0x1e) == SSID)) + { + // Trace Call if enabled + + if (TraceDigi) + memcpy(Digi, AXCall, 7); + + // mark as used; + + Digi[6] |= 0x80; // Used bit + + SendtoDigiPorts(Msg, Len, Port); + return; + } + Call += 7; + Index++; + } + + Call = &TraceAX[0][0]; + Index = 0; + + while (*Call) + { + if (memcmp(Digi, Call, TraceLen[Index]) == 0) + { + // if possible move calls along + // insert our call, set used + // decrement ssid, and if zero, mark as used; + + SSID = (Digi[6] & 0x1E) >> 1; + + if (SSID == 0) + return; // Shouldn't have SSID 0 for Rrace/Flood + + if (SSID > MaxTraceHops) + SSID = MaxTraceHops; // Enforce our limit + + SSID--; + + if (SSID ==0) // Finihed with it ? + Digi[6] = (SSID << 1) | 0xe0; // Used and Fixed bits + else + Digi[6] = (SSID << 1) | 0x60; // Fixed bits + + if (Digis < 8) + { + memmove(Digi + 7, Digi, (Digis - FirstUnused) * 7); + } + + memcpy(Digi, AXCall, 7); + Digi[6] |= 0x80; + + SendtoDigiPorts(Msg, Len, Port); + + return; + } + Call += 7; + Index++; + } + + Index = 0; + Call = &FloodAX[0][0]; + + while (*Call) + { + if (memcmp(Digi, Call, FloodLen[Index]) == 0) + { + // decrement ssid, and if zero, mark as used; + + SSID = (Digi[6] & 0x1E) >> 1; + + if (SSID == 0) + return; // Shouldn't have SSID 0 for Trace/Flood + + if (SSID > MaxFloodHops) + SSID = MaxFloodHops; // Enforce our limit + + SSID--; + + if (SSID ==0) // Finihed with it ? + Digi[6] = (SSID << 1) | 0xe0; // Used and Fixed bits + else + Digi[6] = (SSID << 1) | 0x60; // Fixed bits + + SendtoDigiPorts(Msg, Len, Port); + + return; + } + Call += 7; + Index++; + } +} + + + +static VOID SendtoDigiPorts(PDIGIMESSAGE Block, DWORD Len, UCHAR Port) +{ + // Can't use API SENDRAW, as that tries to get the semaphore, which we already have + // Len is the Payload Length (from CTL onwards) + // The message can contain DIGIS - The payload must be copied forwards if there are less than 8 + + // We send to all ports enabled in CrossPortMap + + UCHAR * EndofDigis = &Block->CTL; + int i = 0; + int toPort; + + while (Block->DIGIS[i][0] && i < 8) + { + i++; + } + + EndofDigis = &Block->DIGIS[i][0]; + *(EndofDigis -1) |= 1; // Set End of Address Bit + + if (i != 8) + memmove(EndofDigis, &Block->CTL, Len); + + Len = Len + (i * 7) + 14; // Include Source, Dest and Digis + +// Block->DEST[6] &= 0x7e; // Clear End of Call +// Block->ORIGIN[6] |= 1; // Set End of Call + +// Block->CTL = 3; //UI + + for (toPort = 1; toPort <= MaxBPQPortNo; toPort++) + { + if (CrossPortMap[Port][toPort]) + Send_AX((PMESSAGE)Block, Len, toPort); + } + return; + +} + +VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port) +{ + // Can't use API SENDRAW, as that tries to get the semaphore, which we already have + + // Len is the Payload Length (CTL, PID, Data) + + // The message can contain DIGIS - The payload must be copied forwards if there are less than 8 + + UCHAR * EndofDigis = &Block->CTL; + + int i = 0; + + while (Block->DIGIS[i][0] && i < 8) + { + i++; + } + + EndofDigis = &Block->DIGIS[i][0]; + *(EndofDigis -1) |= 1; // Set End of Address Bit + + if (i != 8) + memmove(EndofDigis, &Block->CTL, Len); // Include PID + + Len = Len + (i * 7) + 14; // Include Source, Dest and Digis + + Send_AX((PMESSAGE)Block, Len, Port); + + return; + +} + +static BOOL APRSReadConfigFile() +{ + char * Config; + char * ptr1, * ptr2; + + char buf[256],errbuf[256]; + + Config = PortConfig[APRSConfigSlot]; // Config from bpq32.cfg + + sprintf(StatusMsg, "BPQ32 Igate V %s", VersionString); // Set Default Status Message + + if (Config) + { + // Using config from bpq32.cfg + + ptr1 = Config; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!APRSProcessLine(buf)) + { + WritetoConsole("APRS Bad config record "); + strcat(errbuf, "\r\n"); + WritetoConsole(errbuf); + } + } + return TRUE; + } + return FALSE; +} + +BOOL ConvertCalls(char * DigiCalls, UCHAR * AX, int * Lens) +{ + int Index = 0; + char * ptr; + char * Context; + UCHAR Work[MAXCALLS][7] = {0}; + int Len[MAXCALLS] = {0}; + + ptr = strtok_s(DigiCalls, ", ", &Context); + + while(ptr) + { + if (Index == MAXCALLS) return FALSE; + + ConvToAX25(ptr, &Work[Index][0]); + Len[Index++] = (int)strlen(ptr); + ptr = strtok_s(NULL, ", ", &Context); + } + + memcpy(AX, Work, sizeof(Work)); + memcpy(Lens, Len, sizeof(Len)); + return TRUE; +} + + + +static int APRSProcessLine(char * buf) +{ + char * ptr, * p_value; + + ptr = strtok(buf, "= \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + +// OBJECT PATH=APRS,WIDE1-1 PORT=1,IS INTERVAL=30 TEXT=;444.80TRF*111111z4807.60N/09610.63Wr%156 R15m + + if (_stricmp(ptr, "OBJECT") == 0) + { + char * p_Path, * p_Port, * p_Text; + int Interval; + struct OBJECT * Object; + int Digi = 2; + char * Context; + int SendTo; + + p_value = strtok(NULL, "="); + if (p_value == NULL) return FALSE; + if (_stricmp(p_value, "PATH")) + return FALSE; + + p_Path = strtok(NULL, "\t\n\r "); + if (p_Path == NULL) return FALSE; + + p_value = strtok(NULL, "="); + if (p_value == NULL) return FALSE; + if (_stricmp(p_value, "PORT")) + return FALSE; + + p_Port = strtok(NULL, "\t\n\r "); + if (p_Port == NULL) return FALSE; + + p_value = strtok(NULL, "="); + if (p_value == NULL) return FALSE; + if (_stricmp(p_value, "INTERVAL")) + return FALSE; + + p_value = strtok(NULL, " \t"); + if (p_value == NULL) return FALSE; + + Interval = atoi(p_value); + + if (Interval == 0) + return FALSE; + + p_value = strtok(NULL, "="); + if (p_value == NULL) return FALSE; + if (_stricmp(p_value, "TEXT")) + return FALSE; + + p_Text = strtok(NULL, "\n\r"); + if (p_Text == NULL) return FALSE; + + Object = zalloc(sizeof(struct OBJECT)); + + if (Object == NULL) + return FALSE; + + Object->Next = ObjectList; + ObjectList = Object; + + if (Interval < 10) + Interval = 10; + + Object->Interval = Interval; + Object->Timer = (ObjectCount++) * 10 + 30; // Spread them out; + + // Convert Path to AX.25 + + ConvToAX25(APRSCall, &Object->Path[1][0]); + + ptr = strtok_s(p_Path, ",\t\n\r", &Context); + + if (_stricmp(ptr, "APRS") == 0) // First is Dest + ConvToAX25(APRSDest, &Object->Path[0][0]); + else if (_stricmp(ptr, "APRS-0") == 0) + ConvToAX25("APRS", &Object->Path[0][0]); + else + ConvToAX25(ptr, &Object->Path[0][0]); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + ConvToAX25(ptr, &Object->Path[Digi++][0]); + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + Object->PathLen = Digi * 7; + + // Process Port List + + ptr = strtok_s(p_Port, ",", &Context); + + while (ptr) + { + SendTo = atoi(ptr); // this gives zero for IS + + if (SendTo > MaxBPQPortNo) + return FALSE; + + Object->PortMap[SendTo] = TRUE; + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + if (strlen(p_Text) > 80) + p_Text[80] = 0; + + strcpy(Object->Message, p_Text); + return TRUE; + } + + if (_stricmp(ptr, "STATUSMSG") == 0) + { + p_value = strtok(NULL, ";\t\n\r"); + memcpy(StatusMsg, p_value, 128); // Just in case too long + StatusMsgLen = (int)strlen(p_value); + return TRUE; + } + + if (_stricmp(ptr, "WXFileName") == 0) + { + p_value = strtok(NULL, ";\t\n\r"); + strcpy(WXFileName, p_value); + SendWX = TRUE; + return TRUE; + } + if (_stricmp(ptr, "WXComment") == 0) + { + p_value = strtok(NULL, ";\t\n\r"); + + if (p_value == NULL) + return TRUE; + + if (strlen(p_value) > 79) + p_value[80] = 0; + + strcpy(WXComment, p_value); + return TRUE; + } + + + if (_stricmp(ptr, "ISFILTER") == 0) + { + p_value = strtok(NULL, ";\t\n\r"); + strcpy(ISFilter, p_value); + strcpy(NodeFilter, ISFilter); + return TRUE; + } + + if (_stricmp(ptr, "ReplaceDigiCalls") == 0) + { + TraceDigi = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "Multiple") == 0) + { + multiple = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "SATGate") == 0) + { + SATGate = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "RXOnly") == 0) + { + RXOnly = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "DISTKM") == 0) + { + DefaultDistKM = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "LOCALTIME") == 0) + { + DefaultLocalTime = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "LOGAPRSIS") == 0) + { + LogAPRSIS = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "SaveAPRSMsgs") == 0) + { + SaveAPRSMsgs = TRUE; + return TRUE; + } + + p_value = strtok(NULL, " \t\n\r"); + + if (p_value == NULL) + return FALSE; + + if (_stricmp(ptr, "APRSCALL") == 0) + { + strcpy(APRSCall, p_value); + strcpy(LoppedAPRSCall, p_value); + memcpy(CallPadded, APRSCall, (int)strlen(APRSCall)); // Call Padded to 9 chars for APRS Messaging + + // Convert to ax.25 + + return ConvToAX25(APRSCall, AXCall); + } + + if (_stricmp(ptr, "WXCALL") == 0) + { + strcpy(WXCall, p_value); + return TRUE; + } + + if (_stricmp(ptr, "APRSPATH") == 0) + { + int Digi = 2; + int Port; + char * Context; + + p_value = strtok_s(p_value, "=\t\n\r", &Context); + + Port = atoi(p_value); + + if (GetPortTableEntryFromPortNum(Port) == NULL) + return FALSE; + + APRSPortMask |= (uint64_t)1 << (Port - 1); + + if (Context == NULL || Context[0] == 0) + return TRUE; // No dest - a receive-only port + + BeaconPath[Port] = _strdup(_strupr(Context)); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + if (ptr == NULL) + return FALSE; + + ConvToAX25(APRSCall, &BeaconHeader[Port][1][0]); + + if (_stricmp(ptr, "APRS") == 0) // First is Dest + ConvToAX25(APRSDest, &BeaconHeader[Port][0][0]); + else if (_stricmp(ptr, "APRS-0") == 0) + ConvToAX25("APRS", &BeaconHeader[Port][0][0]); + else + ConvToAX25(ptr, &BeaconHeader[Port][0][0]); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + ConvToAX25(ptr, &BeaconHeader[Port][Digi++][0]); + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + BeaconHddrLen[Port] = Digi * 7; + + return TRUE; + } + + if (_stricmp(ptr, "GATEDPATH") == 0) + { + int Digi = 2; + int Port; + char * Context; + + p_value = strtok_s(p_value, "=\t\n\r", &Context); + + Port = atoi(p_value); + + if (GetPortTableEntryFromPortNum(Port) == NULL) + return FALSE; + +// APRSPortMask |= 1 << (Port - 1); + + if (Context == NULL || Context[0] == 0) + return TRUE; // No dest - a receive-only port + + BeaconPath[Port] = _strdup(_strupr(Context)); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + if (ptr == NULL) + return FALSE; + + ConvToAX25(APRSCall, &GatedHeader[Port][1][0]); + + if (_stricmp(ptr, "APRS") == 0) // First is Dest + ConvToAX25(APRSDest, &GatedHeader[Port][0][0]); + else if (_stricmp(ptr, "APRS-0") == 0) + ConvToAX25("APRS", &GatedHeader[Port][0][0]); + else + ConvToAX25(ptr, &GatedHeader[Port][0][0]); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + ConvToAX25(ptr, &GatedHeader[Port][Digi++][0]); + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + GatedHddrLen[Port] = Digi * 7; + + return TRUE; + } + + + if (_stricmp(ptr, "DIGIMAP") == 0) + { + int DigiTo; + int Port; + char * Context; + + p_value = strtok_s(p_value, "=\t\n\r", &Context); + + Port = atoi(p_value); + + if (GetPortTableEntryFromPortNum(Port) == NULL) + return FALSE; + + // Check that port can digi (SCS Pactor can't set digi'd bit in calls) + + if (CanPortDigi(Port) == 0) + return FALSE; + + + CrossPortMap[Port][Port] = FALSE; // Cancel Default mapping + CrossPortMap[Port][0] = FALSE; // Cancel Default APRSIS + + if (Context == NULL || Context[0] == 0) + return TRUE; + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + DigiTo = atoi(ptr); // this gives zero for IS + + if (DigiTo && GetPortTableEntryFromPortNum(DigiTo) == NULL) + return FALSE; + + CrossPortMap[Port][DigiTo] = TRUE; + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + return TRUE; + } + if (_stricmp(ptr, "BRIDGE") == 0) + { + int DigiTo; + int Port; + char * Context; + + p_value = strtok_s(p_value, "=\t\n\r", &Context); + + Port = atoi(p_value); + + if (GetPortTableEntryFromPortNum(Port) == NULL) + return FALSE; + + if (Context == NULL) + return FALSE; + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + DigiTo = atoi(ptr); // this gives zero for IS + + if (DigiTo > MaxBPQPortNo) + return FALSE; + + APRSBridgeMap[Port][DigiTo] = TRUE; + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + return TRUE; + } + + + if (_stricmp(ptr, "BeaconInterval") == 0) + { + BeaconInterval = atoi(p_value); + + if (BeaconInterval < 5) + BeaconInterval = 5; + + if (BeaconInterval) + BeaconCounter = 30; // Send first after 30 secs + + return TRUE; + } + + if (_stricmp(ptr, "MobileBeaconInterval") == 0) + { + MobileBeaconInterval = atoi(p_value) * 60; + return TRUE; + } + if (_stricmp(ptr, "MobileBeaconIntervalSecs") == 0) + { + MobileBeaconInterval = atoi(p_value); + if (MobileBeaconInterval < 10) + MobileBeaconInterval = 10; + + return TRUE; + } + + if (_stricmp(ptr, "BeacontoIS") == 0) + { + BeacontoIS = atoi(p_value); + return TRUE; + } + + + if (_stricmp(ptr, "TRACECALLS") == 0) + { + TraceCalls = _strdup(_strupr(p_value)); + ConvertCalls(TraceCalls, &TraceAX[0][0], &TraceLen[0]); + return TRUE; + } + + if (_stricmp(ptr, "FLOODCALLS") == 0) + { + FloodCalls = _strdup(_strupr(p_value)); + ConvertCalls(FloodCalls, &FloodAX[0][0], &FloodLen[0]); + return TRUE; + } + + if (_stricmp(ptr, "DIGICALLS") == 0) + { + char AllCalls[1024]; + + DigiCalls = _strdup(_strupr(p_value)); + strcpy(AllCalls, APRSCall); + strcat(AllCalls, ","); + strcat(AllCalls, DigiCalls); + ConvertCalls(AllCalls, &DigiAX[0][0], &DigiLen[0]); + return TRUE; + } + + if (_stricmp(ptr, "MaxStations") == 0) + { + MaxStations = atoi(p_value); + + if (MaxStations > 10000) + MaxStations = 10000; + + return TRUE; + } + + if (_stricmp(ptr, "MaxAge") == 0) + { + ExpireTime = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "GPSPort") == 0) + { + if (strcmp(p_value, "0") != 0) + strcpy(GPSPort, p_value); + return TRUE; + } + + if (_stricmp(ptr, "GPSSpeed") == 0) + { + GPSSpeed = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "GPSRelay") == 0) + { + if (strlen(p_value) > 79) + return FALSE; + + strcpy(GPSRelay, p_value); + return TRUE; + } + + if (_stricmp(ptr, "BlueNMEA") == 0 || _stricmp(ptr, "TCPHost") == 0 || _stricmp(ptr, "AISHost") == 0) + { + if (strlen(p_value) > 70) + return FALSE; + + strcpy(HostName, p_value); + return TRUE; + } + + if (_stricmp(ptr, "TCPPort") == 0 || _stricmp(ptr, "AISPort") == 0) + { + HostPort = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "GPSDHost") == 0) + { + if (strlen(p_value) > 70) + return FALSE; + + strcpy(GPSDHost, p_value); + return TRUE; + } + + if (_stricmp(ptr, "GPSDPort") == 0) + { + GPSDPort = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "ADSBHost") == 0) + { + if (strlen(p_value) > 70) + return FALSE; + + strcpy(ADSBHost, p_value); + return TRUE; + } + + if (_stricmp(ptr, "ADSBPort") == 0) + { + ADSBPort = atoi(p_value); + return TRUE; + } + + + + if (_stricmp(ptr, "GPSSetsLocator") == 0) + { + GPSSetsLocator = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "LAT") == 0) + { + if (strlen(p_value) != 8) + return FALSE; + + memcpy(LAT, _strupr(p_value), 8); + PosnSet = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "LON") == 0) + { + if (strlen(p_value) != 9) + return FALSE; + + memcpy(LON, _strupr(p_value), 9); + PosnSet = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "SYMBOL") == 0) + { + if (p_value[0] > ' ' && p_value[0] < 0x7f) + CFGSYMBOL = p_value[0]; + + return TRUE; + } + + if (_stricmp(ptr, "SYMSET") == 0) + { + CFGSYMSET = p_value[0]; + return TRUE; + } + + if (_stricmp(ptr, "PHG") == 0) + { + PHG = _strdup(p_value); + return TRUE; + } + + if (_stricmp(ptr, "MaxTraceHops") == 0) + { + MaxTraceHops = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "MaxFloodHops") == 0) + { + MaxFloodHops = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "ISHOST") == 0) + { + strncpy(ISHost, p_value, 250); + return TRUE; + } + + if (_stricmp(ptr, "ISPORT") == 0) + { + ISPort = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "ISPASSCODE") == 0) + { + ISPasscode = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "MaxDigisforIS") == 0) + { + MaxDigisforIS = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "GateLocalDistance") == 0) + { + GateLocalDistance = atoi(p_value); + if (GateLocalDistance > 0.0) + GateLocal = TRUE; + + return TRUE; + } + + if (_stricmp(ptr, "WXInterval") == 0) + { + WXInterval = atoi(p_value); + WXCounter = (WXInterval - 1) * 60; + return TRUE; + } + + if (_stricmp(ptr, "WXPortList") == 0) + { + char ParamCopy[80]; + char * Context; + int Port; + char * ptr; + int index = 0; + + for (index = 0; index < MaxBPQPortNo; index++) + WXPort[index] = FALSE; + + if (strlen(p_value) > 79) + p_value[80] = 0; + + strcpy(ParamCopy, p_value); + + ptr = strtok_s(ParamCopy, " ,\t\n\r", &Context); + + while (ptr) + { + Port = atoi(ptr); // this gives zero for IS + + WXPort[Port] = TRUE; + + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + return TRUE; + } + + if (_stricmp(ptr, "Run") == 0) + { + strcpy(RunProgram, p_value); + return TRUE; + } + + // + // Bad line + // + return (FALSE); +} + +VOID SendAPRSMessageEx(char * Message, int toPort, char * FromCall, int Gated); + + +VOID SendAPRSMessage(char * Message, int toPort) +{ + SendAPRSMessageEx(Message, toPort, APRSCall, 0); +} + +// Ex allows setting source call (For WX Messages) + + +VOID SendAPRSMessageEx(char * Message, int toPort, char * FromCall, int Gated) +{ + int Port; + DIGIMESSAGE Msg; + + int Len; + + // toPort = -1 means all tadio ports. 0 = IS + + if (toPort == -1) + { + for (Port = 1; Port <= MaxBPQPortNo; Port++) + { + if (Gated && GatedHddrLen[Port]) + memcpy(Msg.DEST, &GatedHeader[Port][0][0], 10 * 7); + else if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined + memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7); + else + continue; + + Msg.DEST[6] |= 0x80; // set Command Bit + + ConvToAX25(FromCall, Msg.ORIGIN); + Msg.PID = 0xf0; + Msg.CTL = 3; + Len = sprintf(Msg.L2DATA, "%s", Message); + Send_AX_Datagram(&Msg, Len + 2, Port); + } + + return; + } + + if (toPort == 0 && APRSISOpen) + { + char ISMsg[300]; + + Len = sprintf(ISMsg, "%s>%s,TCPIP*:%s\r\n", FromCall, APRSDest, Message); + ISSend(sock, ISMsg, Len, 0); + } + + if (toPort == 0) + return; + + if (Gated && GatedHddrLen[toPort]) + memcpy(Msg.DEST, &GatedHeader[toPort][0][0], 10 * 7); + else if (BeaconHddrLen[toPort]) // Only send to ports with a DEST defined + memcpy(Msg.DEST, &BeaconHeader[toPort][0][0], 10 * 7); + else + return; + + Msg.DEST[6] |= 0x80; // set Command Bit + + ConvToAX25(FromCall, Msg.ORIGIN); + Msg.PID = 0xf0; + Msg.CTL = 3; + Len = sprintf(Msg.L2DATA, "%s", Message); + Send_AX_Datagram(&Msg, Len + 2, toPort); + + return; +} + + +VOID ProcessSpecificQuery(char * Query, int Port, char * Origin, char * DestPlusDigis) +{ + if (_memicmp(Query, "APRSS", 5) == 0) + { + char Message[255]; + + sprintf(Message, ":%-9s:%s", Origin, StatusMsg); + SendAPRSMessage(Message, Port); + + return; + } + + if (_memicmp(Query, "APRST", 5) == 0 || _memicmp(Query, "PING?", 5) == 0) + { + // Trace Route + //:KH2ZV :?APRST :N8UR :KH2Z>APRS,DIGI1,WIDE*: + //:G8BPQ-14 :Path - G8BPQ-14>APU25N + + char Message[255]; + + sprintf(Message, ":%-9s:Path - %s>%s", Origin, Origin, DestPlusDigis); + SendAPRSMessage(Message, Port); + + return; + } +} + +VOID ProcessQuery(char * Query) +{ + if (memcmp(Query, "IGATE?", 6) == 0) + { + IStatusCounter = (rand() & 31) + 5; // 5 - 36 secs delay + return; + } + + if (memcmp(Query, "APRS?", 5) == 0) + { + BeaconCounter = (rand() & 31) + 5; // 5 - 36 secs delay + return; + } +} +Dll VOID APIENTRY APISendBeacon() +{ + BeaconCounter = 2; +} + +typedef struct _BeaconParams +{ + int toPort; + char * BeaconText; + BOOL SendStatus; + BOOL SendSOGCOG; +} Params; + + +Params BeaconParams; + +void SendBeaconThread(void * Params); + +VOID SendBeacon(int toPort, char * BeaconText, BOOL SendStatus, BOOL SendSOGCOG) +{ + // Send to IS if needed then start a thread to send to radio ports + + if (PosnSet == FALSE) + return; + + if (APRSISOpen && toPort == 0 && BeacontoIS) + { + char SOGCOG[10] = ""; + char ISMsg[300]; + int Len; + + Debugprintf("Sending APRS Beacon to APRS-IS"); + + if (SendSOGCOG | (COG != 0.0)) + sprintf(SOGCOG, "%03.0f/%03.0f", COG, SOG); + + if (PHG) // Send PHG instead of SOG COG + Len = sprintf(ISMsg, "%s>%s,TCPIP*:%c%s%c%s%c%s%s\r\n", APRSCall, APRSDest, + (APRSApplConnected) ? '=' : '!', LAT, SYMSET, LON, SYMBOL, PHG, BeaconText); + else + Len = sprintf(ISMsg, "%s>%s,TCPIP*:%c%s%c%s%c%s%s\r\n", APRSCall, APRSDest, + (APRSApplConnected) ? '=' : '!', LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + + ISSend(sock, ISMsg, Len, 0); + Debugprintf(">%s", ISMsg); + } + + BeaconParams.toPort = toPort; + BeaconParams.BeaconText = BeaconText; + BeaconParams.SendStatus = SendStatus; + BeaconParams.SendSOGCOG = SendSOGCOG; + + _beginthread(SendBeaconThread, 0, (VOID *) &BeaconParams); +} + +void SendBeaconThread(void * Param) +{ + // runs as a thread so we can sleep() between calls + + // Params are passed via a param block + + Params * BeaconParams = (Params *)Param; + + int toPort = BeaconParams->toPort; + char * BeaconText = BeaconParams->BeaconText; + BOOL SendStatus = BeaconParams->SendStatus; + BOOL SendSOGCOG = BeaconParams->SendSOGCOG; + + int Port; + DIGIMESSAGE Msg; + int Len; + char SOGCOG[256] = ""; + struct STATIONRECORD * Station; + struct PORTCONTROL * PORT; + + if (PosnSet == FALSE) + return; + + if (PHG) // Send PHG instead of SOG COG + strcpy(SOGCOG, PHG); + else + if (SendSOGCOG | (COG != 0.0)) + sprintf(SOGCOG, "%03.0f/%03.0f", COG, SOG); + + BeaconCounter = BeaconInterval * 60; + + if (ISPort && IGateEnabled) + Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', + LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + else + Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', + LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + + Msg.PID = 0xf0; + Msg.CTL = 3; + + // Add to dup check list, so we won't digi it if we hear it back + // Should we drop it if we've sent it recently ?? + + if (CheckforDups(APRSCall, Msg.L2DATA, Len - (19 + sizeof(void *)))) + return; + + // Add to our station list + + Station = FindStation(APRSCall, TRUE); + + if (Station == NULL) + return; + + + strcpy(Station->Path, "APBPQ1"); + strcpy(Station->LastPacket, Msg.L2DATA); +// Station->LastPort = Port; + + DecodeAPRSPayload(Msg.L2DATA, Station); + Station->TimeLastUpdated = time(NULL); + + if (toPort) + { + if (BeaconHddrLen[toPort] == 0) + return; + + Debugprintf("Sending APRS Beacon to port %d", toPort); + + memcpy(Msg.DEST, &BeaconHeader[toPort][0][0], 10 * 7); // Clear unused digis + Msg.DEST[6] |= 0x80; // set Command Bit + + GetSemaphore(&Semaphore, 12); + Send_AX_Datagram(&Msg, Len + 2, toPort); + FreeSemaphore(&Semaphore); + + return; + } + + for (Port = 1; Port <= MaxBPQPortNo; Port++) // Check all ports + { + if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined + { + Debugprintf("Sending APRS Beacon to port %d", Port); + + if (ISPort && IGateEnabled) + Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', + LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + else + Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', + LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + Msg.PID = 0xf0; + Msg.CTL = 3; + + memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7); + Msg.DEST[6] |= 0x80; // set Command Bit + + GetSemaphore(&Semaphore, 12); + Send_AX_Datagram(&Msg, Len + 2, Port); + FreeSemaphore(&Semaphore); + + // if Port has interlock set pause before next + + PORT = GetPortTableEntryFromPortNum(Port); + + // Just pause for all ports + +// if (PORT && PORT->PORTINTERLOCK) + Sleep(20000); + } + } + return ; +} + +VOID SendObject(struct OBJECT * Object) +{ + int Port; + DIGIMESSAGE Msg; + int Len; + + // Add to dup list in case we get it back + + CheckforDups(APRSCall, Object->Message, (int)strlen(Object->Message)); + + for (Port = 1; Port <= MaxBPQPortNo; Port++) + { + if (Object->PortMap[Port]) + { + Msg.PID = 0xf0; + Msg.CTL = 3; + Len = sprintf(Msg.L2DATA, "%s", Object->Message); + memcpy(Msg.DEST, &Object->Path[0][0], Object->PathLen + 1); + Msg.DEST[6] |= 0x80; // set Command Bit + + Send_AX_Datagram(&Msg, Len + 2, Port); + } + } + + // Also send to APRS-IS if connected + + if (APRSISOpen && Object->PortMap[0]) + { + char ISMsg[300]; + Len = sprintf(ISMsg, "%s>%s,TCPIP*:%s\r\n", APRSCall, APRSDest, Object->Message); + ISSend(sock, ISMsg, Len, 0); + } +} + + +/* +VOID SendStatus(char * StatusText) +{ + int Port; + DIGIMESSAGE Msg; + int Len; + + if (APRSISOpen) + { + Msg.PID = 0xf0; + Msg.CTL = 3; + + Len = sprintf(Msg.L2DATA, ">%s", StatusText); + + for (Port = 1; Port <= NUMBEROFPORTS; Port++) + { + if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined + { + memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7); + Send_AX_Datagram(&Msg, Len + 2, Port); + } + } + + Len = sprintf(Msg.L2DATA, "%s>%s,TCPIP*:>%s\r\n", APRSCall, APRSDest, StatusText); + ISSend(sock, Msg.L2DATA, Len, 0); +// Debugprintf(">%s", Msg.L2DATA); + } +} + + +*/ +VOID SendIStatus() +{ + int Port; + DIGIMESSAGE Msg; + int Len; + + IStatusCounter = 3600; // One per hour + + if (APRSISOpen && BeacontoIS && RXOnly == 0) + { + Msg.PID = 0xf0; + Msg.CTL = 3; + + Len = sprintf(Msg.L2DATA, "%s,TCPIP*:%s", Msg.L2DATA); + } + +} + + +VOID DoSecTimer() +{ + struct OBJECT * Object = ObjectList; + + while (Object) + { + Object->Timer--; + + if (Object->Timer == 0) + { + Object->Timer = 60 * Object->Interval; + SendObject(Object); + } + Object = Object->Next; + } + + // Check SatGate Mode delay Q + + if (SatISQueue) + { + time_t NOW = time(NULL); + ISDELAY * SatISEntry = SatISQueue; + ISDELAY * Prev = NULL; + + while (SatISEntry) + { + if (SatISEntry->SendTIme < NOW) + { + // Send it + + ISSend(sock, SatISEntry->ISMSG, (int)strlen(SatISEntry->ISMSG), 0); + free(SatISEntry->ISMSG); + + if (Prev) + Prev->Next = SatISEntry->Next; + else + SatISQueue = SatISEntry->Next; + + free(SatISEntry); + return; // unlinkely to get 2 in sam esecond and doesn;t matter if we delay a bit more + } + + Prev = SatISEntry; + SatISEntry = SatISEntry->Next; + } + } + + if (ISPort && APRSISOpen == 0 && IGateEnabled) + { + ISDelayTimer++; + + if (ISDelayTimer > 60) + { + ISDelayTimer = 0; + _beginthread(APRSISThread, 0, (VOID *) TRUE); + } + } + + if (HostName[0]) + { + if (BlueNMEAOK == 0) + { + BlueNMEATimer++; + if (BlueNMEATimer > 15) + { + BlueNMEATimer = 0; + _beginthread(TCPConnect, 0, 0); + } + } + } + + if (GPSDHost[0]) + { + if (GPSDOK == 0) + { + GPSDTimer++; + if (GPSDTimer > 15) + { + GPSDTimer = 0; + _beginthread(GPSDConnect, 0, 0); + } + } + } + + if (BeaconCounter) + { + BeaconCounter--; + + if (BeaconCounter == 0) + { + BeaconCounter = BeaconInterval * 60; + SendBeacon(0, StatusMsg, TRUE, FALSE); + } + } + + if (IStatusCounter) + { + IStatusCounter--; + + if (IStatusCounter == 0) + { + SendIStatus(); + } + } + + if (GPSOK) + { + GPSOK--; + + if (GPSOK == 0) +#ifdef LINBPQ + Debugprintf("GPS Lost"); +#else + SetDlgItemText(hConsWnd, IDC_GPS, "No GPS"); +#endif + } + + APRSSecTimer(); // Code from APRS APPL +} + +int CountPool() +{ + struct STATIONRECORD * ptr = StationRecordPool; + int n = 0; + + while (ptr) + { + n++; + ptr = ptr->Next; + } + return n; +} + +static VOID DoMinTimer() +{ + struct STATIONRECORD * ptr = *StationRecords; + struct STATIONRECORD * last = NULL; + time_t AgeLimit = time(NULL ) - (ExpireTime * 60); + int i = 0; + + // Remove old records + + while (ptr) + { + if (ptr->TimeLastUpdated < AgeLimit) + { + StationCount--; + + if (last) + { + last->Next = ptr->Next; + + // Put on front of free chain + + ptr->Next = StationRecordPool; + StationRecordPool = ptr; + + ptr = last->Next; + } + else + { + // First in list + + *StationRecords = ptr->Next; + + // Put on front of free chain + + ptr->Next = StationRecordPool; + StationRecordPool = ptr; + + if (*StationRecords) + { + ptr = *StationRecords; + } + else + { + ptr = NULL; + } + } + } + else + { + last = ptr; + ptr = ptr->Next; + } + } +} + +char APRSMsg[300]; + +int ISHostIndex = 0; +char RealISHost[256]; + +VOID APRSISThread(void * Report) +{ + // Receive from core server + + char Signon[500]; + unsigned char work[4]; + + struct sockaddr_in sinx; + int addrlen=sizeof(sinx); + struct addrinfo hints, *res = 0, *saveres; + size_t len; + int err; + u_long param=1; + BOOL bcopt=TRUE; + char Buffer[1000]; + int InputLen = 1; // Non-zero + char errmsg[300]; + char * ptr; + size_t inptr = 0; + char APRSinMsg[1000]; + char PortString[20]; + char serv[256]; + + Debugprintf("BPQ32 APRS IS Thread"); +#ifndef LINBPQ + SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Connecting"); +#endif + + if (ISFilter[0]) + sprintf(Signon, "user %s pass %d vers BPQ32 %s filter %s\r\n", + APRSCall, ISPasscode, TextVerstring, ISFilter); + else + sprintf(Signon, "user %s pass %d vers BPQ32 %s\r\n", + APRSCall, ISPasscode, TextVerstring); + + + sprintf(PortString, "%d", ISPort); + + // get host info, make socket, and connect it + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever + hints.ai_socktype = SOCK_STREAM; + getaddrinfo(ISHost, PortString, &hints, &res); + + InputLen = sprintf(errmsg, "Connecting to APRS Host %s\r\n", ISHost); + MonitorAPRSIS(errmsg, InputLen, FALSE); + + if (!res) + { + err = WSAGetLastError(); + InputLen = sprintf(errmsg, "APRS IS Resolve %s Failed Error %d\r\n", ISHost, err); + MonitorAPRSIS(errmsg, InputLen, FALSE); + + return; // Resolve failed + + } + + // Step thorough the list of hosts + + saveres = res; // Save for free + + if (res->ai_next) // More than one + { + int n = ISHostIndex; + + while (n && res->ai_next) + { + res = res->ai_next; + n--; + } + + if (n) + { + // We have run off the end of the list + + ISHostIndex = 0; // Back to start + res = saveres; + } + else + ISHostIndex++; + + } + + getnameinfo(res->ai_addr, (int)res->ai_addrlen, RealISHost, 256, serv, 256, 0); + + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + + if (sock == INVALID_SOCKET) + return; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + memcpy(work, res->ai_addr->sa_data, 4); + + Debugprintf("Trying APRSIS Host %d.%d.%d.%d (%d) %s", work[0], work[1], work[2], work[3], ISHostIndex, RealISHost); + + if (connect(sock, res->ai_addr, (int)res->ai_addrlen)) + { + err=WSAGetLastError(); + + // + // Connect failed + // + +#ifndef LINBPQ + MySetWindowText(GetDlgItem(hConsWnd, IGATESTATE), "IGate State: Connect Failed"); +#else + printf("APRS Igate connect failed\n"); +#endif + err=WSAGetLastError(); + InputLen = sprintf(errmsg, "Connect Failed %s af %d Error %d \r\n", RealISHost, res->ai_family, err); + MonitorAPRSIS(errmsg, InputLen, FALSE); + + freeaddrinfo(res); + return; + } + + freeaddrinfo(saveres); + + APRSISOpen = TRUE; + +#ifndef LINBPQ + MySetWindowText(GetDlgItem(hConsWnd, IGATESTATE), "IGate State: Connected"); +#endif + + InputLen=recv(sock, Buffer, 500, 0); + + if (InputLen > 0) + { + Buffer[InputLen] = 0; + Debugprintf(Buffer); + MonitorAPRSIS(Buffer, InputLen, FALSE); + } + + ISSend(sock, Signon, (int)strlen(Signon), 0); +/* + InputLen=recv(sock, Buffer, 500, 0); + + if (InputLen > 0) + { + Buffer[InputLen] = 0; + Debugprintf(Buffer); + MonitorAPRSIS(Buffer, InputLen, FALSE); + } + + InputLen=recv(sock, Buffer, 500, 0); + + if (InputLen > 0) + { + Buffer[InputLen] = 0; + Debugprintf(Buffer); + MonitorAPRSIS(Buffer, InputLen, FALSE); + } +*/ + while (InputLen > 0 && IGateEnabled) + { + InputLen = recv(sock, &APRSinMsg[inptr], (int)(500 - inptr), 0); + + if (InputLen > 0) + { + inptr += InputLen; + + ptr = memchr(APRSinMsg, 0x0a, inptr); + + while (ptr != NULL) + { + ptr++; // include lf + len = ptr-(char *)APRSinMsg; + + inptr -= len; // bytes left + + // UIView server has a null before crlf + + if (*(ptr - 3) == 0) + { + *(ptr - 3) = 13; + *(ptr - 2) = 10; + *(ptr - 1) = 0; + + len --; + } + + if (len > 10 && len < 300) // Ignore if way too long or too short + { + memcpy(&APRSMsg, APRSinMsg, len); + MonitorAPRSIS(APRSMsg, (int)len, FALSE); + if (APRSMsg[len - 2] == 13) + APRSMsg[len - 2] = 0; + else + APRSMsg[len - 1] = 0; + +// Debugprintf("%s", APRSMsg); + + ProcessAPRSISMsg(APRSMsg); + } + + if (inptr > 0) + { + memmove(APRSinMsg, ptr, inptr); + ptr = memchr(APRSinMsg, 0x0a, inptr); + } + else + ptr = 0; + + if (inptr < 0) + break; + } + } + } + + closesocket(sock); + + APRSISOpen = FALSE; + + Debugprintf("BPQ32 APRS IS Thread Exited"); + +#ifndef LINBPQ + if (IGateEnabled) + SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Disconnected"); + else + SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Disabled"); +#endif + ISDelayTimer = 30; // Retry pretty quickly + return; +} + +VOID ProcessAPRSISMsg(char * APRSMsg) +{ + char * Payload; + char * Source; + char * Dest; + char IGateCall[10] = " "; + char * ptr; + char Message[255]; + PAPRSHEARDRECORD MH; + time_t NOW = time(NULL); + char ISCopy[1024]; + struct STATIONRECORD * Station = NULL; +#ifdef WIN32 + struct _EXCEPTION_POINTERS exinfo; + char EXCEPTMSG[80] = ""; +#endif + + if (APRSMsg[0] == '#') // Comment + return; + + // if APRS Appl is atttached, queue message to it + + strcpy(ISCopy, APRSMsg); + + GetSemaphore(&Semaphore, 12); + +#ifdef WIN32 + + strcpy(EXCEPTMSG, "ProcessAPRSISMsg"); + + __try + { + + Station = DecodeAPRSISMsg(ISCopy); + + } + #include "StdExcept.c" + Debugprintf(APRSMsg); + } +#else + Station = DecodeAPRSISMsg(ISCopy); +#endif + + FreeSemaphore(&Semaphore); + +//}WB4APR-14>APRS,RELAY,TCPIP,G9RXG*::G3NRWVVVV:Hi Ian{001 +//KE7XO-2>hg,TCPIP*,qAC,T2USASW::G8BPQ-14 :Path - G8BPQ-14>APU25N +//IGATECALL>APRS,GATEPATH}FROMCALL>TOCALL,TCPIP,IGATECALL*:original packet data + + Payload = strchr(APRSMsg, ':'); + + // Get call of originating Igate + + ptr = Payload; + + if (Payload == NULL) + return; + + *(Payload++) = 0; + + while (ptr[0] != ',') + ptr--; + + ptr++; + + if (strlen(ptr) > 9) + return; + + memcpy(IGateCall, ptr, (int)strlen(ptr)); + + if (strstr(APRSMsg, ",qAS,") == 0) // Findu generates invalid q construct + { + MH = FindStationInMH(IGateCall); + if (MH) + { +// Debugprintf("Setting Igate Flag - %s:%s", APRSMsg, Payload); + MH->IGate = TRUE; // If we have seen this station on RF, set it as an Igate + } + } + Source = APRSMsg; + Dest = strchr(APRSMsg, '>'); + + if (Dest == NULL) + return; + + *(Dest++) = 0; // Termainate Source + ptr = strchr(Dest, ','); + + if (ptr) + *ptr = 0; + + MH = UpdateHeard(Source, 0); + + MH->Station = Station; + + // See if we should gate to RF. + + // Have we heard dest recently? (use the message dest (not ax.25 dest) - does this mean we only gate Messages? + // Not if it is an Igate (it will get a copy direct) + // Have we recently sent a message from this call - if so, we gate the next Position + +/* + + From http://www.aprs-is.net/IGateDetails.aspx + + Gate message packets and associated posits to RF if all of the following are true: + + the receiving station has been heard within range within a predefined time period (range defined + as digi hops, distance, or both). + + the sending station has not been heard via RF within a predefined time period (packets gated + from the Internet by other stations are excluded from this test). + + the sending station does not have TCPXX, NOGATE, or RFONLY in the header. + + the receiving station has not been heard via the Internet within a predefined time period. + + A station is said to be heard via the Internet if packets from the station contain TCPIP* or + TCPXX* in the header or if gated (3rd-party) packets are seen on RF gated by the station + and containing TCPIP or TCPXX in the 3rd-party header (in other words, the station is seen on RF + as being an IGate). + +*/ + + if (Payload[0] == ':') // Message + { + char MsgDest[10]; + APRSHEARDRECORD * STN; + + if (strlen(Payload) > 100) // I don't think any valid APRS msgs are more than this + return; + + memcpy(MsgDest, &Payload[1], 9); + MsgDest[9] = 0; + + if (strcmp(MsgDest, CallPadded) == 0) // to us + return; + + // Check that the sending station has not been heard via RF recently + + if (MH->rfPort && (NOW - MH->MHTIME) < GATETIMELIMIT) + return; + + STN = FindStationInMH(MsgDest); + + // Shouldn't we check DUP list, in case we have digi'ed this message directly? + + if (CheckforDups(Source, Payload, (int)strlen(Payload))) + return; + + // has the receiving station has been heard on RF and is not an IGate + + if (STN && STN->rfPort && !STN->IGate && (NOW - STN->MHTIME) < GATETIMELIMIT) + { + sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload); + + GetSemaphore(&Semaphore, 12); + SendAPRSMessageEx(Message, STN->rfPort, APRSCall, 1); // Set gated to IS flag + FreeSemaphore(&Semaphore); + + MessageCount++; + MH->LASTMSG = NOW; + + return; + } + } + + // Not a message. If it is a position report gate if have sent a message recently + + if (Payload[0] == '!' || Payload[0] == '/' || Payload[0] == '=' || Payload[0] == '@') // Posn Reports + { + if ((NOW - MH->LASTMSG) < 900 && MH->rfPort) + { + sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload); + + GetSemaphore(&Semaphore, 12); + SendAPRSMessageEx(Message, MH->rfPort, APRSCall, 1); // Set gated to IS flag + FreeSemaphore(&Semaphore); + + return; + } + } + + // If Gate Local to RF is defined, and station is in range, Gate it + + if (GateLocal && Station) + { + if (Station->Object) + Station = Station->Object; // If Object Report, base distance on Object, not station + + if (Station->Lat != 0.0 && Station->Lon != 0.0 && myDistance(Station->Lat, Station->Lon, 0) < GateLocalDistance) + { + sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload); + GetSemaphore(&Semaphore, 12); + SendAPRSMessage(Message, -1); // Send to all APRS Ports + FreeSemaphore(&Semaphore); + + return; + } + } +} + +APRSHEARDRECORD * FindStationInMH(char * Call) +{ + APRSHEARDRECORD * MH = MHDATA; + int i; + + // We keep call in ascii format, as that is what we get from APRS-IS, and we have it in that form + + for (i = 0; i < HEARDENTRIES; i++) + { + if (memcmp(Call, MH->MHCALL, 9) == 0) + return MH; + + MH++; + } + + return NULL; +} + +APRSHEARDRECORD * UpdateHeard(UCHAR * Call, int Port) +{ + APRSHEARDRECORD * MH = MHDATA; + APRSHEARDRECORD * MHBASE = MH; + int i; + time_t NOW = time(NULL); + time_t OLDEST = NOW - MAXAGE; + char CallPadded[10] = " "; + BOOL SaveIGate = FALSE; + time_t SaveLastMsg = 0; + int SaveheardViaIS = 0; + + // We keep call in ascii format, space padded, as that is what we get from APRS-IS, and we have it in that form + + // Make Sure Space Padded + + memcpy(CallPadded, Call, (int)strlen(Call)); + + for (i = 0; i < MAXHEARDENTRIES; i++) + { + if (memcmp(CallPadded, MH->MHCALL, 10) == 0) + { + // if from APRS-IS, only update if record hasn't been heard via RF + + if (Port == 0) + MH->heardViaIS = 1; // Flag heard via IS + + if (Port == 0 && MH->rfPort) + return MH; // Don't update RF with IS + + if (Port == MH->rfPort) + { + SaveIGate = MH->IGate; + SaveLastMsg = MH->LASTMSG; + SaveheardViaIS = MH->heardViaIS; + goto DoMove; + } + } + + if (MH->MHCALL[0] == 0 || MH->MHTIME < OLDEST) // Spare entry + goto DoMove; + + MH++; + } + + // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP + + i = MAXHEARDENTRIES - 1; + + // Move others down and add at front +DoMove: + if (i != 0) // First + memmove(MHBASE + 1, MHBASE, i * sizeof(APRSHEARDRECORD)); + + if (i >= HEARDENTRIES) + { + char Status[80]; + + HEARDENTRIES = i + 1; + + sprintf(Status, "IGATE Stats: Msgs %d Local Stns %d", MessageCount , CountLocalStations()); +#ifndef LINBPQ + SetDlgItemText(hConsWnd, IGATESTATS, Status); +#endif + } + + memcpy (MHBASE->MHCALL, CallPadded, 10); + MHBASE->rfPort = Port; + MHBASE->MHTIME = NOW; + MHBASE->IGate = SaveIGate; + MHBASE->LASTMSG = SaveLastMsg; + MHBASE->heardViaIS = SaveheardViaIS; + + return MHBASE; +} + +int CountLocalStations() +{ + APRSHEARDRECORD * MH = MHDATA; + int i, n = 0; + + for (i = 0; i < HEARDENTRIES; i++) + { + if (MH->rfPort) // DOn't count IS Stations + n++; + + MH++; + } + return n; +} + + +BOOL CheckforDups(char * Call, char * Msg, int Len) +{ + // Primitive duplicate suppression - see if same call and text reeived in last few seconds + + time_t Now = time(NULL); + time_t DupCheck = Now - DUPSECONDS; + int i, saveindex = -1; + char * ptr1; + + if (Len < 1) + return TRUE; + + for (i = 0; i < MAXDUPS; i++) + { + if (DupInfo[i].DupTime < DupCheck) + { + // too old - use first if we need to save it + + if (saveindex == -1) + { + saveindex = i; + } + + if (DupInfo[i].DupTime == 0) // Off end of used area + break; + + continue; + } + + if ((Len == DupInfo[i].DupLen || (DupInfo[i].DupLen == 99 && Len > 99)) && memcmp(Call, DupInfo[i].DupUser, 7) == 0 && (memcmp(Msg, DupInfo[i].DupText, DupInfo[i].DupLen) == 0)) + { + // Duplicate, so discard + + Msg[Len] = 0; + ptr1 = strchr(Msg, 13); + if (ptr1) + *ptr1 = 0; + +// Debugprintf("Duplicate Message suppressed %s", Msg); + return TRUE; // Duplicate + } + } + + // Not in list + + if (saveindex == -1) // List is full + saveindex = MAXDUPS - 1; // Stick on end + + DupInfo[saveindex].DupTime = Now; + memcpy(DupInfo[saveindex].DupUser, Call, 7); + + if (Len > 99) Len = 99; + + DupInfo[saveindex].DupLen = Len; + memcpy(DupInfo[saveindex].DupText, Msg, Len); + + return FALSE; +} + +char * FormatAPRSMH(APRSHEARDRECORD * MH) + { + // Called from CMD.ASM + + struct tm * TM; + static char MHLine[50]; + time_t szClock = MH->MHTIME; + + szClock = (time(NULL) - szClock); + TM = gmtime(&szClock); + + sprintf(MHLine, "%-10s %d %.2d:%.2d:%.2d:%.2d %s\r", + MH->MHCALL, MH->rfPort, TM->tm_yday, TM->tm_hour, TM->tm_min, TM->tm_sec, (MH->IGate) ? "IGATE" : ""); + + return MHLine; + } + +// GPS Handling Code + +void SelectSource(BOOL Recovering); +void DecodeRMC(char * msg, size_t len); + +void PollGPSIn(); + + +UINT GPSType = 0xffff; // Source of Postion info - 1 = Phillips 2 = AIT1000. ffff = not posn message + +int RecoveryTimer; // Serial Port recovery + +double PI = 3.1415926535; +double P2 = 3.1415926535 / 180; + +double Latitude, Longtitude, SOG, COG, LatIncrement, LongIncrement; +double LastSOG = -1.0; + +BOOL Check0183CheckSum(char * msg, size_t len) +{ + BOOL retcode=TRUE; + char * ptr; + UCHAR sum,xsum1,xsum2; + + sum=0; + ptr=++msg; // Skip $ + +loop: + + if (*(ptr)=='*') goto eom; + + sum ^=*(ptr++); + + len--; + + if (len > 0) goto loop; + + return TRUE; // No Checksum + +eom: + _strupr(ptr); + + xsum1=*(++ptr); + xsum1-=0x30; + if (xsum1 > 9) xsum1-=7; + + xsum2=*(++ptr); + xsum2-=0x30; + if (xsum2 > 9) xsum2-=7; + + xsum1=xsum1<<4; + xsum1+=xsum2; + + return (xsum1==sum); +} + +BOOL OpenGPSPort() +{ + struct PortInfo * portptr = &InPorts[0]; + + // open COMM device + + if (strlen(GPSPort) < 4) + { + int port = atoi(GPSPort); +#ifdef WIN32 + sprintf(GPSPort, "COM%d", port); +#else + sprintf(GPSPort, "com%d", port); +#endif + } + + portptr->hDevice = OpenCOMPort(GPSPort, GPSSpeed, TRUE, TRUE, FALSE, 0); + + if (portptr->hDevice == 0) + { + return FALSE; + } + + return TRUE; +} + +void PollGPSIn() +{ + size_t len; + char GPSMsg[2000] = "$GPRMC,061213.000,A,5151.5021,N,00056.8388,E,0.15,324.11,190414,,,A*6F"; + char * ptr; + struct PortInfo * portptr; + + portptr = &InPorts[0]; + + if (!portptr->hDevice) + return; + + getgpsin: + +// Comm Error - probably lost USB Port. Try closing and reopening after a delay + +// if (RecoveryTimer == 0) +// { +// RecoveryTimer = 100; // 10 Secs +// return; +// } +// } + + if (portptr->gpsinptr == 160) + portptr->gpsinptr = 0; + + len = ReadCOMBlock(portptr->hDevice, &portptr->GPSinMsg[portptr->gpsinptr], + 160 - portptr->gpsinptr); + + if (len > 0) + { + portptr->gpsinptr += (int)len; + + ptr = memchr(portptr->GPSinMsg, 0x0a, portptr->gpsinptr); + + while (ptr != NULL) + { + ptr++; // include lf + len=ptr-(char *)&portptr->GPSinMsg; + memcpy(&GPSMsg,portptr->GPSinMsg,len); + + GPSMsg[len] = 0; + + if (Check0183CheckSum(GPSMsg, len)) + if (memcmp(&GPSMsg[3], "RMC", 3) == 0) + DecodeRMC(GPSMsg, len); + + portptr->gpsinptr -= (int)len; // bytes left + + if (portptr->gpsinptr > 0 && *ptr == 0) + { + *ptr++; + portptr->gpsinptr--; + } + + if (portptr->gpsinptr > 0) + { + memmove(portptr->GPSinMsg,ptr, portptr->gpsinptr); + ptr = memchr(portptr->GPSinMsg, 0x0a, portptr->gpsinptr); + } + else + ptr=0; + } + + goto getgpsin; + } + return; +} + + +void ClosePorts() +{ + if (InPorts[0].hDevice) + { + CloseCOMPort(InPorts[0].hDevice); + InPorts[0].hDevice=0; + } + + return; +} + +void DecodeRMC(char * msg, size_t len) +{ + char * ptr1; + char * ptr2; + char TimHH[3], TimMM[3], TimSS[3]; + char OurSog[5], OurCog[4]; + char LatDeg[3], LonDeg[4]; + char NewLat[10] = "", NewLon[10] = ""; + struct STATIONRECORD * Stn1; + + char Day[3]; + + ptr1 = &msg[7]; + + len-=7; + + ptr2=(char *)memchr(ptr1,',',15); + + if (ptr2 == 0) return; // Duff + + *(ptr2++)=0; + + memcpy(TimHH,ptr1,2); + memcpy(TimMM,ptr1+2,2); + memcpy(TimSS,ptr1+4,2); + TimHH[2]=0; + TimMM[2]=0; + TimSS[2]=0; + + ptr1=ptr2; + + if (*(ptr1) != 'A') // ' Data Not Valid + { +#ifndef LINBPQ + SetDlgItemText(hConsWnd, IDC_GPS, "No GPS Fix"); +#endif + return; + } + + ptr1+=2; + + ptr2=(char *)memchr(ptr1,',',15); + + if (ptr2 == 0) return; // Duff + + *(ptr2++)=0; + + memcpy(NewLat, ptr1, 7); + memcpy(LatDeg, ptr1, 2); + LatDeg[2]=0; + Lat=atof(LatDeg) + (atof(ptr1+2)/60); + + if (*(ptr1+7) > '4') if (NewLat[6] < '9') NewLat[6]++; + + ptr1=ptr2; + + NewLat[7] = (*ptr1); + if ((*ptr1) == 'S') Lat=-Lat; + + ptr1+=2; + + ptr2=(char *)memchr(ptr1,',',15); + + if (ptr2 == 0) return; // Duff + *(ptr2++)=0; + + memcpy(NewLon, ptr1, 8); + + memcpy(LonDeg,ptr1,3); + LonDeg[3]=0; + Lon=atof(LonDeg) + (atof(ptr1+3)/60); + + if (*(ptr1+8) > '4') if (NewLon[7] < '9') NewLon[7]++; + + ptr1=ptr2; + + NewLon[8] = (*ptr1); + if ((*ptr1) == 'W') Lon=-Lon; + + // Now have a valid posn, so stop sending Undefined LOC Sysbol + + SYMBOL = CFGSYMBOL; + SYMSET = CFGSYMSET; + + PosnSet = TRUE; + + Stn1 = (struct STATIONRECORD *)StnRecordBase; // Pass to App + Stn1->Lat = Lat; + Stn1->Lon = Lon; + + if (GPSOK == 0) + { +#ifdef LINBPQ + Debugprintf("GPS OK"); + printf("GPS OK\n"); +#else + SetDlgItemText(hConsWnd, IDC_GPS, "GPS OK"); +#endif + } + + GPSOK = 30; + + ptr1+=2; + + ptr2 = (char *)memchr(ptr1,',',30); + + if (ptr2 == 0) return; // Duff + + *(ptr2++)=0; + + memcpy(OurSog, ptr1, 4); + OurSog[4] = 0; + + ptr1=ptr2; + + ptr2 = (char *)memchr(ptr1,',',15); + + if (ptr2 == 0) return; // Duff + + *(ptr2++)=0; + + memcpy(OurCog, ptr1, 3); + OurCog[3] = 0; + + memcpy(Day,ptr2,2); + Day[2]=0; + + SOG = atof(OurSog); + COG = atof(OurCog); + + if (strcmp(NewLat, LAT) || strcmp(NewLon, LON)) + { + if (MobileBeaconInterval) + { + time_t NOW = time(NULL); + + if ((NOW - LastMobileBeacon) > MobileBeaconInterval) + { + LastMobileBeacon = NOW; + SendBeacon(0, StatusMsg, FALSE, TRUE); + } + } + if (GPSSetsLocator) + { + ToLOC(Lat, Lon, LOC); + sprintf(LOCATOR, "%f:%f", Lat, Lon); + } + } + + strcpy(LAT, NewLat); + strcpy(LON, NewLon); +} + +Dll VOID APIENTRY APRSConnect(char * Call, char * Filter) +{ + // Request APRS Data from Switch (called by APRS Applications) + + APRSApplConnected = TRUE; + APRSWeb = TRUE; + + strcpy(APPLFilter, Filter); + + if (APPLFilter[0]) + { + // This is called in APPL context so must queue the message + + char Msg[2000]; + PMSGWITHLEN buffptr; + + sprintf(Msg, "filter %s", Filter); + + if (strlen(Msg) > 240) + Msg[240] = 0; + + + GetSemaphore(&Semaphore, 11); + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + strcpy(&buffptr->Data[0], "SERVER"); + strcpy(&buffptr->Data[10], Msg); + C_Q_ADD(&APPLTX_Q, buffptr); + } + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + strcpy(&buffptr->Data[0], "SERVER"); + strcpy(&buffptr->Data[10], "filter?"); + C_Q_ADD(&APPLTX_Q, buffptr); + } + FreeSemaphore(&Semaphore); + } + strcpy(Call, CallPadded); +} + +Dll VOID APIENTRY APRSDisconnect() +{ + // Stop requesting APRS Data from Switch (called by APRS Applications) + + char Msg[2000]; + PMSGWITHLEN buffptr; + + strcpy(ISFilter, NodeFilter); + sprintf(Msg, "filter %s", NodeFilter); + + APRSApplConnected = FALSE; + APRSWeb = FALSE; + + GetSemaphore(&Semaphore, 11); + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + strcpy(&buffptr->Data[0], "SERVER"); + strcpy(&buffptr->Data[10], Msg); + C_Q_ADD(&APPLTX_Q, buffptr); + } + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + strcpy(&buffptr->Data[0], "SERVER"); + strcpy(&buffptr->Data[10], "filter?"); + C_Q_ADD(&APPLTX_Q, buffptr); + } + + while (APPL_Q) + { + buffptr = Q_REM(&APPL_Q); + ReleaseBuffer(buffptr); + } + + FreeSemaphore(&Semaphore); +} + + +Dll char * APIENTRY APRSGetStatusMsgPtr() +{ + return StatusMsg; +} + + + +Dll BOOL APIENTRY GetAPRSFrame(char * Frame, char * Call) +{ + // Request APRS Data from Switch (called by APRS Applications) + + void ** buffptr; +#ifdef bpq32 + struct _EXCEPTION_POINTERS exinfo; +#endif + + GetSemaphore(&Semaphore, 10); + { + if (APPL_Q) + { + buffptr = Q_REM(&APPL_Q); + + memcpy(Call, (char *)&buffptr[2], 12); + strcpy(Frame, (char *)&buffptr[5]); + + ReleaseBuffer(buffptr); + FreeSemaphore(&Semaphore); + return TRUE; + } + } + + FreeSemaphore(&Semaphore); + + return FALSE; +} + +Dll BOOL APIENTRY PutAPRSFrame(char * Frame, int Len, int Port) +{ + // Called from BPQAPRS App + // Message has to be queued so it can be sent by Timer Process (IS sock is not valid in this context) + + PMSGWITHLEN buffptr; + + GetSemaphore(&Semaphore, 11); + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = ++Len; // Len doesn't include Null + memcpy(&buffptr->Data[0], Frame, Len); + C_Q_ADD(&APPLTX_Q, buffptr); + } + +// buffptr-> = Port; // Pass to SendAPRSMessage(); + + FreeSemaphore(&Semaphore); + + return TRUE; +} + +Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall) +{ + // Called from BPQAPRS App or BPQMail + // Message has to be queued so it can be sent by Timer Process (IS sock is not valid in this context) + + PMSGWITHLEN buffptr; + + if (APRSActive == 0) + return FALSE; + + GetSemaphore(&Semaphore, 11); + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + memcpy(&buffptr->Data[0], ToCall, 9); + buffptr->Data[9] = 0; + strcpy(&buffptr->Data[10], Text); + C_Q_ADD(&APPLTX_Q, buffptr); + } + + FreeSemaphore(&Semaphore); + + return TRUE; +} + +Dll BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon) +{ + *PLat = Lat; + *PLon = Lon; + + return GPSOK; +} + +Dll BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon) +{ + strcpy(PLat, LAT); + strcpy(PLon, LON); + + return GPSOK; +} + +// Code to support getting GPS from Andriod Device running BlueNMEA + + +#define SD_BOTH 0x02 + +static VOID ProcessReceivedData(SOCKET TCPSock) +{ + char UDPMsg[8192]; + char Buffer[65536]; + + int len = recv(TCPSock, Buffer, 65500, 0); + + char * ptr; + char * Lastptr; + + if (len <= 0) + { + closesocket(TCPSock); + BlueNMEAOK = FALSE; + return; + } + + ptr = Lastptr = Buffer; + Buffer[len] = 0; + + while (len > 0) + { + ptr = strchr(Lastptr, 10); + + if (ptr) + { + size_t Len = ptr - Lastptr -1; + + if (Len > 8100) + return; + + memcpy(UDPMsg, Lastptr, Len); + UDPMsg[Len++] = 13; + UDPMsg[Len++] = 10; + UDPMsg[Len] = 0; + + if (!Check0183CheckSum(UDPMsg, Len)) + { + Debugprintf("Checksum Error %s", UDPMsg); + } + else + { + if (memcmp(&UDPMsg[3], "RMC", 3) == 0) + DecodeRMC(UDPMsg, Len); + + else if (memcmp(UDPMsg, "!AIVDM", 6) == 0) + ProcessAISMessage(UDPMsg, Len); + + } + Lastptr = ptr + 1; + len -= (int)Len; + } + else + return; + } +} + +static VOID TCPConnect(void * unused) +{ + int err, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + struct sockaddr_in destaddr; + SOCKET TCPSock; + + if (HostName[0] == 0) + return; + + destaddr.sin_addr.s_addr = inet_addr(HostName); + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(HostPort); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname(HostName); + + if (!HostEnt) + return; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + TCPSock = socket(AF_INET,SOCK_STREAM,0); + + if (TCPSock == INVALID_SOCKET) + { + return; + } + + setsockopt (TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + BlueNMEAOK = TRUE; // So we don't try to reconnect while waiting + + if (connect(TCPSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) + { + // + // Connected successful + // + + ioctl(TCPSock, FIONBIO, ¶m); + } + else + { + err=WSAGetLastError(); +#ifdef LINBPQ + printf("Connect Failed for AIS socket - error code = %d\n", err); +#else + Debugprintf("Connect Failed for AIS socket - error code = %d", err); +#endif + closesocket(TCPSock); + BlueNMEAOK = FALSE; + + return; + } + + BlueNMEAOK = TRUE; + + while (TRUE) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TCPSock,&readfs); + FD_SET(TCPSock,&errorfs); + + timeout.tv_sec = 900; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TCPSock, &readfs)) + { + ProcessReceivedData(TCPSock); + } + + if (FD_ISSET(TCPSock, &errorfs)) + { +Lost: +#ifdef LINBPQ + printf("AIS Connection lost\n"); +#endif + closesocket(TCPSock); + BlueNMEAOK = FALSE;; + return; + } + } + else + { + // 15 mins without data. Shouldn't happen + + shutdown(TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TCPSock); + BlueNMEAOK = FALSE; + return; + } + } +} + +int GPSDAlerted = 0; + +static VOID GPSDConnect(void * unused) +{ + int err, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + struct sockaddr_in destaddr; + SOCKET TCPSock; + + if (GPSDHost[0] == 0) + return; + + destaddr.sin_addr.s_addr = inet_addr(GPSDHost); + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(GPSDPort); + + TCPSock = socket(AF_INET,SOCK_STREAM,0); + + if (TCPSock == INVALID_SOCKET) + { + return; + } + + setsockopt (TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + GPSDOK = TRUE; // So we don't try to reconnect while waiting + + if (connect(TCPSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) + { + // + // Connected successful + // + +#ifdef LINBPQ + printf("GPSD Connected\n"); +#else + Debugprintf("GPSD Connected"); +#endif + GPSDAlerted = 0; + ioctl(TCPSock, FIONBIO, ¶m); + + // Request data + + send(TCPSock, "?WATCH={\"enable\":true,\"nmea\":true}\r\n", 36, 0); + } + else + { + err=WSAGetLastError(); + if (GPSDAlerted == 0) +#ifdef LINBPQ + printf("GPSD Connect Failed - error code = %d\n", err); +#else + Debugprintf("GPSD Connect Failed - error code = %d", err); +#endif + GPSDAlerted = 1; + closesocket(TCPSock); + GPSDOK = FALSE; + + return; + } + + while (TRUE) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TCPSock,&readfs); + FD_SET(TCPSock,&errorfs); + + timeout.tv_sec = 900; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TCPSock, &readfs)) + { + char Buffer[65536]; + int len = recv(TCPSock, Buffer, 65500, 0); + char TCPMsg[8192]; + + char * ptr; + char * Lastptr; + + if (len == 0) + { + closesocket(TCPSock); + GPSDOK = FALSE;; + return; + } + + if (len < 9000) + { + Buffer[len] = 0; + + ptr = Lastptr = Buffer; + Buffer[len] = 0; + + while (len > 0) + { + ptr = strchr(Lastptr, 10); + + if (ptr) + { + size_t Len = ptr - Lastptr -1; + + if (Len > 8100) + return; + + memcpy(TCPMsg, Lastptr, Len); + TCPMsg[Len++] = 13; + TCPMsg[Len++] = 10; + TCPMsg[Len] = 0; + + if (!Check0183CheckSum(TCPMsg, Len)) + { + Debugprintf("Checksum Error %s", TCPMsg); + } + else + { + if (memcmp(&TCPMsg[3], "RMC", 3) == 0) + DecodeRMC(TCPMsg, Len); + } + Lastptr = ptr + 1; + len -= (int)Len; + } + else + return; + } + } + } + + if (FD_ISSET(TCPSock, &errorfs)) + { +Lost: +#ifdef LINBPQ + printf("GPSD Connection lost\n"); +#endif + closesocket(TCPSock); + GPSDOK = FALSE;; + return; + } + } + else + { + // 15 mins without data. Shouldn't happen + + shutdown(TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TCPSock); + GPSDOK = FALSE; + return; + } + } +} + + + + + +// Code Moved from APRS Application + +// +// APRS Mapping and Messaging App for BPQ32 Switch. +// + + +VOID APIENTRY APRSConnect(char * Call, char * Filter); +VOID APIENTRY APRSDisconnect(); +BOOL APIENTRY GetAPRSFrame(char * Frame, char * Call); +BOOL APIENTRY PutAPRSFrame(char * Frame, int Len, int Port); +BOOL APIENTRY PutAPRSMessage(char * Frame, int Len); +BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon); +BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon); +VOID APIENTRY APISendBeacon(); + + +int NewLine(HWND hWnd); +VOID ProcessBuff(HWND hWnd, MESSAGE * buff,int len,int stamp); +int TogglePort(HWND hWnd, int Item, int mask); +VOID SendFrame(UCHAR * buff, int txlen); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +int KissDecode(UCHAR * inbuff, int len); +//void UpdateStation(char * Call, char * Path, char * Comment, double V_Lat, double V_Lon, double V_SOG, double V_COG, int iconRow, int iconCol); +VOID FindStationsByPixel(int MouseX, int MouseY); +void RefreshStation(struct STATIONRECORD * ptr); +void RefreshStationList(); +void RefreshStationMap(); +BOOL DecodeLocationString(UCHAR * Payload, struct STATIONRECORD * Station); +VOID Decode_MIC_E_Packet(char * Payload, struct STATIONRECORD * Station); +BOOL GetLocPixels(double Lat, double Lon, int * X, int * Y); +VOID APRSPoll(); +VOID OSMThread(); +VOID ResolveThread(); +VOID RefreshTile(char * FN, int Zoom, int x, int y); +int ProcessMessage(char * Payload, struct STATIONRECORD * Station); +VOID APRSSecTimer(); +double myBearing(double laa, double loa); + +BOOL CreatePipeThread(); + +VOID SendWeatherBeacon(); +VOID DecodeWXPortList(); + + +VOID DecodeWXReport(struct APRSConnectionInfo * sockptr, char * WX) +{ + UCHAR * ptr = strchr(WX, '_'); + char Type; + int Val; + + if (ptr == 0) + return; + + sockptr->WindDirn = atoi(++ptr); + ptr += 4; + sockptr->WindSpeed = atoi(ptr); + ptr += 3; +WXLoop: + + Type = *(ptr++); + + if (*ptr =='.') // Missing Value + { + while (*ptr == '.') + ptr++; + + goto WXLoop; + } + + Val = atoi(ptr); + + switch (Type) + { + case 'c': // = wind direction (in degrees). + + sockptr->WindDirn = Val; + break; + + case 's': // = sustained one-minute wind speed (in mph). + + sockptr->WindSpeed = Val; + break; + + case 'g': // = gust (peak wind speed in mph in the last 5 minutes). + + sockptr->WindGust = Val; + break; + + case 't': // = temperature (in degrees Fahrenheit). Temperatures below zero are expressed as -01 to -99. + + sockptr->Temp = Val; + break; + + case 'r': // = rainfall (in hundredths of an inch) in the last hour. + + sockptr->RainLastHour = Val; + break; + + case 'p': // = rainfall (in hundredths of an inch) in the last 24 hours. + + sockptr->RainLastDay = Val; + break; + + case 'P': // = rainfall (in hundredths of an inch) since midnight. + + sockptr->RainToday = Val; + break; + + case 'h': // = humidity (in %. 00 = 100%). + + sockptr->Humidity = Val; + break; + + case 'b': // = barometric pressure (in tenths of millibars/tenths of hPascal). + + sockptr->Pressure = Val; + break; + + default: + + return; + } + while(isdigit(*ptr)) + { + ptr++; + } + + if (*ptr != ' ') + goto WXLoop; +} + +static char HeaderTemplate[] = "Accept: */*\r\nHost: %s\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n"; +//char Header[] = "Accept: */*\r\nHost: tile.openstreetmap.org\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n"; + +char APRSMsg[300]; + +Dll struct STATIONRECORD * APIENTRY APPLFindStation(char * Call, BOOL AddIfNotFount) +{ + // Called from APRS Appl + + struct STATIONRECORD * Stn; + + GetSemaphore(&Semaphore, 12); + Stn = FindStation(Call, AddIfNotFount) ; + FreeSemaphore(&Semaphore); + + return Stn; +} + +Dll struct APRSMESSAGE * APIENTRY APRSGetMessageBuffer() +{ + struct APRSMESSAGE * ptr = MessageRecordPool; + + if (ptr == NULL) + { + // try getting oldest + + ptr = SMEM->Messages; + + if (ptr) + { + SMEM->Messages = ptr->Next; + memset(ptr, 0, sizeof(struct APRSMESSAGE)); + } + return ptr; + } + + if (ptr) + { + MessageRecordPool = ptr->Next; // Unchain + MessageCount++; + + ptr->Next = NULL; + + memset(ptr, 0, sizeof(struct APRSMESSAGE)); + } + + return ptr; +} + + +struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFount) +{ + int i = 0; + struct STATIONRECORD * find; + struct STATIONRECORD * ptr; + struct STATIONRECORD * last = NULL; + int sum = 0; + + if (APRSActive == 0 || StationRecords == 0) + return FALSE; + + if (strlen(Call) > 9) + { + Debugprintf("APRS Call too long %s", Call); + Call[9] = 0; + } + + find = *StationRecords; + while(find) + { + if (strlen(find->Callsign) > 9) + { + Debugprintf("APRS Call in Station List too long %s", find->Callsign); + find->Callsign[9] = 0; + } + + if (strcmp(find->Callsign, Call) == 0) + return find; + + last = find; + find = find->Next; + i++; + } + + // Not found - add on end + + if (AddIfNotFount) + { + // Get first from station record pool + + ptr = StationRecordPool; + + if (ptr) + { + StationRecordPool = ptr->Next; // Unchain + StationCount++; + } + else + { + // Get First from Stations + + ptr = *StationRecords; + + if (ptr) + *StationRecords = ptr->Next; + } + + if (ptr == NULL) + return NULL; + + memset(ptr, 0, sizeof(struct STATIONRECORD)); + +// EnterCriticalSection(&Crit); + + if (*StationRecords == NULL) + *StationRecords = ptr; + else + last->Next = ptr; + +// LeaveCriticalSection(&Crit); + + // Debugprintf("APRS Add Stn %s Station Count = %d", Call, StationCount); + + strcpy(ptr->Callsign, Call); + ptr->TimeLastUpdated = ptr->TimeAdded = time(NULL); + ptr->Index = i; + ptr->NoTracks = DefaultNoTracks; + + for (i = 0; i < 9; i++) + sum += Call[i]; + + sum %= 20; + + ptr->TrackColour = sum; + ptr->Moved = TRUE; + + return ptr; + } + else + return NULL; +} + +struct STATIONRECORD * ProcessRFFrame(char * Msg, int len, int * ourMessage) +{ + char * Payload; + char * Path = NULL; + char * Comment = NULL; + char * Callsign; + char * ptr; + int Port = 0; + + struct STATIONRECORD * Station = NULL; + + Msg[len - 1] = 0; + +// Debugprintf("RF Frame %s", Msg); + + Msg += 10; // Skip Timestamp + + Payload = strchr(Msg, ':'); // End of Address String + + if (Payload == NULL) + { + Debugprintf("Invalid Msg %s", Msg); + return Station; + } + + ptr = strstr(Msg, "Port="); + + if (ptr) + Port = atoi(&ptr[5]); + + Payload++; + + if (*Payload != 0x0d) + return Station; + + *Payload++ = 0; + + Callsign = Msg; + + Path = strchr(Msg, '>'); + + if (Path == NULL) + { + Debugprintf("Invalid Header %s", Msg); + return Station; + } + + *Path++ = 0; + + ptr = strchr(Path, ' '); + + if (ptr) + *ptr = 0; + + // Look up station - create a new one if not found + + if (strcmp(Callsign, "AIS") == 0) + { + if (needAIS) + { + Payload += 3; + ProcessAISMessage(Payload, strlen(Payload)); + } + else + Debugprintf(Payload); + + return 0; + } + + Station = FindStation(Callsign, TRUE); + + strcpy(Station->Path, Path); + strcpy(Station->LastPacket, Payload); + Station->LastPort = Port; + + *ourMessage = DecodeAPRSPayload(Payload, Station); + Station->TimeLastUpdated = time(NULL); + + return Station; +} + + +/* +2E0AYY>APU25N,TCPIP*,qAC,AHUBSWE2:=5105.18N/00108.19E-Paul in Folkestone Kent {UIV32N} +G0AVP-12>APT310,MB7UC*,WIDE3-2,qAR,G3PWJ:!5047.19N\00108.45Wk074/000/Paul mobile +G0CJM-12>CQ,TCPIP*,qAC,AHUBSWE2:=/3&Rio94sg +M0HFC>APRS,WIDE2-1,qAR,G0MNI:!5342.83N/00013.79W# Humber Fortress ARC Look us up on QRZ +G8WVW-3>APTT4,WIDE1-1,WIDE2-1,qAS,G8WVW:T#063,123,036,000,000,000,00000000 +*/ + + +struct STATIONRECORD * DecodeAPRSISMsg(char * Msg) +{ + char * Payload; + char * Path = NULL; + char * Comment = NULL; + char * Callsign; + struct STATIONRECORD * Station = NULL; + +// Debugprintf(Msg); + + Payload = strchr(Msg, ':'); // End of Address String + + if (Payload == NULL) + { + Debugprintf("Invalid Msg %s", Msg); + return Station; + } + + *Payload++ = 0; + + Callsign = Msg; + + Path = strchr(Msg, '>'); + + if (Path == NULL) + { + Debugprintf("Invalid Msg %s", Msg); + return Station; + } + + *Path++ = 0; + + // Look up station - create a new one if not found + + if (strlen(Callsign) > 11) + { + Debugprintf("Invalid Msg %s", Msg); + return Station; + } + + Station = FindStation(Callsign, TRUE); + + strcpy(Station->Path, Path); + strcpy(Station->LastPacket, Payload); + Station->LastPort = 0; + + DecodeAPRSPayload(Payload, Station); + Station->TimeLastUpdated = time(NULL); + + return Station; +} + +double Cube91 = 91.0 * 91.0 * 91.0; +double Square91 = 91.0 * 91.0; + +BOOL DecodeLocationString(UCHAR * Payload, struct STATIONRECORD * Station) +{ + UCHAR SymChar; + char SymSet; + char NS; + char EW; + double NewLat, NewLon; + char LatDeg[3], LonDeg[4]; + char save; + + // Compressed has first character not a digit (it is symbol table) + + // /YYYYXXXX$csT + + if (Payload[0] == '!') + return FALSE; // Ultimeter 2000 Weather Station + + if (!isdigit(*Payload)) + { + int C, S; + + SymSet = *Payload; + SymChar = Payload[9]; + + NewLat = 90.0 - ((Payload[1] - 33) * Cube91 + (Payload[2] - 33) * Square91 + + (Payload[3] - 33) * 91.0 + (Payload[4] - 33)) / 380926.0; + + Payload += 4; + + NewLon = -180.0 + ((Payload[1] - 33) * Cube91 + (Payload[2] - 33) * Square91 + + (Payload[3] - 33) * 91.0 + (Payload[4] - 33)) / 190463.0; + + C = Payload[6] - 33; + + if (C >= 0 && C < 90 ) + { + S = Payload[7] - 33; + + Station->Course = C * 4; + Station->Speed = (pow(1.08, S) - 1) * 1.15077945; // MPH; + } + + + + } + else + { + // Standard format ddmm.mmN/dddmm.mmE? + + NS = Payload[7] & 0xdf; // Mask Lower Case Bit + EW = Payload[17] & 0xdf; + + SymSet = Payload[8]; + SymChar = Payload[18]; + + if (SymChar == '_') // WX + { + if (strlen(Payload) > 30) + strcpy(Station->LastWXPacket, Payload); + } + + memcpy(LatDeg, Payload,2); + LatDeg[2]=0; + NewLat = atof(LatDeg) + (atof(Payload+2) / 60); + + if (NS == 'S') + NewLat = -NewLat; + else + if (NS != 'N') + return FALSE; + + memcpy(LonDeg,Payload + 9, 3); + + if (SymChar != '_' && Payload[22] == '/') // not if WX + { + Station->Course = atoi(Payload + 19); + Station->Speed = atoi(Payload + 23); + } + + LonDeg[3]=0; + + save = Payload[17]; + Payload[17] = 0; + NewLon = atof(LonDeg) + (atof(Payload+12) / 60); + Payload[17] = save; + + if (EW == 'W') + NewLon = -NewLon; + else + if (EW != 'E') + return FALSE; + } + + Station->Symbol = SymChar; + + if (SymChar > ' ' && SymChar < 0x7f) + SymChar -= '!'; + else + SymChar = 0; + + Station->IconOverlay = 0; + + if ((SymSet >= '0' && SymSet <= '9') || (SymSet >= 'A' && SymSet <= 'Z')) + { + SymChar += 96; + Station->IconOverlay = SymSet; + } + else + if (SymSet == '\\') + SymChar += 96; + + Station->iconRow = SymChar >> 4; + Station->iconCol = SymChar & 15; + + if (NewLat > 90 || NewLat < -90 || NewLon > 180 || NewLon < -180) + return TRUE; + + if (Station->Lat != NewLat || Station->Lon != NewLon) + { + time_t NOW = time(NULL); + time_t Age = NOW - Station->TimeLastTracked; + + if (Age > 15) // Don't update too often + { + // Add to track + + Station->TimeLastTracked = NOW; + +// if (memcmp(Station->Callsign, "ISS ", 4) == 0) +// Debugprintf("%s %s %s ",Station->Callsign, Station->Path, Station->LastPacket); + + Station->LatTrack[Station->Trackptr] = NewLat; + Station->LonTrack[Station->Trackptr] = NewLon; + Station->TrackTime[Station->Trackptr] = NOW; + + Station->Trackptr++; + Station->Moved = TRUE; + + if (Station->Trackptr == TRACKPOINTS) + Station->Trackptr = 0; + } + + Station->Lat = NewLat; + Station->Lon = NewLon; + Station->Approx = 0; + } + + + return TRUE; +} + +int DecodeAPRSPayload(char * Payload, struct STATIONRECORD * Station) +{ + char * TimeStamp; + char * ObjName; + char ObjState; + struct STATIONRECORD * Object; + BOOL Item = FALSE; + char * ptr; + char * Callsign; + char * Path; + char * Msg; + char * context; + struct STATIONRECORD * TPStation; + + Station->Object = NULL; + + if (strcmp(Station->Callsign, "LA1ZDA-2") == 0) + { + int i = 1; + } + switch(*Payload) + { + case '`': + case 0x27: // ' + case 0x1c: + case 0x1d: // MIC-E + + Decode_MIC_E_Packet(Payload, Station); + return 0; + + case '$': // NMEA + Debugprintf(Payload); + break; + + case ')': // Item + +// Debugprintf("%s %s %s", Station->Callsign, Station->Path, Payload); + + Item = TRUE; + ObjName = ptr = Payload + 1; + + while (TRUE) + { + ObjState = *ptr; + if (ObjState == 0) + return 0; // Corrupt + + if (ObjState == '!' || ObjState == '_') // Item Terminator + break; + + ptr++; + } + + *ptr = 0; // Terminate Name + + Object = FindStation(ObjName, TRUE); + Object->ObjState = *ptr++ = ObjState; + + strcpy(Object->Path, Station->Callsign); + strcat(Object->Path, ">"); + if (Object == Station) + { + char Temp[256]; + strcpy(Temp, Station->Path); + strcat(Object->Path, Temp); + Debugprintf("item is station %s", Payload); + } + else + strcat(Object->Path, Station->Path); + + strcpy(Object->LastPacket, Payload); + + if (ObjState != '_') // Deleted Objects may have odd positions + DecodeLocationString(ptr, Object); + + Object->TimeLastUpdated = time(NULL); + Station->Object = Object; + return 0; + + + case ';': // Object + + ObjName = Payload + 1; + ObjState = Payload[10]; // * Live, _Killed + + Payload[10] = 0; + Object = FindStation(ObjName, TRUE); + Object->ObjState = Payload[10] = ObjState; + + strcpy(Object->Path, Station->Callsign); + strcat(Object->Path, ">"); + if (Object == Station) + { + char Temp[256]; + strcpy(Temp, Station->Path); + strcat(Object->Path, Temp); + Debugprintf("Object is station %s", Payload); + } + else + strcat(Object->Path, Station->Path); + + + strcpy(Object->LastPacket, Payload); + + TimeStamp = Payload + 11; + + if (ObjState != '_') // Deleted Objects may have odd positions + DecodeLocationString(Payload + 18, Object); + + Object->TimeLastUpdated = time(NULL); + Object->LastPort = Station->LastPort; + Station->Object = Object; + return 0; + + case '@': + case '/': // Timestamp, No Messaging + + TimeStamp = ++Payload; + Payload += 6; + + case '=': + case '!': + + Payload++; + + DecodeLocationString(Payload, Station); + + return 0; + + case '>': // Status + + strcpy(Station->Status, &Payload[1]); + + case '<': // Capabilities + case '_': // Weather + case 'T': // Telemetry + + break; + + case ':': // Message + + return ProcessMessage(Payload, Station); + + case '}': // Third Party Header + + // Process Payload as a new message + + // }GM7HHB-9>APDR12,TCPIP,MM1AVR*:=5556.62N/00303.55W>204/000/A=000213 http://www.dstartv.com + + Callsign = Msg = &Payload[1]; + Path = strchr(Msg, '>'); + + if (Path == NULL) + return 0; + + *Path++ = 0; + + Payload = strchr(Path, ':'); + + if (Payload == NULL) + return 0; + + *(Payload++) = 0; + + // Check Dup Filter + + if (CheckforDups(Callsign, Payload, (int)strlen(Payload))) + return 0; + + // Look up station - create a new one if not found + + TPStation = FindStation(Callsign, TRUE); + + strcpy(TPStation->Path, Path); + strcpy(TPStation->LastPacket, Payload); + TPStation->LastPort = 0; // Heard on RF, but info is from IS + + DecodeAPRSPayload(Payload, TPStation); + TPStation->TimeLastUpdated = time(NULL); + + return 0; + + default: + + // Non - APRS Message. If Payload contains a valid 6 char locator derive a position from it + + if (Station->Lat != 0.0 || Station->Lon != 0.0) + return 0; // already have position + + ptr = strtok_s(Payload, ",[](){} \n", &context); + + while (ptr && ptr[0]) + { + if (strlen(ptr) == 6) // could be locator + { + double Lat = 0.0, Lon = 0.0; + + if (FromLOC(ptr, &Lat, &Lon)) + { + if (Lat != 0.0 && Lon != 0.0) + { + // Randomise in locator square. + + Lat = Lat + ((rand() / 24.0) / RAND_MAX); + Lon = Lon + ((rand() / 12.0) / RAND_MAX); + Station->Lat = Lat; + Station->Lon = Lon; + Station->Approx = 1; + Debugprintf("%s %s %s", Station->Callsign, Station->Path, Payload); + } + } + } + + ptr = strtok_s(NULL, ",[](){} \n", &context); + } + + return 0; + } + return 0; +} + +// Convert MIC-E Char to Lat Digit (offset by 0x30) +// 0123456789 @ABCDEFGHIJKLMNOPQRSTUVWXYZ +char MicELat[] = "0123456789???????0123456789 ???0123456789 " ; + +char MicECode[]= "0000000000???????111111111110???22222222222" ; + + +VOID Decode_MIC_E_Packet(char * Payload, struct STATIONRECORD * Station) +{ + // Info is encoded in the Dest Addr (in Station->Path) as well as Payload. + // See APRS Spec for full details + + char Lat[10]; // DDMMHH + char LatDeg[3]; + char * ptr; + char c; + int i, n; + int LonDeg, LonMin; + BOOL LonOffset = FALSE; + char NS = 'S'; + char EW = 'E'; + UCHAR SymChar, SymSet; + double NewLat, NewLon; + int SP, DC, SE; // Course/Speed Encoded + int Course, Speed; + + // Make sure packet is long enough to have an valid address + + if (strlen(Payload) < 9) + return; + + ptr = &Station->Path[0]; + + for (i = 0; i < 6; i++) + { + n = (*(ptr++)) - 0x30; + c = MicELat[n]; + + if (c == '?') // Illegal + return; + + if (c == ' ') + c = '0'; // Limited Precision + + Lat[i] = c; + + } + + Lat[6] = 0; + + if (Station->Path[3] > 'O') + NS = 'N'; + + if (Station->Path[5] > 'O') + EW = 'W'; + + if (Station->Path[4] > 'O') + LonOffset = TRUE; + + n = Payload[1] - 28; // Lon Degrees S9PU0T,WIDE1-1,WIDE2-2,qAR,WB9TLH-15:`rB0oII>/]"6W}44 + + if (LonOffset) + n += 100; + + if (n > 179 && n < 190) + n -= 80; + else + if (n > 189 && n < 200) + n -= 190; + + LonDeg = n; + +/* + To decode the longitude degrees value: +1. subtract 28 from the d+28 value to obtain d. +2. if the longitude offset is +100 degrees, add 100 to d. +3. subtract 80 if 180 ˜ d ˜ 189 +(i.e. the longitude is in the range 100–109 degrees). +4. or, subtract 190 if 190 ˜ d ˜ 199. +(i.e. the longitude is in the range 0–9 degrees). +*/ + + n = Payload[2] - 28; // Lon Mins + + if (n > 59) + n -= 60; + + LonMin = n; + + n = Payload[3] - 28; // Lon Mins/100; + +//1. subtract 28 from the m+28 value to obtain m. +//2. subtract 60 if m ™ 60. +//(i.e. the longitude minutes is in the range 0–9). + + + memcpy(LatDeg, Lat, 2); + LatDeg[2]=0; + + NewLat = atof(LatDeg) + (atof(Lat+2) / 6000.0); + + if (NS == 'S') + NewLat = -NewLat; + + NewLon = LonDeg + LonMin / 60.0 + n / 6000.0; + + if (EW == 'W') // West + NewLon = -NewLon; + + + SP = Payload[4] - 28; + DC = Payload[5] - 28; + SE = Payload[6] - 28; // Course 100 and 10 degs + + Speed = DC / 10; // Quotient = Speed Units + Course = DC - (Speed * 10); // Remainder = Course Deg/100 + + Course = SE + (Course * 100); + + Speed += SP * 10; + + if (Speed >= 800) + Speed -= 800; + + if (Course >= 400) + Course -= 400; + + Station->Course = Course; + Station->Speed = Speed * 1.15077945; // MPH + +// Debugprintf("MIC-E Course/Speed %s %d %d", Station->Callsign, Course, Speed); + + SymChar = Payload[7]; // Symbol + SymSet = Payload[8]; // Symbol + + SymChar -= '!'; + + Station->IconOverlay = 0; + + if ((SymSet >= '0' && SymSet <= '9') || (SymSet >= 'A' && SymSet <= 'Z')) + { + SymChar += 96; + Station->IconOverlay = SymSet; + } + else + if (SymSet == '\\') + SymChar += 96; + + Station->iconRow = SymChar >> 4; + Station->iconCol = SymChar & 15; + + if (NewLat > 90 || NewLat < -90 || NewLon > 180 || NewLon < -180) + return; + + if (Station->Lat != NewLat || Station->Lon != NewLon) + { + time_t NOW = time(NULL); + time_t Age = NOW - Station->TimeLastUpdated; + + if (Age > 15) // Don't update too often + { + // Add to track + +// if (memcmp(Station->Callsign, "ISS ", 4) == 0) +// Debugprintf("%s %s %s ",Station->Callsign, Station->Path, Station->LastPacket); + + Station->LatTrack[Station->Trackptr] = NewLat; + Station->LonTrack[Station->Trackptr] = NewLon; + Station->TrackTime[Station->Trackptr] = NOW; + + Station->Trackptr++; + Station->Moved = TRUE; + + if (Station->Trackptr == TRACKPOINTS) + Station->Trackptr = 0; + } + + Station->Lat = NewLat; + Station->Lon = NewLon; + Station->Approx = 0; + } + + return; + +} + +/* + +INT_PTR CALLBACK ChildDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ +// This processes messages from controls on the tab subpages + int Command; + +// int retCode, disp; +// char Key[80]; +// HKEY hKey; +// BOOL OK; +// OPENFILENAME ofn; +// char Digis[100]; + + int Port = PortNum[CurrentPage]; + + switch (message) + { + case WM_NOTIFY: + + switch (((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGE: + OnSelChanged(hDlg); + return TRUE; + // More cases on WM_NOTIFY switch. + case NM_CHAR: + return TRUE; + } + + break; + case WM_INITDIALOG: + OnChildDialogInit( hDlg); + return (INT_PTR)TRUE; + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_COMMAND: + + Command = LOWORD(wParam); + + if (Command == 2002) + return TRUE; + + switch (Command) + { +/* case IDC_FILE: + + memset(&ofn, 0, sizeof (OPENFILENAME)); + ofn.lStructSize = sizeof (OPENFILENAME); + ofn.hwndOwner = hDlg; + ofn.lpstrFile = &FN[Port][0]; + ofn.nMaxFile = 250; + ofn.lpstrTitle = "File to send as beacon"; + ofn.lpstrInitialDir = GetBPQDirectory(); + + if (GetOpenFileName(&ofn)) + SetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0]); + + break; + + + case IDOK: + + GetDlgItemText(hDlg, IDC_UIDEST, &UIDEST[Port][0], 10); + + if (UIDigi[Port]) + { + free(UIDigi[Port]); + UIDigi[Port] = NULL; + } + + if (UIDigiAX[Port]) + { + free(UIDigiAX[Port]); + UIDigiAX[Port] = NULL; + } + + GetDlgItemText(hDlg, IDC_UIDIGIS, Digis, 99); + + UIDigi[Port] = _strdup(Digis); + + GetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0], 255); + GetDlgItemText(hDlg, IDC_MESSAGE, &Message[Port][0], 1000); + + Interval[Port] = GetDlgItemInt(hDlg, IDC_INTERVAL, &OK, FALSE); + + MinCounter[Port] = Interval[Port]; + + SendFromFile[Port] = IsDlgButtonChecked(hDlg, IDC_FROMFILE); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil\\UIPort%d", PortNum[CurrentPage]); + + retCode = RegCreateKeyEx(REGTREE, + Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "UIDEST", 0, REG_SZ,(BYTE *)&UIDEST[Port][0], strlen(&UIDEST[Port][0])); + retCode = RegSetValueEx(hKey, "FileName", 0, REG_SZ,(BYTE *)&FN[Port][0], strlen(&FN[Port][0])); + retCode = RegSetValueEx(hKey, "Message", 0, REG_SZ,(BYTE *)&Message[Port][0], strlen(&Message[Port][0])); + retCode = RegSetValueEx(hKey, "Interval", 0, REG_DWORD,(BYTE *)&Interval[Port], 4); + retCode = RegSetValueEx(hKey, "SendFromFile", 0, REG_DWORD,(BYTE *)&SendFromFile[Port], 4); + retCode = RegSetValueEx(hKey, "Enabled", 0, REG_DWORD,(BYTE *)&UIEnabled[Port], 4); + retCode = RegSetValueEx(hKey, "Digis",0, REG_SZ, Digis, strlen(Digis)); + + RegCloseKey(hKey); + } + + SetupUI(Port); + + return (INT_PTR)TRUE; + + + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case ID_TEST: + + SendBeacon(Port); + return TRUE; + + + + + } + break; + + } + return (INT_PTR)FALSE; +} + + + + +VOID WINAPI OnTabbedDialogInit(HWND hDlg) +{ + DLGHDR *pHdr = (DLGHDR *) LocalAlloc(LPTR, sizeof(DLGHDR)); + DWORD dwDlgBase = GetDialogBaseUnits(); + int cxMargin = LOWORD(dwDlgBase) / 4; + int cyMargin = HIWORD(dwDlgBase) / 8; + + TC_ITEM tie; + RECT rcTab; + + int i, pos, tab = 0; + INITCOMMONCONTROLSEX init; + + char PortNo[60]; + struct _EXTPORTDATA * PORTVEC; + + hwndDlg = hDlg; // Save Window Handle + + // Save a pointer to the DLGHDR structure. + + SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) pHdr); + + // Create the tab control. + + + init.dwICC = ICC_STANDARD_CLASSES; + init.dwSize=sizeof(init); + i=InitCommonControlsEx(&init); + + pHdr->hwndTab = CreateWindow(WC_TABCONTROL, "", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, + 0, 0, 100, 100, hwndDlg, NULL, hInst, NULL); + + if (pHdr->hwndTab == NULL) { + + // handle error + + } + + // Add a tab for each of the child dialog boxes. + + tie.mask = TCIF_TEXT | TCIF_IMAGE; + + tie.iImage = -1; + + for (i = 1; i <= GetNumberofPorts(); i++) + { + // Only allow UI on ax.25 ports + + PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntry(i); + + if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL + if (PORTVEC->PORTCONTROL.PROTOCOL == 10) // Pactor/WINMOR + continue; + + sprintf(PortNo, "Port %2d", GetPortNumber(i)); + PortNum[tab] = GetPortNumber(i); + + tie.pszText = PortNo; + TabCtrl_InsertItem(pHdr->hwndTab, tab, &tie); + + pHdr->apRes[tab++] = DoLockDlgRes("PORTPAGE"); + + } + + PageCount = tab; + + // Determine the bounding rectangle for all child dialog boxes. + + SetRectEmpty(&rcTab); + + for (i = 0; i < PageCount; i++) + { + if (pHdr->apRes[i]->cx > rcTab.right) + rcTab.right = pHdr->apRes[i]->cx; + + if (pHdr->apRes[i]->cy > rcTab.bottom) + rcTab.bottom = pHdr->apRes[i]->cy; + + } + + MapDialogRect(hwndDlg, &rcTab); + +// rcTab.right = rcTab.right * LOWORD(dwDlgBase) / 4; + +// rcTab.bottom = rcTab.bottom * HIWORD(dwDlgBase) / 8; + + // Calculate how large to make the tab control, so + + // the display area can accomodate all the child dialog boxes. + + TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab); + + OffsetRect(&rcTab, cxMargin - rcTab.left, cyMargin - rcTab.top); + + // Calculate the display rectangle. + + CopyRect(&pHdr->rcDisplay, &rcTab); + + TabCtrl_AdjustRect(pHdr->hwndTab, FALSE, &pHdr->rcDisplay); + + // Set the size and position of the tab control, buttons, + + // and dialog box. + + SetWindowPos(pHdr->hwndTab, NULL, rcTab.left, rcTab.top, rcTab.right - rcTab.left, rcTab.bottom - rcTab.top, SWP_NOZORDER); + + // Move the Buttons to bottom of page + + pos=rcTab.left+cxMargin; + + + // Size the dialog box. + + SetWindowPos(hwndDlg, NULL, 0, 0, rcTab.right + cyMargin + 2 * GetSystemMetrics(SM_CXDLGFRAME), + rcTab.bottom + 2 * cyMargin + 2 * GetSystemMetrics(SM_CYDLGFRAME) + GetSystemMetrics(SM_CYCAPTION), + SWP_NOMOVE | SWP_NOZORDER); + + // Simulate selection of the first item. + + OnSelChanged(hwndDlg); + +} + +// DoLockDlgRes - loads and locks a dialog template resource. + +// Returns a pointer to the locked resource. + +// lpszResName - name of the resource + +DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName) +{ + HRSRC hrsrc = FindResource(NULL, lpszResName, RT_DIALOG); + HGLOBAL hglb = LoadResource(hInst, hrsrc); + + return (DLGTEMPLATE *) LockResource(hglb); +} + +//The following function processes the TCN_SELCHANGE notification message for the main dialog box. The function destroys the dialog box for the outgoing page, if any. Then it uses the CreateDialogIndirect function to create a modeless dialog box for the incoming page. + +// OnSelChanged - processes the TCN_SELCHANGE notification. + +// hwndDlg - handle of the parent dialog box + +VOID WINAPI OnSelChanged(HWND hwndDlg) +{ + char PortDesc[40]; + int Port; + + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + + CurrentPage = TabCtrl_GetCurSel(pHdr->hwndTab); + + // Destroy the current child dialog box, if any. + + if (pHdr->hwndDisplay != NULL) + + DestroyWindow(pHdr->hwndDisplay); + + // Create the new child dialog box. + + pHdr->hwndDisplay = CreateDialogIndirect(hInst, pHdr->apRes[CurrentPage], hwndDlg, ChildDialogProc); + + hwndDisplay = pHdr->hwndDisplay; // Save + + Port = PortNum[CurrentPage]; + // Fill in the controls + + GetPortDescription(PortNum[CurrentPage], PortDesc); + + SetDlgItemText(hwndDisplay, IDC_PORTNAME, PortDesc); + +// CheckDlgButton(hwndDisplay, IDC_FROMFILE, SendFromFile[Port]); + +// SetDlgItemInt(hwndDisplay, IDC_INTERVAL, Interval[Port], FALSE); + + SetDlgItemText(hwndDisplay, IDC_UIDEST, &UIDEST[Port][0]); + SetDlgItemText(hwndDisplay, IDC_UIDIGIS, UIDigi[Port]); + + + +// SetDlgItemText(hwndDisplay, IDC_FILENAME, &FN[Port][0]); +// SetDlgItemText(hwndDisplay, IDC_MESSAGE, &Message[Port][0]); + + ShowWindow(pHdr->hwndDisplay, SW_SHOWNORMAL); + +} + +//The following function processes the WM_INITDIALOG message for each of the child dialog boxes. You cannot specify the position of a dialog box created using the CreateDialogIndirect function. This function uses the SetWindowPos function to position the child dialog within the tab control's display area. + +// OnChildDialogInit - Positions the child dialog box to fall + +// within the display area of the tab control. + +VOID WINAPI OnChildDialogInit(HWND hwndDlg) +{ + HWND hwndParent = GetParent(hwndDlg); + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndParent, GWL_USERDATA); + + SetWindowPos(hwndDlg, HWND_TOP, pHdr->rcDisplay.left, pHdr->rcDisplay.top, 0, 0, SWP_NOSIZE); +} + + +*/ + + +/* +VOID ProcessMessage(char * Payload, struct STATIONRECORD * Station) +{ + char MsgDest[10]; + struct APRSMESSAGE * Message; + struct APRSMESSAGE * ptr = Messages; + char * TextPtr = &Payload[11]; + char * SeqPtr; + int n = 0; + char FromCall[10] = " "; + struct tm * TM; + time_t NOW; + + memcpy(FromCall, Station->Callsign, (int)strlen(Station->Callsign)); + memcpy(MsgDest, &Payload[1], 9); + MsgDest[9] = 0; + + SeqPtr = strchr(TextPtr, '{'); + + if (SeqPtr) + { + *(SeqPtr++) = 0; + if(strlen(SeqPtr) > 6) + SeqPtr[7] = 0; + } + + if (_memicmp(TextPtr, "ack", 3) == 0) + { + // Message Ack. See if for one of our messages + + ptr = OutstandingMsgs; + + if (ptr == 0) + return; + + do + { + if (strcmp(ptr->FromCall, MsgDest) == 0 + && strcmp(ptr->ToCall, FromCall) == 0 + && strcmp(ptr->Seq, &TextPtr[3]) == 0) + { + // Message is acked + + ptr->Retries = 0; + ptr->Acked = TRUE; +// if (hMsgsOut) +// UpdateTXMessageLine(hMsgsOut, n, ptr); + + return; + } + ptr = ptr->Next; + n++; + + } while (ptr); + + return; + } + + Message = malloc(sizeof(struct APRSMESSAGE)); + memset(Message, 0, sizeof(struct APRSMESSAGE)); + strcpy(Message->FromCall, Station->Callsign); + strcpy(Message->ToCall, MsgDest); + + if (SeqPtr) + { + strcpy(Message->Seq, SeqPtr); + + // If a REPLY-ACK Seg, copy to LastRXSeq, and see if it acks a message + + if (SeqPtr[2] == '}') + { + struct APRSMESSAGE * ptr1; + int nn = 0; + + strcpy(Station->LastRXSeq, SeqPtr); + + ptr1 = OutstandingMsgs; + + while (ptr1) + { + if (strcmp(ptr1->FromCall, MsgDest) == 0 + && strcmp(ptr1->ToCall, FromCall) == 0 + && memcmp(&ptr1->Seq, &SeqPtr[3], 2) == 0) + { + // Message is acked + + ptr1->Acked = TRUE; + ptr1->Retries = 0; +// if (hMsgsOut) +// UpdateTXMessageLine(hMsgsOut, nn, ptr); + + break; + } + ptr1 = ptr1->Next; + nn++; + } + } + else + { + // Station is not using reply-ack - set to send simple numeric sequence (workround for bug in APRS Messanger + + Station->SimpleNumericSeq = TRUE; + } + } + + if (strlen(TextPtr) > 100) + TextPtr[100] = 0; + + strcpy(Message->Text, TextPtr); + + NOW = time(NULL); + + if (DefaultLocalTime) + TM = localtime(&NOW); + else + TM = gmtime(&NOW); + + sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + if (_stricmp(MsgDest, APRSCall) == 0 && SeqPtr) // ack it if it has a sequence + { + // For us - send an Ack + + char ack[30]; + + int n = sprintf(ack, ":%-9s:ack%s", Message->FromCall, Message->Seq); + PutAPRSMessage(ack, n); + } + + if (ptr == NULL) + { + Messages = Message; + } + else + { + n++; + while(ptr->Next) + { + ptr = ptr->Next; + n++; + } + ptr->Next = Message; + } + + if (strcmp(MsgDest, APRSCall) == 0) // to me? + { + } +} + +*/ + +VOID APRSSecTimer() +{ + + // Check Message Retries + + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + int n = 0; + + if (SendWX) + SendWeatherBeacon(); + + + while (ptr) + { + if (ptr->Acked == FALSE) + { + if (ptr->Retries) + { + ptr->RetryTimer--; + + if (ptr->RetryTimer == 0) + { + ptr->Retries--; + + if (ptr->Retries) + { + // Send Again + + char Msg[255]; + APRSHEARDRECORD * STN; + + sprintf(Msg, ":%-9s:%s{%s", ptr->ToCall, ptr->Text, ptr->Seq); + + STN = FindStationInMH(ptr->ToCall); + + if (STN) + SendAPRSMessage(Msg, STN->rfPort); + else + { + if (memcmp(ptr->ToCall, "SERVER ", 9)) + SendAPRSMessage(Msg, -1); // All RF ports unless to SERVER + SendAPRSMessage(Msg, 0); // IS + } + ptr->RetryTimer = RetryTimer; + } + } + } + } + + ptr = ptr->Next; + n++; + } +} + +double radians(double Degrees) +{ + return M_PI * Degrees / 180; +} +double degrees(double Radians) +{ + return Radians * 180 / M_PI; +} + +double Distance(double laa, double loa, double lah, double loh, BOOL KM) +{ + +/* + +'Great Circle Calculations. + +'dif = longitute home - longitute away + + +' (this should be within -180 to +180 degrees) +' (Hint: This number should be non-zero, programs should check for +' this and make dif=0.0001 as a minimum) +'lah = latitude of home +'laa = latitude of away + +'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif)) +'distance = dis / 180 * pi * ERAD +'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis))) + +'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians +*/ + + loh = radians(loh); lah = radians(lah); + loa = radians(loa); laa = radians(laa); + + loh = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945; + + if (KM) + loh *= 1.60934; + + return loh; +} + + +double myDistance(double laa, double loa, BOOL KM) +{ + double lah, loh; + + GetAPRSLatLon(&lah, &loh); + + return Distance(laa, loa, lah, loh, KM); +} + +/* + +'Great Circle Calculations. + +'dif = longitute home - longitute away + + +' (this should be within -180 to +180 degrees) +' (Hint: This number should be non-zero, programs should check for +' this and make dif=0.0001 as a minimum) +'lah = latitude of home +'laa = latitude of away + +'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif)) +'distance = dis / 180 * pi * ERAD +'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis))) + +'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians + + + loh = radians(loh); lah = radians(lah); + loa = radians(loa); laa = radians(laa); + + loh = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945; + + if (KM) + loh *= 1.60934; + + return loh; +} +*/ + +double Bearing(double lat2, double lon2, double lat1, double lon1) +{ + double dlat, dlon, TC1; + + lat1 = radians(lat1); + lat2 = radians(lat2); + lon1 = radians(lon1); + lon2 = radians(lon2); + + dlat = lat2 - lat1; + dlon = lon2 - lon1; + + if (dlat == 0 || dlon == 0) return 0; + + TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2))); + TC1 = degrees(TC1); + + if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270; + + if (dlat > 0) + { + if (dlon > 0) return -TC1; + if (dlon < 0) return 360 - TC1; + return 0; + } + + if (dlat < 0) + { + if (dlon > 0) return TC1 = 180 - TC1; + if (dlon < 0) return TC1 = 180 - TC1; // 'ok? + return 180; + } + + return 0; +} + + +double myBearing(double lat2, double lon2) +{ + double lat1, lon1; + + GetAPRSLatLon(&lat1, &lon1); + + return Bearing(lat2, lon2, lat1, lon1); +} +/* + + + + + lat1 = radians(lat1); + lat2 = radians(lat2); + lon1 = radians(lon1); + lon2 = radians(lon2); + + dlat = lat2 - lat1; + dlon = lon2 - lon1; + + if (dlat == 0 || dlon == 0) return 0; + + TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2))); + TC1 = degrees(TC1); + + if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270; + + if (dlat > 0) + { + if (dlon > 0) return -TC1; + if (dlon < 0) return 360 - TC1; + return 0; + } + + if (dlat < 0) + { + if (dlon > 0) return TC1 = 180 - TC1; + if (dlon < 0) return TC1 = 180 - TC1; // 'ok? + return 180; + } + + return 0; +} +*/ + +// Weather Data + +static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + +VOID SendWeatherBeacon() +{ + char Msg[256]; + char DD[3]=""; + char HH[3]=""; + char MM[3]=""; + char Lat[10], Lon[10]; + size_t Len; + int index; + char WXMessage[1024]; + char * WXptr; + char * WXend; + time_t WXTime; + time_t now = time(NULL); + FILE * hFile; + struct tm * TM; + struct stat STAT; + + WXCounter++; + + if (WXCounter < WXInterval * 60) + return; + + WXCounter = 0; + +// Debugprintf("BPQAPRS - Trying to open WX file %s", WXFileName); + + if (stat(WXFileName, &STAT)) + { + Debugprintf("APRS WX File %s stat() failed %d", WXFileName, GetLastError()); + return; + } + + WXTime = (now - STAT.st_mtime) /60; // Minutes + + if (WXTime > (3 * WXInterval)) + { + Debugprintf("APRS Send WX File %s too old - %d minutes", WXFileName, WXTime); + return; + } + + hFile = fopen(WXFileName, "rb"); + + if (hFile) + Len = fread(WXMessage, 1, 1024, hFile); + else + { + Debugprintf("APRS WX File %s open() failed %d", WXFileName, GetLastError()); + return; + } + + + if (Len < 30) + { + Debugprintf("BPQAPRS - WX file %s is too short - %d Chars", WXFileName, Len); + fclose(hFile); + return; + } + + WXMessage[Len] = 0; + + // see if wview format + +//04-09-13, 2245 +//TempIn 23 +//TempEx 18 +//WindHi 0 +//WindAv 0 +//WindDr 200 +//BarmPs 30167 +//HumdIn 56 +//HumdEx 100 +//RnFall 0.00 +//DailyRnFall 0.00 + + if (strstr(WXMessage, "TempIn")) + { + int Wind = 0; + int Gust = 0; + int Temp = 0; + int Winddir = 0; + int Humidity = 0; + int Raintoday = 0; + int Rain24hrs = 0; + int Pressure = 0; + + char * ptr; + + ptr = strstr(WXMessage, "TempEx"); + if (ptr) + Temp = (int)(atof(ptr + 7) * 1.8) + 32; + + ptr = strstr(WXMessage, "WindHi"); + if (ptr) + Gust = atoi(ptr + 7); + + ptr = strstr(WXMessage, "WindAv"); + if (ptr) + Wind = atoi(ptr + 7); + + ptr = strstr(WXMessage, "WindDr"); + if (ptr) + Winddir = atoi(ptr + 7); + + ptr = strstr(WXMessage, "BarmPs"); + if (ptr) + Pressure = (int)(atof(ptr + 7) * 0.338638866667); // Inches to 1/10 millbars + + ptr = strstr(WXMessage, "HumdEx"); + if (ptr) + Humidity = atoi(ptr + 7); + + ptr = strstr(WXMessage, "RnFall"); + if (ptr) + Rain24hrs = (int)(atof(ptr + 7) * 100.0); + + ptr = strstr(WXMessage, "DailyRnFall"); + if (ptr) + Raintoday = (int)(atof(ptr + 12) * 100.0); + + if (Humidity > 99) + Humidity = 99; + + sprintf(WXMessage, "%03d/%03dg%03dt%03dr%03dP%03dp%03dh%02db%05d", + Winddir, Wind, Gust, Temp, 0, Raintoday, Rain24hrs, Humidity, Pressure); + + } + + WXptr = strchr(WXMessage, 10); + + if (WXptr) + { + WXend = strchr(++WXptr, 13); + if (WXend == 0) + WXend = strchr(WXptr, 10); + if (WXend) + *WXend = 0; + } + else + WXptr = &WXMessage[0]; + + // Get DDHHMM from Filetime + + TM = gmtime(&STAT.st_mtime); + + sprintf(DD, "%02d", TM->tm_mday); + sprintf(HH, "%02d", TM->tm_hour); + sprintf(MM, "%02d", TM->tm_min); + + GetAPRSLatLonString(Lat, Lon); + + Len = sprintf(Msg, "@%s%s%sz%s/%s_%s%s", DD, HH, MM, Lat, Lon, WXptr, WXComment); + + Debugprintf(Msg); + + for (index = 0; index < MaxBPQPortNo; index++) + { + if (WXPort[index]) + SendAPRSMessageEx(Msg, index, WXCall, FALSE); + } + + fclose(hFile); +} + + +/* +Jan 22 2012 14:10 +123/005g011t031r000P000p000h00b10161 + +/MITWXN Mitchell IN weather Station N9LYA-3 {UIV32} +< previous + +@221452z3844.42N/08628.33W_203/006g007t032r000P000p000h00b10171 +Complete Weather Report Format — with Lat/Long position, no Timestamp +! or = Lat Sym Table ID Long Symbol Code _ Wind Directn/ Speed Weather Data APRS Software WX Unit uuuu + 1 8 1 9 1 7 n 1 2-4 +Examples +!4903.50N/07201.75W_220/004g005t077r000p000P000h50b09900wRSW +!4903.50N/07201.75W_220/004g005t077r000p000P000h50b.....wRSW + +*/ + +// Web Server Code + +// The actual HTTP socket code is in bpq32.dll. Any requests for APRS data are passed in +// using a Named Pipe. The request looks exactly like one from a local socket, and the respone is +// a fully pormatted HTTP packet + + +#define InputBufferLen 1000 + + +#define MaxSessions 100 + + +HANDLE PipeHandle; + +int HTTPPort = 80; +BOOL IPV6 = TRUE; + +#define MAX_PENDING_CONNECTS 5 + +BOOL OpenSockets6(); + +char HTDocs[MAX_PATH] = "HTML"; +char SpecialDocs[MAX_PATH] = "Special Pages"; + +char SymbolText[192][20] = { + +"Police Stn", "No Symbol", "Digi", "Phone", "DX Cluster", "HF Gateway", "Plane sm", "Mob Sat Stn", +"WheelChair", "Snowmobile", "Red Cross", "Boy Scout", "Home", "X", "Red Dot", "Circle (0)", +"Circle (1)", "Circle (2)", "Circle (3)", "Circle (4)", "Circle (5)", "Circle (6)", "Circle (7)", "Circle (8)", +"Circle (9)", "Fire", "Campground", "Motorcycle", "Rail Eng.", "Car", "File svr", "HC Future", + +"Aid Stn", "BBS", "Canoe", "No Symbol", "Eyeball", "Tractor", "Grid Squ.", "Hotel", +"Tcp/ip", "No Symbol", "School", "Usr Log-ON", "MacAPRS", "NTS Stn", "Balloon", "Police", +"TBD", "Rec Veh'le", "Shuttle", "SSTV", "Bus", "ATV", "WX Service", "Helo", +"Yacht", "WinAPRS", "Jogger", "Triangle", "PBBS", "Plane lrge", "WX Station", "Dish Ant.", + +"Ambulance", "Bike", "ICP", "Fire Station", "Horse", "Fire Truck", "Glider", "Hospital", +"IOTA", "Jeep", "Truck", "Laptop", "Mic-E Rptr", "Node", "EOC", "Rover", +"Grid squ.", "Antenna", "Power Boat", "Truck Stop", "Truck 18wh", "Van", "Water Stn", "XAPRS", +"Yagi", "Shelter", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "", "", + +"Emergency", "No Symbol", "No. Digi", "Bank", "No Symbol", "No. Diam'd", "Crash site", "Cloudy", +"MEO", "Snow", "Church", "Girl Scout", "Home (HF)", "UnknownPos", "Destination", "No. Circle", +"No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", +"Petrol Stn", "Hail", "Park", "Gale Fl", "No Symbol", "No. Car", "Info Kiosk", "Hurricane", + +"No. Box", "Snow blwng", "Coast G'rd", "Drizzle", "Smoke", "Fr'ze Rain", "Snow Shwr", "Haze", +"Rain Shwr", "Lightning", "Kenwood", "Lighthouse", "No Symbol", "Nav Buoy", "Rocket", "Parking ", +"Quake", "Restaurant", "Sat/Pacsat", "T'storm", "Sunny", "VORTAC", "No. WXS", "Pharmacy", +"No Symbol", "No Symbol", "Wall Cloud", "No Symbol", "No Symbol", "No. Plane", "No. WX Stn", "Rain", + +"No. Diamond", "Dust blwng", "No. CivDef", "DX Spot", "Sleet", "Funnel Cld", "Gale", "HAM store", +"No. Blk Box", "WorkZone", "SUV", "Area Locns", "Milepost", "No. Triang", "Circle sm", "Part Cloud", +"No Symbol", "Restrooms", "No. Boat", "Tornado", "No. Truck", "No. Van", "Flooding", "No Symbol", +"Sky Warn", "No Symbol", "Fog", "No Symbol", "No Symbol", "No Symbol", "", ""}; + +// All Calls (8 per line) + +//EI7IG-1 +//G7TKK-1 +//GB7GL-B +//GM1TCN +//GM8BPQ +//GM8BPQ-14 +//LA2VPA-9 +//LA3FIA-10 +//LA6JF-2LD4STM0CHK-7M0OZH-7MB7UFO-1MB7UNMM0DXE-15PA2AYX-9 +//PA3AQW-5PD1CPD5LWD-2PI1ECO + + +char * DoSummaryLine(struct STATIONRECORD * ptr, int n, int Width) +{ + static char Line2[80]; + int x; + char XCall[256]; + char * ptr1 = ptr->Callsign; + char * ptr2 = XCall; + + // Object Names can contain spaces + + while(*ptr1) + { + if (*ptr1 == ' ') + { + memcpy(ptr2, "%20", 3); + ptr2 += 3; + } + else + *(ptr2++) = *ptr1; + + ptr1++; + } + + *ptr2 = 0; + + + // Object Names can contain spaces + + + sprintf(Line2, "%s", + XCall, ptr->Callsign); + + x = ++n/Width; + x = x * Width; + + if (x == n) + strcat(Line2, ""); + + return Line2; +} + +char * DoDetailLine(struct STATIONRECORD * ptr, BOOL LocalTime, BOOL KM) +{ + static char Line[512]; + double Lat = ptr->Lat; + double Lon = ptr->Lon; + char NS='N', EW='E'; + + char LatString[20], LongString[20], DistString[20], BearingString[20]; + int Degrees; + double Minutes; + char Time[80]; + struct tm * TM; + char XCall[256]; + + char * ptr1 = ptr->Callsign; + char * ptr2 = XCall; + + // Object Names can contain spaces + + while(*ptr1) + { + if (*ptr1 == ' ') + { + memcpy(ptr2, "%20", 3); + ptr2 += 3; + } + else + *(ptr2++) = *ptr1; + + ptr1++; + } + + *ptr2 = 0; + + +// if (ptr->ObjState == '_') // Killed Object +// return; + + if (LocalTime) + TM = localtime(&ptr->TimeLastUpdated); + else + TM = gmtime(&ptr->TimeLastUpdated); + + sprintf(Time, "%.2d:%.2d:%.2d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + if (ptr->Lat < 0) + { + NS = 'S'; + Lat=-Lat; + } + if (Lon < 0) + { + EW = 'W'; + Lon=-Lon; + } + +#pragma warning(push) +#pragma warning(disable:4244) + + Degrees = Lat; + Minutes = Lat * 60.0 - (60 * Degrees); + + sprintf(LatString,"%2d°%05.2f'%c", Degrees, Minutes, NS); + + Degrees = Lon; + +#pragma warning(pop) + + Minutes = Lon * 60 - 60 * Degrees; + + sprintf(LongString, "%3d°%05.2f'%c",Degrees, Minutes, EW); + + sprintf(DistString, "%6.1f", myDistance(ptr->Lat, ptr->Lon, KM)); + sprintf(BearingString, "%3.0f", myBearing(ptr->Lat, ptr->Lon)); + + sprintf(Line, " %s%s%s%s %s%s%s%s\r\n", + XCall, ptr->Callsign, + (strchr(ptr->Path, '*'))? "*": "", &SymbolText[ptr->iconRow << 4 | ptr->iconCol][0], LatString, LongString, DistString, BearingString, Time); + + return Line; +} + + +int CompareFN(const void *a, const void *b) +{ + const struct STATIONRECORD * x = a; + const struct STATIONRECORD * y = b; + + x = x->Next; + y = y->Next; + + return strcmp(x->Callsign, y->Callsign); + + /* strcmp functions works exactly as expected from + comparison function */ +} + + + +char * CreateStationList(BOOL RFOnly, BOOL WX, BOOL Mobile, char Objects, int * Count, char * Param, BOOL KM) +{ + char * Line = malloc(100000); + struct STATIONRECORD * ptr = *StationRecords; + int n = 0, i; + struct STATIONRECORD * List[1000]; + int TableWidth = 8; + BOOL LocalTime = DefaultLocalTime; + + if (strstr(Param, "time=local")) + LocalTime = TRUE; + else if (strstr(Param, "time=utc")) + LocalTime = FALSE; + + Line[0] = 0; + + if (Param && Param[0]) + { + char * Key, *Context; + + Key = strtok_s(Param, "=", &Context); + + if (_stricmp(Key, "width") == 0) + TableWidth = atoi(Context); + + if (TableWidth == 0) + TableWidth = 8; + } + + // Build list of calls + + while (ptr) + { + if (ptr->ObjState == Objects && ptr->Lat != 0.0 && ptr->Lon != 0.0) + { + if ((WX && (ptr->LastWXPacket[0] == 0)) || (RFOnly && (ptr->LastPort == 0)) || + (Mobile && ((ptr->Speed < 0.1) || ptr->LastWXPacket[0] != 0))) + { + ptr = ptr->Next; + continue; + } + + List[n++] = ptr; + + if (n > 999) + break; + + } + ptr = ptr->Next; + } + + if (n > 1) + qsort(List, n, sizeof(void *), CompareFN); + + for (i = 0; i < n; i++) + { + if (RFOnly) + strcat(Line, DoDetailLine(List[i], LocalTime, KM)); + else + strcat(Line, DoSummaryLine(List[i], i, TableWidth)); + } + + *Count = n; + + return Line; + +} + +char * APRSLookupKey(struct APRSConnectionInfo * sockptr, char * Key, BOOL KM) +{ + struct STATIONRECORD * stn = sockptr->SelCall; + + if (strcmp(Key, "##MY_CALLSIGN##") == 0) + return _strdup(LoppedAPRSCall); + + if (strcmp(Key, "##CALLSIGN##") == 0) + return _strdup(sockptr->Callsign); + + if (strcmp(Key, "##CALLSIGN_NOSSID##") == 0) + { + char * Call = _strdup(sockptr->Callsign); + char * ptr = strchr(Call, '-'); + if (ptr) + *ptr = 0; + return Call; + } + + if (strcmp(Key, "##MY_WX_CALLSIGN##") == 0) + return _strdup(LoppedAPRSCall); + + if (strcmp(Key, "##MY_BEACON_COMMENT##") == 0) + return _strdup(StatusMsg); + + if (strcmp(Key, "##MY_WX_BEACON_COMMENT##") == 0) + return _strdup(WXComment); + + if (strcmp(Key, "##MILES_KM##") == 0) + if (KM) + return _strdup("KM"); + else + return _strdup("Miles"); + + if (strcmp(Key, "##EXPIRE_TIME##") == 0) + { + char val[80]; + sprintf(val, "%d", ExpireTime); + return _strdup(val); + } + + if (strcmp(Key, "##LOCATION##") == 0) + { + char val[80]; + double Lat = sockptr->SelCall->Lat; + double Lon = sockptr->SelCall->Lon; + char NS='N', EW='E'; + char LatString[20]; + int Degrees; + double Minutes; + + if (Lat < 0) + { + NS = 'S'; + Lat=-Lat; + } + if (Lon < 0) + { + EW = 'W'; + Lon=-Lon; + } + +#pragma warning(push) +#pragma warning(disable:4244) + + Degrees = Lat; + Minutes = Lat * 60.0 - (60 * Degrees); + + sprintf(LatString,"%2d°%05.2f'%c",Degrees, Minutes, NS); + + Degrees = Lon; + +#pragma warning(pop) + + Minutes = Lon * 60 - 60 * Degrees; + + sprintf(val,"%s %3d°%05.2f'%c", LatString, Degrees, Minutes, EW); + + return _strdup(val); + } + + if (strcmp(Key, "##LOCDDMMSS##") == 0) + { + char val[80]; + double Lat = sockptr->SelCall->Lat; + double Lon = sockptr->SelCall->Lon; + char NS='N', EW='E'; + char LatString[20]; + int Degrees; + double Minutes; + + // 48.45.18N, 002.18.37E + + if (Lat < 0) + { + NS = 'S'; + Lat=-Lat; + } + if (Lon < 0) + { + EW = 'W'; + Lon=-Lon; + } + +#pragma warning(push) +#pragma warning(disable:4244) + + Degrees = Lat; + Minutes = Lat * 60.0 - (60 * Degrees); +// IntMins = Minutes; +// Seconds = Minutes * 60.0 - (60 * IntMins); + + sprintf(LatString,"%2d.%05.2f%c",Degrees, Minutes, NS); + + Degrees = Lon; + Minutes = Lon * 60.0 - 60 * Degrees; +// IntMins = Minutes; +// Seconds = Minutes * 60.0 - (60 * IntMins); + +#pragma warning(pop) + + sprintf(val,"%s, %03d.%05.2f%c", LatString, Degrees, Minutes, EW); + + return _strdup(val); + } + + if (strcmp(Key, "##LATLON##") == 0) + { + char val[80]; + double Lat = sockptr->SelCall->Lat; + double Lon = sockptr->SelCall->Lon; + char NS='N', EW='E'; + + // 58.5, -6.2 + + sprintf(val,"%f, %f", Lat, Lon); + return _strdup(val); + } + + if (strcmp(Key, "##STATUS_TEXT##") == 0) + return _strdup(stn->Status); + + if (strcmp(Key, "##LASTPACKET##") == 0) + return _strdup(stn->LastPacket); + + + if (strcmp(Key, "##LAST_HEARD##") == 0) + { + char Time[80]; + struct tm * TM; + time_t Age = time(NULL) - stn->TimeLastUpdated; + + TM = gmtime(&Age); + + sprintf(Time, "%.2d:%.2d:%.2d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + return _strdup(Time); + } + + if (strcmp(Key, "##FRAME_HEADER##") == 0) + return _strdup(stn->Path); + + if (strcmp(Key, "##FRAME_INFO##") == 0) + return _strdup(stn->LastWXPacket); + + if (strcmp(Key, "##BEARING##") == 0) + { + char val[80]; + + sprintf(val, "%03.0f", myBearing(sockptr->SelCall->Lat, sockptr->SelCall->Lon)); + return _strdup(val); + } + + if (strcmp(Key, "##COURSE##") == 0) + { + char val[80]; + + sprintf(val, "%03.0f", stn->Course); + return _strdup(val); + } + + if (strcmp(Key, "##SPEED_MPH##") == 0) + { + char val[80]; + + sprintf(val, "%5.1f", stn->Speed); + return _strdup(val); + } + + if (strcmp(Key, "##DISTANCE##") == 0) + { + char val[80]; + + sprintf(val, "%5.1f", myDistance(sockptr->SelCall->Lat, sockptr->SelCall->Lon, KM)); + return _strdup(val); + } + + + + if (strcmp(Key, "##WIND_DIRECTION##") == 0) + { + char val[80]; + + sprintf(val, "%03d", sockptr->WindDirn); + return _strdup(val); + } + + if (strcmp(Key, "##WIND_SPEED_MPH##") == 0) + { + char val[80]; + + sprintf(val, "%d", sockptr->WindSpeed); + return _strdup(val); + } + + if (strcmp(Key, "##WIND_GUST_MPH##") == 0) + { + char val[80]; + + sprintf(val, "%d", sockptr->WindGust); + return _strdup(val); + } + + if (strcmp(Key, "##TEMPERATURE_F##") == 0) + { + char val[80]; + + sprintf(val, "%d", sockptr->Temp); + return _strdup(val); + } + + if (strcmp(Key, "##HUMIDITY##") == 0) + { + char val[80]; + + sprintf(val, "%d", sockptr->Humidity); + return _strdup(val); + } + + if (strcmp(Key, "##PRESSURE_HPA##") == 0) + { + char val[80]; + + sprintf(val, "%05.1f", sockptr->Pressure /10.0); + return _strdup(val); + } + + if (strcmp(Key, "##RAIN_TODAY_IN##") == 0) + { + char val[80]; + + sprintf(val, "%5.2f", sockptr->RainToday /100.0); + return _strdup(val); + } + + + if (strcmp(Key, "##RAIN_24_IN##") == 0) + { + char val[80]; + + sprintf(val, "%5.2f", sockptr->RainLastDay /100.0); + return _strdup(val); + } + + + if (strcmp(Key, "##RAIN_HOUR_IN##") == 0) + { + char val[80]; + + sprintf(val, "%5.2f", sockptr->RainLastHour /100.0); + return _strdup(val); + } + + if (strcmp(Key, "##MAP_LAT_LON##") == 0) + { + char val[256]; + + sprintf(val, "%f,%f", stn->Lat, stn->Lon); + return _strdup(val); + } + + if (strcmp(Key, "##SYMBOL_DESCRIPTION##") == 0) + return _strdup(&SymbolText[stn->iconRow << 4 | stn->iconCol][0]); + + +/* +##WIND_SPEED_MS## - wind speed metres/sec +##WIND_SPEED_KMH## - wind speed km/hour +##WIND_GUST_MPH## - wind gust miles/hour +##WIND_GUST_MS## - wind gust metres/sec +##WIND_GUST_KMH## - wind gust km/hour +##WIND_CHILL_F## - wind chill F +##WIND_CHILL_C## - wind chill C +##TEMPERATURE_C## - temperature C +##DEWPOINT_F## - dew point temperature F +##DEWPOINT_C## - dew point temperature C +##PRESSURE_IN## - pressure inches of mercury +##PRESSURE_HPA## - pressure hPa (mb) +##RAIN_HOUR_MM## - rain in last hour mm +##RAIN_TODAY_MM## - rain today mm +##RAIN_24_MM## - rain in last 24 hours mm +##FRAME_HEADER## - frame header of the last posit heard from the station +##FRAME_INFO## - information field of the last posit heard from the station +##MAP_LARGE_SCALE##" - URL of a suitable large scale map on www.vicinity.com +##MEDIUM_LARGE_SCALE##" - URL of a suitable medium scale map on www.vicinity.com +##MAP_SMALL_SCALE##" - URL of a suitable small scale map on www.vicinity.com +##MY_LOCATION## - 'Latitude', 'Longitude' in 'Station Setup' +##MY_STATUS_TEXT## - status text configured in 'Status Text' +##MY_SYMBOL_DESCRIPTION## - 'Symbol' that would be shown for our station in 'Station List' +##HIT_COUNTER## - The number of times the page has been accessed +##DOCUMENT_LAST_CHANGED## - The date/time the page was last edited + +##FRAME_HEADER## - frame header of the last posit heard from the station +##FRAME_INFO## - information field of the last posit heard from the station + +*/ + return NULL; +} + +VOID APRSProcessSpecialPage(struct APRSConnectionInfo * sockptr, char * Buffer, int FileSize, char * StationTable, int Count, BOOL WX, BOOL KM) +{ + // replaces ##xxx### constructs with the requested data + + char * NewMessage = malloc(250000); + char * ptr1 = Buffer, * ptr2, * ptr3, * ptr4, * NewPtr = NewMessage; + size_t PrevLen; + size_t BytesLeft = FileSize; + size_t NewFileSize = FileSize; + char * StripPtr = ptr1; + int HeaderLen; + char Header[256]; + + if (WX && sockptr->SelCall && sockptr->SelCall->LastWXPacket) + { + DecodeWXReport(sockptr, sockptr->SelCall->LastWXPacket); + } + + // strip comments blocks + + while (ptr4 = strstr(ptr1, ""); + if (ptr2) + { + PrevLen = (ptr4 - ptr1); + memcpy(StripPtr, ptr1, PrevLen); + StripPtr += PrevLen; + ptr1 = ptr2 + 3; + BytesLeft = FileSize - (ptr1 - Buffer); + } + } + + + memcpy(StripPtr, ptr1, BytesLeft); + StripPtr += BytesLeft; + + BytesLeft = StripPtr - Buffer; + + FileSize = (int)BytesLeft; + NewFileSize = FileSize; + ptr1 = Buffer; + ptr1[FileSize] = 0; + +loop: + ptr2 = strstr(ptr1, "##"); + + if (ptr2) + { + PrevLen = (ptr2 - ptr1); // Bytes before special text + + ptr3 = strstr(ptr2+2, "##"); + + if (ptr3) + { + char Key[80] = ""; + size_t KeyLen; + char * NewText; + size_t NewTextLen; + + ptr3 += 2; + KeyLen = ptr3 - ptr2; + + if (KeyLen < 80) + memcpy(Key, ptr2, KeyLen); + + if (strcmp(Key, "##STATION_TABLE##") == 0) + { + NewText = _strdup(StationTable); + } + else + { + if (strcmp(Key, "##TABLE_COUNT##") == 0) + { + char val[80]; + sprintf(val, "%d", Count); + NewText = _strdup(val); + } + else + NewText = APRSLookupKey(sockptr, Key, KM); + } + + if (NewText) + { + NewTextLen = strlen(NewText); + NewFileSize = NewFileSize + NewTextLen - KeyLen; + // NewMessage = realloc(NewMessage, NewFileSize); + + memcpy(NewPtr, ptr1, PrevLen); + NewPtr += PrevLen; + memcpy(NewPtr, NewText, NewTextLen); + NewPtr += NewTextLen; + + free(NewText); + NewText = NULL; + } + else + { + // Key not found, so just leave + + memcpy(NewPtr, ptr1, PrevLen + KeyLen); + NewPtr += (PrevLen + KeyLen); + } + + ptr1 = ptr3; // Continue scan from here + BytesLeft = Buffer + FileSize - ptr3; + } + else // Unmatched ## + { + memcpy(NewPtr, ptr1, PrevLen + 2); + NewPtr += (PrevLen + 2); + ptr1 = ptr2 + 2; + } + goto loop; + } + + // Copy Rest + + memcpy(NewPtr, ptr1, BytesLeft); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)NewFileSize); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, NewMessage, (int)NewFileSize, 0); + + free (NewMessage); + free(StationTable); + + return; +} + +VOID APRSSendMessageFile(struct APRSConnectionInfo * sockptr, char * FN) +{ + int FileSize = 0; + char * MsgBytes = 0; + char * SaveMsgBytes = 0; + + char MsgFile[MAX_PATH]; + FILE * hFile; + BOOL Special = FALSE; + int HeaderLen; + char Header[256]; + char * Param = 0; + struct stat STAT; + int Sent; + char * ptr; + + if (strchr(FN, '?')) + strtok_s(FN, "?", &Param); + + UndoTransparency(FN); + + if (strstr(FN, "..")) + { + FN[0] = '/'; + FN[1] = 0; + } + + if (strcmp(FN, "/") == 0) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s/index.html", BPQDirectory, APRSDir, SpecialDocs); + else + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s%s", BPQDirectory, APRSDir, SpecialDocs, &FN[5]); + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + { + // Try normal pages + + + if (strcmp(FN, "/") == 0) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s/index.html", BPQDirectory, APRSDir, HTDocs); + else + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s%s", BPQDirectory,APRSDir, HTDocs, &FN[5]); + + // My standard page set is now hard coded + + + + MsgBytes = SaveMsgBytes = GetStandardPage(&FN[6], &FileSize); + + if (MsgBytes) + { + if (FileSize == 0) + FileSize = strlen(MsgBytes); + } + else + { + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + { + HeaderLen = sprintf(Header, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + send(sockptr->sock, Header, HeaderLen, 0); + return; + } + } + } + else + Special = TRUE; + + if (FileSize == 0) // Not from internal template + { + if (stat(MsgFile, &STAT) == 0) + FileSize = STAT.st_size; + + MsgBytes = SaveMsgBytes = malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + } + + // if HTML file, look for ##...## substitutions + + if ((strstr(FN, "htm" ) || strstr(FN, "HTM")) && strstr(MsgBytes, "##" )) + { + // Build Station list, depending on URL + + int Count = 0; + BOOL RFOnly = !(strstr(_strlwr(FN), "rf") == NULL); // Leaves FN in lower case + BOOL WX =!(strstr(FN, "wx") == NULL); + BOOL Mobile = !(strstr(FN, "mobile") == NULL); + char Objects = (strstr(FN, "obj"))? '*' :0; + char * StationList; + BOOL KM = DefaultDistKM; + + if (Param == 0) + Param =""; + else + _strlwr(Param); + + if (strstr(Param, "dist=km")) + KM = TRUE; + else if (strstr(Param, "dist=miles")) + KM = FALSE; + + + StationList = CreateStationList(RFOnly, WX, Mobile, Objects, &Count, Param, KM); + + APRSProcessSpecialPage(sockptr, MsgBytes, FileSize, StationList, Count, WX, KM); + free (MsgBytes); + return; // ProcessSpecial has sent the reply + } + + ptr = FN; + + while (strchr(ptr, '.')) + { + ptr = strchr(ptr, '.'); + ++ptr; + } + + if (_stricmp(ptr, "jpg") == 0 || _stricmp(ptr, "jpeg") == 0 || _stricmp(ptr, "png") == 0 || _stricmp(ptr, "gif") == 0 || _stricmp(ptr, "ico") == 0) + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: image\r\n\r\n", FileSize); + else + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", FileSize); + + send(sockptr->sock, Header, HeaderLen, 0); + + Sent = send(sockptr->sock, MsgBytes, FileSize, 0); +// printf("Send %d %d\n", FileSize, Sent); + + while (Sent < FileSize) + { + FileSize -= Sent; + MsgBytes += Sent; + Sent = send(sockptr->sock, MsgBytes, FileSize, 0); +// printf("Send %d %d\n", FileSize, Sent); + if (Sent == -1) + { + Sleep(10); + Sent = 0; + } + } + + free (SaveMsgBytes); +} + +char WebHeader[] = "" + "" + "APRS Messaging" + "" + "" + "" + "" + "" + "" + "" + "" + "
APRS MapReceived MessagesSent MessagesSend MessageStation PagesReturn to Node Pages
" + "

%s's Messages

" + ""; + +char WebTXHeader[] = "" + "" + "APRS Messaging" + "" + "
FromToSeqTime Message
" + "" + "" + "" + "" + "" + "" + "
APRS MapReceived MessagesSent MessagesSend MessageStation PagesReturn to Node Pages
" + "

Message Sent by %s

" + ""; + +char WebLine[] = ""; + +char WebTXLine[] = "" + ""; + + +char WebTrailer[] = "
ToSeqTimeStatemessage
%s %s %s %s" + "Reply %s
%s %s %s %s %s
"; + +char SendMsgPage[] = "BPQ32 APRS Messaging" + "

APRS Message Input

" + "
" + "" + "" + "
To
Message
" + "

"; + +char APRSIndexPage[] = "BPQ32 Web Server APRS Pages" + "

" + "

BPQ32 APRS Server

" + "" + "" + "" + "" + "" + "" + "" + "
APRS MapReceived MessagesSent MessagesSend MessageStation PagesReturn to Node Pages
%s"; + +extern char Tail[]; + +VOID APRSProcessHTTPMessage(SOCKET sock, char * MsgPtr, BOOL LOCAL, BOOL COOKIE) +{ + int InputLen = 0; + int OutputLen = 0; + char * URL; + char * ptr; + struct APRSConnectionInfo CI; + struct APRSConnectionInfo * sockptr = &CI; + char Key[12] = ""; + char OutBuffer[100000]; + char Header[1024]; + int HeaderLen = 0; + + memset(&CI, 0, sizeof(CI)); + + sockptr->sock = sock; + + if (memcmp(MsgPtr, "POST" , 3) == 0) + { + char * To; + char * Msg = ""; + + URL = &MsgPtr[5]; + + ptr = strstr(URL, "\r\n\r\n"); + + if (ptr) + { + char * param, * context; + + UndoTransparency(ptr); + + param = strtok_s(ptr + 4, "&", &context); + + while (param) + { + char * val = strlop(param, '='); + + if (val) + { + if (_stricmp(param, "call") == 0) + To = _strupr(val); + else if (_stricmp(param, "message") == 0) + Msg = val; + else if (_stricmp(param, "Cancel") == 0) + { + + // Return APRS Index Page + + OutputLen = sprintf(OutBuffer, APRSIndexPage, "



Message Cancelled

"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + } + } + + param = strtok_s(NULL,"&", &context); + } + + strlop(To, ' '); + + if (strlen(To) < 2) + { + OutputLen = sprintf(OutBuffer, SendMsgPage, To); + OutputLen += sprintf(&OutBuffer[OutputLen], "

Invalid Callsign

"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + } + + if (Msg[0] == 0) + { + OutputLen = sprintf(OutBuffer, SendMsgPage, To); + OutputLen += sprintf(&OutBuffer[OutputLen], "

No Message

"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + } + + // Send APRS Messsage + + if (strlen(Msg) > 100) + Msg[100] = 0; + + InternalSendAPRSMessage(Msg, To); + + OutputLen = sprintf(OutBuffer, APRSIndexPage, "



Message Sent

"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + } + } + + URL = &MsgPtr[4]; + + ptr = strstr(URL, " HTTP"); + + if (ptr) + *ptr = 0; + + if (_stricmp(URL, "/APRS") == 0) + { + // Return APRS Index Page + + OutputLen = sprintf(OutBuffer, APRSIndexPage, ""); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + } + + + if (_memicmp(URL, "/aprs/msgs/entermsg", 19) == 0 || _memicmp(URL, "/aprs/entermsg", 14) == 0) + { + char * To = strchr(URL, '='); + + if (LOCAL == FALSE && COOKIE == FALSE) + { + // Send Not Authorized + + OutputLen = sprintf(OutBuffer, APRSIndexPage, "
Not authorized - please return to Node Menu and sign in"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)(OutputLen + strlen(Tail))); + send(sock, Header, HeaderLen, 0); + send(sock, OutBuffer, OutputLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return; + } + + + if (To) + { + To++; + UndoTransparency(To); + strlop(To, '&'); + } + else + To = ""; + + OutputLen = sprintf(OutBuffer, SendMsgPage, To); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + + } + + else if (_memicmp(URL, "/aprs/msgs", 10) == 0) + { + // Return Messages Received Page + + struct APRSMESSAGE * ptr = SMEM->Messages; + int n = 0; + char BaseCall[10]; + char BaseFrom[10]; + char * MsgCall = LoppedAPRSCall; + BOOL OnlyMine = TRUE; + BOOL AllSSID = TRUE; + BOOL OnlySeq = FALSE; + BOOL ShowBulls = TRUE; + + // Parse parameters + + // ?call=g8bpq&bulls=true&seqonly=true&onlymine=true + + char * params = strchr(URL, '?'); + + if (params) + { + char * param, * context; + + param = strtok_s(++params, "&", &context); + + while (param) + { + char * val = strlop(param, '='); + + if (val) + { + strlop(val, ' '); + if (_stricmp(param, "call") == 0) + MsgCall = _strupr(val); + else if (_stricmp(param, "bulls") == 0) + ShowBulls = !_stricmp(val, "true"); + else if (_stricmp(param, "onlyseq") == 0) + OnlySeq = !_stricmp(val, "true"); + else if (_stricmp(param, "onlymine") == 0) + OnlyMine = !_stricmp(val, "true"); + else if (_stricmp(param, "AllSSID") == 0) + AllSSID = !_stricmp(val, "true"); + } + param = strtok_s(NULL,"&", &context); + } + } + if (AllSSID) + { + memcpy(BaseCall, MsgCall, 10); + strlop(BaseCall, '-'); + } + + OutputLen = sprintf(OutBuffer, WebHeader, MsgCall, MsgCall); + + while (ptr) + { + char ToLopped[11] = ""; + memcpy(ToLopped, ptr->ToCall, 10); + strlop(ToLopped, ' '); + + if (memcmp(ToLopped, "BLN", 3) == 0) + if (ShowBulls == TRUE) + goto wantit; + + if (strcmp(ToLopped, MsgCall) == 0) // to me? + goto wantit; + + if (strcmp(ptr->FromCall, MsgCall) == 0) // to me? + goto wantit; + + if (AllSSID) + { + memcpy(BaseFrom, ToLopped, 10); + strlop(BaseFrom, '-'); + + if (strcmp(BaseFrom, BaseCall) == 0) + goto wantit; + + memcpy(BaseFrom, ptr->FromCall, 10); + strlop(BaseFrom, '-'); + + if (strcmp(BaseFrom, BaseCall) == 0) + goto wantit; + + } + + if (OnlyMine == FALSE) // Want All + if (OnlySeq == FALSE || ptr->Seq[0] != 0) + goto wantit; + + // ignore + + ptr = ptr->Next; + continue; + wantit: + OutputLen += sprintf(&OutBuffer[OutputLen], WebLine, + ptr->FromCall, ptr->ToCall, ptr->Seq, ptr->Time, + ptr->FromCall, ptr->ToCall, ptr->Text); + + ptr = ptr->Next; + + if (OutputLen > 99000) + break; + + } + + OutputLen += sprintf(&OutBuffer[OutputLen], "%s", WebTrailer); + + HeaderLen = sprintf(Header, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, OutBuffer, OutputLen); + + return; + + } + + else if (_memicmp(URL, "/aprs/txmsgs", 12) == 0) + { + // Return Messages Received Page + + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + char * MsgCall = LoppedAPRSCall; + + char Retries[10]; + + + OutputLen = sprintf(OutBuffer, WebTXHeader, MsgCall, MsgCall); + + while (ptr) + { + char ToLopped[11] = ""; + + if (ptr->Acked) + strcpy(Retries, "A"); + else if (ptr->Retries == 0) + strcpy(Retries, "F"); + else + sprintf(Retries, "%d", ptr->Retries); + + memcpy(ToLopped, ptr->ToCall, 10); + strlop(ToLopped, ' '); + + OutputLen += sprintf(&OutBuffer[OutputLen], WebTXLine, + ptr->ToCall, ptr->Seq, ptr->Time, Retries, ptr->Text); + ptr = ptr->Next; + + if (OutputLen > 99000) + break; + + } + + OutputLen += sprintf(&OutBuffer[OutputLen], "%s", WebTrailer); + + HeaderLen = sprintf(Header, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, OutBuffer, OutputLen); + + return; + + } + + + if (_memicmp(URL, "/aprs/find.cgi?call=", 20) == 0) + { + // return Station details + + char * Call = &URL[20]; + BOOL RFOnly, WX, Mobile, Object = FALSE; + struct STATIONRECORD * stn; + char * Referrer = strstr(ptr + 1, "Referer:"); + + // Undo any % transparency in call + + char * ptr1 = Call; + char * ptr2 = Key; + char c; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + *(ptr2++) = m * 16 + n; + } + else if (c == '+') + *(ptr2++) = ' '; + else + *(ptr2++) = c; + + c = *(ptr1++); + } + + *(ptr2++) = 0; + + if (Referrer) + { + ptr = strchr(Referrer, 13); + if (ptr) + { + *ptr = 0; + RFOnly = !(strstr(Referrer, "rf") == NULL); + WX = !(strstr(Referrer, "wx") == NULL); + Mobile = !(strstr(Referrer, "mobile") == NULL); + Object = !(strstr(Referrer, "obj") == NULL); + + if (WX) + strcpy(URL, "/aprs/infowx_call.html"); + else if (Mobile) + strcpy(URL, "/aprs/infomobile_call.html"); + else if (Object) + strcpy(URL, "/aprs/infoobj_call.html"); + else + strcpy(URL, "/aprs/info_call.html"); + } + } + + if (Object) + { + // Name is space padded, and could have embedded spaces + + int Keylen = (int)strlen(Key); + + if (Keylen < 9) + memset(&Key[Keylen], 32, 9 - Keylen); + } + + stn = FindStation(Key, FALSE); + + if (stn == NULL) + strcpy(URL, "/aprs/noinfo.html"); + else + sockptr->SelCall = stn; + } + + + strcpy(sockptr->Callsign, Key); + + APRSSendMessageFile(sockptr, URL); + + return; +} + +// Code for handling APRS messages within BPQ32/LinBPQ instead of GUI + + +int ProcessMessage(char * Payload, struct STATIONRECORD * Station) +{ + char MsgDest[10]; + struct APRSMESSAGE * Message; + struct APRSMESSAGE * ptr = SMEM->Messages; + char * TextPtr = &Payload[11]; + char * SeqPtr; + int n = 0; + char FromCall[10] = " "; + struct tm * TM; + time_t NOW; + char noSeq[] = ""; + int ourMessage = 0; + + memcpy(FromCall, Station->Callsign, strlen(Station->Callsign)); + memcpy(MsgDest, &Payload[1], 9); + MsgDest[9] = 0; + + if (strcmp(MsgDest, CallPadded) == 0) // to me? + { + SMEM->NeedRefresh = 255; // Flag to control Msg popup + ourMessage = 1; + } + else + SMEM->NeedRefresh = 1; + + SeqPtr = strchr(TextPtr, '{'); + + if (SeqPtr) + { + *(SeqPtr++) = 0; + if(strlen(SeqPtr) > 6) + SeqPtr[7] = 0; + } + else + SeqPtr = noSeq; + + if (_memicmp(TextPtr, "ack", 3) == 0) + { + // Message Ack. See if for one of our messages + + ptr = SMEM->OutstandingMsgs; + + if (ptr == 0) + return ourMessage; + + do + { + if (strcmp(ptr->FromCall, MsgDest) == 0 + && strcmp(ptr->ToCall, FromCall) == 0 + && strcmp(ptr->Seq, &TextPtr[3]) == 0) + { + // Message is acked + + ptr->Retries = 0; + ptr->Acked = TRUE; + + return ourMessage; + } + ptr = ptr->Next; + n++; + + } while (ptr); + + return ourMessage; + } + + // See if we already have this message + + ptr = SMEM->Messages; + + while(ptr) + { + if (strcmp(ptr->ToCall, MsgDest) == 0 + && strcmp(ptr->FromCall, FromCall) == 0 + && strcmp(ptr->Seq, SeqPtr) == 0 + && strcmp(ptr->Text, TextPtr) == 0) + + // Duplicate + + return ourMessage; + + ptr = ptr->Next; + } + + Message = APRSGetMessageBuffer(); + + if (Message == NULL) + return ourMessage; + + memset(Message, 0, sizeof(struct APRSMESSAGE)); + memset(Message->FromCall, ' ', 9); + memcpy(Message->FromCall, Station->Callsign, strlen(Station->Callsign)); + strcpy(Message->ToCall, MsgDest); + + if (SeqPtr) + { + strcpy(Message->Seq, SeqPtr); + + // If a REPLY-ACK Seg, copy to LastRXSeq, and see if it acks a message + + if (SeqPtr[2] == '}') + { + struct APRSMESSAGE * ptr1; + int nn = 0; + + strcpy(Station->LastRXSeq, SeqPtr); + + ptr1 = SMEM->OutstandingMsgs; + + while (ptr1) + { + if (strcmp(ptr1->FromCall, MsgDest) == 0 + && strcmp(ptr1->ToCall, FromCall) == 0 + && memcmp(&ptr1->Seq, &SeqPtr[3], 2) == 0) + { + // Message is acked + + ptr1->Acked = TRUE; + ptr1->Retries = 0; + + break; + } + ptr1 = ptr1->Next; + nn++; + } + } + else + { + // Station is not using reply-ack - set to send simple numeric sequence (workround for bug in APRS Messanges + + Station->SimpleNumericSeq = TRUE; + } + } + + if (strlen(TextPtr) > 100) + TextPtr[100] = 0; + + strcpy(Message->Text, TextPtr); + + NOW = time(NULL); + + if (DefaultLocalTime) + TM = localtime(&NOW); + else + TM = gmtime(&NOW); + + sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + if (_stricmp(MsgDest, CallPadded) == 0 && SeqPtr) // ack it if it has a sequence + { + // For us - send an Ack + + char ack[30]; + APRSHEARDRECORD * STN; + + sprintf(ack, ":%-9s:ack%s", Message->FromCall, Message->Seq); + + if (memcmp(Message->FromCall, "SERVER ", 9) == 0) + { + SendAPRSMessage(ack, 0); // IS + } + else + { + STN = FindStationInMH(Message->ToCall); + + if (STN) + SendAPRSMessage(ack, STN->rfPort); + else + { + SendAPRSMessage(ack, -1); // All RF ports + SendAPRSMessage(ack, 0); // IS + } + } + } + + if (SaveAPRSMsgs) + SaveAPRSMessage(Message); + + ptr = SMEM->Messages; + + if (ptr == NULL) + { + SMEM->Messages = Message; + } + else + { + n++; + while(ptr->Next) + { + ptr = ptr->Next; + n++; + } + ptr->Next = Message; + } + + return ourMessage; +} + +BOOL InternalSendAPRSMessage(char * Text, char * Call) +{ + char Msg[255]; + size_t len = strlen(Call); + APRSHEARDRECORD * STN; + struct tm * TM; + time_t NOW; + + struct APRSMESSAGE * Message; + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + + Message = APRSGetMessageBuffer(); + + if (Message == NULL) + return FALSE; + + memset(Message, 0, sizeof(struct APRSMESSAGE)); + strcpy(Message->FromCall, CallPadded); + + memset(Message->ToCall, ' ', 9); + memcpy(Message->ToCall, Call, len); + + Message->ToStation = FindStation(Call, TRUE); + + if (Message->ToStation == NULL) + return FALSE; + + SMEM->NeedRefresh = TRUE; + + if (Message->ToStation->LastRXSeq[0]) // Have we received a Reply-Ack message from him? + sprintf(Message->Seq, "%02X}%c%c", ++Message->ToStation->NextSeq, Message->ToStation->LastRXSeq[0], Message->ToStation->LastRXSeq[1]); + else + { + if (Message->ToStation->SimpleNumericSeq) + sprintf(Message->Seq, "%d", ++Message->ToStation->NextSeq); + else + sprintf(Message->Seq, "%02X}", ++Message->ToStation->NextSeq); // Don't know, so assume message-ack capable + } + + if (strlen(Text) > 100) + Text[100] = 0; + + strcpy(Message->Text, Text); + Message->Retries = RetryCount; + Message->RetryTimer = RetryTimer; + + NOW = time(NULL); + + if (DefaultLocalTime) + TM = localtime(&NOW); + else + TM = gmtime(&NOW); + + sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + + // Chain to Outstanding Queue + + if (ptr == NULL) + { + SMEM->OutstandingMsgs = Message; + } + else + { + while(ptr->Next) + { + ptr = ptr->Next; + } + ptr->Next = Message; + } + + sprintf(Msg, ":%-9s:%s{%s", Call, Text, Message->Seq); + + if (strcmp(Call, "SERVER") == 0) + { + SendAPRSMessage(Msg, 0); // IS + return TRUE; + } + + STN = FindStationInMH(Message->ToCall); + + if (STN && STN->MHTIME > (time(NULL) - 900)) // Heard in last 15 mins + SendAPRSMessage(Msg, STN->rfPort); + else + { + SendAPRSMessage(Msg, -1); // All RF ports + SendAPRSMessage(Msg, 0); // IS + } + return TRUE; +} + + + + + +extern BOOL APRSReconfigFlag; +extern struct DATAMESSAGE * REPLYBUFFER; +extern char COMMANDBUFFER[81]; +extern char OrigCmdBuffer[81]; + +BOOL isSYSOP(TRANSPORTENTRY * Session, char * Bufferptr); + +VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + // APRS Subcommands. Default for compatibility is APRSMH + + // Others are STATUS ENABLEIGATE DISABLEIGATE RECONFIG + + APRSHEARDRECORD * MH = MHDATA; + int n = MAXHEARDENTRIES; + char * ptr; + char * Pattern, * context; + int Port = -1; + char dummypattern[] =""; + + if (memcmp(CmdTail, "? ", 2) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "APRS Subcommmands:\r"); + Bufferptr = Cmdprintf(Session, Bufferptr, "STATUS SEND MSGS SENT ENABLEIGATE DISABLEIGATE BEACON RECONFIG\r"); + Bufferptr = Cmdprintf(Session, Bufferptr, "Default is Station list - Params [Port] [Pattern]\r"); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "RECONFIG ", 5) == 0) + { + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + if (!ProcessConfig()) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Configuration File check failed - will continue with old config"); + } + else + { + APRSReconfigFlag=TRUE; + Bufferptr = Cmdprintf(Session, Bufferptr, "Reconfiguration requested\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + + if (memcmp(CmdTail, "ENABLEIGATE ", 6) == 0) + { + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + IGateEnabled = TRUE; + Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "DISABLEIGATE ", 6) == 0) + { + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + IGateEnabled = FALSE; + Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Disabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "STATUS ", 7) == 0) + { + if (IGateEnabled == FALSE) + Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Disabled\r"); + else + { + Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Enabled "); + if (APRSISOpen) + Bufferptr = Cmdprintf(Session, Bufferptr, "and connected to %s\r", RealISHost); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "but not connected\r"); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "BEACON ", 7) == 0) + { + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + BeaconCounter = 2; + Bufferptr = Cmdprintf(Session, Bufferptr, "Beacons requested\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "MSGS ", 5) == 0) + { + struct APRSMESSAGE * ptr = SMEM->Messages; + char Addrs[32]; + + Bufferptr = Cmdprintf(Session, Bufferptr, + "\rTime Calls Seq Text\r"); + + while (ptr) + { + char ToLopped[11] = ""; + + memcpy(ToLopped, ptr->ToCall, 10); + strlop(ToLopped, ' '); + + sprintf(Addrs, "%s>%s", ptr->FromCall, ToLopped); + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s %-20s%-5s %s\r", + ptr->Time, Addrs, ptr->Seq, ptr->Text); + + ptr = ptr->Next; + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "SENT ", 5) == 0) + { + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + char Addrs[32]; + + Bufferptr = Cmdprintf(Session, Bufferptr, + "\rTime Calls Seq State Text\r"); + + while (ptr) + { + char ToLopped[11] = ""; + char Retries[10]; + + if (ptr->Acked) + strcpy(Retries, "A"); + else if (ptr->Retries == 0) + strcpy(Retries, "F"); + else + sprintf(Retries, "%d", ptr->Retries); + + + memcpy(ToLopped, ptr->ToCall, 10); + strlop(ToLopped, ' '); + + sprintf(Addrs, "%s>%s", ptr->FromCall, ToLopped); + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s %-20s%-5s %-2s %s\r", + ptr->Time, Addrs, ptr->Seq, Retries, ptr->Text); + + ptr = ptr->Next; + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "SEND ", 5) == 0) + { + // Send Message. Params are Call and Message + + char * Call = strtok_s(&CmdTail[5], " \r", &context); + char * Text = strtok_s(NULL, " \r", &context); + int len = 0; + + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + if (Call) + len = (int)strlen(Call); + + if (len < 3 || len > 9) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Callsign\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (Text == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "No Message Text\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + // Replace command tail with original (before conversion to upper case + + Text = Text + (OrigCmdBuffer - COMMANDBUFFER); + + InternalSendAPRSMessage(Text, Call); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // DISPLAY APRS HEARD LIST + + // APRS [Port] [Pattern] + + Pattern = strtok_s(CmdTail, " \r", &context); + + if (Pattern && (int)strlen(Pattern) < 3) + { + // could be port number + + if (isdigit(Pattern[0]) && (Pattern[1] == 0 || isdigit(Pattern[1]))) + { + Port = atoi(Pattern); + Pattern = strtok_s(NULL, " \r", &context); + } + } + + // Param is a pattern to match + + if (Pattern == NULL) + Pattern = dummypattern; + + if (Pattern[0] == ' ') + { + // Prepare Pattern + + char * ptr1 = Pattern + 1; + char * ptr2 = Pattern; + char c; + + do + { + c = *ptr1++; + *(ptr2++) = c; + } + while (c != ' '); + + *(--ptr2) = 0; + } + + strlop(Pattern, ' '); + _strupr(Pattern); + + *(Bufferptr++) = 13; + + while (n--) + { + if (MH->MHCALL[0] == 0) + break; + + if ((Port > -1) && Port != MH->rfPort) + { + MH++; + continue; + } + + ptr = FormatAPRSMH(MH); + + MH++; + + if (ptr) + { + if (Pattern[0] && strstr(ptr, Pattern) == 0) + continue; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", ptr); + } + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +int GetPosnFromAPRS(char * Call, double * Lat, double * Lon) +{ + struct STATIONRECORD * Station; + + Station = FindStation(Call, FALSE); + + if (Station) + { + *Lat = Station->Lat; + *Lon = Station->Lon; + + return 1; + } + + return 0; +} + +// Station Name Font + +const unsigned char ASCII[][5] = { +//const u08 ASCII[][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00} // 20 + ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 ! + ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 " + ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 # + ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $ + ,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 % + ,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 & + ,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 ' + ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 ( + ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 ) + ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a * + ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b + + ,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c , + ,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d - + ,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e . + ,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f / + ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0 + ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1 + ,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2 + ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3 + ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4 + ,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5 + ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6 + ,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7 + ,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8 + ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9 + ,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a : + ,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ; + ,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c < + ,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d = + ,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e > + ,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ? + ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @ + ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A + ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B + ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C + ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D + ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E + ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F + ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G + ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H + ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I + ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J + ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K + ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L + ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M + ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N + ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O + ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P + ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q + ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R + ,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S + ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T + ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U + ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V + ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W + ,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X + ,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y + ,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z + ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [ + ,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c + ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ] + ,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^ + ,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _ + ,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 ` + ,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a + ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b + ,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c + ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d + ,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e + ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f + ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g + ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h + ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i + ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j + ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k + ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l + ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m + ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n + ,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o + ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p + ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q + ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r + ,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s + ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t + ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u + ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v + ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w + ,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x + ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y + ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z + ,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b { + ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c | + ,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d } + ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ~ + ,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f DEL +}; + + +// APRS Web Map Code + +// Not sure yet what is best way to do station icons but for now build and cache any needed icons + +extern int IconDataLen; +extern unsigned long long IconData[]; // Symbols as a png image.& + +// IconData is a png image, so needs to be uncompressed to an RGB array + + +// Will key cached icons by IconRow, IconCol, Overlay Char - xxxxA + +int cachedIconCount = 0; + +// We need key, icon data, icon len for each. Maybe a simple linked list - we never remove any + +struct iconCacheEntry +{ + struct iconCacheEntry * Next; + char key[8]; + int pngimagelen; + int pngmalloclen; + unsigned char * pngimage; +}; + +struct iconCacheEntry * iconCache = NULL; + + +// Each icon has to be created as an RGB array, then converted to a png image, as +// Leaflet icons need a png file + +#include "mypng.h" + + +struct png_info_struct * info_ptr = NULL; + +unsigned char * PngEncode (png_byte *pDiData, int iWidth, int iHeight, struct iconCacheEntry * Icon); + +void createIcon(char * Key, int iconRow, int iconCol, char Overlay) +{ + int i, j, index, mask; + int row; + int col; // First row + unsigned char * rgbData = malloc(68 * 22); // 1323 + unsigned char * ptr = rgbData; + png_color colour = {0, 0, 0}; + int Pointer; + char c; + int bit; + struct iconCacheEntry * Icon = zalloc(sizeof(struct iconCacheEntry)); + + strcpy(Icon->key, Key); + + // icon data is in info_ptr->row_pointers (we have 255 of them) + + row = iconRow * 21; + col = iconCol * 21 * 3; + + for (j = 0; j < 22; j++) + { + memcpy(ptr, info_ptr->row_pointers[row + j] + col, 22 * 3); // One scan line + ptr += 68; // Rounded up to mod 4 + } + + + // This code seems to assume an icon is 16 pixels, but image is 22 x 22 ??? + +// j = ptr->iconRow * 21 * 337 * 3 + ptr->iconCol * 21 * 3 + 9 + 337 * 9; +// for (i = 0; i < 16; i++) +// { +// memcpy(nptr, &iconImage[j], 16 * 3); +// nptr += 6144; +// j += 337 * 3; +// } + + + // If an overlay is specified, add it + + if (Overlay) + { + Pointer = 68 * 7 + 7 * 3; // 7th row, 7th col + mask = 1; + + for (index = 0 ; index < 7 ; index++) + { + rgbData[Pointer++] = 255; // Blank line above chars + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + } + + Pointer = 68 * 8 + 7 * 3; // 8th row, 7th col + + for (i = 0; i < 7; i++) + { + rgbData[Pointer++] = 255; // Blank col + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + + for (index = 0 ; index < 5 ; index++) + { + c = ASCII[Overlay - 0x20][index]; // Font data + bit = c & mask; + + if (bit) + { + rgbData[Pointer++] = 0; + rgbData[Pointer++] = 0; + rgbData[Pointer++] = 0; + } + else + { + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + } + } + + rgbData[Pointer++] = 255; // Blank col + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + + mask <<= 1; + Pointer += 47; + } + for (index = 0 ; index < 7 ; index++) + { + rgbData[Pointer++] = 255; // Blank line above chars + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + } + } + + // Encode + + PngEncode(rgbData, 22, 22, Icon); + + if (iconCache) + Icon->Next = iconCache; + + iconCache = Icon; + +} + +int GetAPRSIcon(unsigned char * _REPLYBUFFER, char * NodeURL) +{ + char Key[8] = ""; + struct iconCacheEntry * Icon = iconCache; + + memcpy(Key, &NodeURL[5], 5); + + while (Icon) + { + if (strcmp(Icon->key, Key) == 0) // Have it + { + memcpy(_REPLYBUFFER, Icon->pngimage, Icon->pngimagelen); + return Icon->pngimagelen; + } + Icon = Icon->Next; + } + + return 0; +} + +char * doHTMLTransparency(char * string) +{ + // Make sure string doesn't contain forbidden XML chars (<>"'&) + + char * newstring = malloc(5 * strlen(string) + 1); // If len is zero still need null terminator + + char * in = string; + char * out = newstring; + char c; + + c = *(in++); + + while (c) + { + switch (c) + { + case '<': + + strcpy(out, "<"); + out += 4; + break; + + case '>': + + strcpy(out, ">"); + out += 4; + break; + + case '"': + + strcpy(out, """); + out += 6; + break; + + case '\'': + + strcpy(out, "'"); + out += 6; + break; + + case '&': + + strcpy(out, "&"); + out += 5; + break; + + case ',': + + strcpy(out, ","); + out += 5; + break; + + case '|': + + strcpy(out, "|"); + out += 5; + break; + + default: + + *(out++) = c; + } + c = *(in++); + } + + *(out++) = 0; + return newstring; +} + +int GetAPRSPageInfo(char * Buffer, double N, double S, double W, double E, int aprs, int ais, int adsb) +{ + struct STATIONRECORD * ptr = *StationRecords; + int n = 0, Len = 0; + struct tm * TM; + time_t NOW = time(NULL); + char popup[65536] = ""; + char Msg[2048]; + int LocalTime = 0; + int KM = DefaultDistKM; + char * ptr1; + + while (ptr && aprs && Len < 240000) + { + if (ptr->Lat != 0.0 && ptr->Lon != 0.0) + { + if (ptr->Lat > S && ptr->Lat < N && ptr->Lon > W && ptr->Lon < E) + { + // See if we have the Icon - if not build + + char IconKey[6]; + struct iconCacheEntry * Icon = iconCache; + + sprintf(IconKey, "%02X%02X ", ptr->iconRow, ptr->iconCol); + + if (ptr->IconOverlay) + IconKey[4] = ptr->IconOverlay; + else + IconKey[4] = '@'; + + while (Icon) + { + if (strcmp(Icon->key, IconKey) == 0) // Have it + break; + + Icon = Icon->Next; + } + + if (Icon == NULL) + createIcon(IconKey, ptr->iconRow, ptr->iconCol, ptr->IconOverlay); + + popup[0] = 0; + + if (ptr->Approx) + { + sprintf(Msg, "Approximate Position From Locator
"); + strcat(popup, Msg); + } + ptr1 = doHTMLTransparency(ptr->Path); + sprintf(Msg, "%s
", ptr1); + strcat(popup, Msg); + free(ptr1); + ptr1 = doHTMLTransparency(ptr->LastPacket); + sprintf(Msg, "%s
", ptr1); + strcat(popup, Msg); + free(ptr1); + ptr1 = doHTMLTransparency(ptr->Status); + sprintf(Msg, "%s
", ptr1); + strcat(popup, Msg); + free(ptr1); + if (LocalTime) + TM = localtime(&ptr->TimeLastUpdated); + else + TM = gmtime(&ptr->TimeLastUpdated); + + sprintf(Msg, "Last Heard: %.2d:%.2d:%.2d on Port %d
", + TM->tm_hour, TM->tm_min, TM->tm_sec, ptr->LastPort); + + strcat(popup, Msg); + + sprintf(Msg, "Distance %6.1f Bearing %3.0f Course %1.0f° Speed %3.1f
", + myDistance(ptr->Lat, ptr->Lon, KM), + myBearing(ptr->Lat, ptr->Lon), ptr->Course, ptr->Speed); + strcat(popup, Msg); + + if (ptr->LastWXPacket[0]) + { + //display wx info + + struct APRSConnectionInfo temp; + + memset(&temp, 0, sizeof(temp)); + + DecodeWXReport(&temp, ptr->LastWXPacket); + + sprintf(Msg, "Wind Speed %d MPH
", temp.WindSpeed); + strcat(popup, Msg); + + sprintf(Msg, "Wind Gust %d MPH
", temp.WindGust); + strcat(popup, Msg); + + sprintf(Msg, "Wind Direction %d\xC2\xB0
", temp.WindDirn); + strcat(popup, Msg); + + sprintf(Msg, "Temperature %d\xC2\xB0 F
", temp.Temp); + strcat(popup, Msg); + + sprintf(Msg, "Pressure %05.1f
", temp.Pressure / 10.0); + strcat(popup, Msg); + + sprintf(Msg, "Humidity %d%%
", temp.Humidity); + strcat(popup, Msg); + + sprintf(Msg, "Rainfall Last Hour/Last 24H/Today %5.2f, %5.2f, %5.2f (inches)", + temp.RainLastHour / 100.0, temp.RainLastDay / 100.0, temp.RainToday / 100.0); + + ptr1 = doHTMLTransparency(Msg); + sprintf(Msg, "%s
", ptr1); + strcat(popup, Msg); + free(ptr1); + } + + Len += sprintf(&Buffer[Len],"A,%.4f,%.4f,%s,%s,%s,%d\r\n|", + ptr->Lat, ptr->Lon, ptr->Callsign, popup, IconKey, + NOW - ptr->TimeLastUpdated); + + if (ptr->TrackTime[0] && ptr->TrackTime[1]) // Have trackpoints + { + int n = ptr->Trackptr; + int i; + double lastLat = 0; + + // We read from next track point (oldest) for TRACKPOINT records, ignoring zeros + + Len += sprintf(&Buffer[Len],"T,"); + + for (i = 0; i < TRACKPOINTS; i++) + { + if (ptr->TrackTime[n]) + { + Len += sprintf(&Buffer[Len],"%.4f,%.4f,", ptr->LatTrack[n], ptr->LonTrack[n]); + lastLat = ptr->LatTrack[n]; + } + + n++; + if (n == TRACKPOINTS) + n = 0; + } + if (lastLat != ptr->Lat) + Len += sprintf(&Buffer[Len],"%.4f,%.4f,\r\n|", ptr->Lat, ptr->Lon); //Add current position to end of track + else + Len += sprintf(&Buffer[Len],"\r\n|"); + } + } + } + + ptr = ptr->Next; + } + return Len; +} + + + + + /* The png_jmpbuf() macro, used in error handling, became available in + * libpng version 1.0.6. If you want to be able to run your code with older + * versions of libpng, you must define the macro yourself (but only if it + * is not already defined by libpng!). + */ +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) ((png_ptr)->png_jmpbuf) +#endif +/* Check to see if a file is a PNG file using png_sig_cmp(). png_sig_cmp() + * returns zero if the image is a PNG and nonzero if it isn't a PNG. + * + * The function check_if_png() shown here, but not used, returns nonzero (true) + * if the file can be opened and is a PNG, 0 (false) otherwise. + * + * If this call is successful, and you are going to keep the file open, + * you should call png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); once + * you have created the png_ptr, so that libpng knows your application + * has read that many bytes from the start of the file. Make sure you + * don't call png_set_sig_bytes() with more than 8 bytes read or give it + * an incorrect number of bytes read, or you will either have read too + * many bytes (your fault), or you are telling libpng to read the wrong + * number of magic bytes (also your fault). + * + * Many applications already read the first 2 or 4 bytes from the start + * of the image to determine the file type, so it would be easiest just + * to pass the bytes to png_sig_cmp() or even skip that if you know + * you have a PNG file, and call png_set_sig_bytes(). + */ + +unsigned char * user_io_ptr = 0; + +void __cdecl user_read_fn(png_struct * png, png_bytep Buffer, png_size_t Len) +{ + unsigned char ** ptr = png->io_ptr; + unsigned char * ptr1; + + ptr1 = ptr[0]; + + memcpy(Buffer, ptr1, Len); + ptr[0]+= Len; +} + + +// This is based on example https://www1.udel.edu/CIS/software/dist/libpng-1.2.8/example.c + +// This decodes a png encoded image from memory + +int read_png(unsigned char *bytes) +{ + png_structp png_ptr; + unsigned int sig_read = 0; + + /* Create and initialize the png_struct with the desired error handler + * functions. If you want to use the default stderr and longjump method, + * you can supply NULL for the last three parameters. We also supply the + * the compiler header file version, so that we know if the application + * was compiled with a compatible version of the library. REQUIRED + */ + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (png_ptr == NULL) + { + return (0); + } + /* Allocate/initialize the memory for image information. REQUIRED. */ + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + return (0); + } + /* Set error handling if you are using the setjmp/longjmp method (this is + * the normal method of doing things with libpng). REQUIRED unless you + * set up your own error handlers in the png_create_read_struct() earlier. + */ + + user_io_ptr = (unsigned char *)&IconData; + + png_set_read_fn(png_ptr, (void *)&user_io_ptr,(png_rw_ptr)user_read_fn); + + png_set_sig_bytes(png_ptr, sig_read); + + png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, NULL); + + // Data is in info->row_pointers. Can we use it from there ?? + +// printf("%d %d %d\n", info_ptr->width, info_ptr->height, info_ptr->valid); + + return TRUE; +} + +void Myabort() +{} + +// This is based on pngfile.c + +//------------------------------------- +// PNGFILE.C -- Image File Functions +//------------------------------------- + +// Copyright 2000, Willem van Schaik. For conditions of distribution and +// use, see the copyright/license/disclaimer notice in png.h + +// Encodes pDiData to png format in memory + + + +void my_png_write_data(struct png_struct_def * png_ptr, png_bytep data, png_size_t length) +{ + struct iconCacheEntry * Icon = png_ptr->io_ptr; + + if (Icon->pngimagelen + (int)length > Icon->pngmalloclen) + { + Icon->pngmalloclen += length; + Icon->pngimage = realloc(Icon->pngimage, Icon->pngmalloclen); + } + + memcpy(&Icon->pngimage[Icon->pngimagelen], data, length); + Icon->pngimagelen += length; +} + + // io_ptr = (FILE *)CVT_PTR((png_ptr->io_ptr)); + // Area png_uint_32 check; + + + +static void png_flush(png_structp png_ptr) +{ +} + +unsigned char * PngEncode (png_byte *pDiData, int iWidth, int iHeight, struct iconCacheEntry * Icon) +{ + const int ciBitDepth = 8; + const int ciChannels = 3; + png_structp png_ptr; + png_infop info_ptr = NULL; + png_uint_32 ulRowBytes; + static png_byte **ppbRowPointers = NULL; + int i; + + // Set up image array and pointer. First allocate a buffer as big as the original + // in the unlikely event of it being too small write_data will realloc it + + Icon->pngmalloclen = iWidth * iHeight * 3; + Icon->pngimage = malloc(Icon->pngmalloclen); + Icon->pngimagelen = 0; + + // prepare the standard PNG structures + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!png_ptr) + { + return FALSE; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + return FALSE; + } + + { + // initialize the png structure + + png_set_write_fn(png_ptr, Icon, my_png_write_data, png_flush); + + png_set_IHDR(png_ptr, info_ptr, iWidth, iHeight, ciBitDepth, + PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + + // write the file header information + + png_write_info(png_ptr, info_ptr); + + // row_bytes is the width x number of channels + + ulRowBytes = iWidth * ciChannels; + + // we can allocate memory for an array of row-pointers + + if ((ppbRowPointers = (png_bytepp) malloc(iHeight * sizeof(png_bytep))) == NULL) + Debugprintf( "Visualpng: Out of memory"); + + // set the individual row-pointers to point at the correct offsets + + for (i = 0; i < iHeight; i++) + ppbRowPointers[i] = pDiData + i * (((ulRowBytes + 3) >> 2) << 2); + + // write out the entire image data in one call + + png_write_image (png_ptr, ppbRowPointers); + + // write the additional chunks to the PNG file (not really needed) + + png_write_end(png_ptr, info_ptr); + + // and we're done + + free (ppbRowPointers); + ppbRowPointers = NULL; + + // clean up after the write, and free any memory allocated + + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + + // yepp, done + } + + return Icon->pngimage; +} + +void SaveAPRSMessage(struct APRSMESSAGE * ptr) +{ + // Save messages in case of a restart + + char FN[250]; + FILE *file; + + // Set up filename + + if (BPQDirectory[0] == 0) + { + strcpy(FN,"APRSMsgs.dat"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"APRSMsgs.dat"); + } + + if ((file = fopen(FN, "a")) == NULL) + return ; + + fprintf(file, "%d %s,%s,%s,%s,%s\n", time(NULL), ptr->FromCall, ptr->ToCall, ptr->Seq, ptr->Time, ptr->Text); + + fclose(file); +} + +void ClearSavedMessages() +{ + char FN[250]; + FILE *file; + + // Set up filename + + if (BPQDirectory[0] == 0) + { + strcpy(FN,"APRSMsgs.dat"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"APRSMsgs.dat"); + } + + if ((file = fopen(FN, "w")) == NULL) + return ; + + fclose(file); +} + +void GetSavedAPRSMessages() +{ + // Get Saved messages + + // 1668768157 SERVER ,GM8BPQ-2 ,D7Yx,10:42,filter m/200 active + + char FN[250]; + FILE *file; + struct APRSMESSAGE * Message; + struct APRSMESSAGE * ptr; + char Line[512]; + char * Stamp = 0; + char * From = 0; + char * To = 0; + char * Seq = 0; + char * Time = 0; + char * Text = 0; + + // Set up filename + + if (BPQDirectory[0] == 0) + { + strcpy(FN,"APRSMsgs.dat"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"APRSMsgs.dat"); + } + + if ((file = fopen(FN, "r")) == NULL) + return ; + + while (fgets(Line, sizeof(Line), file)) + { + Stamp = Line; + From = strlop(Stamp, ' '); + To = strlop(From, ','); + Seq = strlop(To, ','); + Time = strlop(Seq, ','); + Text = strlop(Time, ','); + + if (Stamp && From && To && Seq && Time && Text) + { + Message = APRSGetMessageBuffer(); + + if (Message == NULL) + break; + + memset(Message, 0, sizeof(struct APRSMESSAGE)); + + strcpy(Message->FromCall, From); + strcpy(Message->ToCall, To); + strcpy(Message->Seq, Seq); + strcpy(Message->Time, Time); + strcpy(Message->Text, Text); + + ptr = SMEM->Messages; + + if (ptr == NULL) + { + SMEM->Messages = Message; + } + else + { + while(ptr->Next) + { + ptr = ptr->Next; + } + ptr->Next = Message; + } + + } + } + fclose(file); +} diff --git a/.svn/pristine/34/34d906ee596d34b802885c25b2400c9fa92b1fa6.svn-base b/.svn/pristine/34/34d906ee596d34b802885c25b2400c9fa92b1fa6.svn-base new file mode 100644 index 0000000..f1c8b3e --- /dev/null +++ b/.svn/pristine/34/34d906ee596d34b802885c25b2400c9fa92b1fa6.svn-base @@ -0,0 +1,563 @@ + +/* pngwtran.c - transforms the data in a row for PNG writers + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Transform the data according to the user's wishes. The order of + * transformations is significant. + */ +void /* PRIVATE */ +png_do_write_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_write_transformations\n"); + + if (png_ptr == NULL) + return; + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + if(png_ptr->write_user_transform_fn != NULL) + (*(png_ptr->write_user_transform_fn)) /* user write transform function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#endif +#if defined(PNG_WRITE_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->flags); +#endif +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->bit_depth); +#endif +#if defined(PNG_WRITE_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +} + +#if defined(PNG_WRITE_PACK_SUPPORTED) +/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The + * row_info bit depth should be 8 (one pixel per byte). The channels + * should be 1 (this only happens on grayscale and paletted images). + */ +void /* PRIVATE */ +png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) +{ + png_debug(1, "in png_do_pack\n"); + if (row_info->bit_depth == 8 && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->channels == 1) + { + switch ((int)bit_depth) + { + case 1: + { + png_bytep sp, dp; + int mask, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + mask = 0x80; + v = 0; + + for (i = 0; i < row_width; i++) + { + if (*sp != 0) + v |= mask; + sp++; + if (mask > 1) + mask >>= 1; + else + { + mask = 0x80; + *dp = (png_byte)v; + dp++; + v = 0; + } + } + if (mask != 0x80) + *dp = (png_byte)v; + break; + } + case 2: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 6; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x03); + v |= (value << shift); + if (shift == 0) + { + shift = 6; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 2; + sp++; + } + if (shift != 6) + *dp = (png_byte)v; + break; + } + case 4: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 4; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x0f); + v |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 4; + + sp++; + } + if (shift != 4) + *dp = (png_byte)v; + break; + } + } + row_info->bit_depth = (png_byte)bit_depth; + row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Shift pixel values to take advantage of whole range. Pass the + * true number of bits in bit_depth. The row should be packed + * according to row_info->bit_depth. Thus, if you had a row of + * bit depth 4, but the pixels only had values from 0 to 7, you + * would pass 3 as bit_depth, and this routine would translate the + * data to 0 to 15. + */ +void /* PRIVATE */ +png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) +{ + png_debug(1, "in png_do_shift\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && +#else + if ( +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift_start[4], shift_dec[4]; + int channels = 0; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift_start[channels] = row_info->bit_depth - bit_depth->red; + shift_dec[channels] = bit_depth->red; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->green; + shift_dec[channels] = bit_depth->green; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->blue; + shift_dec[channels] = bit_depth->blue; + channels++; + } + else + { + shift_start[channels] = row_info->bit_depth - bit_depth->gray; + shift_dec[channels] = bit_depth->gray; + channels++; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift_start[channels] = row_info->bit_depth - bit_depth->alpha; + shift_dec[channels] = bit_depth->alpha; + channels++; + } + + /* with low row depths, could only be grayscale, so one channel */ + if (row_info->bit_depth < 8) + { + png_bytep bp = row; + png_uint_32 i; + png_byte mask; + png_uint_32 row_bytes = row_info->rowbytes; + + if (bit_depth->gray == 1 && row_info->bit_depth == 2) + mask = 0x55; + else if (row_info->bit_depth == 4 && bit_depth->gray == 3) + mask = 0x11; + else + mask = 0xff; + + for (i = 0; i < row_bytes; i++, bp++) + { + png_uint_16 v; + int j; + + v = *bp; + *bp = 0; + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & mask); + } + } + } + else if (row_info->bit_depth == 8) + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (i = 0; i < istop; i++, bp++) + { + + png_uint_16 v; + int j; + int c = (int)(i%channels); + + v = *bp; + *bp = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & 0xff); + } + } + } + else + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (bp = row, i = 0; i < istop; i++) + { + int c = (int)(i%channels); + png_uint_16 value, v; + int j; + + v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1)); + value = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + value |= (png_uint_16)((v << j) & (png_uint_16)0xffff); + else + value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff); + } + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + } + } +} +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_swap_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from ARGB to RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AARRGGBB to RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from AG to GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AAGG to GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + } +} +#endif + +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_invert_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + } +} +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +/* undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_write_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_intrapixel\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((*rp - *(rp+1))&0xff); + *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); + png_uint_32 red = (png_uint_32)((s0-s1) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2-s1) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/.svn/pristine/36/3681daad04d8b99a143f1b8c9d89c80a40119ff6.svn-base b/.svn/pristine/36/3681daad04d8b99a143f1b8c9d89c80a40119ff6.svn-base new file mode 100644 index 0000000..7a968b4 --- /dev/null +++ b/.svn/pristine/36/3681daad04d8b99a143f1b8c9d89c80a40119ff6.svn-base @@ -0,0 +1,5871 @@ +/* +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 +*/ + +// +// C replacement for cmd.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE +#pragma data_seg("_BPQDATA") + +//#include "windows.h" +//#include "winerror.h" + + +#include "time.h" +#include "stdio.h" +#include +//#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); + + +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; + + +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 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; + + + +struct CMDX COMMANDS[]; + +int CMDXLEN = sizeof (struct CMDX); + +VOID SENDNODESMSG(); +VOID KISSCMD(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 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); + + + +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 SENDNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + SENDNODESMSG(); + + 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 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; + + 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) +{ + 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)); +} + +VOID CMDL00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + // PROCESS 'LINKS' MESSAGE + + struct _LINKTABLE * LINK = LINKS; + int n = MAXLINKS; + int len; + char Normcall[11] = ""; + + Bufferptr = Cmdprintf(Session, Bufferptr, "Links\r"); + + while (n--) + { + if (LINK->LINKCALL[0]) + { + len = ConvFromAX25(LINK->LINKCALL, Normcall); + + 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\r", + LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE); + else + Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=%d\r", + LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE, 2 - LINK->VER1FLAG); + } + 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/1000.0; + double nsrtt = Routes->NeighbourSRTT/1000.0; + + Bufferptr = Cmdprintf(Session, Bufferptr, " %4.2fs %4.2fs", srtt, nsrtt); + } + + 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) +{ + TRANSPORTENTRY * NewSess; + + NewSess = SetupNewSession(Session, Bufferptr); + + if (NewSess == NULL) + return; // Tables Full + + NewSess->L4CIRCUITTYPE = SESSION + DOWNLINK; + + NewSess->L4TARGET.DEST = Dest; + NewSess->L4STATE = 2; // CONNECTING + + NewSess->SPYFLAG = Spy; + + ReleaseBuffer((UINT *)REPLYBUFFER); + + SENDL4CONNECT(NewSess); + + L4CONNECTSOUT++; + + return; +} + +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; + + +#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; + } + + 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 + + // 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); + return; + } + Dest++; + } + } + + Dest = DESTS; + n = MAXDESTS; + + while (n--) + { + if (CompareCalls(Dest->DEST_CALL, axcalls)) + { + DoNetromConnect(Session, Bufferptr, Dest, Spy); + 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"); + + // Send Port List + + 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 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->ROUTE[0]; + + Active = Dest->DEST_ROUTE; + + for (i = 1; i < 4; i++) + { + Neighbour = Route->ROUT_NEIGHBOUR; + + if (Neighbour) + { + double srtt = Route->SRTT/1000.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 && Dest->NRROUTE[n].ROUT_NEIGHBOUR->INP3Node == 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->ROUTE[n].ROUT_NEIGHBOUR != 0) + { + srtt = Dest->ROUTE[n].SRTT/1000.0; + + len=ConvFromAX25(Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall); + Portcall[len]=0; + + len=sprintf(&line[cursor],"%s %d %d %4.2fs ", + Portcall, + Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT, + Dest->ROUTE[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; countNRROUTE[0].ROUT_NEIGHBOUR == 0 && Dest->ROUTE[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->ROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + || (Dest->ROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)) + || (Dest->ROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[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; + + + // 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') // 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, + "SENDNODES ",8, &SENDNODES,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), + "VALIDCALLS ",5,VALNODES,0, + "WL2KSYSOP ",5,WL2KSYSOP,0, + "STOPPORT ",4,STOPPORT,0, + "STARTPORT ",5,STARTPORT,0, + "STOPCMS ",7,STOPCMS,0, + "STARTCMS ",8,STARTCMS,0, + + "FINDBUFFS ",4,FINDBUFFS,0, + "KISS ",4,KISSCMD,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, + "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 + "*** 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, + "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, + "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; + + 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++; + + } + 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; +} + + + +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; + + if (KISS->FIRSTPORT != KISS) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Send Command + + KissLen = KissEncode(KissString, ENCBUFF, KissLen); + + PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; // ALL FRAMES GO ON SAME Q + + 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); + + // 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; +} + +VOID QTSMCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + int port; + struct PORTCONTROL * PORT; + struct KISSINFO * KISS; + + CmdTail = CmdTail + (OrigCmdBuffer - COMMANDBUFFER); // Replace with original case version + + port = atoi(CmdTail); + + 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; + + 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; +} + + + + + + + + + + + + + diff --git a/.svn/pristine/37/372372d84118da08690f9f31593baf88e3087406.svn-base b/.svn/pristine/37/372372d84118da08690f9f31593baf88e3087406.svn-base new file mode 100644 index 0000000..c5212b7 --- /dev/null +++ b/.svn/pristine/37/372372d84118da08690f9f31593baf88e3087406.svn-base @@ -0,0 +1,455 @@ +// Mail and Chat Server for BPQ32 Packet Switch +// +// Monitor Window(s) Module + +#include "BPQChat.h" + +static char ClassName[]="BPQMONWINDOW"; + + +static WNDPROC wpOrigInputProc; +static WNDPROC wpOrigOutputProc; + +HWND hMonitor; + +static HWND hwndInput; +static HWND hwndOutput; + +static HMENU hMenu; // handle of menu + + +#define InputBoxHeight 25 +RECT MonitorRect; +RECT OutputRect; + +int Height, Width, LastY; + +static char kbbuf[160]; +static int kbptr=0; + +static char * readbuff; +static int readbufflen; + +static BOOL StripLF = TRUE; +static BOOL MonBBS = TRUE; +static BOOL MonCHAT = TRUE; +static BOOL MonTCP = TRUE; + +BOOL LogBBS = TRUE; +BOOL LogCHAT = TRUE; +BOOL LogTCP = TRUE; + +static int PartLinePtr=0; +static int PartLineIndex=0; // Listbox index of (last) incomplete line + + +static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +static LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY MonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static void MoveWindows(); + +#define BGCOLOUR RGB(236,233,216) + +extern char MonitorSize[32]; + +BOOL CreateMonitor() +{ + WNDCLASS wc; + HBRUSH bgBrush; + char Text[80]; + + if (hMonitor) + { + ShowWindow(hMonitor, SW_SHOWNORMAL); + SetForegroundWindow(hMonitor); + return FALSE; // Alreaqy open + } + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = MonWndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + + hMonitor=CreateDialog(hInst,ClassName,0,NULL); + + if (!hMonitor) + return (FALSE); + + wsprintf(Text, "Chat %s Monitor", Session); + SetWindowText(hMonitor, Text); + + readbuff = zalloc(1000); + readbufflen = 1000; + + hMenu=GetMenu(hMonitor); + + CheckMenuItem(hMenu,MONBBS, MonBBS ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,MONCHAT, MonCHAT ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,MONTCP, MonTCP ? MF_CHECKED : MF_UNCHECKED); + + DrawMenuBar(hWnd); + + // Retrieve the handlse to the edit controls. + + hwndOutput = GetDlgItem(hMonitor, 121); + + // Set our own WndProcs for the controls. + + wpOrigOutputProc = (WNDPROC)SetWindowLong(hwndOutput, GWL_WNDPROC, (LONG)OutputProc); + + if (cfgMinToTray) + { + AddTrayMenuItem(hMonitor, Text); + } + + ShowWindow(hMonitor, SW_SHOWNORMAL); + + if (MonitorRect.right < 100 || MonitorRect.bottom < 100) + { + GetWindowRect(hMonitor, &MonitorRect); + } + + MoveWindow(hMonitor,MonitorRect.left,MonitorRect.top, MonitorRect.right-MonitorRect.left, MonitorRect.bottom-MonitorRect.top, TRUE); + + MoveWindows(); + + return TRUE; + +} + + +static void MoveWindows() +{ + RECT rcMain, rcClient; + int ClientHeight, ClientWidth; + + GetWindowRect(hMonitor, &rcMain); + GetClientRect(hMonitor, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + +// MoveWindow(hwndMon,2, 0, ClientWidth-4, SplitPos, TRUE); + MoveWindow(hwndOutput,2, 2, ClientWidth-4, ClientHeight-4, TRUE); +// MoveWindow(hwndSplit,0, SplitPos, ClientWidth, SplitBarHeight, TRUE); +} + + +static LRESULT CALLBACK MonWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + + switch (message) { + + case WM_ACTIVATE: + + SetFocus(hwndInput); + break; + + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case MONBBS: + + ToggleParam(hMenu, hWnd, &MonBBS, MONBBS); + break; + + case MONCHAT: + + ToggleParam(hMenu, hWnd, &MonCHAT, MONCHAT); + break; + + case MONTCP: + + ToggleParam(hMenu, hWnd, &MonTCP, MONTCP); + break; + + + case BPQCLEAROUT: + + SendMessage(hwndOutput,LB_RESETCONTENT, 0, 0); + break; + + case BPQCOPYOUT: + + CopyToClipboard(hwndOutput); + break; + + + + //case BPQHELP: + + // HtmlHelp(hWnd,"BPQTerminal.chm",HH_HELP_FINDER,0); + // break; + + default: + + return 0; + + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case SC_MINIMIZE: + + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Height = lprc->bottom-lprc->top; + Width = lprc->right-lprc->left; + + MoveWindows(); + + return TRUE; + + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &MonitorRect); // For save soutine + + SetWindowLong(hwndInput, GWL_WNDPROC, + (LONG) wpOrigInputProc); + + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + + hMonitor = NULL; + + free(readbuff); + readbufflen = 0; + + break; + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + + +LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + + // Trap mouse messages, so we cant select stuff in output and mon windows, + // otherwise scrolling doesnt work. + + if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_LBUTTONDBLCLK) + return TRUE; + + return CallWindowProc(wpOrigOutputProc, hwnd, uMsg, wParam, lParam); +} + +int WritetoMonitorWindow(char * Msg, int len) +{ + char * ptr1, * ptr2; + int index; + + if (len+PartLinePtr > readbufflen) + { + readbufflen += len+PartLinePtr; + readbuff = realloc(readbuff, readbufflen); + } + + if (PartLinePtr != 0) + SendMessage(hwndOutput,LB_DELETESTRING,PartLineIndex,(LPARAM)(LPCTSTR) 0 ); + + memcpy(&readbuff[PartLinePtr], Msg, len); + + len=len+PartLinePtr; + + ptr1=&readbuff[0]; + readbuff[len]=0; + + do { + ptr2=memchr(ptr1,7,len); + + if (ptr2) + *(ptr2)=32; + + } while (ptr2); + +lineloop: + +// if (PartLinePtr > 300) +// PartLinePtr = 0; + + if (len > 0) + { + // copy text to control a line at a time + + ptr2=memchr(ptr1,13,len); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + PartLinePtr=len; + memmove(readbuff,ptr1,len); + PartLineIndex=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) PartLineIndex, MAKELPARAM(FALSE, 0)); + + return (0); + + } + + *(ptr2++)=0; + + index=SendMessage(hwndOutput,LB_ADDSTRING,0,(LPARAM)(LPCTSTR) ptr1 ); + + // if (LogOutput) WriteMonitorLine(ptr1, ptr2 - ptr1); + + PartLinePtr=0; + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + if ((len > 0) && StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + + if (index > 1200) + + do{ + + index=SendMessage(hwndOutput,LB_DELETESTRING, 0, 0); + + } while (index > 1000); + + SendMessage(hwndOutput,LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); + + goto lineloop; + } + + + return (0); +} + +static int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item) +{ + *Param = !(*Param); + + CheckMenuItem(hMenu,Item, (*Param) ? MF_CHECKED : MF_UNCHECKED); + + return (0); +} + +static void CopyToClipboard(HWND hWnd) +{ + int i,n, len=0; + HGLOBAL hMem; + char * ptr; + // + // Copy List Box to clipboard + // + + n = SendMessage(hWnd, LB_GETCOUNT, 0, 0); + + for (i=0; itm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]); + + LogHandle[Flags] = CreateFile(FN, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + SetFilePointer(LogHandle[Flags], 0, 0, FILE_END); + + return (LogHandle[Flags] != INVALID_HANDLE_VALUE); +} + diff --git a/.svn/pristine/37/37d12f1e26a2bde01b2f65c4d63bf015d20de865.svn-base b/.svn/pristine/37/37d12f1e26a2bde01b2f65c4d63bf015d20de865.svn-base new file mode 100644 index 0000000..c3a55f0 --- /dev/null +++ b/.svn/pristine/37/37d12f1e26a2bde01b2f65c4d63bf015d20de865.svn-base @@ -0,0 +1,3117 @@ +/* +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 +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use WINMOR as a Port Driver +// +// Uses BPQ EXTERNAL interface +// + + +// Version 1.0 January 2009 - Initial Version +// + +// March 22 2010 + +// Send FAULTS to Monitor Window +// Force PROTOCOL = WINMOR/PACTOR (to simplifiy Config) + +// July 2010 +// Support up to 32 BPQ Ports +// Support up to 32 Applications + +// Version 1.2.1.2 August 2010 + +// Save Minimized State +// Handle new "BLOCKED by Busy channel" message from TNC + +// Version 1.2.1.4 August 2010 + +// Add Scan control of BW setting +// Reset TNC if stuck in Disconnecting +// Add option to send reports to WL2K +// Disconnect if appl not available + +// Version 1.2.1.5 August 2010 + +// Updates to WL2K Reporting +// Send Watchdog polls every minute and restart if no response. +// Don't connect if channel is busy (unless specifically overridden) + +// Version 1.2.1.6 September 2010 + +// Add option to kill and restart TNC after each transfer +// Fix PTT operation after Node reconfig + +// Version 1.2.2.1 September 2010 + +// Add option to get config from bpq32.cfg +// Merge with BPQ32.dll + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#include "cheaders.h" + +#ifdef WIN32 +#include +#endif + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#include "bpq32.h" + +#include "tncinfo.h" + + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +static int Socket_Data(int sock, int error, int eventcode); + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +int KillPopups(struct TNCINFO * TNC); +VOID MoveWindows(struct TNCINFO * TNC); +int SendReporttoWL2K(struct TNCINFO * TNC); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +BOOL KillOldTNC(char * Path); +int standardParams(struct TNCINFO * TNC, char * buf); + +static char ClassName[]="WINMORSTATUS"; +static char WindowTitle[] = "WINMOR"; +static int RigControlRow = 165; + +#define WINMOR +#define NARROWMODE 21 +#define WIDEMODE 22 + +#ifndef LINBPQ +#include +#endif + +extern int SemHeldByAPI; + +static RECT Rect; + +static int ProcessLine(char * buf, int Port); + +// RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + +// There seem to be timing issues when calling SendMessage from multiple threads. +// Queue and process in main thread + +UINT * WINMORTraceQ; +UINT * SetWindowTextQ; + +VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len) +{ + int index = 0; + UCHAR * ptr1 = Msg, * ptr2; + UCHAR Line[1000]; + int LineLen, i; + UCHAR Save; + int SaveLen = Len; + if (Len < 0) + return; + + Save = Msg[Len]; + Msg[Len] = 0; + +#ifndef LINBPQ + index=SendMessage(TNC->hMonitor, LB_SETCURSEL, -1, 0); +#endif + +lineloop: + + if (Len > 0) + { + // copy text to control a line at a time + + ptr2 = memchr(ptr1, 13, Len); + + if (ptr2) + { + ptr2++; + LineLen = (int)(ptr2 - ptr1); + Len -= LineLen; + memcpy(Line, ptr1, LineLen); + memcpy(&Line[LineLen - 1], "", 4); + LineLen += 3; + + if ((*ptr2) == 10) + { + memcpy(&Line[LineLen], "", 4); + LineLen += 4; + ptr2++; + Len --; + } + + Line[LineLen] = 0; + + // If line contains any data above 7f, assume binary and dont display + + for (i = 0; i < LineLen; i++) + { + if (Line[i] > 126 || Line[i] < 32) + goto Skip; + } + + // We now also pass to Monitor Window + + if (strlen(Line) < 250) + { + MESSAGE Monframe; + memset(&Monframe, 0, sizeof(Monframe)); + + Monframe.PORT = TNC->Port; + Monframe.LENGTH = 12 + strlen(Line); + Monframe.DEST[0] = 1; // Plain Text Monitor + strcpy(&Monframe.DEST[1], Line); + + time(&Monframe.Timestamp); + BPQTRACE((MESSAGE *)&Monframe, FALSE); + } + +#ifdef LINBPQ +#else + index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Line); +#endif + // Write to Web Buffer + + strcat(TNC->WebBuffer, Line); + strcat(TNC->WebBuffer, "\r\n"); + if (strlen(TNC->WebBuffer) > 4500) + memmove(TNC->WebBuffer, &TNC->WebBuffer[500], 4490); // Make sure null is moved + Skip: + ptr1 = ptr2; + + goto lineloop; + + } + + // Process incomplete line + + for (i = 0; i < Len; i++) + { + if (ptr1[i] > 126 || ptr1[i] < 32) + break; + } + + if (i == Len) + { + if (Len < 250) + { + MESSAGE Monframe; + memset(&Monframe, 0, sizeof(Monframe)); + + Monframe.PORT = TNC->Port; + Monframe.LENGTH = 12 + Len; + Monframe.DEST[0] = 1; // Plain Text Monitor + + memcpy(&Monframe.DEST[1], ptr1, Len); + Monframe.DEST[1 + Len] = 0; + + time(&Monframe.Timestamp); + BPQTRACE((MESSAGE *)&Monframe, FALSE); + } + + +#ifdef LINBPQ +#else + index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) ptr1 ); +#endif + strcat(TNC->WebBuffer, ptr1); + strcat(TNC->WebBuffer, "\r\n"); + if (strlen(TNC->WebBuffer) > 4500) + memmove(TNC->WebBuffer, &TNC->WebBuffer[500], 4490); // Make sure null is moved + } + } + +#ifdef LINBPQ +#else + + if (index > 1200) + do + index=index=SendMessage(TNC->hMonitor, LB_DELETESTRING, 0, 0); + while (index > 1000); + + if (index > -1) + index=SendMessage(TNC->hMonitor, LB_SETCARETINDEX,(WPARAM) index, MAKELPARAM(FALSE, 0)); +#endif + Msg[SaveLen] = Save; + +} + +VOID MySetWindowTextWithSem(HWND hWnd, char * Msg) +{ +#ifndef LINBPQ + + PMSGWITHLEN buffptr; + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len= (UINT)hWnd; + memcpy(&buffptr->Data[0], Msg, strlen(Msg) + 1); + + C_Q_ADD(&SetWindowTextQ, buffptr); + } + +#endif +} + +int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF); + +struct SEM SetWindTextSem = {0, 0, 0, 0}; + +VOID MySetWindowText(HWND hWnd, char * Msg) +{ +#ifndef LINBPQ + + PMSGWITHLEN buffptr; + + GetSemaphore(&SetWindTextSem, 61); + buffptr = zalloc(400); + + if (buffptr) + { + buffptr->Len= (UINT)hWnd; + memcpy(&buffptr->Data[0], Msg, strlen(Msg) + 1); + + C_Q_ADD_NP(&SetWindowTextQ, buffptr); + } + + FreeSemaphore(&SetWindTextSem); +#endif +} + +VOID SetWindowTextSupport() +{ + PMSGWITHLEN Buffer; + + while (SetWindowTextQ) + { + GetSemaphore(&SetWindTextSem, 61); + Buffer = Q_REM_NP(&SetWindowTextQ); + SetWindowText((HWND)Buffer->Len, Buffer->Data); + FreeSemaphore(&SetWindTextSem); + free(Buffer); + } +} + + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len) +{ + // It seems writing from multiple threads can cause problems in Windows + // Queue and process in main thread + +#ifdef LINBPQ + WritetoTraceSupport(TNC, Msg, Len); +} +#else + UINT * buffptr; + BOOL Sem = FALSE; + + if (Len < 0) + return; + + // Get semaphore if it isn't set + + if (InterlockedExchange(&Semaphore.Flag, 1) == 0) + { + Sem = TRUE; + Semaphore.Gets++; + } + + buffptr = GetBuff(); + + if (buffptr) + { + if (Len > 340) + Len = 340; + + buffptr[1] = (UINT)TNC; + buffptr[2] = (UINT)Len; + memcpy(&buffptr[3], Msg, Len + 1); + + C_Q_ADD(&WINMORTraceQ, buffptr); + } + + if (Sem) + FreeSemaphore(&Semaphore); + +} +#endif + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if ((_memicmp(buf, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) + {} // Ignore + else +/* + if (_memicmp(buf, "PATH", 4) == 0) + { + char * Context; + p_cmd = strtok_s(&buf[5], "\n\r", &Context); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + else +*/ + if (_memicmp(buf, "STARTINROBUST", 13) == 0) + TNC->StartInRobust = TRUE; + + else + if (_memicmp(buf, "ROBUST", 6) == 0) + { + if (_memicmp(&buf[7], "TRUE", 4) == 0) + TNC->Robust = TRUE; + + strcat (TNC->InitScript, buf); + } + else if (standardParams(TNC, buf) == FALSE) + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + + + +void WINMORThread(void * portptr); +VOID ProcessDataSocketData(int port); +int ConnecttoWINMOR(int port); +static int ProcessReceivedData(struct TNCINFO * TNC); +int V4ProcessReceivedData(struct TNCINFO * TNC); +VOID ReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static time_t ltime; + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + + + +VOID ChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + +// send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + + datalen = sprintf(TXMsg, "MYC %s\r\n", Call); + send(TNC->TCPSock,TXMsg, datalen, 0); + +// send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); +// TNC->StartSent = TRUE; + + send(TNC->TCPSock, "MYC\r\n", 5, 0); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int i,winerr; + size_t datalen; + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int bytes; + size_t txlen = 0; + char ErrMsg[255]; + size_t Param; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + fd_set readfs; + fd_set writefs; + fd_set errorfs; + struct timeval timeout; + + if (TNC == NULL) + return 0; // Port not defined + + switch (fn) + { + case 1: // poll + + // Check session limit timer + + if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) + { + if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) + { + send(TNC->TCPSock,"DISCONNECT\r\n", 12, 0); + STREAM->Disconnecting = TRUE; + } + } + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + + if (TNC->Busy) // Count down to clear + { + if ((TNC->BusyFlags & CDBusy) == 0) // TNC Has reported not busy + { + TNC->Busy--; + if (TNC->Busy == 0) + SetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } + } + + if (TNC->ConnectCmd && TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send + + send(TNC->TCPSock, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd), 0); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[8], strlen(TNC->ConnectCmd)-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + free(TNC->ConnectCmd); + TNC->ConnectCmd = 0; + + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 39; + memcpy(buffptr->Data,"Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + if (TNC->HeartBeat++ > 600 || (TNC->Streams[0].Connected && TNC->HeartBeat > 50)) // Every Minute unless connected + { + if (TNC->HeartBeat > 600 && TNC->hWnd) + { + char wtext[100]; + sprintf (wtext, "WINMOR Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + MySetWindowText(TNC->hWnd, wtext); + } + + TNC->HeartBeat = 0; + + if (TNC->CONNECTED) + { + // Probe link + + if (TNC->Streams[0].Connecting || TNC->Streams[0].Connected) + send(TNC->TCPSock, "MODE\r\n", 6, 0); + else + { + if (time(NULL) - TNC->WinmorRestartCodecTimer > 900) // 15 mins + { + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); + } + else + send(TNC->TCPSock, "STATE\r\n", 7, 0); + } + } + } + + if (TNC->FECMode) + { + if (TNC->FECIDTimer++ > 6000) // ID every 10 Mins + { + if (!TNC->Busy) + { + TNC->FECIDTimer = 0; + send(TNC->TCPSock, "SENDID 0\r\n", 10, 0); + } + } + if (TNC->FECPending) // Check if FEC Send needed + { + if (!TNC->Busy) + { + TNC->FECPending = 0; + + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + } + } + } + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + send(TNC->TCPSock, "DISCONNECT\r\n", 12, 0); + } + } + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + + if (TNC->PID) + { + KillTNC(TNC); + RestartTNC(TNC); + } + } + } + + if (TNC->TimeSinceLast++ > 800) // Allow 10 secs for Keepalive + { + // Restart TNC + + if (TNC->ProgramPath && TNC->CONNECTED) + { + if (strstr(TNC->ProgramPath, "WINMOR TNC")) + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + SetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + SetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); + + KillTNC(TNC); + RestartTNC(TNC); + + TNC->TimeSinceLast = 0; + } + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + TNC->Streams[0].Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + // Stop Listening, and set MYCALL to user's call + + send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + ChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + + } + + if (TNC->Streams[0].Attached) + CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); + + if (TNC->Streams[0].ReportDISC) + { + TNC->Streams[0].ReportDISC = FALSE; + buff->PORT = 0; + return -1; + } + + + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + ConnecttoWINMOR(port); + TNC->lasttime = ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); + + FD_ZERO(&writefs); + + if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + FD_ZERO(&errorfs); + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + timeout.tv_sec = 0; + timeout.tv_usec = 0; // poll + + if (select((int)TNC->TCPDataSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + ProcessDataSocketData(port); + + if (FD_ISSET(TNC->TCPDataSock, &writefs)) + { + // Write block has cleared. Send rest of packet + + buffptr=Q_REM(&TNC->BPQtoWINMOR_Q); + txlen = buffptr->Len; + memcpy(txbuff,buffptr->Data,txlen); + bytes=send(TNC->TCPSock, (const char FAR *)&txbuff, (int)txlen, 0); + ReleaseBuffer(buffptr); + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { + i=sprintf(ErrMsg, "WINMOR Data Connection lost for BPQ Port %d\r\n", port); + WritetoConsole(ErrMsg); + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + } + } + + // See if any frames for this port + + if (TNC->WINMORtoBPQ_Q != 0) + { + buffptr=Q_REM(&TNC->WINMORtoBPQ_Q); + + datalen = buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA[0], buffptr->Data, datalen); // Data goes to +7, but we have an extra byte + datalen = buffptr->Len; + + datalen += sizeof(void *) + 4; + PutLengthinBuffer(buff, (int)datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + return (0); + + case 2: // send + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 36; + memcpy(buffptr->Data, "No Connection to WINMOR Virtual TNC\r", 36); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + txlen = buffptr->Len; + memcpy(txbuff, buffptr->Data, txlen); + bytes = send(TNC->TCPDataSock, txbuff, (int)txlen, 0); + STREAM->bytesTXed += bytes; + WritetoTrace(TNC, txbuff, (int)txlen); + ReleaseBuffer(buffptr); + } + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + + if (TNC->Streams[0].Connected) + { + STREAM->PacketsSent++; + + if (STREAM->PacketsSent == 3) + { + if (TNC->Robust) + send(TNC->TCPSock, "ROBUST TRUE\r\n", 13, 0); + else + send(TNC->TCPSock, "ROBUST FALSE\r\n", 14, 0); + } + + bytes = send(TNC->TCPDataSock,buff->L2DATA, (int)txlen, 0); + STREAM->bytesTXed += bytes; + WritetoTrace(TNC, &buff->L2DATA[0], (int)txlen); + + } + else + { + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + if (TNC->FECMode) + { + char Buffer[300]; + int len; + + // Send FEC Data + + buff->L2DATA[txlen] = 0; + len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, &buff->L2DATA); + + send(TNC->TCPDataSock, Buffer, len, 0); + + if (TNC->BusyFlags) + { + TNC->FECPending = 1; + } + else + { + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + } + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf(&buffptr->Data[0], "Winmor} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "MAXCONREQ", 9) == 0) + { + if (buff->L2DATA[9] != 13) + { + // Limit connects + + int tries = atoi(&buff->L2DATA[10]); + int len; + + if (tries > 10) tries = 10; + len = sprintf(&buff->L2DATA[0], "MAXCONREQ %d\r\nMAXCONREQ\r\n", tries); + + send(TNC->TCPSock, &buff->L2DATA[0], len, 0); + return 0; + } + } + + if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0) + { + if (buff->L2DATA[16] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "Winmor} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 0; + } + } + + if ((_memicmp(buff->L2DATA, "BW 500", 6) == 0) || (_memicmp(buff->L2DATA, "BW 1600", 7) == 0)) + { + // Generate a local response + + PMSGWITHLEN buffptr = GetBuff(); + + if (_memicmp(buff->L2DATA, "BW 500", 6) == 0) + TNC->WL2KMode = 21; + else + TNC->WL2KMode = 22; + + if (buffptr) + { + buffptr->Len = sprintf(&buffptr->Data[0], "Winmor} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + TNC->WinmorCurrentMode = 0; // So scanner will set next value + } + + if (_memicmp(buff->L2DATA, "CODEC TRUE", 9) == 0) + TNC->StartSent = TRUE; + + if (_memicmp(buff->L2DATA, "ROBUST", 6) == 0) + { + if (_memicmp(&buff->L2DATA[7], "TRUE", 4) == 0) + TNC->Robust = TRUE; + else + TNC->Robust = FALSE; + } + + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + if (_memicmp(buff->L2DATA, "FEC\r", 4) == 0 || _memicmp(buff->L2DATA, "FEC ", 4) == 0) + { + TNC->FECMode = TRUE; + TNC->FECIDTimer = 0; + send(TNC->TCPSock,"FECRCV TRUE\r\nFECRCV\r\n", 21, 0); + + if (_memicmp(buff->L2DATA, "FEC 1600", 8) == 0) + TNC->FEC1600 = TRUE; + else + TNC->FEC1600 = FALSE; + + return 0; + } + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80] = "CONNECT "; + + memcpy(&Connect[8], &buff->L2DATA[2], txlen); + txlen += 6; + Connect[txlen++] = 0x0a; + Connect[txlen] = 0; + + _strupr(Connect); + + ChangeMYC(TNC, TNC->Streams[0].MyCall); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + bytes = send(TNC->TCPSock, Connect, (int)txlen, 0); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &Connect[8], txlen-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else + { + buff->L2DATA[txlen++] = 0x0a; + bytes = send(TNC->TCPSock, &buff->L2DATA[0], (int)txlen, 0); + } + } + if (bytes != txlen) + { + + // WINMOR doesn't seem to recover from a blocked write. For now just reset + + winerr = WSAGetLastError(); + sprintf(ErrMsg, "WINMOR Write Failed for port %d - error code = %d\r\n", port, winerr); + WritetoConsole(ErrMsg); + closesocket(TNC->TCPSock); + TNC->CONNECTED = FALSE; + + return (0); + } + + return (0); + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + if (TNC->Streams[0].Attached == 0) + return TNC->CONNECTED << 8 | 1; + + return (TNC->CONNECTED << 8 | TNC->Streams[0].Disconnecting << 15); // OK + + break; + + case 4: // reinit + + return (0); + + case 5: // Close + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + Sleep(100); + shutdown(TNC->TCPDataSock, SD_BOTH); + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPDataSock); + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return (0); + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (shouldn't happen) + { + Debugprintf("Scan Check Permission called on FLDIGI"); + return 1; // OK to change + } + + if (!TNC->TCPSock) + return 0; // No connection so no interlock + + if (Param == 1) // Request Permission + { + if (TNC->ConnectPending) + return TRUE; // Not OK to Change + + if (TNC->CONNECTED) + { + TNC->GavePermission = TRUE; + send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + } + return FALSE; + } + + if (Param == 3) // Release Permission + { + if (TNC->CONNECTED) + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); + } + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + + if (Scan->Bandwidth == 'W') // Set Wide Mode + { + if (TNC->WinmorCurrentMode != 1600) + { + if (TNC->WinmorCurrentMode == 0) + if (TNC->CONNECTED) + send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); + + if (TNC->CONNECTED) + send(TNC->TCPSock, "BW 1600\r\n", 9, 0); + TNC->WinmorCurrentMode = 1600; + } + TNC->WL2KMode = 22; + return 0; + } + + + if (Scan->Bandwidth == 'N') // Set Narrow Mode + { + if (TNC->WinmorCurrentMode != 500) + { + if (TNC->WinmorCurrentMode == 0) + if (TNC->CONNECTED) + send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); + + TNC->WinmorCurrentMode = 500; + if (TNC->CONNECTED) + send(TNC->TCPSock, "BW 500\r\n", 8, 0); + } + TNC->WL2KMode = 21; + return 0; + } + + if (Scan->Bandwidth == 'X') // Dont Allow Connects + { + if (TNC->WinmorCurrentMode != 0) + { + if (TNC->CONNECTED) + send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + TNC->WinmorCurrentMode = 0; + } + + TNC->WL2KMode = 0; + return 0; + } + + return 0; + } + return 0; +} + +VOID ReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[256]; + char wtext[100]; + + ChangeMYC(TNC, TNC->NodeCall); + + if (TNC->CONNECTED) + send(TNC->TCPSock, "LISTEN TRUE\r\nMAXCONREQ 4\r\n", 26, 0); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (TNC->hWnd) + { + sprintf (wtext, "WINMOR Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + MySetWindowText(TNC->hWnd, wtext); + } + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC) +{ + // Disable other TNCs in same Interlock Group + + struct TNCINFO * TNC; + int i; + int rxInterlock = ThisTNC->RXRadio; + int txInterlock = ThisTNC->TXRadio; + + if (rxInterlock == 0 || txInterlock == 0) + return; + + for (i = 1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC == ThisTNC) + continue; + + if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group + if (TNC->SuspendPortProc) + TNC->SuspendPortProc(TNC, ThisTNC); + } +} + +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC) +{ + // Enable other TNCs in same Interlock Group + + struct TNCINFO * TNC; + int i; + int rxInterlock = ThisTNC->RXRadio; + int txInterlock = ThisTNC->TXRadio; + + if (rxInterlock == 0 && txInterlock == 0) + return; + + for (i=1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC == ThisTNC) + continue; + + if (rxInterlock == TNC->RXRadio || txInterlock == TNC->TXRadio) // Same Group + if (TNC->ReleasePortProc) + TNC->ReleasePortProc(TNC); + } +} + +VOID WinmorSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ + if (TNC->CONNECTED) + send(TNC->TCPSock, "CODEC FALSE\r\n", 14, 0); + + if (TNC->Busy) + { + TNC->Busy = FALSE; // Can't clear detector if CODEC off. + MySetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } +} + +VOID WinmorReleasePort(struct TNCINFO * TNC) +{ + if (TNC->CONNECTED) + send(TNC->TCPSock, "CODEC TRUE\r\n", 13, 0); +} + +extern char WebProcTemplate[]; +extern char sliderBit[]; + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, WebProcTemplate, TNC->Port, TNC->Port, "WINMOR Status", "WINMOR Status"); + + if (TNC->TXFreq) + Len += sprintf(&Buff[Len], sliderBit, TNC->TXOffset, TNC->TXOffset); + + + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +void * WinmorExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + APPLCALLS * APPL; + struct TNCINFO * TNC; + char Aux[100] = "MYAUX "; + char Appl[11]; + char * TempScript; + + // + // Will be called once for each WINMOR port + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->PortRecord = PortEntry; + + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_WINMOR; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->MAXHOSTMODESESSIONS = 1; + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = WinmorSuspendPort; + TNC->ReleasePortProc = WinmorReleasePort; + + TNC->ModemCentre = 1500; // WINMOR is always 1500 Offset + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = malloc(1000); + + strcpy(TempScript, "DebugLog True\r\n"); + strcat(TempScript, "CWID False\r\n"); + strcat(TempScript, "BW 1600\r\n"); + strcat(TempScript, "ROBUST False\r\n"); + strcat(TempScript, "MODE AUTO\r\n"); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + TNC->WL2KMode = 22; // in case not scanning + + // Set MYCALL + + strcat(TNC->InitScript,"FECRCV True\r\n"); + strcat(TNC->InitScript,"AUTOBREAK True\r\n"); + + sprintf(Msg, "MYC %s\r\nCODEC TRUE\r\nLISTEN TRUE\r\nMYC\r\n", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + strcat(TNC->InitScript,"PROCESSID\r\n"); + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + char * ptr; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + { + *ptr++ = ','; + *ptr = 0; + } + + strcat(Aux, Appl); + } + } + strcat(TNC->InitScript, Aux); + strcat(TNC->InitScript,"\r\nMYAUX\r\n"); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->HostName=malloc(10); + + if (TNC->HostName != NULL) + strcpy(TNC->HostName,"127.0.0.1"); + + } + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,40,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill Winmor TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart Winmor TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + MoveWindows(TNC); +#endif + Consoleprintf("WINMOR Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port)); + + ConnecttoWINMOR(port); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + +int ConnecttoWINMOR(int port) +{ + _beginthread(WINMORThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID WINMORThread(void * portptr) +{ + // Opens both sockets and looks for data on control socket. Data socket is polled from BG, + // but we need fast response to control messages for PTT porcessing + + int port = (int)(size_t)portptr; + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + if (TNC->HostName == NULL) + return; + + TNC->CONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +#ifdef WIN32 + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + if (TNC->PID == 0) + { + TNC->CONNECTING = FALSE; + return; // Not listening so no point trying to connect + } + } +#endif + +// // If we started the TNC make sure it is still running. + +// if (!IsProcess(TNC->PID)) +// { +// RestartTNC(TNC); +// Sleep(3000); +// } + + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + } + + if (TNC->TCPSock) + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + if (TNC->TCPDataSock) + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for WINMOR socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + setsockopt (TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(TNC->TCPSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for WINMOR socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for WINMOR socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + TNC->CONNECTING = FALSE; + TNC->TCPSock = 0; + + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; // so V4 display will be updated + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + setsockopt (TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for WINMOR socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + if (bind(TNC->TCPDataSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for WINMOR Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + ioctlsocket (TNC->TCPDataSock,FIONBIO,¶m); // Set nonblocking + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + // Send INIT script + + send(TNC->TCPSock, TNC->InitScript , (int)strlen(TNC->InitScript), 0); + TNC->Alerted = TRUE; + + if (TNC->Hardware == H_V4) + sprintf(TNC->WEB_COMMSSTATE, "Connected to V4 TNC"); + else + sprintf(TNC->WEB_COMMSSTATE, "Connected to WINMOR TNC"); + + GetSemaphore(&Semaphore, 40); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + FreeSemaphore(&Semaphore); + + } + else + { + sprintf(Msg, "Connect Failed for WINMOR Data socket Port %d - error code = %d\r\n", port, WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + TNC->HeartBeat = 0; + + while (TRUE) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TNC->TCPSock,&readfs); + FD_SET(TNC->TCPSock,&errorfs); + + timeout.tv_sec = 90; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TNC->TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + printf("Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + if (TNC->Hardware == H_V4) + V4ProcessReceivedData(TNC); + else + ProcessReceivedData(TNC); + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { +Lost: + sprintf(Msg, "WINMOR Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + GetSemaphore(&Semaphore, 40); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + FreeSemaphore(&Semaphore); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + return; + } + } + else + { + // 90 secs without data. Shouldn't happen + + sprintf(Msg, "WINMOR Connection Timeout Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + GetSemaphore(&Semaphore, 40); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + FreeSemaphore(&Semaphore); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + + Sleep(100); + shutdown(TNC->TCPDataSock, SD_BOTH); + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPDataSock); + closesocket(TNC->TCPSock); + TNC->TCPDataSock = 0; + TNC->TCPSock= 0; + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + return; + } + } +} + +#ifdef WIN32 + +BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[100]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowText(hwnd,wtext,99); + + if (memcmp(wtext,"WINMOR Sound Card TNC", 21) == 0) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + if (TNC->PID == ProcessId) + { + // Our Process + + TNC->hWnd = hwnd; // save so we can reset title when sessicn closes + sprintf (wtext, "WINMOR Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + SetWindowText(hwnd, wtext); + return FALSE; + } + } + + return (TRUE); +} +#endif + +VOID ProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + // Response on WINMOR control channel. Could be a reply to a command, or + // an Async Response + + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + Buffer[MsgLen - 2] = 0; + + if (_memicmp(Buffer, "FAULT failure to Restart Sound card", 20) == 0) + { + Debugprintf(Buffer); + + // Force a restart + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); + } + else + { + TNC->TimeSinceLast = 0; + } + + + if (_memicmp(Buffer, "STATE ", 6) == 0) + { + Debugprintf(Buffer); + + if (_memicmp(&Buffer[6], "OFFLINE", 7) == 0) + { + // Force a restart + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); + } + return; + } + + Buffer[MsgLen - 2] = 0; // Remove CRLF + + if (_memicmp(Buffer, "PTT T", 5) == 0) + { + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + return; + } + if (_memicmp(Buffer, "PTT F", 5) == 0) + { + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + return; + } + + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + SetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->BusyHold) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + if (_memicmp(Buffer, "TARGET", 6) == 0) + { + Debugprintf(Buffer); + GetSemaphore(&Semaphore, 50); + WritetoTrace(TNC, Buffer, MsgLen - 2); + FreeSemaphore(&Semaphore); + memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "OFFSET", 6) == 0) + { +// WritetoTrace(TNC, Buffer, MsgLen - 2); +// memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "CONNECTED", 9) == 0) + { + char Call[11]; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + + Debugprintf(Buffer); + + GetSemaphore(&Semaphore, 50); + WritetoTrace(TNC, Buffer, MsgLen - 2); + FreeSemaphore(&Semaphore); + + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->PacketsSent = 0; + + if (TNC->StartInRobust) + send(TNC->TCPSock, "ROBUST TRUE\r\n", 13, 0); + + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + TRANSPORTENTRY * SESS; + + // Incomming Connect + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + GetSemaphore(&Semaphore, 50); + + ProcessIncommingConnectEx(TNC, Call, 0, TRUE, TRUE); + FreeSemaphore(&Semaphore); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + SESS->Mode = TNC->WL2KMode; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Status); + Debugprintf("WINMOR Call from %s rejected", Call); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(SESS->L4USER, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Status); + Debugprintf("WINMOR Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + if (STREAM->BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + + send(TNC->TCPDataSock, buffptr->Data, (int)buffptr->Len, 0); + STREAM->bytesTXed += (int)buffptr->Len; + WritetoTrace(TNC, buffptr->Data, (int)buffptr->Len); + ReleaseBuffer(buffptr); + } + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(Buffer, "%s\r", AppName); + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(buffptr->Data, Buffer, MsgLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + FreeSemaphore(&Semaphore); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + send(TNC->TCPDataSock, Msg, (int)strlen(Msg), 0); + STREAM->NeedDisc = 100; // 10 secs + } + } + + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + ReplyLen = sprintf(Reply, "*** Connected to %s\r", &Buffer[10]); + + buffptr->Len = ReplyLen; + memcpy(buffptr->Data, Reply, ReplyLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + FreeSemaphore(&Semaphore); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + + if (TNC->RIG) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, Call, '+', 'O'); + return; + } + } + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0) + { + Debugprintf(Buffer); + + if (TNC->FECMode) + return; + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + + buffptr->Len = sprintf(buffptr->Data, "Winmor} Failure with %s\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + FreeSemaphore(&Semaphore); + + if (TNC->RestartAfterFailure) + { + if (TNC->PID) + { + KillTNC(TNC); + RestartTNC(TNC); + } + } + + return; + } + + + // Release Session + + if (TNC->Streams[0].Connected) + { + hookL4SessionDeleted(TNC, STREAM); + + GetSemaphore(&Semaphore, 50); + WritetoTrace(TNC, Buffer, MsgLen - 2); + FreeSemaphore(&Semaphore); + } + + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + if (TNC->Streams[0].Disconnecting) // + ReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + + return; + } + + if (_memicmp(Buffer, "MONCALL", 7) == 0) + { + Debugprintf(Buffer); + + // Add to MHEARD + + GetSemaphore(&Semaphore, 50); + WritetoTrace(TNC, Buffer, MsgLen - 2); + FreeSemaphore(&Semaphore); + UpdateMH(TNC, &Buffer[8], '!', 0); + + if (!TNC->FECMode) + return; // If in FEC mode pass ID messages to user. + } + + if (_memicmp(Buffer, "CMD", 3) == 0) + { + return; + } + + if (_memicmp(Buffer, "BUFFERS", 7) == 0) + { + int inq, inrx, Sent, BPM; + + sscanf(&Buffer[8], "%d%d%d%d%d", &inq, &inrx, &TNC->Streams[0].BytesOutstanding, &Sent, &BPM); + + if (TNC->Streams[0].BytesOutstanding == 0) + { + // all sent + + if (TNC->Streams[0].Disconnecting) // Disconnect when all sent + { + if (STREAM->NeedDisc == 0) + STREAM->NeedDisc = 60; // 6 secs + } +// else +// if (TNC->TXRXState == 'S') +// send(TNC->TCPSock,"OVER\r\n", 6, 0); + + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + SetWindowText(TNC->xIDC_TRAFFIC, &Buffer[8]); + strcpy(TNC->WEB_TRAFFIC, &Buffer[8]); + return; + } + + Debugprintf(Buffer); + + if (_memicmp(Buffer, "MODE", 4) == 0) + { + // Debugprintf("WINMOR RX: %s", Buffer); + + strcpy(TNC->WEB_MODE, &Buffer[5]); + GetSemaphore(&Semaphore, 50); + MySetWindowText(TNC->xIDC_MODE, &Buffer[5]); + FreeSemaphore(&Semaphore); + return; + } + + if (_memicmp(Buffer, "PENDING", 6) == 0) + return; + + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + if (_memicmp(Buffer, "NEWSTATE", 8) == 0) + { + TNC->WinmorRestartCodecTimer = time(NULL); + + SetWindowText(TNC->xIDC_PROTOSTATE, &Buffer[9]); + strcpy(TNC->WEB_PROTOSTATE, &Buffer[9]); + + if (_memicmp(&Buffer[9], "CONNECTPENDING", 14) == 0) // Save Pending state for scan control + TNC->ConnectPending = TRUE; + else + TNC->ConnectPending = FALSE; + + if (_memicmp(&Buffer[9], "DISCONNECTING", 13) == 0) // So we can timout stuck discpending + { + TNC->DiscPending = 600; + return; + } + if (_memicmp(&Buffer[9], "DISCONNECTED", 12) == 0) + { + TNC->DiscPending = FALSE; + return; + } + + if (strcmp(&Buffer[9], "ISS") == 0) // Save Pending state for scan control + TNC->TXRXState = 'S'; + else if (strcmp(&Buffer[9], "IRS") == 0) + TNC->TXRXState = 'R'; + + return; + } + + + if (_memicmp(Buffer, "PROCESSID", 9) == 0) + { + HANDLE hProc; + char ExeName[256] = ""; + + TNC->PID = atoi(&Buffer[10]); + +#ifdef WIN32 + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + + // Set Window Title to reflect BPQ Port Description + + EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); +#endif + } + + if ((_memicmp(Buffer, "FAULT Not from state FEC", 24) == 0) || (_memicmp(Buffer, "FAULT Blocked by Busy Lock", 24) == 0)) + { + if (TNC->FECMode) + { + Sleep(1000); + + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + return; + } + } + + if (_memicmp(Buffer, "PLAYBACKDEVICES", 15) == 0) + { + TNC->PlaybackDevices = _strdup(&Buffer[16]); + } + // Others should be responses to commands + + if (_memicmp(Buffer, "BLOCKED", 6) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + if (_memicmp(Buffer, "OVER", 4) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + + buffptr->Len = sprintf(buffptr->Data, "Winmor} %s\r", Buffer); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + FreeSemaphore(&Semaphore); +} + +static int ProcessReceivedData(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[2000]; + + // May have several messages per packet, or message split over packets + + if (TNC->InputLen > 1000) // Shouldnt have lines longer than this on command connection + TNC->InputLen=0; + + InputLen=recv(TNC->TCPSock, &TNC->TCPBuffer[TNC->InputLen], 1000 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + return 0; + } + + TNC->InputLen += InputLen; + +loop: + + ptr = memchr(TNC->TCPBuffer, '\n', TNC->InputLen); + + if (ptr) // CR in buffer + { + ptr2 = &TNC->TCPBuffer[TNC->InputLen]; + ptr++; // Assume LF Follows CR + + if (ptr == ptr2) + { + // Usual Case - single meg in buffer + + ProcessResponse(TNC, TNC->TCPBuffer, TNC->InputLen); + TNC->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = TNC->InputLen - (int)(ptr2-ptr); + + memcpy(Buffer, TNC->TCPBuffer, MsgLen); + + ProcessResponse(TNC, Buffer, MsgLen); + memmove(TNC->TCPBuffer, ptr, TNC->InputLen-MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + } + return 0; +} + + +VOID ProcessDataSocketData(int port) +{ + // Info on Data Socket - just packetize and send on + + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int InputLen, PacLen = 236; + PMSGWITHLEN buffptr; + char * msg; + + TNC->TimeSinceLast = 0; + +loop: + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + InputLen = recv(TNC->TCPDataSock, buffptr->Data, PacLen, 0); + + if (InputLen == -1) + { + ReleaseBuffer(buffptr); + return; + } + + + //Debugprintf("Winmor: RXD %d bytes", InputLen); + + if (InputLen == 0) + { + // Does this mean closed? + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + ReleaseBuffer(buffptr); + return; + } + + STREAM->bytesRXed += InputLen; + + msg = &buffptr->Data[0]; + msg[InputLen] = 0; + + WritetoTrace(TNC, msg, InputLen); + + if (TNC->FECMode) + { + InputLen = (int)strlen(&buffptr->Data[0]); + + if (msg[InputLen - 1] == 3) // End of errored block + msg[InputLen++] = 13; // Add CR + + } + buffptr->Len = InputLen; + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + goto loop; +} + +/* +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Cmd = LOWORD(wParam); + + switch (message) + { + case WM_INITDIALOG: + { + struct TNCINFO * TNC = (struct TNCINFO * )lParam; + char * ptr1, *ptr2; + int ptr3 = 0; + char Line[1000]; + int len; + + ptr1 = TNC->CaptureDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_CAPTURE, Line); + + ptr3 = 0; + + ptr1 = TNC->PlaybackDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_PLAYBACK, Line); + + SendDlgItemMessage(hDlg, IDC_PLAYBACK, EM_SETSEL, -1, 0); + +// KillTNC(TNC); + + return TRUE; + } + + case WM_SIZING: + { + return TRUE; + } + + case WM_ACTIVATE: + +// SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + break; + + + case WM_COMMAND: + + + if (Cmd == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} +*/ + +#ifdef LINBPQ +#include +#include +#endif + + +int KillTNC(struct TNCINFO * TNC) +{ + if (TNC->ProgramPath && _memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) + { + // Try to Kill TNC on a remote host + + SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); + struct sockaddr_in destaddr; + char Msg[256]; + int Len; + + if (sock == INVALID_SOCKET) + return 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_port = htons(8500); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + return 0; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + if (TNC->PID) + Len = sprintf(Msg, "KILL %d", TNC->PID); + else + Len = sprintf(Msg, "KILLBYNAME %s", &TNC->ProgramPath[7]); + + sendto(sock, Msg, Len, 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); + Sleep(100); + closesocket(sock); + + TNC->PID = 0; // So we don't try again + return 1; // Cant tell if it worked, but assume ok + } + + if (TNC->PID == 0) + return 0; + +#ifdef WIN32 + { + HANDLE hProc; + + Debugprintf("KillTNC Called for Pid %d", TNC->PID); + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + } +#else + + printf("KillTNC Called for Pid %d Returned %d\n", TNC->PID, kill(TNC->PID, SIGTERM)); + +#endif + TNC->PID = 0; // So we don't try again + + return 0; +} + +BOOL RestartTNC(struct TNCINFO * TNC) +{ + if (TNC->ProgramPath == NULL || TNC->DontRestart) + return 0; + + if (_memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) + { + int n; + + // Try to start TNC on a remote host + + SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); + struct sockaddr_in destaddr; + + Debugprintf("trying to restart TNC %s", TNC->ProgramPath); + + if (sock == INVALID_SOCKET) + return 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_port = htons(8500); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + return 0; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + n = sendto(sock, TNC->ProgramPath, (int)strlen(TNC->ProgramPath), 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); + + Debugprintf("Restart TNC - sendto returned %d", n); + + Sleep(100); + closesocket(sock); + + return 1; // Cant tell if it worked, but assume ok + } + + // Not Remote + + // Extract any parameters from command string + +#ifndef WIN32 + { + char * arg_list[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + pid_t child_pid; + char * Copy, * Context; + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + Copy = _strdup(TNC->ProgramPath); // Save as strtok mangles it + + arg_list[0] = strtok_s(Copy, " \n\r", &Context); + if (arg_list[0]) + arg_list[1] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[1]) + arg_list[2] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[2]) + arg_list[3] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[3]) + arg_list[4] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[4]) + arg_list[5] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[5]) + arg_list[6] = strtok_s(NULL, " \n\r", &Context); + if (arg_list[6]) + arg_list[7] = strtok_s(NULL, " \n\r", &Context); + + // Fork and Exec TNC + + printf("Trying to start %s\n", TNC->ProgramPath); + + /* Duplicate this process. */ + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("StartTNC fork() Failed\n"); + free(Copy); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + /* The execvp function returns only if an error occurs. */ + + printf ("Failed to start TNC\n"); + exit(0); // Kill the new process + } + else + { + TNC->PID = child_pid; + printf("Started TNC, Process ID = %d\n", TNC->PID); + } + free(Copy); + return TRUE; + } +#else + { + int n = 0; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char workingDirectory[256]; + int i = strlen(TNC->ProgramPath); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + Debugprintf("RestartTNC Called for %s", TNC->ProgramPath); + + strcpy(workingDirectory, TNC->ProgramPath); + + while (i--) + { + if (workingDirectory[i] == '\\' || workingDirectory[i] == '/') + { + workingDirectory[i] = 0; + break; + } + } + + while (KillOldTNC(TNC->ProgramPath) && n++ < 100) + { + Sleep(100); + } + + if (CreateProcess(NULL, TNC->ProgramPath, NULL, NULL, FALSE,0, NULL, workingDirectory, &SInfo, &PInfo)) + { + Debugprintf("Restart TNC OK"); + TNC->PID = PInfo.dwProcessId; + return TRUE; + } + else + { + Debugprintf("Restart TNC Failed %d ", GetLastError()); + return FALSE; + } + } +#endif + return 0; +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[0].BytesOutstanding == 0) + send(TNC->TCPSock,"DISCONNECT\r\n", 12, 0); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + send(TNC->TCPSock,"DIRTYDISCONNECT\r\n", 17, 0); +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + ReleaseTNC(TNC); + + if (TNC->FECMode) + { + TNC->FECMode = FALSE; + send(TNC->TCPSock,"SENDID 0\r\n", 10, 0); + } +} + +BOOL KillOldTNC(char * Path) +{ +#ifdef WIN32 + HANDLE hProc; + char ExeName[256] = ""; + DWORD Pid = 0; + + DWORD Processes[1024], Needed, Count; + unsigned int i; + + if (EnumProcessesPtr == NULL) + return FALSE; + + if (!EnumProcessesPtr(Processes, sizeof(Processes), &Needed)) + return FALSE; + + // Calculate how many process identifiers were returned. + + Count = Needed / sizeof(DWORD); + + for (i = 0; i < Count; i++) + { + if (Processes[i] != 0) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Processes[i]); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + + // Path could have parameters, so use memcmp + + if (_memicmp(ExeName, Path, strlen(ExeName)) == 0) + { + Debugprintf("Killing Pid %d %s", Processes[i], ExeName); + TerminateProcess(hProc, 0); + CloseHandle(hProc); + return TRUE; + } + CloseHandle(hProc); + } + } + } +#endif + return FALSE; +} diff --git a/.svn/pristine/38/381094af7c720e719f04934491b42931722261e4.svn-base b/.svn/pristine/38/381094af7c720e719f04934491b42931722261e4.svn-base new file mode 100644 index 0000000..1ddf86e Binary files /dev/null and b/.svn/pristine/38/381094af7c720e719f04934491b42931722261e4.svn-base differ diff --git a/.svn/pristine/38/383d308a7dfe998700e61ca2ab05d19f8a20efa4.svn-base b/.svn/pristine/38/383d308a7dfe998700e61ca2ab05d19f8a20efa4.svn-base new file mode 100644 index 0000000..831941b --- /dev/null +++ b/.svn/pristine/38/383d308a7dfe998700e61ca2ab05d19f8a20efa4.svn-base @@ -0,0 +1,1554 @@ +/* +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 +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use MultoPSK ALE400 Mode +// +// Uses BPQ EXTERNAL interface +// + + +#define _CRT_SECURE_NO_DEPRECATE + +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" +#include +#include + +#include "tncinfo.h" + +#include "bpq32.h" + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#define TIMESTAMP 352 + +#define CONTIMEOUT 1200 + + + +#define AGWHDDRLEN sizeof(struct AGWHEADER) + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); + +//int ResetExtDriver(int num); + +static void ConnecttoMPSKThread(void * portptr); + +void CreateMHWindow(); +int Update_MH_List(struct in_addr ipad, char * call, char proto); + +static int ConnecttoMPSK(int port); +static int ProcessReceivedData(int bpqport); +static int ProcessLine(char * buf, int Port); +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +VOID ProcessMPSKPacket(struct TNCINFO * TNC, char * Message, int Len); +struct TNCINFO * GetSessionKey(char * key, struct TNCINFO * TNC); +static VOID SendData(struct TNCINFO * TNC, char * Msg, int MsgLen); +static VOID DoMonitorHddr(struct TNCINFO * TNC, struct AGWHEADER * RXHeader, UCHAR * Msg); +VOID SendRPBeacon(struct TNCINFO * TNC); + +extern UCHAR BPQDirectory[]; + +#define MAXMPSKPORTS 16 + +//LOGFONT LFTTYFONT ; + +//HFONT hFont ; + +static int MPSKChannel[MAXBPQPORTS+1]; // BPQ Port to MPSK Port +static int BPQPort[MAXMPSKPORTS][MAXBPQPORTS+1]; // MPSK Port and Connection to BPQ Port + +extern int MasterPort[MAXBPQPORTS+1]; // Pointer to first BPQ port for a specific MPSK host + +// Each port may be on a different machine. We only open one connection to each MPSK instance + +static char * MPSKSignon[MAXBPQPORTS+1]; // Pointer to message for secure signin + +static unsigned int MPSKInst = 0; +static int AttachedProcesses=0; + +static HWND hResWnd,hMHWnd; +static BOOL GotMsg; + +static HANDLE STDOUT=0; + +//SOCKET sock; + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; +static SOCKADDR_IN destaddr[MAXBPQPORTS+1]; + +static int addrlen=sizeof(sinx); + +//static short MPSKPort=0; + +static time_t ltime,lasttime[MAXBPQPORTS+1]; + +static BOOL CONNECTING[MAXBPQPORTS+1]; +static BOOL CONNECTED[MAXBPQPORTS+1]; + +//HANDLE hInstance; + + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + +#ifndef LINBPQ + +static BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[200]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + char FN[MAX_PATH] = ""; + + if (TNC->ProgramPath == NULL) + return FALSE; + + GetWindowText(hwnd, wtext, 199); + + if (strstr(wtext,"* MULTIPSK")) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + TNC->PID = ProcessId; + return FALSE; + } + + return (TRUE); +} + +#endif + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + PMSGWITHLEN buffptr; + unsigned int txlen=0; + struct TNCINFO * TNC = TNCInfo[port]; + int Stream = 0; + struct STREAMINFO * STREAM; + int TNCOK; + + if (TNC == NULL) + return 0; // Port not defined + + // Look for attach on any call + + for (Stream = 0; Stream <= TNC->MPSKInfo->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + char Cmd[80]; + int len; + + // New Attach + + int calllen; + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + STREAM->FramesOutstanding = 0; + + // Stop Scanning + + sprintf(Cmd, "%d SCANSTOP", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Cmd); + + len = sprintf(Cmd, "%cSTOP_BEACON_ARQ_FAE\x1b", '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting + else + send(TNC->TCPSock, Cmd, len, 0); + + } + } + + switch (fn) + { + case 1: // poll + + if (MasterPort[port] == port) + { + // Only on first port using a host + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time( <ime ); + if (ltime-lasttime[port] >9 ) + { + ConnecttoMPSK(port); + lasttime[port]=ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPSock,&readfs); + + + FD_ZERO(&writefs); + + if (TNC->CONNECTING) FD_SET(TNC->TCPSock,&writefs); // Need notification of Connect + + if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPSock,&writefs); // Need notification of busy clearing + + + + FD_ZERO(&errorfs); + + if (TNC->CONNECTING ||TNC->CONNECTED) FD_SET(TNC->TCPSock,&errorfs); + + if (select((int)TNC->TCPSock+ 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock,&readfs)) + { + // data available + + ProcessReceivedData(port); + } + + if (FD_ISSET(TNC->TCPSock,&writefs)) + { + // Connect success + + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + // If required, send signon + + send(TNC->TCPSock,"\x1a", 1, 0); + send(TNC->TCPSock,"DIGITAL MODE ?", 14, 0); + send(TNC->TCPSock,"\x1b", 1, 0); + +// EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); + } + + if (FD_ISSET(TNC->TCPSock,&errorfs)) + { + + // if connecting, then failed, if connected then has just disconnected + +// if (CONNECTED[port]) +// if (!CONNECTING[port]) +// { +// i=sprintf(ErrMsg, "MPSK Connection lost for BPQ Port %d\r\n", port); +// WritetoConsole(ErrMsg); +// } + + CONNECTING[port]=FALSE; + CONNECTED[port]=FALSE; + + } + + } + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= TNC->MPSKInfo->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + // Have to time out connects, as TNC doesn't report failure + + if (STREAM->Connecting) + { + STREAM->Connecting--; + + if (STREAM->Connecting == 0) + { + // Report Connect Failed, and drop back to command mode + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "MPSK} Failure with %s\r", STREAM->RemoteCall); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->DiscWhenAllSent = 10; + + // Send Disc to TNC + + TidyClose(TNC, Stream); + } + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + + // if Busy, send buffer status poll + + if (STREAM->PACTORtoBPQ_Q == 0) + { + if (STREAM->DiscWhenAllSent) + { + STREAM->DiscWhenAllSent--; + if (STREAM->DiscWhenAllSent == 0) + STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion + } + } + else + { + int datalen; + + buffptr = Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + ReleaseBuffer(buffptr); + + return (1); + } + } + + if (TNC->PortRecord->UI_Q) + { + struct _MESSAGE * buffptr; + + SOCKET Sock; + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + Sock = TNCInfo[MasterPort[port]]->TCPSock; + + ReleaseBuffer((UINT *)buffptr); + } + + + return (0); + + + + case 2: // send + + + if (!TNCInfo[MasterPort[port]]->CONNECTED) return 0; // Don't try if not connected to TNC + + Stream = buff->PORT; + + STREAM = &TNC->Streams[Stream]; + +// txlen=(buff[6]<<8) + buff[5] - 8; + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - 8; + + if (STREAM->Connected) + { + SendData(TNC, buff->L2DATA, txlen); + } + else + { + char Command[80]; + int len; + + buff->L2DATA[txlen] = 0; + + _strupr(buff->L2DATA); + + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TidyClose(TNC, buff->PORT); + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + // See if Local command (eg RADIO) + + if (_memicmp(buff->L2DATA, "RADIO ", 6) == 0) + { + sprintf(buff->L2DATA, "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, buff->L2DATA)) + { + } + else + { + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "%s", buff->L2DATA); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (STREAM->Connecting && _memicmp(buff->L2DATA, "ABORT", 5) == 0) + { + len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + return (0); + } + + if (_memicmp(buff->L2DATA, "MODE", 4) == 0) + { + buff->L2DATA[txlen - 1] = 0; // Remove CR + + len = sprintf(Command,"%cDIGITAL MODE %s\x1b", '\x1a', &buff->L2DATA[5]); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + return (0); + } + + + if (_memicmp(buff->L2DATA, "INUSE?", 6) == 0) + { + // Return Error if in use, OK if not + + PMSGWITHLEN buffptr = GetBuff(); + int s = 0; + + while(s <= TNC->MPSKInfo->MaxSessions) + { + if (s != Stream) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[s]) + { + buffptr->Len = sprintf(buffptr->Data, "MPSK} Error - In use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return 1; // Busy + } + } + s++; + } + buffptr->Len = sprintf(buffptr->Data, "MPSK} Ok - Not in use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char * ptr; + char * context; + + buff->L2DATA[txlen] = 0; + _strupr(buff->L2DATA); + + memset(STREAM->RemoteCall, 0, 10); + + ptr = strtok_s(&buff->L2DATA[2], " ,\r", &context); + + if (ptr == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], + "MPSK} Error - Call missing from C command\r", STREAM->MyCall, STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->DiscWhenAllSent = 10; + return 0; + } + + strcpy(STREAM->RemoteCall, ptr); + + len = sprintf(Command,"%cCALLSIGN_TO_CALL_ARQ_FAE %s%c%cSELECTIVE_CALL_ARQ_FAE\x1b", + '\x1a', STREAM->RemoteCall, '\x1b', '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + STREAM->Connecting = TNC->MPSKInfo->ConnTimeOut; // It doesn't report failure + +// sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); +// SetDlgItemText(TNC->hDlg, IDC_TNCSTATE, Status); + + return 0; + } + + // Send any other command to Multipsk + + buff->L2DATA[txlen - 1] = 0; + _strupr(buff->L2DATA); + + len = sprintf(Command,"%c%s\x1b", '\x1a', buff->L2DATA); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + + } + + return (0); + + case 3: + + Stream = (int)(size_t)buff; + + TNCOK = TNCInfo[MasterPort[port]]->CONNECTED; + + STREAM = &TNC->Streams[Stream]; + + if (STREAM->FramesOutstanding > 8) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); + + return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + break; + + case 4: // reinit + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + TNC->CONNECTED = FALSE; + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + + return (0); + + case 5: // Close + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return 0; + } + + return 0; +} + +#ifndef LINBPQ + +static KillTNC(struct TNCINFO * TNC) +{ + HANDLE hProc; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->PID == 0) return 0; + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + TNC->PID = 0; // So we don't try again + + return 0; +} + +static RestartTNC(struct TNCINFO * TNC) +{ + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char HomeDir[MAX_PATH]; + int i, ret; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (TNC->ProgramPath && TNC->DontRestart == 0) + { + strcpy(HomeDir, TNC->ProgramPath); + i = strlen(HomeDir); + + while(--i) + { + if (HomeDir[i] == '/' || HomeDir[i] == '\\') + { + HomeDir[i] = 0; + break; + } + } + ret = CreateProcess(TNC->ProgramPath, "MultiPSK TCP_IP_ON", NULL, NULL, FALSE,0 ,NULL ,HomeDir, &SInfo, &PInfo); + + if (ret) + TNC->PID = PInfo.dwProcessId; + + return ret; + } + return 0; +} +#endif + +void * MPSKExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + struct TNCINFO * TNC; + char * ptr; + + // + // Will be called once for each MPSK port to be mapped to a BPQ Port + // The MPSK port number is in CHANNEL - A=0, B=1 etc + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // Scan Control - None + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 64; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_MPSK; + + MPSKChannel[port] = PortEntry->PORTCONTROL.CHANNELNUM-65; + + PortEntry->MAXHOSTMODESESSIONS = 1; + + i=sprintf(Msg,"MPSK Host %s Port %d \n", + TNC->HostName, TNC->TCPPort); + + WritetoConsole(Msg); + + // See if we already have a port for this host + + MasterPort[port] = port; + + for (i = 1; i < port; i++) + { + if (i == port) continue; + + if (TNCInfo[i] && TNCInfo[i]->TCPPort == TNC->TCPPort && + _stricmp(TNCInfo[i]->HostName, TNC->HostName) == 0) + { + MasterPort[port] = i; + break; + } + } + + BPQPort[PortEntry->PORTCONTROL.CHANNELNUM-65][MasterPort[port]] = port; + +#ifndef LINBPQ + if (MasterPort[port] == port) + { + if (EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC)) + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + ConnecttoMPSK(port); + } +#endif + time(&lasttime[port]); // Get initial time value + +// SendMessage(0x40eaa, WM_COMMAND, 0x03000eaa, 0x40eaa); + + return ExtProc; +} + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + struct MPSKINFO * AGW; + + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + AGW = TNC->MPSKInfo = zalloc(sizeof(struct MPSKINFO)); // AGW Sream Mode Specific Data + + AGW->MaxSessions = 10; + AGW->ConnTimeOut = CONTIMEOUT; + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->TCPPort = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(TNC->TCPPort); + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "CONTIMEOUT", 10) == 0) + AGW->ConnTimeOut = atoi(&buf[11]) * 10; + else + if (_memicmp(buf, "UPDATEMAP", 9) == 0) + TNC->PktUpdateMap = TRUE; + else + if (_memicmp(buf, "ALEBEACON", 9) == 0) // Send Beacon after each session + TNC->MPSKInfo->Beacon = TRUE; + else + if (_memicmp(buf, "DEFAULTMODE", 11) == 0) // Send Beacon after each session + strcpy(TNC->MPSKInfo->DefaultMode, &buf[12]); + else + + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + +static int ConnecttoMPSK(int port) +{ + _beginthread(ConnecttoMPSKThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID ConnecttoMPSKThread(void * portptr) +{ + + int port = (int)(size_t)portptr; + char Msg[255]; + int err,i; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + + Sleep(5000); // Allow init to complete + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) return; // Resolve failed + + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + } + + if (TNC->TCPSock) + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for MPSK socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + + return; + } + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + TNC->CONNECTING = TRUE; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + + TNC->CONNECTED=TRUE; + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for MPSK socket - error code = %d\n", err); + WritetoConsole(Msg); + MySetWindowText(TNC->xIDC_COMMSSTATE, "Connection to TNC failed"); + + TNC->Alerted = TRUE; + } + + TNC->CONNECTING = FALSE; + return; + } + + TNC->LastFreq = 0; // so V4 display will be updated + + MySetWindowText(TNC->xIDC_COMMSSTATE, "Connected to MPSK TNC"); + + return; + +} + +static int ProcessReceivedData(int port) +{ + unsigned int bytes; + int i; + char ErrMsg[255]; + char Message[500]; + struct TNCINFO * TNC = TNCInfo[port]; + + // Need to extract messages from byte stream + + bytes = recv(TNC->TCPSock,(char *)&Message, 500, 0); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for MPSK socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "MPSK Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + // Have some data + + ProcessMPSKPacket(TNC, Message, bytes); // Data may be for another port + + return (0); + +} + +VOID ProcessMSPKCmd(struct TNCINFO * TNC); +VOID ProcessMSPKComment(struct TNCINFO * TNC); +VOID ProcessMSPKData(struct TNCINFO * TNC); + +VOID ProcessMPSKPacket(struct TNCINFO * TNC, char * Message, int Len) +{ + char * MPTR = Message; + +/* +3) each text character transmitted by the client to the server (for the Multipsk TX text editor) must be preceded by the character CHR(25) or CHR(22) in the case of a special link (KISS in Packet or Pax, for example). + +4) each command string transmitted by the client to the server must be preceded by the character CHR(26) and finished by CHR(27), + +5) each character effectively transmitted by Multipsk to the transceiver and transmitted to the client is preceded by the character CHR(28), + +6) each character received by Multipsk and transmitted to the client is preceded by the character CHR(29), + +7) each command string transmitted by the server to the client must be preceded by the character CHR(30) and finished by CHR(31), + +8) all commands (written in readable text ) will have an answer (see further for details), + +9) each server comment (Call ID or RS ID reception, switch to RX or to TX) string transmitted by the server to the client must be preceded by a string: "CHR(23)RX CALL ID=", "CHR(23)RX RS ID=", "CHR(23)SWITCH=RX", "CHR(23) SWITCH=TX", and finished by CHR(24). + +10) each server command, for the transceiver control, transmitted by the server to the client must be preceded by the string "CHR(23) XCVR=" and finished by CHR(24). + +Data + +End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] call "THIS I[End of TX] end of link to GM8BPQ[End of TX] sounding "THIS WAS"[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQFAE BEACON OH5RM Kouvola KP30JR +[End of TX] ARQ FAE selective callGM8BPQ DE OH5RM + +[Connection made with OH5RM] + + +18103 but I have to go out to change antenna + +[End of connection with OH5RM]FAE BEACON OH5RM Kouvola KP30JR +S" to GM8BPQ + +10:23:55 AM Comment: SWITCH=RX +10:24:00 AM Comment: RX RS ID=10:24:00 UTC ALE400 1609 Hz 0 MHz +10:24:19 AM Comment: RX RS ID=10:24:19 UTC ALE400 1604 Hz 0 MHz +10:25:04 AM Comment: SWITCH=TX +10:25:07 AM Comment: SWITCH=RX +10:25:15 AM Comment: SWITCH=TX +:30:22 AM Comment: SWITCH=RX +10:30:25 AM Comment: SWITCH=TX +10:30:27 AM Comment: SWITCH=RX +10:30:35 AM Comment: RX RS ID=10:30:35 UTC ALE400 1598 Hz 0 MHz + + +*/ + + // Reuse the HAL CMD and Data Buffers to build messages from TCP stream + + // See if sequence split over a packet boundary + + if (TNC->CmdEsc == 23) + { + TNC->CmdEsc = 0; + goto CommentEsc; + } + + if (TNC->CmdEsc == 29) + { + TNC->CmdEsc = 0; + goto DataEsc; + } + + if (TNC->CmdEsc == 30) + { + TNC->CmdEsc = 0; + goto CmdEsc; + } + + // No Split + + while(Len) + { + switch (*(MPTR++)) + { + case 29: // Data Char + + Len--; + DataEsc: + if (Len) + { + TNC->DataBuffer[TNC->DataLen++] = *MPTR; + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdEsc = 29; + + if (TNC->DataLen) + ProcessMSPKData(TNC); + + + return; // Nothing left + + case 30: + + Len --; + CmdEsc: + while (Len) + { + if (*MPTR == 31) // End of String + { + ProcessMSPKCmd(TNC); + TNC->CmdLen = 0; + + // Process any data left in buffer + + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdBuffer[TNC->CmdLen++] = *MPTR; + MPTR++; + Len--; + } + + TNC->CmdEsc = 30; + return; // Nothing left + + case 23: // Server Comment + + Len --; + CommentEsc: + while (Len) + { + if (*MPTR == 24) // End of String + { + // Process Comment + + ProcessMSPKCmd(TNC); + TNC->CmdLen = 0; + + // Process any data left in buffer + + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdBuffer[TNC->CmdLen++] = *MPTR; + MPTR++; + Len--; + } + + TNC->CmdEsc = 23; + return; // Nothing left + + default: + + Len--; + + } +OuterLoop:; + } + + if (TNC->DataLen) + ProcessMSPKData(TNC); +} + +VOID ProcessMSPKCmd(struct TNCINFO * TNC) +{ + TNC->CmdBuffer[TNC->CmdLen] = 0; + + if (strcmp(TNC->CmdBuffer, "SWITCH=TX") == 0) + TNC->MPSKInfo->TX = TRUE; + else + { + if (strcmp(TNC->CmdBuffer, "SWITCH=RX") == 0) + { + TNC->MPSKInfo->TX = FALSE; + + // See if a command was queued while busy + + if (TNC->CmdSet) + { + send(TNC->TCPSock, TNC->CmdSet, strlen(TNC->CmdSet), 0); + free (TNC->CmdSet); + TNC->CmdSet = NULL; + } + } + else + { + Debugprintf("MPSK CMD %s", TNC->CmdBuffer); + + if (TNC->InternalCmd) + { + PMSGWITHLEN buffptr = GetBuff(); + char * ptr = strstr(TNC->CmdBuffer, "OK"); + + if (ptr) + *(ptr+2) = 0; // Convert OKn to OK for BBS Connect Script + + TNC->InternalCmd = FALSE; + + if (buffptr) + { + buffptr->Len= sprintf(buffptr->Data, "MPSK} %s\r", TNC->CmdBuffer); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + + if (strstr(TNC->CmdBuffer, "STOP_SELECTIVE_CALL_ARQ_FAE OK")) + TNC->Streams[0].Connecting = FALSE; + + } + } + } +} + +VOID ProcessMSPKComment(struct TNCINFO * TNC) +{ + TNC->CmdBuffer[TNC->CmdLen] = 0; + Debugprintf("MPSK Comment %s", TNC->CmdBuffer); +} + +static int UnStuff(UCHAR * inbuff, int len) +{ + int i,txptr=0; + UCHAR c; + UCHAR * outbuff = inbuff; + + for (i = 0; i < len; i++) + { + c = inbuff[i]; + + if (c == 0xc0) + c = inbuff[++i] - 0x20; + + outbuff[txptr++]=c; + } + + return txptr; +} + +VOID ProcessMSPKData(struct TNCINFO * TNC) +{ + PMSGWITHLEN buffptr; + + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char * ptr; + int Len = TNC->DataLen; + + TNC->DataBuffer[TNC->DataLen] = 0; + + // Process Data + + if (STREAM->Connected) + { + ptr = strstr(TNC->DataBuffer, "[End of connection"); + + if (ptr) + { + // Disconnect + + TNC->DataLen = 0; + + if (STREAM->DiscWhenAllSent) + return; // Already notified + + if (STREAM->Connecting) + { + // Report Connect Failed, and drop back to command mode + + STREAM->Connecting = FALSE; + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "MPSK} Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->DiscWhenAllSent = 10; + + return; + } + + // Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + STREAM->DiscWhenAllSent = 10; + STREAM->FramesOutstanding = 0; + + return; + } + + // Pass to Application. Remove any transparency (hex 0xc0 used as an escape) + + buffptr = GetBuff(); + + if (TNC->DataBuffer[TNC->DataLen - 1] == 0xc0) + return; // Last char is an escape, so wait for the escaped char to arrive + + if (buffptr) + { + if (memchr(TNC->DataBuffer, 0xc0, TNC->DataLen)) + TNC->DataLen = UnStuff(TNC->DataBuffer, TNC->DataLen); + + buffptr->Len = TNC->DataLen; + memcpy(buffptr->Data, TNC->DataBuffer, TNC->DataLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->bytesRXed += TNC->DataLen; + } + + TNC->DataLen = 0; + return; + } + + // Not Connected. We get various status messages, including Connection made, + // but they may be split across packets, or have more that one to a packet. + // I think they are all CR/LF terminated . No they aren't! + + // Look for [] this seems to be what is important + +DataLoop: + + if (memcmp(TNC->DataBuffer, "[End of TX] ARQ FAE CQ", 22) == 0) + { + // Remove string from buffer + + if (Len == 22) // Most Likely + { + TNC->DataLen = 0; + return; + } + + TNC->DataLen -= 22; + memmove(TNC->DataBuffer, &TNC->DataBuffer[22], Len - 21); //Copy Null + Len -= 22; + goto DataLoop; + + } + + ptr = strchr(TNC->DataBuffer, '['); + + if (ptr) + { + // Start of a significant Message + + char * eptr = strchr(TNC->DataBuffer, ']'); + char CallFrom[20]; + char * cptr ; + + if (eptr == 0) + return; // wait for matching [] + + cptr = strstr(TNC->DataBuffer, "[Connection made with "); + + // TNC->DataLen -= LineLen; + // memmove(TNC->DataBuffer, &TNC->DataBuffer[LineLen], 1 + Len - LineLen); //Copy Null + // Len -= LineLen; + // goto DataLoop; + + + if (cptr) // Have a connection + { + + // Connected + + memcpy(CallFrom, &cptr[22], 18); + cptr = strchr(CallFrom, ']'); + if (cptr) + *cptr = 0; + + if (STREAM->Connecting) + { + // Connect Complete + + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = 0; + + buffptr = GetBuff(); + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", CallFrom); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + } + else + { + // Incoming. Look for a free Stream + + STREAM->Connected = TRUE; + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = 0; + + UpdateMH(TNC, CallFrom, '+', 'I'); + + ProcessIncommingConnect(TNC, CallFrom, Stream, FALSE); + + if (HFCTEXTLEN) + { + if (HFCTEXTLEN > 1) + SendData(TNC, HFCTEXT, HFCTEXTLEN); + } + else + { + if (FULL_CTEXT) + { + int Len = CTEXTLEN, CTPaclen = 50; + int Next = 0; + + while (Len > CTPaclen) // CTEXT Paclen + { + SendData(TNC, &CTEXTMSG[Next], CTPaclen); + Next += CTPaclen; + Len -= CTPaclen; + } + SendData(TNC, &CTEXTMSG[Next], Len); + } + } + } + } + + } + + // Doesnt contain [ - just discard + + TNC->DataLen = 0; + Debugprintf(TNC->DataBuffer); + return; + +} + + + +/* + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = RXHeader->DataLength; + memcpy(&buffptr[2], Message, RXHeader->DataLength); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + + return; + + + case 'd': // Disconnected + + + + case 'C': + + // Connect. Can be Incoming or Outgoing + + // "*** CONNECTED To Station [CALLSIGN]" When the other station starts the connection + // "*** CONNECTED With [CALLSIGN]" When we started the connection + + */ + + +VOID SendData(struct TNCINFO * TNC, char * Msg, int MsgLen) +{ + // Preceed each data byte with 25 (decimal) + + char * NewMsg = malloc (MsgLen * 4); + int n; + UCHAR c; + int ExtraLen = 0; + char * ptr = NewMsg; + char * inptr = Msg; + SOCKET sock = TNCInfo[MasterPort[TNC->Port]]->TCPSock; + + TNC->Streams[0].bytesTXed += MsgLen; + + for (n = 0; n < MsgLen; n++) + { + *(ptr++) = 25; + c = *inptr++; + + if (c < 0x20 || c == 0xc0) + { + if (c != 0x0d) + { + *ptr++ = 0x0c0; + *(ptr++) = 25; + *ptr++ = c + 0x20; + ExtraLen += 2; + continue; + } + } + + *ptr++ = c; + } + + send(sock, NewMsg, MsgLen * 2 + ExtraLen, 0); + + free(NewMsg); +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + char Command[80]; + int len; + + len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a'); + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Savde till not transmitting + else + send(TNC->TCPSock, Command, len, 0); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + char Cmd[80]; + int Len; + + sprintf(Cmd, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Cmd); + + Cmd[0] = 0; + + if (TNC->MPSKInfo->DefaultMode[0]) + sprintf(Cmd, "%cDIGITAL MODE %s\x1b", '\x1a', TNC->MPSKInfo->DefaultMode); + + if (TNC->MPSKInfo->Beacon) + sprintf(Cmd, "%s%cBEACON_ARQ_FAE\x1b", Cmd, '\x1a'); + + Len = strlen(Cmd); + + if(Len) + { + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting + else + send(TNC->TCPSock, Cmd, Len, 0); + } +} + diff --git a/.svn/pristine/39/39416be802995891cc71bd70f75248612a42fbf9.svn-base b/.svn/pristine/39/39416be802995891cc71bd70f75248612a42fbf9.svn-base new file mode 100644 index 0000000..a22e6c9 --- /dev/null +++ b/.svn/pristine/39/39416be802995891cc71bd70f75248612a42fbf9.svn-base @@ -0,0 +1,5183 @@ +/* +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 +*/ + +//#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#define _CRT_SECURE_NO_DEPRECATE + +#define DllImport + +#include "cheaders.h" +#include + +#include "tncinfo.h" +#include "time.h" +#include "bpq32.h" +#include "telnetserver.h" + +// This is needed to link with a lib built from source + +#ifdef WIN32 +#define ZEXPORT __stdcall +#endif + +#include + +#define CKernel +#include "httpconnectioninfo.h" + +extern int MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS, L3FRAMES; +extern int NUMBEROFNODES, MAXDESTS, L4CONNECTSOUT, L4CONNECTSIN, L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES; +extern int STATSTIME; +extern TRANSPORTENTRY * L4TABLE; +extern BPQVECSTRUC BPQHOSTVECTOR[]; +extern BOOL APRSApplConnected; +extern char VersionString[]; +VOID FormatTime3(char * Time, time_t cTime); +DllExport int APIENTRY Get_APPLMASK(int Stream); +VOID SaveUIConfig(); +int ProcessNodeSignon(SOCKET sock, struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL); +VOID SetupUI(int Port); +VOID SendUIBeacon(int Port); +VOID GetParam(char * input, char * key, char * value); +VOID ARDOPAbort(struct TNCINFO * TNC); +VOID WriteMiniDump(); +BOOL KillTNC(struct TNCINFO * TNC); +BOOL RestartTNC(struct TNCINFO * TNC); +int GetAISPageInfo(char * Buffer, int ais, int adsb); +int GetAPRSPageInfo(char * Buffer, double N, double S, double W, double E, int aprs, int ais, int adsb); +unsigned char * Compressit(unsigned char * In, int Len, int * OutLen); +char * stristr (char *ch1, char *ch2); +int GetAPRSIcon(unsigned char * _REPLYBUFFER, char * NodeURL); +char * GetStandardPage(char * FN, int * Len); +BOOL SHA1PasswordHash(char * String, char * Hash); +char * byte_base64_encode(char *str, int len); +int APIProcessHTTPMessage(char * response, char * Method, char * URL, char * request, BOOL LOCAL, BOOL COOKIE); +int RHPProcessHTTPMessage(struct ConnectionInfo * conn, char * response, char * Method, char * URL, char * request, BOOL LOCAL, BOOL COOKIE); +unsigned char * Compressit(unsigned char * In, int Len, int * OutLen); +int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen); + +extern struct ROUTE * NEIGHBOURS; +extern int ROUTE_LEN; +extern int MAXNEIGHBOURS; + +extern struct DEST_LIST * DESTS; // NODE LIST +extern int DEST_LIST_LEN; +extern int MAXDESTS; // MAX NODES IN SYSTEM + +extern struct _LINKTABLE * LINKS; +extern int LINK_TABLE_LEN; +extern int MAXLINKS; +extern char * RigWebPage; +extern COLORREF Colours[256]; + +extern BOOL IncludesMail; +extern BOOL IncludesChat; + +extern BOOL APRSWeb; +extern BOOL RigActive; + +extern HKEY REGTREE; + +extern BOOL APRSActive; + +extern UCHAR LogDirectory[]; + +extern struct RIGPORTINFO * PORTInfo[34]; +extern int NumberofPorts; + +extern UCHAR ConfigDirectory[260]; + +VOID sendandcheck(SOCKET sock, const char * Buffer, int Len); +int CompareNode(const void *a, const void *b); +int CompareAlias(const void *a, const void *b); +void ProcessMailHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen, int InputLen, char * Token); +void ProcessChatHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen); +struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); +int SetupNodeMenu(char * Buff, int SYSOP); +int StatusProc(char * Buff); +int ProcessMailSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL); +int ProcessMailAPISignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL); +int ProcessChatSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL); +VOID APRSProcessHTTPMessage(SOCKET sock, char * MsgPtr, BOOL LOCAL, BOOL COOKIE); + + +static struct HTTPConnectionInfo * SessionList; // active term mode sessions + +char Mycall[10]; + +char MAILPipeFileName[] = "\\\\.\\pipe\\BPQMAILWebPipe"; +char CHATPipeFileName[] = "\\\\.\\pipe\\BPQCHATWebPipe"; + +char Index[] = "%s's BPQ32 Web Server

" +"" +"" +"
Node PagesAPRS Pages
"; + +char IndexNoAPRS[] = "" +""; + +//char APRSBit[] = "APRS Pages"; + +//char MailBit[] = "Mail Mgmt" +// "WebMail"; +//char ChatBit[] = "Chat Mgmt"; + +char Tail[] = ""; + +char RouteHddr[] = "

Routes

" +""; + +char RouteLine[] = ""; +char xNodeHddr[] = "
" +"
PortCallQualityNode CountFrame CountRetriesPercentMaxframeFrackLast HeardQueuedRem Qual
%s%d%s%s%d%d%d%d%d%%d%d%02d:%02d%d%d
" +"
" +"" +"
" +"

Nodes %s

"; + +char NodeHddr[] = "
" +"" +"" +"
" +"

Nodes %s

"; + +char NodeLine[] = ""; + + +char StatsHddr[] = "

Node Stats

%s:%s
" +""; + +char PortStatsHddr[] = "

Stats for Port %d

"; + +char PortStatsLine[] = ""; + + +char Beacons[] = "

Beacon Configuration for Port %d

You need to be signed in to save changes

%s %d
" +"" +"
" +"" +"" +"" +"" +"" +"
Send Interval (Minutes)
To
Path
Send From File
Text
" +"" + +"

" +""; + + +char LinkHddr[] = "

Links

" +""; + +char LinkLine[] = ""; + +char UserHddr[] = "

Sessions

Far CallOur CallPortax.25 stateLink Typeax.25 Version
%s%s%d%s%s%d
"; + +char UserLine[] = ""; + +char TermSignon[] = "BPQ32 Node %s Terminal Access" +"

BPQ32 Node %s Terminal Access

" +"

Please enter username and password to access the node

" +"" +"
%s%s%s
" +"" +"
User
Password
" +"

" +""; + + +char PassError[] = "

Sorry, User or Password is invalid - please try again

"; + +char BusyError[] = "

Sorry, No sessions available - please try later

"; + +char LostSession[] = "Sorry, Session had been lost - refresh page to sign in again"; +char NoSessions[] = "Sorry, No Sessions available - refresh page to try again"; + +char TermPage[] = "" +"BPQ32 Node %s" +"" +"" +"

BPQ32 Node %s

" +"
" +"

" +"" +"" +""; + +char TermOutput[] = "" +"" +"" +"" +"" +"" +"" +"
\""; + + +// font-family:monospace;background-color:black;color:lawngreen;font-size:12px + +char TermOutputTail[] = "
"; + +/* +char InputLine[] = "" +"
" +"" +"
"; +*/ +char InputLine[] = "" +"
" +"\" id=inp type=text text width=100%% name=input />" +"
"; + +static char NodeSignon[] = "BPQ32 Node SYSOP Access" +"

BPQ32 Node %s SYSOP Access

" +"

This page sets Cookies. Don't continue if you object to this

" +"

Please enter Callsign and Password to access the Node

" +"
" +"" +"" +"
User
Password
" +"

"; + + +static char MailSignon[] = "BPQ32 Mail Server Access" +"

BPQ32 Mail Server %s Access

" +"

Please enter Callsign and Password to access the BBS

" +"
" +"" +"" +"
User
Password
" +"

"; + +static char ChatSignon[] = "BPQ32 Chat Server Access" +"

BPQ32 Chat Server %s Access

" +"

Please enter Callsign and Password to access the Chat Server

" +"
" +"" +"" +"
User
Password
" +"

"; + + +static char MailLostSession[] = "" +"
" +"Sorry, Session had been lost

    " +"
"; + + +static char ConfigEditPage[] = "" +"Edit Config" +"
" +"

" +"
"; + +static char EXCEPTMSG[80] = ""; + + +void UndoTransparency(char * input) +{ + char * ptr1, * ptr2; + char c; + int hex; + + if (input == NULL) + return; + + ptr1 = ptr2 = input; + + // Convert any %xx constructs + + while (1) + { + c = *(ptr1++); + + if (c == 0) + break; + + if (c == '%') + { + c = *(ptr1++); + if(isdigit(c)) + hex = (c - '0') << 4; + else + hex = (tolower(c) - 'a' + 10) << 4; + + c = *(ptr1++); + if(isdigit(c)) + hex += (c - '0'); + else + hex += (tolower(c) - 'a' + 10); + + *(ptr2++) = hex; + } + else if (c == '+') + *(ptr2++) = 32; + else + *(ptr2++) = c; + } + *ptr2 = 0; +} + + + + +VOID PollSession(struct HTTPConnectionInfo * Session) +{ + int state, change; + int count, len; + char Msg[400] = ""; + char Formatted[8192]; + char * ptr1, * ptr2; + char c; + int Line; + + // Poll Node + + SessionState(Session->Stream, &state, &change); + + if (change == 1) + { + int Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + if (state == 1)// Connected + Session->ScreenLines[Line] = _strdup("*** Connected
\r\n"); + else + Session->ScreenLines[Line] = _strdup("*** Disconnected
\r\n"); + + if (Line == 99) + Session->LastLine = 0; + + Session->Changed = TRUE; + } + + if (RXCount(Session->Stream) > 0) + { + int realLen = 0; + + do + { + GetMsg(Session->Stream, &Msg[0], &len, &count); + + // replace cr with
and space with   + + + ptr1 = Msg; + ptr2 = &Formatted[0]; + + if (Session->PartLine) + { + // Last line was incomplete - append to it + + realLen = Session->PartLine; + + Line = Session->LastLine - 1; + + if (Line < 0) + Line = 99; + + strcpy(Formatted, Session->ScreenLines[Line]); + ptr2 += strlen(Formatted); + + Session->LastLine = Line; + Session->PartLine = FALSE; + } + + while (len--) + { + c = *(ptr1++); + realLen++; + + if (c == 13) + { + int LineLen; + + strcpy(ptr2, "
\r\n"); + + // Write to screen + + Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + LineLen = (int)strlen(Formatted); + + // if line starts with a colour code, process it + + if (Formatted[0] == 0x1b && LineLen > 1) + { + int ColourCode = Formatted[1] - 10; + COLORREF Colour = Colours[ColourCode]; + char ColString[30]; + + memmove(&Formatted[20], &Formatted[2], LineLen); + sprintf(ColString, "", GetRValue(Colour), GetGValue(Colour), GetBValue(Colour)); + memcpy(Formatted, ColString, 20); + strcat(Formatted, ""); + LineLen =+ 28; + } + + Session->ScreenLineLen[Line] = LineLen; + Session->ScreenLines[Line] = _strdup(Formatted); + + if (Line == 99) + Session->LastLine = 0; + + ptr2 = &Formatted[0]; + realLen = 0; + + } + else if (c == 32) + { + memcpy(ptr2, " ", 6); + ptr2 += 6; + + // Make sure line isn't too long + // but beware of spaces expanded to   - count chars in line + + if ((realLen) > 100) + { + strcpy(ptr2, "
\r\n"); + + Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + Session->ScreenLines[Line] = _strdup(Formatted); + + if (Line == 99) + Session->LastLine = 0; + + ptr2 = &Formatted[0]; + realLen = 0; + } + } + else if (c == '>') + { + memcpy(ptr2, ">", 4); + ptr2 += 4; + } + else if (c == '<') + { + memcpy(ptr2, "<", 4); + ptr2 += 4; + } + else + *(ptr2++) = c; + + } + + *ptr2 = 0; + + if (ptr2 != &Formatted[0]) + { + // Incomplete line + + // Save to screen + + Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + Session->ScreenLines[Line] = _strdup(Formatted); + + if (Line == 99) + Session->LastLine = 0; + + Session->PartLine = realLen; + } + + // strcat(Session->ScreenBuffer, Formatted); + Session->Changed = TRUE; + + } while (count > 0); + } +} + + +VOID HTTPTimer() +{ + // Run every tick. Check for status change and data available + + struct HTTPConnectionInfo * Session = SessionList; // active term mode sessions + struct HTTPConnectionInfo * PreviousSession = NULL; + +// inf(); + + while (Session) + { + Session->KillTimer++; + + if (Session->Key[0] != 'T') + { + PreviousSession = Session; + Session = Session->Next; + continue; + } + + if (Session->KillTimer > 3000) // Around 5 mins + { + int i; + int Stream = Session->Stream; + + for (i = 0; i < 100; i++) + { + free(Session->ScreenLines[i]); + } + + SessionControl(Stream, 2, 0); + SessionState(Stream, &i, &i); + DeallocateStream(Stream); + + if (PreviousSession) + PreviousSession->Next = Session->Next; // Remove from chain + else + SessionList = Session->Next; + + free(Session); + + break; + } + + PollSession(Session); + + // if (Session->ResponseTimer == 0 && Session->Changed) + // Debugprintf("Data to send but no outstanding GET"); + + if (Session->ResponseTimer) + { + Session->ResponseTimer--; + + if (Session->ResponseTimer == 0 || Session->Changed) + { + SOCKET sock = Session->sock; + char _REPLYBUFFER[100000]; + int ReplyLen; + char Header[256]; + int HeaderLen; + int Last = Session->LastLine; + int n; + struct TNCINFO * TNC = Session->TNC; + struct TCPINFO * TCP = 0; + + if (TNC) + TCP = TNC->TCPInfo; + + if (TCP && TCP->WebTermCSS) + sprintf(_REPLYBUFFER, TermOutput, TCP->WebTermCSS); + else + sprintf(_REPLYBUFFER, TermOutput, ""); + + for (n = Last;;) + { + if ((strlen(Session->ScreenLines[n]) + strlen(_REPLYBUFFER)) < 99999) + strcat(_REPLYBUFFER, Session->ScreenLines[n]); + + if (n == 99) + n = -1; + + if (++n == Last) + break; + } + + ReplyLen = (int)strlen(_REPLYBUFFER); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", TermOutputTail); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + + Session->ResponseTimer = Session->Changed = 0; + } + } + PreviousSession = Session; + Session = Session->Next; + } +} + +struct HTTPConnectionInfo * AllocateSession(SOCKET sock, char Mode) +{ + time_t KeyVal; + struct HTTPConnectionInfo * Session = zalloc(sizeof(struct HTTPConnectionInfo)); + int i; + + if (Session == NULL) + return NULL; + + if (Mode == 'T') + { + // Terminal + + for (i = 0; i < 20; i++) + Session->ScreenLines[i] = _strdup("Scroll to end
"); + + for (i = 20; i < 100; i++) + Session->ScreenLines[i] = _strdup("
\r\n"); + + Session->Stream = FindFreeStream(); + + if (Session->Stream == 0) + return NULL; + + SessionControl(Session->Stream, 1, 0); + } + + KeyVal = (int)sock * time(NULL); + + sprintf(Session->Key, "%c%012X", Mode, (int)KeyVal); + + if (SessionList) + Session->Next = SessionList; + + SessionList = Session; + + return Session; +} + +struct HTTPConnectionInfo * FindSession(char * Key) +{ + struct HTTPConnectionInfo * Session = SessionList; + + while (Session) + { + if (strcmp(Session->Key, Key) == 0) + return Session; + + Session = Session->Next; + } + + return NULL; +} + +void ProcessTermInput(SOCKET sock, char * MsgPtr, int MsgLen, char * Key) +{ + char _REPLYBUFFER[2048]; + int ReplyLen; + char Header[256]; + int HeaderLen; + int State; + struct HTTPConnectionInfo * Session = FindSession(Key); + int Stream; + int maxlen = 1000; + + + if (Session == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, "%s", LostSession); + } + else + { + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * end = &MsgPtr[MsgLen]; + int Line = Session->LastLine++; + char * ptr1, * ptr2; + char c; + UCHAR hex; + + int msglen = end - input; + + struct TNCINFO * TNC = Session->TNC; + struct TCPINFO * TCP = 0; + + if (TNC) + TCP = TNC->TCPInfo; + + if (TCP && TCP->WebTermCSS) + maxlen -= strlen(TCP->WebTermCSS); + + if (MsgLen > maxlen) + { + Session->KillTimer = 99999; // close session + return; + } + + + if (TCP && TCP->WebTermCSS) + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, TCP->WebTermCSS); + else + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, ""); + + + Stream = Session->Stream; + + input += 10; + ptr1 = ptr2 = input; + + // Convert any %xx constructs + + while (ptr1 != end) + { + c = *(ptr1++); + if (c == '%') + { + c = *(ptr1++); + if(isdigit(c)) + hex = (c - '0') << 4; + else + hex = (tolower(c) - 'a' + 10) << 4; + + c = *(ptr1++); + if(isdigit(c)) + hex += (c - '0'); + else + hex += (tolower(c) - 'a' + 10); + + *(ptr2++) = hex; + } + else if (c == '+') + *(ptr2++) = 32; + else + *(ptr2++) = c; + } + + end = ptr2; + + *ptr2 = 0; + + strcat(input, "
\r\n"); + + free(Session->ScreenLines[Line]); + + Session->ScreenLines[Line] = _strdup(input); + + if (Line == 99) + Session->LastLine = 0; + + *end++ = 13; + *end = 0; + + SessionStateNoAck(Stream, &State); + + if (State == 0) + { + char AXCall[10]; + SessionControl(Stream, 1, 0); + if (BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION == NULL) + { + //No L4 sessions free + + ReplyLen = sprintf(_REPLYBUFFER, "%s", NoSessions); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return; + } + + ConvToAX25(Session->HTTPCall, AXCall); + ChangeSessionCallsign(Stream, AXCall); + if (Session->USER) + BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION->Secure_Session = Session->USER->Secure; + else + Debugprintf("HTTP Term Session->USER is NULL"); + } + + SendMsg(Stream, input, (int)(end - input)); + Session->Changed = TRUE; + Session->KillTimer = 0; + } + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); +} + + +void ProcessTermClose(SOCKET sock, char * MsgPtr, int MsgLen, char * Key, int LOCAL) +{ + char _REPLYBUFFER[8192]; + int ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, ""); + char Header[256]; + int HeaderLen; + struct HTTPConnectionInfo * Session = FindSession(Key); + + if (Session) + { + Session->KillTimer = 99999; + } + + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); +} + +int ProcessTermSignon(struct TNCINFO * TNC, SOCKET sock, char * MsgPtr, int MsgLen, int LOCAL) +{ + char _REPLYBUFFER[8192]; + int ReplyLen; + char Header[256]; + int HeaderLen; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Context, * Appl; + char NoApp[] = ""; + struct TCPINFO * TCP = TNC->TCPInfo; + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + goto Sendit; + } + user = strtok_s(&input[9], "&", &Context); + password = strtok_s(NULL, "=", &Context); + password = strtok_s(NULL, "&", &Context); + + Appl = strtok_s(NULL, "=", &Context); + Appl = strtok_s(NULL, "&", &Context); + + if (Appl == 0) + Appl = NoApp; + + if (password == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Appl); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PassError); + goto Sendit; + } + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if ((strcmp(password, USER->Password) == 0) && + ((_stricmp(user, USER->UserName) == 0 ) || (_stricmp(USER->UserName, "ANON") == 0))) + { + // ok + + struct HTTPConnectionInfo * Session = AllocateSession(sock, 'T'); + + if (Session) + { + char AXCall[10]; + ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, Session->Key, Session->Key, Session->Key); + if (_stricmp(USER->UserName, "ANON") == 0) + strcpy(Session->HTTPCall, _strupr(user)); + else + strcpy(Session->HTTPCall, USER->Callsign); + ConvToAX25(Session->HTTPCall, AXCall); + ChangeSessionCallsign(Session->Stream, AXCall); + BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION->Secure_Session = USER->Secure; + Session->USER = USER; + + if (USER->Appl[0]) + SendMsg(Session->Stream, USER->Appl, (int)strlen(USER->Appl)); + } + else + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", BusyError); + } + break; + } + } + + if (i == TCP->NumberofUsers) + { + // Not found + + ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Appl); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PassError); + } + + } + +Sendit: + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; +} + +char * LookupKey(char * Key) +{ + if (strcmp(Key, "##MY_CALLSIGN##") == 0) + { + char Mycall[10]; + memcpy(Mycall, &MYNODECALL, 10); + strlop(Mycall, ' '); + + return _strdup(Mycall); + } + return NULL; +} + + +int ProcessSpecialPage(char * Buffer, int FileSize) +{ + // replaces ##xxx### constructs with the requested data + + char * NewMessage = malloc(100000); + char * ptr1 = Buffer, * ptr2, * ptr3, * ptr4, * NewPtr = NewMessage; + int PrevLen; + int BytesLeft = FileSize; + int NewFileSize = FileSize; + char * StripPtr = ptr1; + + // strip comments blocks + + while (ptr4 = strstr(ptr1, ""); + if (ptr2) + { + PrevLen = (int)(ptr4 - ptr1); + memcpy(StripPtr, ptr1, PrevLen); + StripPtr += PrevLen; + ptr1 = ptr2 + 3; + BytesLeft = (int)(FileSize - (ptr1 - Buffer)); + } + } + + memcpy(StripPtr, ptr1, BytesLeft); + StripPtr += BytesLeft; + + BytesLeft = (int)(StripPtr - Buffer); + + FileSize = BytesLeft; + NewFileSize = FileSize; + ptr1 = Buffer; + ptr1[FileSize] = 0; + +loop: + ptr2 = strstr(ptr1, "##"); + + if (ptr2) + { + PrevLen = (int)(ptr2 - ptr1); // Bytes before special text + + ptr3 = strstr(ptr2+2, "##"); + + if (ptr3) + { + char Key[80] = ""; + int KeyLen; + char * NewText; + int NewTextLen; + + ptr3 += 2; + KeyLen = (int)(ptr3 - ptr2); + + if (KeyLen < 80) + memcpy(Key, ptr2, KeyLen); + + NewText = LookupKey(Key); + + if (NewText) + { + NewTextLen = (int)strlen(NewText); + NewFileSize = NewFileSize + NewTextLen - KeyLen; + // NewMessage = realloc(NewMessage, NewFileSize); + + memcpy(NewPtr, ptr1, PrevLen); + NewPtr += PrevLen; + memcpy(NewPtr, NewText, NewTextLen); + NewPtr += NewTextLen; + + free(NewText); + NewText = NULL; + } + else + { + // Key not found, so just leave + + memcpy(NewPtr, ptr1, PrevLen + KeyLen); + NewPtr += (PrevLen + KeyLen); + } + + ptr1 = ptr3; // Continue scan from here + BytesLeft = (int)(Buffer + FileSize - ptr3); + } + else // Unmatched ## + { + memcpy(NewPtr, ptr1, PrevLen + 2); + NewPtr += (PrevLen + 2); + ptr1 = ptr2 + 2; + } + goto loop; + } + + // Copy Rest + + memcpy(NewPtr, ptr1, BytesLeft); + NewMessage[NewFileSize] = 0; + + strcpy(Buffer, NewMessage); + free(NewMessage); + + return NewFileSize; +} + +int SendMessageFile(SOCKET sock, char * FN, BOOL OnlyifExists, int allowDeflate) +{ + int FileSize = 0, Sent, Loops = 0; + char * MsgBytes; + char MsgFile[512]; + FILE * hFile; + int ReadLen; + BOOL Special = FALSE; + int Len; + int HeaderLen; + char Header[256]; + char TimeString[64]; + char FileTimeString[64]; + struct stat STAT; + char * ptr; + char * Compressed = 0; + char Encoding[] = "Content-Encoding: deflate\r\n"; + char Type[64] = "Content-Type: text/html\r\n"; + +#ifdef WIN32 + + struct _EXCEPTION_POINTERS exinfo; + strcpy(EXCEPTMSG, "SendMessageFile"); + + __try { +#endif + + UndoTransparency(FN); + + if (strstr(FN, "..")) + { + FN[0] = '/'; + FN[1] = 0; + } + + if (strlen(FN) > 256) + { + FN[256] = 0; + Debugprintf("HTTP File Name too long %s", FN); + } + + if (strcmp(FN, "/") == 0) + if (APRSActive) + sprintf(MsgFile, "%s/HTML/index.html", BPQDirectory); + else + sprintf(MsgFile, "%s/HTML/indexnoaprs.html", BPQDirectory); + else + sprintf(MsgFile, "%s/HTML%s", BPQDirectory, FN); + + + // First see if file exists so we can override standard ones in code + + if (stat(MsgFile, &STAT) == 0 && (hFile = fopen(MsgFile, "rb"))) + { + FileSize = STAT.st_size; + + MsgBytes = zalloc(FileSize + 1); + + ReadLen = (int)fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + // ft.QuadPart -= 116444736000000000; + // ft.QuadPart /= 10000000; + + // ctime = ft.LowPart; + + FormatTime3(FileTimeString, STAT.st_ctime); + } + else + { + // See if it is a hard coded file + + MsgBytes = GetStandardPage(&FN[1], &FileSize); + + if (MsgBytes) + { + if (FileSize == 0) + FileSize = strlen(MsgBytes); + + FormatTime3(FileTimeString, 0); + } + else + { + if (OnlyifExists) // Set if we dont want an error response if missing + return -1; + + Len = sprintf(Header, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + send(sock, Header, Len, 0); + return 0; + } + } + + // if HTML file, look for ##...## substitutions + + if ((strcmp(FN, "/") == 0 || strstr(FN, "htm" ) || strstr(FN, "HTM")) && strstr(MsgBytes, "##" )) + { + FileSize = ProcessSpecialPage(MsgBytes, FileSize); + FormatTime3(FileTimeString, time(NULL)); + + } + + FormatTime3(TimeString, time(NULL)); + + ptr = FN; + + while (strchr(ptr, '.')) + { + ptr = strchr(ptr, '.'); + ++ptr; + } + + if (_stricmp(ptr, "js") == 0) + strcpy(Type, "Content-Type: text/javascript\r\n"); + + if (_stricmp(ptr, "css") == 0) + strcpy(Type, "Content-Type: text/css\r\n"); + + if (_stricmp(ptr, "pdf") == 0) + strcpy(Type, "Content-Type: application/pdf\r\n"); + + if (allowDeflate) + { + Compressed = Compressit(MsgBytes, FileSize, &FileSize); + } + else + { + Encoding[0] = 0; + Compressed = MsgBytes; + } + + if (_stricmp(ptr, "jpg") == 0 || _stricmp(ptr, "jpeg") == 0 || _stricmp(ptr, "png") == 0 || + _stricmp(ptr, "gif") == 0 || _stricmp(ptr, "bmp") == 0 || _stricmp(ptr, "ico") == 0) + strcpy(Type, "Content-Type: image\r\n"); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "%s%s" + "\r\n", FileSize, TimeString, FileTimeString, Type, Encoding); + + send(sock, Header, HeaderLen, 0); + + Sent = send(sock, Compressed, FileSize, 0); + + while (Sent != FileSize && Loops++ < 3000) // 100 secs max + { + if (Sent > 0) // something sent + { +// Debugprintf("%d out of %d sent", Sent, FileSize); + FileSize -= Sent; + memmove(Compressed, &Compressed[Sent], FileSize); + } + + Sleep(30); + Sent = send(sock, Compressed, FileSize, 0); + } + +// Debugprintf("%d out of %d sent %d loops", Sent, FileSize, Loops); + + + free (MsgBytes); + if (allowDeflate) + free (Compressed); + +#ifdef WIN32 + } +#include "StdExcept.c" + Debugprintf("Sending FIle %s", FN); +} +#endif + +return 0; +} + +VOID sendandcheck(SOCKET sock, const char * Buffer, int Len) +{ + int Loops = 0; + int Sent = send(sock, Buffer, Len, 0); + char * Copy = NULL; + + while (Sent != Len && Loops++ < 300) // 10 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, Len, Loops); + + if (Copy == NULL) + { + Copy = malloc(Len); + memcpy(Copy, Buffer, Len); + } + + if (Sent > 0) // something sent + { + Len -= Sent; + memmove(Copy, &Copy[Sent], Len); + } + + Sleep(30); + Sent = send(sock, Copy, Len, 0); + } + + if (Copy) + free(Copy); + + return; +} + +int RefreshTermWindow(struct TCPINFO * TCP, struct HTTPConnectionInfo * Session, char * _REPLYBUFFER) +{ + char Msg[400] = ""; + int HeaderLen, ReplyLen; + char Header[256]; + + PollSession(Session); // See if anything received + + if (Session->Changed) + { + int Last = Session->LastLine; + int n; + + if (TCP && TCP->WebTermCSS) + sprintf(_REPLYBUFFER, TermOutput, TCP->WebTermCSS); + else + sprintf(_REPLYBUFFER, TermOutput, ""); + + for (n = Last;;) + { + strcat(_REPLYBUFFER, Session->ScreenLines[n]); + + if (n == 99) + n = -1; + + if (++n == Last) + break; + } + + Session->Changed = 0; + + ReplyLen = (int)strlen(_REPLYBUFFER); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", TermOutputTail); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen); + sendandcheck(Session->sock, Header, HeaderLen); + sendandcheck(Session->sock, _REPLYBUFFER, ReplyLen); + + return 1; + } + else + return 0; +} + +int SetupNodeMenu(char * Buff, int LOCAL) +{ + int Len = 0, i; + struct TNCINFO * TNC; + int top = 0, left = 0; + + char NodeMenuHeader[] = "%s's BPQ32 Web Server" + "" + + "" + "

BPQ32 Node %s

" + "

" + "" + "" + "" + "" + "" + "" + "%s%s%s%s%s%s"; + + char DriverBit[] = "" + ""; + + char APRSBit[] = ""; + + char MailBit[] = "" + ""; + + char ChatBit[] = ""; + char SigninBit[] = ""; + + char NodeTail[] = + "" + "
RoutesNodesPortsLinksUsersStatsTerminalDriver WindowsStream StatusAPRS PagesMail MgmtWebMailChat MgmtSYSOP SigninEdit Config" + "
"; + + + Len = sprintf(Buff, NodeMenuHeader, Mycall); + + for (i=1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->WebWindowProc) + { + Len += sprintf(&Buff[Len], NodeMenuLine, i, TNC->WebWinX, TNC->WebWinY, top, left); + top += 22; + left += 22; + } + } + + Len += sprintf(&Buff[Len], NodeMenuRest, Mycall, + DriverBit, + (APRSWeb)?APRSBit:"", + (IncludesMail)?MailBit:"", (IncludesChat)?ChatBit:"", (LOCAL)?"":SigninBit, NodeTail); + + return Len; +} + +VOID SaveConfigFile(SOCKET sock , char * MsgPtr, char * Rest, int LOCAL) +{ + int ReplyLen = 0; + char * ptr, * ptr1, * ptr2, *input; + char c; + int MsgLen, WriteLen = 0; + char inputname[250]="bpq32.cfg"; + FILE *fp1; + char Header[256]; + int HeaderLen; + char Reply[4096]; + char Mess[256]; + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + struct stat STAT; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + // ReplyLen = sprintf(Reply, "%s", ""); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return; + } + + ptr = strstr(input, "&Save="); + + if (ptr) + { + *ptr = 0; + + // Undo any % transparency + + ptr1 = ptr2 = input + 8; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + c = m * 16 + n; + } + else if (c == '+') + c = ' '; + +#ifndef WIN32 + if (c != 13) // Strip CR if Linux +#endif + *(ptr2++) = c; + + c = *(ptr1++); + + } + + *(ptr2++) = 0; + + MsgLen = (int)strlen(input + 8); + + if (ConfigDirectory[0] == 0) + { + strcpy(inputname, "bpq32.cfg"); + } + else + { + strcpy(inputname,ConfigDirectory); + strcat(inputname,"/"); + strcat(inputname, "bpq32.cfg"); + } + + // Make a backup of the config + + // Keep 4 Generations + + strcpy(Backup2, inputname); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, inputname); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, inputname); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, inputname); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); // Move .bak to .bak.1 + + CopyFile(inputname, Backup1, FALSE); // Copy to .bak + + // Get length to compare with new length + + stat(inputname, &STAT); + + fp1 = fopen(inputname, "wb"); + + if (fp1) + { + WriteLen = (int)fwrite(input + 8, 1, MsgLen, fp1); + fclose(fp1); + } + + if (WriteLen != MsgLen) + sprintf_s(Mess, sizeof(Mess), "Failed to write Config File"); + else + sprintf_s(Mess, sizeof(Mess), "Configuration Saved, Orig Length %d New Length %d", + STAT.st_size, MsgLen); + } + + ReplyLen = sprintf(Reply, "", Mess); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + } + return; +} + +// Compress using deflate. Caller must free output buffer after use + +unsigned char * Compressit(unsigned char * In, int Len, int * OutLen) +{ + z_stream defstream; + int maxSize; + unsigned char * Out; + + defstream.zalloc = Z_NULL; + defstream.zfree = Z_NULL; + defstream.opaque = Z_NULL; + + defstream.avail_in = Len; // size of input + defstream.next_in = (Bytef *)In; // input char array + + deflateInit(&defstream, Z_BEST_COMPRESSION); + maxSize = deflateBound(&defstream, Len); + + Out = malloc(maxSize); + + defstream.avail_out = maxSize; // size of output + defstream.next_out = (Bytef *)Out; // output char array + + deflate(&defstream, Z_FINISH); + deflateEnd(&defstream); + + *OutLen = defstream.total_out; + + return Out; +} + + +int InnerProcessHTTPMessage(struct ConnectionInfo * conn) +{ + struct TCPINFO * TCP = conn->TNC->TCPInfo; + SOCKET sock = conn->socket; + char * MsgPtr = conn->InputBuffer; + int MsgLen = conn->InputLen; + int InputLen = 0; + int OutputLen = 0; + int Bufferlen; + struct HTTPConnectionInfo CI; + struct HTTPConnectionInfo * sockptr = &CI; + struct HTTPConnectionInfo * Session = NULL; + + char URL[100000]; + char * ptr; + char * encPtr = 0; + int allowDeflate = 0; + char * Compressed = 0; + char * HostPtr = 0; + + char * Context, * Method, * NodeURL = 0, * Key; + char _REPLYBUFFER[250000]; + char Reply[250000]; + + int ReplyLen = 0; + char Header[256]; + int HeaderLen; + char TimeString[64]; + BOOL LOCAL = FALSE; + BOOL COOKIE = FALSE; + int Len; + char * WebSock = 0; + + char PortsHddr[] = "

Ports

" + ""; + +// char PortLine[] = ""; + + char PortLineWithBeacon[] = "" + "\r\n"; + + char SessionPortLine[] = "" + "\r\n"; + + char PortLineWithDriver[] = "" + "\r\n"; + + + char PortLineWithBeaconAndDriver[] = "" + "" + "\r\n"; + + char RigControlLine[] = "" + "\r\n"; + + + char Encoding[] = "Content-Encoding: deflate\r\n"; + +#ifdef WIN32xx + + struct _EXCEPTION_POINTERS exinfo; + strcpy(EXCEPTMSG, "ProcessHTTPMessage"); + + __try { +#endif + + Len = (int)strlen(MsgPtr); + if (Len > 100000) + return 0; + + strcpy(URL, MsgPtr); + + HostPtr = strstr(MsgPtr, "Host: "); + + WebSock = strstr(MsgPtr, "Upgrade: websocket"); + + if (HostPtr) + { + uint32_t Host; + char Hostname[32]= ""; + struct LOCALNET * LocalNet = conn->TNC->TCPInfo->LocalNets; + + HostPtr += 6; + memcpy(Hostname, HostPtr, 31); + strlop(Hostname, ':'); + Host = inet_addr(Hostname); + + if (strcmp(Hostname, "127.0.0.1") == 0) + LOCAL = TRUE; + else + { + if (conn->sin.sin_family != AF_INET6) + { + while(LocalNet) + { + uint32_t MaskedHost = conn->sin.sin_addr.s_addr & LocalNet->Mask; + if (MaskedHost == LocalNet->Network) + { + LOCAL = 1; + break; + } + LocalNet = LocalNet->Next; + } + } + } + } + + encPtr = stristr(MsgPtr, "Accept-Encoding:"); + + if (encPtr && stristr(encPtr, "deflate")) + allowDeflate = 1; + else + Encoding[0] = 0; + + ptr = strstr(MsgPtr, "BPQSessionCookie=N"); + + if (ptr) + { + COOKIE = TRUE; + Key = ptr + 17; + ptr = strchr(Key, ','); + if (ptr) + { + *ptr = 0; + Session = FindSession(Key); + *ptr = ','; + } + else + { + ptr = strchr(Key, 13); + if (ptr) + { + *ptr = 0; + Session = FindSession(Key); + *ptr = 13; + } + } + } + + if (WebSock) + { + // Websock connection request - Reply and remember state. + + char KeyMsg[128]; + char Webx[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // Fixed UID + char Hash[64] = ""; + char * Hash64; // base 64 version + char * ptr; + + //Sec-WebSocket-Key: l622yZS3n+zI+hR6SVWkPw== + + char ReplyMsg[] = + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n" +// "Sec-WebSocket-Protocol: chat\r\n" + "\r\n"; + + ptr = strstr(MsgPtr, "Sec-WebSocket-Key:"); + + if (ptr) + { + ptr += 18; + while (*ptr == ' ') + ptr++; + + memcpy(KeyMsg, ptr, 40); + strlop(KeyMsg, 13); + strlop(KeyMsg, ' '); + strcat(KeyMsg, Webx); + + SHA1PasswordHash(KeyMsg, Hash); + Hash64 = byte_base64_encode(Hash, 20); + + conn->WebSocks = 1; + strlop(&URL[4], ' '); + strcpy(conn->WebURL, &URL[4]); + + ReplyLen = sprintf(Reply, ReplyMsg, Hash64); + + free(Hash64); + goto Returnit; + + } + } + + + ptr = strstr(URL, " HTTP"); + + if (ptr) + *ptr = 0; + + Method = strtok_s(URL, " ", &Context); + + memcpy(Mycall, &MYNODECALL, 10); + strlop(Mycall, ' '); + + + // Look for API messages + + if (_memicmp(Context, "/api/", 5) == 0 || _stricmp(Context, "/api") == 0) + { + char * Compressed; + + // if for mail api process signon here and rearrange url from + // api/v1/mail to mail/api/v1 so it goes to mail handler later + + if (_memicmp(Context, "/api/v1/mail/", 13) == 0) + { + memcpy(MsgPtr, "GET /mail/api/v1/", 17); + + if (memcmp(&Context[13], "login", 5) == 0) + { + ReplyLen = ProcessMailAPISignon(TCP, MsgPtr, "M", Reply, &Session, FALSE, LOCAL); + memcpy(MsgPtr, "GET /mail/api/v1/", 17); + + if (ReplyLen) // Error message + goto Returnit; + } + + memcpy(Context, "/mail/api/v1/", 13); + goto doHeader; + } + else + { + ReplyLen = APIProcessHTTPMessage(_REPLYBUFFER, Method, Context, MsgPtr, LOCAL, COOKIE); + + if (memcmp(_REPLYBUFFER, "HTTP", 4) == 0) + { + // Full Message - just send it + + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + + return 0; + } + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/json\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "%s\r\n", ReplyLen, Encoding); + + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + } + + if (_memicmp(Context, "/rhp/", 5) == 0 || _stricmp(Context, "/rhp") == 0) + { + { + ReplyLen = RHPProcessHTTPMessage(conn, _REPLYBUFFER, Method, Context, MsgPtr, LOCAL, COOKIE); + + if (memcmp(_REPLYBUFFER, "HTTP", 4) == 0) + { + // Full Message - just send it + + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + + return 0; + } + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/json\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "%s\r\n", ReplyLen, Encoding); + + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + } + + + // APRS process internally + + if (_memicmp(Context, "/APRS/", 6) == 0 || _stricmp(Context, "/APRS") == 0) + { + APRSProcessHTTPMessage(sock, MsgPtr, LOCAL, COOKIE); + return 0; + } + + + if (_stricmp(Context, "/Node/Signon?Node") == 0) + { + char * IContext; + + NodeURL = strtok_s(Context, "?", &IContext); + Key = strtok_s(NULL, "?", &IContext); + + ProcessNodeSignon(sock, TCP, MsgPtr, Key, Reply, &Session, LOCAL); + return 0; + + } + + // If for Mail or Chat, check for a session, and send login screen if necessary + + // Moved here to simplify operation with both internal and external clients + + if (_memicmp(Context, "/Mail/", 6) == 0) + { + int RLen = 0; + char Appl; + char * input; + char * IContext; + + NodeURL = strtok_s(Context, "?", &IContext); + Key = strtok_s(NULL, "?", &IContext); + + if (_stricmp(NodeURL, "/Mail/Signon") == 0) + { + ReplyLen = ProcessMailSignon(TCP, MsgPtr, Key, Reply, &Session, FALSE, LOCAL); + + if (ReplyLen) + { + goto Returnit; + } + +#ifdef LINBPQ + strcpy(Context, "/Mail/Header"); +#else + strcpy(MsgPtr, "POST /Mail/Header"); +#endif + goto doHeader; + } + + if (_stricmp(NodeURL, "/Mail/Lost") == 0) + { + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input && strstr(input, "Cancel=Exit")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + RLen = ReplyLen; + goto Returnit; + } + if (Key) + Appl = Key[0]; + + Key = 0; + } + + if (Key == 0 || Key[0] == 0) + { + // No Session + + // if not local send a signon screen, else create a user session + + if (LOCAL || COOKIE) + { + Session = AllocateSession(sock, 'M'); + + if (Session) + { + strcpy(Context, "/Mail/Header"); + goto doHeader; + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + RLen = ReplyLen; + + goto Returnit; + + } + + ReplyLen = sprintf(Reply, MailSignon, Mycall, Mycall); + + RLen = ReplyLen; + goto Returnit; + } + + Session = FindSession(Key); + + + if (Session == NULL && _memicmp(Context, "/Mail/API/", 10) != 0) + { + ReplyLen = sprintf(Reply, MailLostSession, Key); + RLen = ReplyLen; + goto Returnit; + } + } + + if (_memicmp(Context, "/Chat/", 6) == 0) + { + int RLen = 0; + char Appl; + char * input; + char * IContext; + + + HostPtr = strstr(MsgPtr, "Host: "); + + if (HostPtr) + { + uint32_t Host; + char Hostname[32]= ""; + struct LOCALNET * LocalNet = conn->TNC->TCPInfo->LocalNets; + + HostPtr += 6; + memcpy(Hostname, HostPtr, 31); + strlop(Hostname, ':'); + Host = inet_addr(Hostname); + + if (strcmp(Hostname, "127.0.0.1") == 0) + LOCAL = TRUE; + else while(LocalNet) + { + uint32_t MaskedHost = Host & LocalNet->Mask; + if (MaskedHost == LocalNet->Network) + { + char * rest; + LOCAL = 1; + rest = strchr(HostPtr, 13); + if (rest) + { + memmove(HostPtr + 9, rest, strlen(rest) + 1); + memcpy(HostPtr, "127.0.0.1", 9); + break; + } + } + LocalNet = LocalNet->Next; + } + } + + NodeURL = strtok_s(Context, "?", &IContext); + Key = strtok_s(NULL, "?", &IContext); + + if (_stricmp(NodeURL, "/Chat/Signon") == 0) + { + ReplyLen = ProcessChatSignon(TCP, MsgPtr, Key, Reply, &Session, LOCAL); + + if (ReplyLen) + { + goto Returnit; + } + +#ifdef LINBPQ + strcpy(Context, "/Chat/Header"); +#else + strcpy(MsgPtr, "POST /Chat/Header"); +#endif + goto doHeader; + } + + if (_stricmp(NodeURL, "/Chat/Lost") == 0) + { + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input && strstr(input, "Cancel=Exit")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + RLen = ReplyLen; + goto Returnit; + } + if (Key) + Appl = Key[0]; + + Key = 0; + } + + if (Key == 0 || Key[0] == 0) + { + // No Session + + // if not local send a signon screen, else create a user session + + if (LOCAL || COOKIE) + { + Session = AllocateSession(sock, 'C'); + + if (Session) + { + strcpy(Context, "/Chat/Header"); + goto doHeader; + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + RLen = ReplyLen; + + goto Returnit; + + } + + ReplyLen = sprintf(Reply, ChatSignon, Mycall, Mycall); + + RLen = ReplyLen; + goto Returnit; + } + + Session = FindSession(Key); + + if (Session == NULL) + { + int Sent, Loops = 0; + ReplyLen = sprintf(Reply, MailLostSession, Key); + RLen = ReplyLen; +Returnit: + if (memcmp(Reply, "HTTP", 4) == 0) + { + // Full Header provided by appl - just send it + + // Send may block + + Sent = send(sock, Reply, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(Reply, &Reply[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sock, Reply, ReplyLen, 0); + } + + return 0; + } + + if (NodeURL && _memicmp(NodeURL, "/mail/api/", 10) != 0) + { + // Add tail + + strcpy(&Reply[ReplyLen], Tail); + ReplyLen += strlen(Tail); + } + + // compress if allowed + + if (allowDeflate) + Compressed = Compressit(Reply, ReplyLen, &ReplyLen); + else + Compressed = Reply; + + if (NodeURL && _memicmp(NodeURL, "/mail/api/", 10) == 0) + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: application/json\r\nConnection: close\r\n%s\r\n", ReplyLen, Encoding); + else + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n%s\r\n", ReplyLen, Encoding); + + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + } + +doHeader: + +#ifdef LINBPQ + + if ((_memicmp(Context, "/MAIL/", 6) == 0) || (_memicmp(Context, "/WebMail", 8) == 0)) + { + char _REPLYBUFFER[250000]; + struct HTTPConnectionInfo Dummy = {0}; + int Sent, Loops = 0; + char token[16] = ""; + + // look for auth header + + const char * auth_header = "Authorization: Bearer "; + char * token_begin = strstr(MsgPtr, auth_header); + int Flags = 0, n; + + char * Tok; + char * param; + + if (token_begin) + { + // Using Auth Header + + // Extract the token from the request (assuming it's present in the request headers) + + token_begin += strlen(auth_header); // Move to the beginning of the token + strncpy(token, token_begin, 13); + token[13] = '\0'; // Null-terminate the token + } + + ReplyLen = 0; + + if (Session == 0) + Session = &Dummy; + + if (LOCAL) + Session->TNC = (void *)1; // TNC only used for Web Terminal Sessions + else + Session->TNC = (void *)0; + + ProcessMailHTTPMessage(Session, Method, Context, MsgPtr, _REPLYBUFFER, &ReplyLen, MsgLen, token); + + if (Context && _memicmp(Context, "/mail/api/", 10) == 0) + { + if (memcmp(_REPLYBUFFER, "HTTP", 4) == 0) + { + // Full Header provided by appl - just send it + + // Send may block + + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + } + return 0; + } + + // compress if allowed + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: application/json\r\nConnection: close\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + + if (memcmp(_REPLYBUFFER, "HTTP", 4) == 0) + { + // Full Header provided by appl - just send it + + // Send may block + + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + } + return 0; + } + + if (Context && _memicmp(Context, "/mail/api/", 10) != 0) + { + + // Add tail + + strcpy(&_REPLYBUFFER[ReplyLen], Tail); + ReplyLen += strlen(Tail); + + } + + // compress if allowed + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = Reply; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + + +/* + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + + // Send may block + + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + + if (Sent == -1) + return 0; + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + } + + send(sock, Tail, (int)strlen(Tail), 0); + return 0; +*/ + } + + if (_memicmp(Context, "/CHAT/", 6) == 0) + { + char _REPLYBUFFER[100000]; + + ReplyLen = 0; + + ProcessChatHTTPMessage(Session, Method, Context, MsgPtr, _REPLYBUFFER, &ReplyLen); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 0; + + } + + + /* + Sent = send(sock, _REPLYBUFFER, InputLen, 0); + + while (Sent != InputLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], InputLen); + } + + Sleep(30); + Sent = send(sock, _REPLYBUFFER, InputLen, 0); + } + return 0; + } + */ +#else + + // Pass to MailChat if active + + NodeURL = Context; + + if ((_memicmp(Context, "/MAIL/", 6) == 0) || (_memicmp(Context, "/WebMail", 8) == 0)) + { + // If for Mail, Pass to Mail Server via Named Pipe + + HANDLE hPipe; + + hPipe = CreateFile(MAILPipeFileName, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hPipe == (HANDLE)-1) + { + InputLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 28\r\n\r\nMail Data is not available\r\n"); + send(sock, Reply, InputLen, 0); + } + else + { + // int Sent; + int Loops = 0; + struct HTTPConnectionInfo Dummy = {0}; + + if (Session == 0) + Session = &Dummy; + + if (LOCAL) + Session->TNC = (struct TNCINFO *)(uintptr_t)1; // TNC is only used on Web Terminal Sessions so can reuse as LOCAL flag + else + Session->TNC = 0; + + WriteFile(hPipe, Session, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + WriteFile(hPipe, MsgPtr, MsgLen, &InputLen, NULL); + + + ReadFile(hPipe, Session, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + ReadFile(hPipe, Reply, 250000, &ReplyLen, NULL); + if (ReplyLen <= 0) + { + InputLen = GetLastError(); + } + + CloseHandle(hPipe); + goto Returnit; + } + return 0; + } + + if (_memicmp(Context, "/CHAT/", 6) == 0) + { + HANDLE hPipe; + + hPipe = CreateFile(CHATPipeFileName, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hPipe == (HANDLE)-1) + { + InputLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 28\r\n\r\nChat Data is not available\r\n"); + send(sock, Reply, InputLen, 0); + } + else + { + // int Sent; + int Loops = 0; + + WriteFile(hPipe, Session, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + WriteFile(hPipe, MsgPtr, MsgLen, &InputLen, NULL); + + + ReadFile(hPipe, Session, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + ReadFile(hPipe, Reply, 100000, &ReplyLen, NULL); + if (ReplyLen <= 0) + { + InputLen = GetLastError(); + } + + CloseHandle(hPipe); + goto Returnit; + } + return 0; + } + +#endif + + NodeURL = strtok_s(NULL, "?", &Context); + + if (NodeURL == NULL) + return 0; + + if (strcmp(Method, "POST") == 0) + { + if (_stricmp(NodeURL, "/Node/freqOffset") == 0) + { + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + int port = atoi(Context); + + if (input == 0) + return 1; + + input += 4; + + if (port > 0 && port <= MaxBPQPortNo) + { + struct TNCINFO * TNC = TNCInfo[port]; + char value[6]; + + if (TNC == 0) + return 1; + + TNC->TXOffset = atoi(input); +#ifdef WIN32 + sprintf(value, "%d", TNC->TXOffset); + MySetWindowText(TNC->xIDC_TXTUNEVAL, value); + SendMessage(TNC->xIDC_TXTUNE, TBM_SETPOS, (WPARAM) TRUE, (LPARAM) TNC->TXOffset); // min. & max. positions + +#endif + } + return 1; + } + + if (_stricmp(NodeURL, "/Node/PortAction") == 0) + { + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + int port = atoi(Context); + + if (input == 0) + return 1; + + input += 4; + + if (port > 0 && port <= MaxBPQPortNo) + { + struct TNCINFO * TNC = TNCInfo[port]; + + if (TNC == 0) + return 1; + + if (LOCAL == FALSE && COOKIE == FALSE) + return 1; + + if (strcmp(input, "Abort") == 0) + { + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + } + else if (strcmp(input, "Kill") == 0) + { + TNC->DontRestart = TRUE; + KillTNC(TNC); + } + else if (strcmp(input, "KillRestart") == 0) + { + TNC->DontRestart = FALSE; + KillTNC(TNC); + RestartTNC(TNC); + + } + } + return 1; + } + + if (_stricmp(NodeURL, "/TermInput") == 0) + { + ProcessTermInput(sock, MsgPtr, MsgLen, Context); + return 0; + } + + if (_stricmp(NodeURL, "/Node/TermSignon") == 0) + { + ProcessTermSignon(conn->TNC, sock, MsgPtr, MsgLen, LOCAL); + } + + if (_stricmp(NodeURL, "/Node/Signon") == 0) + { + ProcessNodeSignon(sock, TCP, MsgPtr, Key, Reply, &Session, LOCAL); + return 0; + } + + if (_stricmp(NodeURL, "/Node/TermClose") == 0) + { + ProcessTermClose(sock, MsgPtr, MsgLen, Context, LOCAL); + return 0; + } + + if (_stricmp(NodeURL, "/Node/BeaconAction") == 0) + { + char Header[256]; + int HeaderLen; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + int Port; + char Param[100]; +#ifndef LINBPQ + int retCode, disp; + char Key[80]; + HKEY hKey; +#endif + struct PORTCONTROL * PORT; + int Slot = 0; + + + if (LOCAL == FALSE && COOKIE == FALSE) + { + // Send Not Authorized + + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Not authorized - please sign in"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; + } + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; + } + + GetParam(input, "Port=", &Param[0]); + Port = atoi(&Param[0]); + PORT = GetPortTableEntryFromPortNum(Port); // Need slot not number + if (PORT) + Slot = PORT->PortSlot; + + GetParam(input, "Every=", &Param[0]); + Interval[Slot] = atoi(&Param[0]); + + GetParam(input, "Dest=", &Param[0]); + _strupr(Param); + strcpy(UIUIDEST[Slot], &Param[0]); + + GetParam(input, "Path=", &Param[0]); + _strupr(Param); + if (UIUIDigi[Slot]) + free(UIUIDigi[Slot]); + UIUIDigi[Slot] = _strdup(&Param[0]); + + GetParam(input, "File=", &Param[0]); + strcpy(FN[Slot], &Param[0]); + GetParam(input, "Text=", &Param[0]); + strcpy(Message[Slot], &Param[0]); + + MinCounter[Slot] = Interval[Slot]; + + SendFromFile[Slot] = 0; + + if (FN[Slot][0]) + SendFromFile[Slot] = 1; + + SetupUI(Slot); + +#ifdef LINBPQ + SaveUIConfig(); +#else + SaveUIConfig(); + + wsprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil\\UIPort%d", Port); + + retCode = RegCreateKeyEx(REGTREE, + Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "UIDEST", 0, REG_SZ,(BYTE *)&UIUIDEST[Port][0], strlen(&UIUIDEST[Port][0])); + retCode = RegSetValueEx(hKey, "FileName", 0, REG_SZ,(BYTE *)&FN[Port][0], strlen(&FN[Port][0])); + retCode = RegSetValueEx(hKey, "Message", 0, REG_SZ,(BYTE *)&Message[Port][0], strlen(&Message[Port][0])); + retCode = RegSetValueEx(hKey, "Interval", 0, REG_DWORD,(BYTE *)&Interval[Port], 4); + retCode = RegSetValueEx(hKey, "SendFromFile", 0, REG_DWORD,(BYTE *)&SendFromFile[Port], 4); + retCode = RegSetValueEx(hKey, "Digis",0, REG_SZ, UIUIDigi[Port], strlen(UIUIDigi[Port])); + + RegCloseKey(hKey); + } +#endif + if (strstr(input, "Test=Test")) + SendUIBeacon(Slot); + + + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], Beacons, Port, + Interval[Slot], &UIUIDEST[Slot][0], &UIUIDigi[Slot][0], &FN[Slot][0], &Message[Slot][0], Port); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; + } + + if (_stricmp(NodeURL, "/Node/CfgSave") == 0) + { + // Save Config File + + SaveConfigFile(sock, MsgPtr, Key, LOCAL); + return 0; + } + + if (_stricmp(NodeURL, "/Node/LogAction") == 0) + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; + } + + + if (_stricmp(NodeURL, "/Node/ARDOPAbort") == 0) + { + int port = atoi(Context); + + if (port > 0 && port <= MaxBPQPortNo) + { + struct TNCINFO * TNC = TNCInfo[port]; + + if (TNC && TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + + if (TNC && TNC->WebWindowProc) + ReplyLen = TNC->WebWindowProc(TNC, _REPLYBUFFER, LOCAL); + + + ReplyLen = sprintf(Reply, "", "Ok"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + // goto SendResp; + + // HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + strlen(Tail)); + // send(sock, Header, HeaderLen, 0); + // send(sock, _REPLYBUFFER, ReplyLen, 0); + // send(sock, Tail, strlen(Tail), 0); + + return 1; + } + + } + + send(sock, _REPLYBUFFER, InputLen, 0); + return 0; + } + + if (_stricmp(NodeURL, "/") == 0 || _stricmp(NodeURL, "/Index.html") == 0) + { + // Send if present, else use default + + Bufferlen = SendMessageFile(sock, NodeURL, TRUE, allowDeflate); // return -1 if not found + + if (Bufferlen != -1) + return 0; // We've sent it + else + { + if (APRSApplConnected) + ReplyLen = sprintf(_REPLYBUFFER, Index, Mycall, Mycall); + else + ReplyLen = sprintf(_REPLYBUFFER, IndexNoAPRS, Mycall, Mycall); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 0; + } + } + + else if (_stricmp(NodeURL, "/NodeMenu.html") == 0 || _stricmp(NodeURL, "/Node/NodeMenu.html") == 0) + { + // Send if present, else use default + + char Menu[] = "/NodeMenu.html"; + + Bufferlen = SendMessageFile(sock, Menu, TRUE, allowDeflate); // return -1 if not found + + if (Bufferlen != -1) + return 0; // We've sent it + } + + else if (_memicmp(NodeURL, "/aisdata.txt", 12) == 0) + { + char * Compressed; + ReplyLen = GetAISPageInfo(_REPLYBUFFER, 1, 1); + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: Text\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + + else if (_memicmp(NodeURL, "/aprsdata.txt", 13) == 0) + { + char * Compressed; + char * ptr; + double N, S, W, E; + int aprs = 1, ais = 1, adsb = 1; + + ptr = &NodeURL[14]; + + N = atof(ptr); + ptr = strlop(ptr, '|'); + S = atof(ptr); + ptr = strlop(ptr, '|'); + W = atof(ptr); + ptr = strlop(ptr, '|'); + E = atof(ptr); + ptr = strlop(ptr, '|'); + if (ptr) + { + aprs = atoi(ptr); + ptr = strlop(ptr, '|'); + ais = atoi(ptr); + ptr = strlop(ptr, '|'); + adsb = atoi(ptr); + } + ReplyLen = GetAPRSPageInfo(_REPLYBUFFER, N, S, W, E, aprs, ais, adsb); + + if (ReplyLen < 240000) + ReplyLen += GetAISPageInfo(&_REPLYBUFFER[ReplyLen], ais, adsb); + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %d\r\nContent-Type: Text\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + + else if (_memicmp(NodeURL, "/portstats.txt", 15) == 0) + { + char * Compressed; + char * ptr; + int port; + struct PORTCONTROL * PORT; + + ptr = &NodeURL[15]; + + port = atoi(ptr); + + PORT = GetPortTableEntryFromPortNum(port); + + ReplyLen = 0; + + if (PORT && PORT->TX) + { + // We send the last 24 hours worth of data. Buffer is cyclic so oldest byte is at StatsPointer + + int first = PORT->StatsPointer; + int firstlen = 1440 - first; + + memcpy(&_REPLYBUFFER[0], &PORT->TX[first], firstlen); + memcpy(&_REPLYBUFFER[firstlen], PORT->TX, first); + + memcpy(&_REPLYBUFFER[1440], &PORT->BUSY[first], firstlen); + memcpy(&_REPLYBUFFER[1440 + firstlen], PORT->BUSY, first); + + ReplyLen = 2880; + } + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %d\r\nContent-Type: Text\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + + + else if (_memicmp(NodeURL, "/Icon", 5) == 0 && _memicmp(&NodeURL[10], ".png", 4) == 0) + { + // APRS internal Icon + + char * Compressed; + + ReplyLen = GetAPRSIcon(_REPLYBUFFER, NodeURL); + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: Text\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + + } + + else if (_memicmp(NodeURL, "/NODE/", 6)) + { + // Not Node, See if a local file + + Bufferlen = SendMessageFile(sock, NodeURL, FALSE, allowDeflate); // Send error if not found + return 0; + } + + // Node URL + + { + + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + + if (_stricmp(NodeURL, "/Node/webproc.css") == 0) + { + char WebprocCSS[] = + ".dropbtn {position: relative; border: 1px solid black;padding:1px;}\r\n" + ".dropdown {position: relative; display: inline-block;}\r\n" + ".dropdown-content {display: none; position: absolute;background-color: #ccc; " + "min-width: 160px; box-shadow: 0px 8px 16px 0px rgba(0,0,00.2); z-index: 1;}\r\n" + ".dropdown-content a {color: black; padding: 1px 1px;text-decoration:none;display:block;}" + ".dropdown-content a:hover {background-color: #dddfff;}\r\n" + ".dropdown:hover .dropdown-content {display: block;}\r\n" + ".dropdown:hover .dropbtn {background-color: #ddd;}\r\n" + "input.btn:active {background:black;color:white;}\r\n" + "submit.btn:active {background:black;color:white;}\r\n"; + ReplyLen = sprintf(_REPLYBUFFER, "%s", WebprocCSS); + } + + else if (_stricmp(NodeURL, "/Node/Killandrestart") == 0) + { + int port = atoi(Context); + + if (port > 0 && port <= MaxBPQPortNo) + { + struct TNCINFO * TNC = TNCInfo[port]; + + KillTNC(TNC); + TNC->DontRestart = FALSE; + RestartTNC(TNC); + + if (TNC && TNC->WebWindowProc) + ReplyLen = TNC->WebWindowProc(TNC, _REPLYBUFFER, LOCAL); + + } + } + + else if (_stricmp(NodeURL, "/Node/Port") == 0 || _stricmp(NodeURL, "/Node/ARDOPAbort") == 0) + { + int port = atoi(Context); + + if (port > 0 && port <= MaxBPQPortNo) + { + struct TNCINFO * TNC = TNCInfo[port]; + + if (TNC && TNC->WebWindowProc) + ReplyLen = TNC->WebWindowProc(TNC, _REPLYBUFFER, LOCAL); + } + + } + + else if (_stricmp(NodeURL, "/Node/Streams") == 0) + { + ReplyLen = StatusProc(_REPLYBUFFER); + } + + else if (_stricmp(NodeURL, "/Node/Stats.html") == 0) + { + struct tm * TM; + char UPTime[50]; + time_t szClock = STATSTIME * 60; + + TM = gmtime(&szClock); + + sprintf(UPTime, "%.2d:%.2d:%.2d", TM->tm_yday, TM->tm_hour, TM->tm_min); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", StatsHddr); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Version", VersionString); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Uptime (Days Hours Mins)", UPTime); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Semaphore: Get-Rel/Clashes", Semaphore.Gets - Semaphore.Rels, Semaphore.Clashes); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Buffers: Max/Cur/Min/Out/Wait", MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Known Nodes/Max Nodes", NUMBEROFNODES, MAXDESTS); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "L4 Connects Sent/Rxed ", L4CONNECTSOUT, L4CONNECTSIN); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "L4 Frames TX/RX/Resent/Reseq", L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "L3 Frames Relayed", L3FRAMES); + + } + + else if (_stricmp(NodeURL, "/Node/RigControl.html") == 0) + { + char Test[] = + "\r\n" + "Rigcontrol\r\n" + "\r\n" + "\r\n" + "\r\n" + "
Waiting for data...
\r\n" + "\r\n"; + + + char NoRigCtl[] = + "\r\n" + "Rigcontrol\r\n" + "\r\n" + "\r\n" + "
RigControl Not Configured...
\r\n" + "\r\n"; + + if (RigWebPage) + ReplyLen = sprintf(_REPLYBUFFER, "%s", Test); + else + ReplyLen = sprintf(_REPLYBUFFER, "%s", NoRigCtl); + } + + else if (_stricmp(NodeURL, "/Node/ShowLog.html") == 0) + { + char ShowLogPage[] = + "" + "" + "Log Display" + "" + "
" + "
" +// "" + "" + "" + "
"; + + char * _REPLYBUFFER; + int ReplyLen; + char Header[256]; + int HeaderLen; + char * CfgBytes; + int CfgLen; + char inputname[250]; + FILE *fp1; + struct stat STAT; + char DummyKey[] = "DummyKey"; + time_t T; + struct tm * tm; + char Name[64] = ""; + + T = time(NULL); + tm = gmtime(&T); + + if (LOCAL == FALSE && COOKIE == FALSE) + { + // Send Not Authorized + + char _REPLYBUFFER[4096]; + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Not authorized - please sign in"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return 1; + } + + if (COOKIE == FALSE) + Key = DummyKey; + + if (memcmp(Context, "date=", 5) == 0) + { + memset(tm, 0, sizeof(struct tm)); + tm->tm_year = atoi(&Context[5]) - 1900; + tm->tm_mon = atoi(&Context[10]) - 1; + tm->tm_mday = atoi(&Context[13]); + } + + + + if (strcmp(Context, "input=Back") == 0) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return 1; + } + + if (LogDirectory[0] == 0) + { + strcpy(inputname, "logs/"); + } + else + { + strcpy(inputname,LogDirectory); + strcat(inputname,"/"); + strcat(inputname, "/logs/"); + } + + if (strstr(Context, "CMS")) + { + sprintf(Name, "CMSAccess_%04d%02d%02d.log", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday); + } + else if (strstr(Context, "Debug")) + { + sprintf(Name, "log_%02d%02d%02d_DEBUG.txt", + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + } + else if (strstr(Context, "BBS")) + { + sprintf(Name, "log_%02d%02d%02d_BBS.txt", + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + } + else if (strstr(Context, "Chat")) + { + sprintf(Name, "log_%02d%02d%02d_CHAT.txt", + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + } + else if (strstr(Context, "Telnet")) + { + sprintf(Name, "Telnet_%02d%02d%02d.log", + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + } + + strcat(inputname, Name); + + if (stat(inputname, &STAT) == -1) + { + CfgBytes = malloc(256); + sprintf(CfgBytes, "Log %s not found", inputname); + CfgLen = strlen(CfgBytes); + } + else + { + fp1 = fopen(inputname, "rb"); + + if (fp1 == 0) + { + CfgBytes = malloc(256); + sprintf(CfgBytes, "Log %s not found", inputname); + CfgLen = strlen(CfgBytes); + } + else + { + CfgLen = STAT.st_size; + + CfgBytes = malloc(CfgLen + 1); + + CfgLen = (int)fread(CfgBytes, 1, CfgLen, fp1); + CfgBytes[CfgLen] = 0; + } + } + + _REPLYBUFFER = malloc(CfgLen + 1000); + + ReplyLen = sprintf(_REPLYBUFFER, ShowLogPage, CfgBytes); + free (CfgBytes); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + sendandcheck(sock, Tail, (int)strlen(Tail)); + free (_REPLYBUFFER); + + return 1; + } + + else if (_stricmp(NodeURL, "/Node/EditCfg.html") == 0) + { + char * _REPLYBUFFER; + int ReplyLen; + char Header[256]; + int HeaderLen; + char * CfgBytes; + int CfgLen; + char inputname[250]="bpq32.cfg"; + FILE *fp1; + struct stat STAT; + char DummyKey[] = "DummyKey"; + + if (LOCAL == FALSE && COOKIE == FALSE) + { + // Send Not Authorized + + char _REPLYBUFFER[4096]; + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Not authorized - please sign in"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return 1; + } + + if (COOKIE ==FALSE) + Key = DummyKey; + + if (ConfigDirectory[0] == 0) + { + strcpy(inputname, "bpq32.cfg"); + } + else + { + strcpy(inputname,ConfigDirectory); + strcat(inputname,"/"); + strcat(inputname, "bpq32.cfg"); + } + + + if (stat(inputname, &STAT) == -1) + { + CfgBytes = _strdup("Config File not found"); + } + else + { + fp1 = fopen(inputname, "rb"); + + if (fp1 == 0) + { + CfgBytes = _strdup("Config File not found"); + } + else + { + CfgLen = STAT.st_size; + + CfgBytes = malloc(CfgLen + 1); + + CfgLen = (int)fread(CfgBytes, 1, CfgLen, fp1); + CfgBytes[CfgLen] = 0; + } + } + + _REPLYBUFFER = malloc(CfgLen + 1000); + + ReplyLen = sprintf(_REPLYBUFFER, ConfigEditPage, Key, CfgBytes); + free (CfgBytes); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + sendandcheck(sock, Tail, (int)strlen(Tail)); + free (_REPLYBUFFER); + + return 1; + } + + + + if (_stricmp(NodeURL, "/Node/PortBeacons") == 0) + { + char * PortChar = strtok_s(NULL, "&", &Context); + int PortNo = atoi(PortChar); + struct PORTCONTROL * PORT; + int PortSlot = 0; + + PORT = GetPortTableEntryFromPortNum(PortNo); // Need slot not number + if (PORT) + PortSlot = PORT->PortSlot; + + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], Beacons, PortNo, + Interval[PortSlot], &UIUIDEST[PortSlot][0], &UIUIDigi[PortSlot][0], &FN[PortSlot][0], &Message[PortSlot][0], PortNo); + } + + + + if (_stricmp(NodeURL, "/Node/PortStats") == 0) + { + struct _EXTPORTDATA * Port; + + char * PortChar = strtok_s(NULL, "&", &Context); + int PortNo = atoi(PortChar); + int Protocol; + int PortType; + + // char PORTTYPE; // H/W TYPE + // 0 = ASYNC, 2 = PC120, 4 = DRSI + // 6 = TOSH, 8 = QUAD, 10 = RLC100 + // 12 = RLC400 14 = INTERNAL 16 = EXTERNAL + +#define KISS 0 +#define NETROM 2 +#define HDLC 6 +#define L2 8 +#define WINMOR 10 + + + // char PROTOCOL; // PORT PROTOCOL + // 0 = KISS, 2 = NETROM, 4 = BPQKISS + //; 6 = HDLC, 8 = L2 + + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsHddr, PortNo); + + Port = (struct _EXTPORTDATA *)GetPortTableEntryFromPortNum(PortNo); + + if (Port == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, "Invalid Port"); + goto SendResp; + } + + Protocol = Port->PORTCONTROL.PROTOCOL; + PortType = Port->PORTCONTROL.PROTOCOL; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Frames Digied", Port->PORTCONTROL.L2DIGIED); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Frames Heard", Port->PORTCONTROL.L2FRAMES); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Frames Received", Port->PORTCONTROL.L2FRAMESFORUS); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Frames Sent", Port->PORTCONTROL.L2FRAMESSENT); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Timeouts", Port->PORTCONTROL.L2TIMEOUTS); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "REJ Frames Received", Port->PORTCONTROL.L2REJCOUNT); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "RX out of Seq", Port->PORTCONTROL.L2OUTOFSEQ); + // ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Resequenced", Port->PORTCONTROL.L2RESEQ); + if (Protocol == HDLC) + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "Underrun", Port->PORTCONTROL.L2URUNC); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "RX Overruns", Port->PORTCONTROL.L2ORUNC); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "RX CRC Errors", Port->PORTCONTROL.RXERRORS); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "Frames abandoned", Port->PORTCONTROL.L1DISCARD); + } + else if ((Protocol == KISS && Port->PORTCONTROL.KISSFLAGS) || Protocol == NETROM) + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "Poll Timeout", Port->PORTCONTROL.L2URUNC); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "RX CRC Errors", Port->PORTCONTROL.RXERRORS); + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "FRMRs Sent", Port->PORTCONTROL.L2FRMRTX); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "FRMRs Received", Port->PORTCONTROL.L2FRMRRX); + + // DB 'Link Active % ' + // DD AVSENDING + + } + + if (_stricmp(NodeURL, "/Node/Ports.html") == 0) + { + struct _EXTPORTDATA * ExtPort; + struct PORTCONTROL * Port; + + int count; + char DLL[20]; + char StatsURL[64]; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PortsHddr); + + for (count = 1; count <= NUMBEROFPORTS; count++) + { + Port = GetPortTableEntryFromSlot(count); + ExtPort = (struct _EXTPORTDATA *)Port; + + // see if has a stats page + + if (Port->AVACTIVE) + sprintf(StatsURL, " Stats Graph", Port->PORTNUMBER); + else + StatsURL[0] = 0; + + if (Port->PORTTYPE == 0x10) + { + strcpy(DLL, ExtPort->PORT_DLL_NAME); + strlop(DLL, '.'); + } + else if (Port->PORTTYPE == 0) + strcpy(DLL, "ASYNC"); + + else if (Port->PORTTYPE == 22) + strcpy(DLL, "I2C"); + + else if (Port->PORTTYPE == 14) + strcpy(DLL, "INTERNAL"); + + else if (Port->PORTTYPE > 0 && Port->PORTTYPE < 14) + strcpy(DLL, "HDLC"); + + + if (Port->TNC && Port->TNC->WebWindowProc) // Has a Window + { + if (Port->UICAPABLE) + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithBeaconAndDriver, Port->PORTNUMBER, DLL, + Port->PORTDESCRIPTION, Port->PORTNUMBER, Port->PORTNUMBER, Port->TNC->WebWinX, Port->TNC->WebWinY, 200, 200, StatsURL); + else + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithDriver, Port->PORTNUMBER, DLL, + Port->PORTDESCRIPTION, Port->PORTNUMBER, Port->TNC->WebWinX, Port->TNC->WebWinY, 200, 200, StatsURL); + + continue; + } + + if (Port->PORTTYPE == 16 && Port->PROTOCOL == 10 && Port->UICAPABLE == 0) // EXTERNAL, Pactor/WINMO + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], SessionPortLine, Port->PORTNUMBER, DLL, + Port->PORTDESCRIPTION, Port->PORTNUMBER, StatsURL); + else + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithBeacon, Port->PORTNUMBER, Port->PORTNUMBER, + DLL, DLL, Port->PORTDESCRIPTION, Port->PORTNUMBER, StatsURL); + } + + if (RigActive) + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RigControlLine, 64, "Rig Control", "Rig Control", 600, 350, 200, 200); + + } + + if (_stricmp(NodeURL, "/Node/Nodes.html") == 0) + { + struct DEST_LIST * Dests = DESTS; + int count, i; + char Normcall[10]; + char Alias[10]; + int Width = 5; + int x = 0, n = 0; + struct DEST_LIST * List[1000]; + char Param = 0; + + if (Context) + { + _strupr(Context); + Param = Context[0]; + } + + for (count = 0; count < MAXDESTS; count++) + { + if (Dests->DEST_CALL[0] != 0) + { + if (Param != 'T' || Dests->DEST_COUNT) + List[n++] = Dests; + + if (n > 999) + break; + } + + Dests++; + } + + if (n > 1) + { + if (Param == 'C') + qsort(List, n, sizeof(void *), CompareNode); + else + qsort(List, n, sizeof(void *), CompareAlias); + } + + Alias[6] = 0; + + if (Param == 'T') + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], NodeHddr, "with traffic"); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], ""); + } + else if (Param == 'C') + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], NodeHddr, "sorted by Call"); + else + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], NodeHddr, "sorted by Alias"); + + for (i = 0; i < n; i++) + { + int len = ConvFromAX25(List[i]->DEST_CALL, Normcall); + Normcall[len]=0; + + memcpy(Alias, List[i]->DEST_ALIAS, 6); + strlop(Alias, ' '); + + if (Param == 'T') + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + Normcall, Alias, List[i]->DEST_COUNT, List[i]->DEST_RTT /16, + (List[i]->DEST_STATE & 0x40)? 'B':' ', (List[i]->DEST_STATE & 63)); + + } + else + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], NodeLine, Normcall, Alias, Normcall); + + if (++x == Width) + { + x = 0; + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], ""); + } + } + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], ""); + } + + if (_stricmp(NodeURL, "/Node/NodeDetail") == 0) + { + UCHAR AXCall[8]; + struct DEST_LIST * Dest = DESTS; + struct NR_DEST_ROUTE_ENTRY * NRRoute; + struct ROUTE * Neighbour; + char Normcall[10]; + int i, len, count, Active; + char Alias[7]; + + Alias[6] = 0; + + _strupr(Context); + + ConvToAX25(Context, AXCall); + + for (count = 0; count < MAXDESTS; count++) + { + if (CompareCalls(Dest->DEST_CALL, AXCall)) + { + break; + } + Dest++; + } + + if (count == MAXDESTS) + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "

Call %s not found

", Context); + goto SendResp; + } + + memcpy(Alias, Dest->DEST_ALIAS, 6); + strlop(Alias, ' '); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], + "

Info for Node %s:%s

", Alias, Context); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "

PortDriverIDBeaconsDriver WindowStats Graph
%d %s%s
%d %s%s Beacons %s
%d%s%s %s
%d%s%s Driver Window%s
%d%s%s BeaconsDriver Window%s
%d%s%s Rig Control
%s%s
%s%s
%s%d%d
%s%d%d%d%d%d
%s%d%d
%s%d%d
%s%d%d%d%d
%s%d
CallFramesRTTBPQ?Hops
%s:%s%d%d%c%.0d
"); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
FramesRTTBPQ?Hops
%d%d%c%.0d
", + Dest->DEST_COUNT, Dest->DEST_RTT /16, + (Dest->DEST_STATE & 0x40)? 'B':' ', (Dest->DEST_STATE & 63)); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "

Neighbours

"); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], + "" + ""); + + 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; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + (Active == i)?'>':' ',NRRoute->ROUT_QUALITY, NRRoute->ROUT_OBSCOUNT, Neighbour->NEIGHBOUR_PORT, Normcall); + } + NRRoute++; + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Qual Obs Port Call
%c %d%d%d%s
"); + + goto SendResp; + + } + /* + + MOV ESI,OFFSET32 NODEROUTEHDDR + MOV ECX,11 + REP MOVSB + + LEA ESI,DEST_CALL[EBX] + CALL DECODENODENAME ; CONVERT TO ALIAS:CALL + REP MOVSB + + CMP DEST_RTT[EBX],0 + JE SHORT @f ; TIMER NOT SET - DEST PROBABLY NOT USED + + MOVSB ; ADD SPACE + CALL DORTT + + @@: + MOV AL,CR + STOSB + + MOV ECX,3 + MOV DH,DEST_ROUTE[EBX] ; CURRENT ACTIVE ROUTE + MOV DL,1 + + push ebx + + PUBLIC CMDN110 + CMDN110: + + MOV ESI,ROUT1_NEIGHBOUR[EBX] + CMP ESI,0 + JE CMDN199 + + + MOV AX,' ' + CMP DH,DL + JNE SHORT CMDN112 ; NOT CURRENT DEST + MOV AX,' >' + + CMDN112: + + STOSW + + PUSH ECX + + MOV AL,ROUT1_QUALITY[EBX] + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + MOV AL,ROUT1_OBSCOUNT[EBX] + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + MOV AL,NEIGHBOUR_PORT[ESI] ; GET PORT + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + + PUSH EDI + CALL CONVFROMAX25 ; CONVERT TO CALL + POP EDI + + MOV ESI,OFFSET32 NORMCALL + REP MOVSB + + MOV AL,CR + STOSB + + ADD EBX,ROUTEVECLEN + INC DL ; ROUTE NUMBER + + POP ECX + DEC ECX + JNZ CMDN110 + + PUBLIC CMDN199 + CMDN199: + + POP EBX + + ; DISPLAY INP3 ROUTES + + MOV ECX,3 + MOV DL,4 + + PUBLIC CMDNINP3 + CMDNINP3: + + MOV ESI,INPROUT1_NEIGHBOUR[EBX] + CMP ESI,0 + JE CMDNINPEND + + MOV AX,' ' + CMP DH,DL + JNE SHORT @F ; NOT CURRENT DEST + MOV AX,' >' + + @@: + + STOSW + + PUSH ECX + + MOV AL, Hops1[EBX] + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + MOVZX EAX, SRTT1[EBX] + + MOV EDX,0 + MOV ECX, 100 + DIV ECX + CALL CONV_5DIGITS + MOV AL,'.' + STOSB + MOV EAX, EDX + CALL PRINTNUM + MOV AL,'s' + STOSB + MOV AL,' ' + STOSB + + MOV AL,NEIGHBOUR_PORT[ESI] ; GET PORT + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + PUSH EDI + CALL CONVFROMAX25 ; CONVERT TO CALL + POP EDI + + MOV ESI,OFFSET32 NORMCALL + REP MOVSB + + + MOV AL,CR + STOSB + + ADD EBX,INPROUTEVECLEN + INC DL ; ROUTE NUMBER + + POP ECX + LOOP CMDNINP3 + + CMDNINPEND: + + ret + + */ + + + if (_stricmp(NodeURL, "/Node/Routes.html") == 0) + { + struct ROUTE * Routes = NEIGHBOURS; + int MaxRoutes = MAXNEIGHBOURS; + int count; + char Normcall[10]; + char locked[4] = " "; + int NodeCount; + int Percent = 0; + int Iframes, Retries; + char Active[10]; + int Queued; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", RouteHddr); + + for (count=0; countNEIGHBOUR_CALL[0] != 0) + { + int len = ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall); + Normcall[len]=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) + Queued = COUNT_AT_L2(Routes->NEIGHBOUR_LINK); + else + Queued = 0; + + Iframes = Routes->NBOUR_IFRAMES; + Retries = Routes->NBOUR_RETRIES; + + if (Routes->NEIGHBOUR_LINK && Routes->NEIGHBOUR_LINK->L2STATE >= 5) + strcpy(Active, ">"); + else + strcpy(Active, " "); + + if (Iframes) + Percent = (Retries * 100) / Iframes; + else + Percent = 0; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RouteLine, Active, Routes->NEIGHBOUR_PORT, Normcall, locked, + Routes->NEIGHBOUR_QUAL, NodeCount, Iframes, Retries, Percent, Routes->NBOUR_MAXFRAME, Routes->NBOUR_FRACK, + Routes->NEIGHBOUR_TIME >> 8, Routes->NEIGHBOUR_TIME & 0xff, Queued, Routes->OtherendsRouteQual); + } + Routes+=1; + } + } + + if (_stricmp(NodeURL, "/Node/Links.html") == 0) + { + struct _LINKTABLE * Links = LINKS; + int MaxLinks = MAXLINKS; + int count; + char Normcall1[10]; + char Normcall2[10]; + char State[12] = "", Type[12] = "Uplink"; + int axState; + int cctType; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", LinkHddr); + + for (count=0; countLINKCALL[0] != 0) + { + int len = ConvFromAX25(Links->LINKCALL, Normcall1); + Normcall1[len] = 0; + + len = ConvFromAX25(Links->OURCALL, Normcall2); + Normcall2[len] = 0; + + axState = Links->L2STATE; + + if (axState == 2) + strcpy(State, "Connecting"); + else if (axState == 3) + strcpy(State, "FRMR"); + else if (axState == 4) + strcpy(State, "Closing"); + else if (axState == 5) + strcpy(State, "Active"); + else if (axState == 6) + strcpy(State, "REJ Sent"); + + cctType = Links->LINKTYPE; + + if (cctType == 1) + strcpy(Type, "Uplink"); + else if (cctType == 2) + strcpy(Type, "Downlink"); + else if (cctType == 3) + strcpy(Type, "Node-Node"); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], LinkLine, Normcall1, Normcall2, Links->LINKPORT->PORTNUMBER, + State, Type, 2 - Links->VER1FLAG ); + + Links+=1; + } + } + } + + if (_stricmp(NodeURL, "/Node/Users.html") == 0) + { + TRANSPORTENTRY * L4 = L4TABLE; + TRANSPORTENTRY * Partner; + int MaxLinks = MAXLINKS; + int count; + char State[12] = "", Type[12] = "Uplink"; + char LHS[50] = "", MID[10] = "", RHS[50] = ""; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", UserHddr); + + for (count=0; count < MAXCIRCUITS; count++) + { + 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: + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], UserLine, LHS, MID, RHS); + } +CMDS60: + L4++; + } + } + /* + PUBLIC CMDUXX_1 + CMDUXX_1: + push EBX + push ESI + PUSH ECX + push EDI + + call _FINDDESTINATION + pop EDI + + jz SHORT NODE_FOUND + + push EDI ; NET/ROM not found + call CONVFROMAX25 ; CONVERT TO CALL + pop EDI + mov ESI,OFFSET32 NORMCALL + rep movsb + + jmp SHORT END_CMDUXX + + PUBLIC NODE_FOUND + NODE_FOUND: + + lea ESI,DEST_CALL[EBX] + call DECODENODENAME + + REP MOVSB + + PUBLIC END_CMDUXX + END_CMDUXX: + + POP ECX + pop ESI + pop EBX + ret + + }}} + */ + + else if (_stricmp(NodeURL, "/Node/Terminal.html") == 0) + { + if (COOKIE && Session) + { + // Already signed in as sysop + + struct UserRec * USER = Session->USER; + + struct HTTPConnectionInfo * NewSession = AllocateSession(sock, 'T'); + + if (NewSession) + { + char AXCall[10]; + ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, NewSession->Key, NewSession->Key, NewSession->Key); + strcpy(NewSession->HTTPCall, USER->Callsign); + ConvToAX25(NewSession->HTTPCall, AXCall); + ChangeSessionCallsign(NewSession->Stream, AXCall); + BPQHOSTVECTOR[NewSession->Stream -1].HOSTSESSION->Secure_Session = USER->Secure; + Session->USER = USER; + NewSession->TNC = conn->TNC; + + + // if (Appl[0]) + // { + // strcat(Appl, "\r"); + // SendMsg(Session->Stream, Appl, strlen(Appl)); + // } + + } + else + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", BusyError); + } + } + else if (LOCAL) + { + // connected to 127.0.0.1 so sign in using node call + + struct HTTPConnectionInfo * NewSession = AllocateSession(sock, 'T'); + + if (NewSession) + { + ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, NewSession->Key, NewSession->Key, NewSession->Key); + strcpy(NewSession->HTTPCall, MYNODECALL); + ChangeSessionCallsign(NewSession->Stream, MYCALL); + BPQHOSTVECTOR[NewSession->Stream -1].HOSTSESSION->Secure_Session = TRUE; + NewSession->TNC = conn->TNC; + } + } + else + ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Context); + } + + else if (_stricmp(NodeURL, "/Node/Signon.html") == 0) + { + ReplyLen = sprintf(_REPLYBUFFER, NodeSignon, Mycall, Mycall, Context); + } + + else if (_stricmp(NodeURL, "/Node/Drivers") == 0) + { + int Bufferlen = SendMessageFile(sock, "/Drivers.htm", TRUE, allowDeflate); // return -1 if not found + + if (Bufferlen != -1) + return 0; // We've sent it + } + + else if (_stricmp(NodeURL, "/Node/OutputScreen.html") == 0) + { + struct HTTPConnectionInfo * Session = FindSession(Context); + + if (Session == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, "%s", LostSession); + } + else + { + Session->sock = sock; // socket to reply on + ReplyLen = RefreshTermWindow(TCP, Session, _REPLYBUFFER); + + if (ReplyLen == 0) // Nothing new + { + // Debugprintf("GET with no data avail - response held"); + Session->ResponseTimer = 1200; // Delay response for up to a minute + } + else + { + // Debugprintf("GET - outpur sent, timer was %d, set to zero", Session->ResponseTimer); + Session->ResponseTimer = 0; + } + + Session->KillTimer = 0; + return 0; // Refresh has sent any available output + } + } + + else if (_stricmp(NodeURL, "/Node/InputLine.html") == 0) + { + struct TNCINFO * TNC = conn->TNC; + struct TCPINFO * TCP = 0; + + if (TNC) + TCP = TNC->TCPInfo; + + if (TCP && TCP->WebTermCSS) + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Context, TCP->WebTermCSS); + else + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Context, ""); + + } + + else if (_stricmp(NodeURL, "/Node/PTT") == 0) + { + struct TNCINFO * TNC = conn->TNC; + int x = atoi(Context); + } + + +SendResp: + + FormatTime3(TimeString, time(NULL)); + + strcpy(&_REPLYBUFFER[ReplyLen], Tail); + ReplyLen += (int)strlen(Tail); + + + if (allowDeflate) + { + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + } + else + { + Encoding[0] = 0; + Compressed = _REPLYBUFFER; + } + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "Date: %s\r\n%s\r\n", ReplyLen, TimeString, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + } + return 0; + +#ifdef WIN32xx + } +#include "StdExcept.c" +} +return 0; +#endif +} + +void ProcessHTTPMessage(void * conn) +{ + // conn is a malloc'ed copy to handle reused connections, so need to free it + + InnerProcessHTTPMessage((struct ConnectionInfo *)conn); + free(conn); + return; +} + +static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + + +VOID FormatTime3(char * Time, time_t cTime) +{ + struct tm * TM; + TM = gmtime(&cTime); + + sprintf(Time, "%s, %02d %s %3d %02d:%02d:%02d GMT", dat[TM->tm_wday], TM->tm_mday, month[TM->tm_mon], + TM->tm_year + 1900, TM->tm_hour, TM->tm_min, TM->tm_sec); + +} + +// Sun, 06 Nov 1994 08:49:37 GMT + +int StatusProc(char * Buff) +{ + int i; + char callsign[12] = ""; + char flag[3]; + UINT Mask, MaskCopy; + int Flags; + int AppNumber; + int OneBits; + int Len = sprintf(Buff, "" + "Stream Status"); + + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + + for (i=1;i<65; i++) + { + callsign[0]=0; + + if (GetAllocationState(i)) + + strcpy(flag,"*"); + else + strcpy(flag," "); + + GetCallsign(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) + Len += sprintf(&Buff[Len], "" + "", + i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName); + + else + Len += sprintf(&Buff[Len], "" + "", + i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName); + + if ((i & 1) == 0) + Len += sprintf(&Buff[Len], ""); + + } + + Len += sprintf(&Buff[Len], "
    RX   TX   MON  App  Flg Callsign  Program    RX   TX   MON  App  Flg Callsign  Program
%d%s%d%d%d%x%x%s%s%d%s%d%d%d%d%x%s%s
"); + return Len; +} + +int ProcessNodeSignon(SOCKET sock, struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + char Header[256]; + int HeaderLen; + struct HTTPConnectionInfo *Sess; + + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "\r\n", (int)(ReplyLen + strlen(Tail))); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + } + user = strtok_s(&input[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if (strcmp(password, USER->Password) == 0 && USER->Secure) + { + // ok + + Sess = *Session = AllocateSession(sock, 'N'); + Sess->USER = USER; + + ReplyLen = SetupNodeMenu(Reply, LOCAL); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "Set-Cookie: BPQSessionCookie=%s; Path = /\r\n\r\n", (int)(ReplyLen + strlen(Tail)), Sess->Key); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return ReplyLen; + } + } + } + } + + ReplyLen = sprintf(Reply, NodeSignon, Mycall, Mycall); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)(ReplyLen + strlen(Tail))); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 0; + + + return ReplyLen; +} + +int ProcessMailAPISignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password; + struct HTTPConnectionInfo * NewSession; + int i; + struct UserRec * USER; + + if (strchr(MsgPtr, '?')) + { + // Check Password + + user = strlop(MsgPtr, '?'); + password = strlop(user, '&'); + strlop(password, ' '); + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if ((strcmp(password, USER->Password) == 0) && (USER->Secure || WebMail)) + { + // ok + + NewSession = AllocateSession(Appl[0], 'M'); + + *Session = NewSession; + + if (NewSession) + { + ReplyLen = 0; + strcpy(NewSession->Callsign, USER->Callsign); + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + return ReplyLen; + + } + } + } + + // Pass failed attempt to BBS code so it can try a bbs user login + + // Need to put url back together + + if (user && user[0] && password && password[0]) + { + sprintf(MsgPtr, "%s?%s&%s", MsgPtr, user, password); + } + } + + NewSession = AllocateSession(Appl[0], 'M'); + + *Session = NewSession; + + if (NewSession) + ReplyLen = 0; + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + + return ReplyLen; +} + + + + +int ProcessMailSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + struct HTTPConnectionInfo * NewSession; + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + return ReplyLen; + } + user = strtok_s(&input[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if (strcmp(password, USER->Password) == 0 && (USER->Secure || WebMail)) + { + // ok + + NewSession = AllocateSession(Appl[0], 'M'); + + *Session = NewSession; + + if (NewSession) + { + + ReplyLen = 0; + strcpy(NewSession->Callsign, USER->Callsign); + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + return ReplyLen; + } + } + } + } + + ReplyLen = sprintf(Reply, MailSignon, Mycall, Mycall); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); + + return ReplyLen; +} + + +int ProcessChatSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + return ReplyLen; + } + + user = strtok_s(&input[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if (strcmp(password, USER->Password) == 0 && USER->Secure) + { + // ok + + *Session = AllocateSession(Appl[0], 'C'); + + if (Session) + { + ReplyLen = 0; + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + return ReplyLen; + } + } + } + } + + ReplyLen = sprintf(Reply, ChatSignon, Mycall, Mycall); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); + + return ReplyLen; + +} + +#define SHA1_HASH_LEN 20 + +/* + +Copyright (C) 1998, 2009 +Paul E. Jones + +Freeware Public License (FPL) + +This software is licensed as "freeware." Permission to distribute +this software in source and binary forms, including incorporation +into other products, is hereby granted without a fee. THIS SOFTWARE +IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHOR SHALL NOT BE HELD +LIABLE FOR ANY DAMAGES RESULTING FROM THE USE OF THIS SOFTWARE, EITHER +DIRECTLY OR INDIRECTLY, INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA +OR DATA BEING RENDERED INACCURATE. +*/ + +/* sha1.h + * + * Copyright (C) 1998, 2009 + * Paul E. Jones + * All Rights Reserved + * + ***************************************************************************** + * $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $ + ***************************************************************************** + * + * Description: + * This class implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * Many of the variable names in the SHA1Context, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +/* + * This structure will hold context information for the hashing + * operation + */ +typedef struct SHA1Context +{ + unsigned Message_Digest[5]; /* Message Digest (output) */ + + unsigned Length_Low; /* Message length in bits */ + unsigned Length_High; /* Message length in bits */ + + unsigned char Message_Block[64]; /* 512-bit message blocks */ + int Message_Block_Index; /* Index into message block array */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corruped? */ +} SHA1Context; + +/* + * Function Prototypes + */ +void SHA1Reset(SHA1Context *); +int SHA1Result(SHA1Context *); +void SHA1Input( SHA1Context *, const unsigned char *, unsigned); + +#endif + +BOOL SHA1PasswordHash(char * lpszPassword, char * Hash) +{ + SHA1Context sha; + int i; + + SHA1Reset(&sha); + SHA1Input(&sha, lpszPassword, strlen(lpszPassword)); + SHA1Result(&sha); + + // swap byte order if little endian + + for (i = 0; i < 5; i++) + sha.Message_Digest[i] = htonl(sha.Message_Digest[i]); + + memcpy(Hash, &sha.Message_Digest[0], 20); + + return TRUE; +} + +int BuildRigCtlPage(char * _REPLYBUFFER) +{ + int ReplyLen; + + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + int p, i; + + char Page[] = + "\r\n" + // "\r\n" + "Rigcontrol\r\n" + "" + "

Rigcontrol

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + ""; + char RigLine[] = + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n"; + char Tail[] = + "
RadioFreqModeSTPorts
%s%s%s/1%c%c%s
\r\n" + "\r\n"; + + ReplyLen = sprintf(_REPLYBUFFER, "%s", Page); + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RigLine, RIG->WEB_Label, RIG->WEB_FREQ, RIG->WEB_MODE, RIG->WEB_SCAN, RIG->WEB_PTT, RIG->WEB_PORTS, RIG->Interlock); + } + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", Tail); + return ReplyLen; +} + + +void SendRigWebPage() +{ + int i, n; + struct ConnectionInfo * sockptr; + struct TNCINFO * TNC; + struct TCPINFO * TCP; + + for (i = 0; i < 33; i++) + { + TNC = TNCInfo[i]; + + if (TNC && TNC->Hardware == H_TELNET) + { + TCP = TNC->TCPInfo; + + if (TCP) + { + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->SocketActive) + { + if (sockptr->HTTPMode && sockptr->WebSocks && strcmp(sockptr->WebURL, "RIGCTL") == 0) + { + char RigMsg[8192]; + int RigMsgLen = strlen(RigWebPage); + char* ptr; + + RigMsg[0] = 0x81; // Fin, Data + RigMsg[1] = 126; // Unmasked, Extended Len + RigMsg[2] = RigMsgLen >> 8; + RigMsg[3] = RigMsgLen & 0xff; + strcpy(&RigMsg[4], RigWebPage); + + // If secure session enable PTT button + + if (sockptr->WebSecure) + { + while (ptr = strstr(RigMsg, "hidden")) + memcpy(ptr, " ", 6); + } + + send(sockptr->socket, RigMsg, RigMsgLen + 4, 0); + } + } + } + } + } + } +} + +// Webmail web socket code + +int ProcessWebmailWebSock(char * MsgPtr, char * OutBuffer); + +void ProcessWebmailWebSockThread(void * conn) +{ + // conn is a malloc'ed copy to handle reused connections, so need to free it + + struct ConnectionInfo * sockptr = (struct ConnectionInfo *)conn; + char * URL = sockptr->WebURL; + int Loops = 0; + int Sent; + struct HTTPConnectionInfo Dummy = {0}; + int ReplyLen = 0; + int InputLen = 0; + +#ifdef LINBPQ + + char _REPLYBUFFER[250000]; + + ReplyLen = ProcessWebmailWebSock(URL, _REPLYBUFFER); + + // Send may block + + Sent = send(sockptr->socket, _REPLYBUFFER, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + if (Sent > 0) // something sent + { + ReplyLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sockptr->socket, _REPLYBUFFER, ReplyLen, 0); + } + +#else + // Send URL to BPQMail via Pipe. Just need a dummy session, as URL contains session key + + HANDLE hPipe; + char Reply[250000]; + + + + hPipe = CreateFile(MAILPipeFileName, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hPipe == (HANDLE)-1) + { + free(conn); + return; + } + + WriteFile(hPipe, &Dummy, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + WriteFile(hPipe, URL, strlen(URL), &InputLen, NULL); + + ReadFile(hPipe, &Dummy, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + ReadFile(hPipe, Reply, 250000, &ReplyLen, NULL); + + if (ReplyLen <= 0) + { + InputLen = GetLastError(); + } + + CloseHandle(hPipe); + + // ?? do we need a thread to handle write which may block + + Sent = send(sockptr->socket, Reply, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(Reply, &Reply[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sockptr->socket, Reply, ReplyLen, 0); + } +#endif + free(conn); + return; +} + +/* + * sha1.c + * + * Copyright (C) 1998, 2009 + * Paul E. Jones + * All Rights Reserved + * + ***************************************************************************** + * $Id: sha1.c 12 2009-06-22 19:34:25Z paulej $ + ***************************************************************************** + * + * Description: + * This file implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * The Secure Hashing Standard, which uses the Secure Hashing + * Algorithm (SHA), produces a 160-bit message digest for a + * given data stream. In theory, it is highly improbable that + * two messages will produce the same message digest. Therefore, + * this algorithm can serve as a means of providing a "fingerprint" + * for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code was + * written with the expectation that the processor has at least + * a 32-bit machine word size. If the machine word size is larger, + * the code should still function properly. One caveat to that + * is that the input functions taking characters and character + * arrays assume that only 8 bits of information are stored in each + * character. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated for + * messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is a + * multiple of the size of an 8-bit character. + * + */ + +/* + * Define the circular shift macro + */ +#define SHA1CircularShift(bits,word) \ + ((((word) << (bits)) & 0xFFFFFFFF) | \ + ((word) >> (32-(bits)))) + +/* Function prototypes */ +void SHA1ProcessMessageBlock(SHA1Context *); +void SHA1PadMessage(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Reset(SHA1Context *context) +{ + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Message_Digest[0] = 0x67452301; + context->Message_Digest[1] = 0xEFCDAB89; + context->Message_Digest[2] = 0x98BADCFE; + context->Message_Digest[3] = 0x10325476; + context->Message_Digest[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array within the SHA1Context provided + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * + * Returns: + * 1 if successful, 0 if it failed. + * + * Comments: + * + */ +int SHA1Result(SHA1Context *context) +{ + + if (context->Corrupted) + { + return 0; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + context->Computed = 1; + } + + return 1; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion of + * the message. + * + * Parameters: + * context: [in/out] + * The SHA-1 context to update + * message_array: [in] + * An array of characters representing the next portion of the + * message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Input( SHA1Context *context, + const unsigned char *message_array, + unsigned length) +{ + if (!length) + { + return; + } + + if (context->Computed || context->Corrupted) + { + context->Corrupted = 1; + return; + } + + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + /* Force it to 32 bits */ + context->Length_Low &= 0xFFFFFFFF; + if (context->Length_Low == 0) + { + context->Length_High++; + /* Force it to 32 bits */ + context->Length_High &= 0xFFFFFFFF; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in the SHAContext, especially the + * single character names, were used because those were the names + * used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const unsigned K[] = /* Constants defined in SHA-1 */ + { + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + unsigned temp; /* Temporary word value */ + unsigned W[80]; /* Word sequence */ + unsigned A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = ((unsigned) context->Message_Block[t * 4]) << 24; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]); + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Message_Digest[0]; + B = context->Message_Digest[1]; + C = context->Message_Digest[2]; + D = context->Message_Digest[3]; + E = context->Message_Digest[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Message_Digest[0] = + (context->Message_Digest[0] + A) & 0xFFFFFFFF; + context->Message_Digest[1] = + (context->Message_Digest[1] + B) & 0xFFFFFFFF; + context->Message_Digest[2] = + (context->Message_Digest[2] + C) & 0xFFFFFFFF; + context->Message_Digest[3] = + (context->Message_Digest[3] + D) & 0xFFFFFFFF; + context->Message_Digest[4] = + (context->Message_Digest[4] + E) & 0xFFFFFFFF; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call SHA1ProcessMessageBlock() + * appropriately. When it returns, it can be assumed that the + * message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (context->Length_High >> 24) & 0xFF; + context->Message_Block[57] = (context->Length_High >> 16) & 0xFF; + context->Message_Block[58] = (context->Length_High >> 8) & 0xFF; + context->Message_Block[59] = (context->Length_High) & 0xFF; + context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF; + context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF; + context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF; + context->Message_Block[63] = (context->Length_Low) & 0xFF; + + SHA1ProcessMessageBlock(context); +} + + diff --git a/.svn/pristine/39/3947b9018b6c61da3c28b671402e2559f58e3adc.svn-base b/.svn/pristine/39/3947b9018b6c61da3c28b671402e2559f58e3adc.svn-base new file mode 100644 index 0000000..7f01106 Binary files /dev/null and b/.svn/pristine/39/3947b9018b6c61da3c28b671402e2559f58e3adc.svn-base differ diff --git a/.svn/pristine/3b/3b82e1b8319f2d77f8171911c0864fac159d90d5.svn-base b/.svn/pristine/3b/3b82e1b8319f2d77f8171911c0864fac159d90d5.svn-base new file mode 100644 index 0000000..2dc08ea --- /dev/null +++ b/.svn/pristine/3b/3b82e1b8319f2d77f8171911c0864fac159d90d5.svn-base @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2017, 2020 IBM Corp. and others + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * https://www.eclipse.org/legal/epl-2.0/ + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#if !defined(MQTTREASONCODES_H) +#define MQTTREASONCODES_H + +#include "MQTTExportDeclarations.h" + +/** The MQTT V5 one byte reason code */ +enum MQTTReasonCodes { + MQTTREASONCODE_SUCCESS = 0, + MQTTREASONCODE_NORMAL_DISCONNECTION = 0, + MQTTREASONCODE_GRANTED_QOS_0 = 0, + MQTTREASONCODE_GRANTED_QOS_1 = 1, + MQTTREASONCODE_GRANTED_QOS_2 = 2, + MQTTREASONCODE_DISCONNECT_WITH_WILL_MESSAGE = 4, + MQTTREASONCODE_NO_MATCHING_SUBSCRIBERS = 16, + MQTTREASONCODE_NO_SUBSCRIPTION_FOUND = 17, + MQTTREASONCODE_CONTINUE_AUTHENTICATION = 24, + MQTTREASONCODE_RE_AUTHENTICATE = 25, + MQTTREASONCODE_UNSPECIFIED_ERROR = 128, + MQTTREASONCODE_MALFORMED_PACKET = 129, + MQTTREASONCODE_PROTOCOL_ERROR = 130, + MQTTREASONCODE_IMPLEMENTATION_SPECIFIC_ERROR = 131, + MQTTREASONCODE_UNSUPPORTED_PROTOCOL_VERSION = 132, + MQTTREASONCODE_CLIENT_IDENTIFIER_NOT_VALID = 133, + MQTTREASONCODE_BAD_USER_NAME_OR_PASSWORD = 134, + MQTTREASONCODE_NOT_AUTHORIZED = 135, + MQTTREASONCODE_SERVER_UNAVAILABLE = 136, + MQTTREASONCODE_SERVER_BUSY = 137, + MQTTREASONCODE_BANNED = 138, + MQTTREASONCODE_SERVER_SHUTTING_DOWN = 139, + MQTTREASONCODE_BAD_AUTHENTICATION_METHOD = 140, + MQTTREASONCODE_KEEP_ALIVE_TIMEOUT = 141, + MQTTREASONCODE_SESSION_TAKEN_OVER = 142, + MQTTREASONCODE_TOPIC_FILTER_INVALID = 143, + MQTTREASONCODE_TOPIC_NAME_INVALID = 144, + MQTTREASONCODE_PACKET_IDENTIFIER_IN_USE = 145, + MQTTREASONCODE_PACKET_IDENTIFIER_NOT_FOUND = 146, + MQTTREASONCODE_RECEIVE_MAXIMUM_EXCEEDED = 147, + MQTTREASONCODE_TOPIC_ALIAS_INVALID = 148, + MQTTREASONCODE_PACKET_TOO_LARGE = 149, + MQTTREASONCODE_MESSAGE_RATE_TOO_HIGH = 150, + MQTTREASONCODE_QUOTA_EXCEEDED = 151, + MQTTREASONCODE_ADMINISTRATIVE_ACTION = 152, + MQTTREASONCODE_PAYLOAD_FORMAT_INVALID = 153, + MQTTREASONCODE_RETAIN_NOT_SUPPORTED = 154, + MQTTREASONCODE_QOS_NOT_SUPPORTED = 155, + MQTTREASONCODE_USE_ANOTHER_SERVER = 156, + MQTTREASONCODE_SERVER_MOVED = 157, + MQTTREASONCODE_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 158, + MQTTREASONCODE_CONNECTION_RATE_EXCEEDED = 159, + MQTTREASONCODE_MAXIMUM_CONNECT_TIME = 160, + MQTTREASONCODE_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED = 161, + MQTTREASONCODE_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 162 +}; + +/** + * Returns a printable string description of an MQTT V5 reason code. + * @param value an MQTT V5 reason code. + * @return the printable string description of the input reason code. + * NULL if the code was not found. + */ +LIBMQTT_API const char* MQTTReasonCode_toString(enum MQTTReasonCodes value); + +#endif diff --git a/.svn/pristine/3c/3cb42769c3ad72e785ad5cd64b6a65acf68aaf1f.svn-base b/.svn/pristine/3c/3cb42769c3ad72e785ad5cd64b6a65acf68aaf1f.svn-base new file mode 100644 index 0000000..78c11e0 --- /dev/null +++ b/.svn/pristine/3c/3cb42769c3ad72e785ad5cd64b6a65acf68aaf1f.svn-base @@ -0,0 +1,1666 @@ + // Mail and Chat Server for BPQ32 Packet Switch +// +// Console Window Module + +#include "bpqmail.h" + +extern BOOL WINE; + +char ClassName[]="CONSOLEWINDOW"; + +char SYSOPCall[50]; + +struct UserInfo * user; + +struct ConsoleInfo BBSConsole; +struct ConsoleInfo ChatConsole; +struct ConsoleInfo * ConsHeader[2] = {&BBSConsole, &ChatConsole}; + +struct ConsoleInfo * InitHeader; + +extern struct SEM ChatSemaphore; +extern int NumberofStreams; +extern ConnectionInfo Connections[]; + +BOOL Bells; +BOOL FlashOnBell; // Flash instead of Beep +BOOL StripLF; + +BOOL WarnWrap; +BOOL FlashOnConnect; +BOOL WrapInput; +BOOL CloseWindowOnBye; + +char RTFHeader[4000]; +int RTFHddrLen = 0; + +RECT ConsoleRect; + +char chatMsg[] = "\rSysop wants to chat to you\r"; +char endChatMsg[] = "Sysop ended chat\r"; + +//CIRCUIT * Console; +HWND hConsole; +//RECT ConsoleRect; + +COLORREF Colours[256] = {0, + RGB(0,0,0), RGB(0,0,128), RGB(0,0,192), RGB(0,0,255), // 1 - 4 + RGB(0,64,0), RGB(0,64,128), RGB(0,64,192), RGB(0,64,255), // 5 - 8 + RGB(0,128,0), RGB(0,128,128), RGB(0,128,192), RGB(0,128,255), // 9 - 12 + RGB(0,192,0), RGB(0,192,128), RGB(0,192,192), RGB(0,192,255), // 13 - 16 + RGB(0,255,0), RGB(0,255,128), RGB(0,255,192), RGB(0,255,255), // 17 - 20 + + RGB(64,0,0), RGB(64,0,128), RGB(64,0,192), RGB(0,0,255), // 17 + RGB(64,64,0), RGB(64,64,128), RGB(64,64,192), RGB(64,64,255), + RGB(64,128,0), RGB(64,128,128), RGB(64,128,192), RGB(64,128,255), + RGB(64,192,0), RGB(64,192,128), RGB(64,192,192), RGB(64,192,255), + RGB(64,255,0), RGB(64,255,128), RGB(64,255,192), RGB(64,255,255), + + RGB(128,0,0), RGB(128,0,128), RGB(128,0,192), RGB(128,0,255), // 33 + RGB(128,64,0), RGB(128,64,128), RGB(128,64,192), RGB(128,64,255), + RGB(128,128,0), RGB(128,128,128), RGB(128,128,192), RGB(128,128,255), + RGB(128,192,0), RGB(128,192,128), RGB(128,192,192), RGB(128,192,255), + RGB(128,255,0), RGB(128,255,128), RGB(128,255,192), RGB(128,255,255), + + RGB(192,0,0), RGB(192,0,128), RGB(192,0,192), RGB(192,0,255), // 49 + RGB(192,64,0), RGB(192,64,128), RGB(192,64,192), RGB(192,64,255), + RGB(192,128,0), RGB(192,128,128), RGB(192,128,192), RGB(192,128,255), + RGB(192,192,0), RGB(192,192,128), RGB(192,192,192), RGB(192,192,255), + RGB(192,255,0), RGB(192,255,128), RGB(192,255,192), RGB(192,2552,255), + + RGB(255,0,0), RGB(255,0,128), RGB(255,0,192), RGB(255,0,255), // 49 + RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,64,255), + RGB(255,128,0), RGB(255,128,128), RGB(255,128,192), RGB(255,128,255), + RGB(255,192,0), RGB(255,192,128), RGB(255,192,192), RGB(255,192,255), + RGB(255,255,0), RGB(255,255,128), RGB(255,255,192), RGB(255,2552,255) +}; + + +#define InputBoxHeight 25 +static LRESULT CALLBACK ConsWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +static LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY OutputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +static LRESULT APIENTRY MonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; +void MoveWindows(struct ConsoleInfo * Cinfo); +VOID CloseConsoleSupport(struct ConsoleInfo * Cinfo); +VOID AddLinetoWindow(struct ConsoleInfo * Cinfo, char * Line); +VOID DoRefresh(struct ConsoleInfo * Cinfo); + +#define BGCOLOUR RGB(236,233,216) + + +HMENU trayMenu = 0, hBBSUSERCHAT = 0; + +BOOL CreateConsole(int Stream) +{ + WNDCLASS wc = {0}; + HBRUSH bgBrush; + HMENU hMenu,hActionMenu; + char RTFColours[3000]; + struct ConsoleInfo * Cinfo; + int i, n; + + Cinfo = &BBSConsole; + + InitHeader = Cinfo; + + if (Cinfo->hConsole) + { + ShowWindow(Cinfo->hConsole, SW_SHOWNORMAL); + SetForegroundWindow(Cinfo->hConsole); + return FALSE; // Already open + } + + memset(Cinfo, 0, sizeof(struct ConsoleInfo)); + + Cinfo->BPQStream = Stream; + + bgBrush = CreateSolidBrush(BGCOLOUR); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = ConsWndProc; + + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon( hInst, MAKEINTRESOURCE(BPQICON) ); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = bgBrush; + + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName; + + RegisterClass(&wc); + + hConsole = CreateDialog(hInst,ClassName,0,NULL); + + if (!hConsole) + return (FALSE); + + Cinfo->Bells = Bells; + Cinfo->FlashOnBell = FlashOnBell; + Cinfo->StripLF = StripLF; + Cinfo->CloseWindowOnBye = CloseWindowOnBye; + Cinfo->WarnWrap = WarnWrap; + Cinfo->WrapInput= WrapInput; + Cinfo->FlashOnConnect = FlashOnConnect; + + Cinfo->ConsoleRect = ConsoleRect; + + Cinfo->readbuff = zalloc(1000); + Cinfo->readbufflen = 1000; + + hMenu=GetMenu(hConsole); + Cinfo->hMenu = hMenu; + + CheckMenuItem(hMenu,BPQBELLS, (Cinfo->Bells) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,BPQFLASHONBELL, (Cinfo->FlashOnBell) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,BPQStripLF, (Cinfo->StripLF) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_WARNINPUT, (Cinfo->WarnWrap) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_WRAPTEXT, (Cinfo->WrapInput) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_Flash, (Cinfo->FlashOnConnect) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hMenu,IDM_CLOSEWINDOW, (Cinfo->CloseWindowOnBye) ? MF_CHECKED : MF_UNCHECKED); + + + hActionMenu=GetSubMenu(hMenu,2); + hBBSUSERCHAT=GetSubMenu(hActionMenu,0); + + DrawMenuBar(hWnd); + + if (trayMenu == 0) + { + trayMenu = CreatePopupMenu(); + AppendMenu(trayMenu,MF_STRING,40000,"Copy"); + } + + // Set up RTF Header, including Colours String; + + memcpy(RTFColours, "{\\colortbl ;", 12); + n = 12; + + for (i = 1; i < 100; i++) + { + COLORREF Colour = Colours[i]; + n += sprintf(&RTFColours[n], "\\red%d\\green%d\\blue%d;", GetRValue(Colour), GetGValue(Colour),GetBValue(Colour)); + } + + RTFColours[n++] = '}'; + RTFColours[n] = 0; + + strcpy(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fprq1 FixedSys;}}"); +// strcpy(RTFHeader, "{\\rtf1\\deff0{\\fonttbl{\\f0\\fmodern\\fcharset204\\fprq1 FixedSys;}}"); + strcat(RTFHeader, RTFColours); + strcat(RTFHeader, "\\viewkind4\\uc1\\pard\\f0"); + + RTFHddrLen = strlen(RTFHeader); + + // Create a Rich Text Control + + Cinfo->SendHeader = TRUE; + Cinfo->Finished = TRUE; + Cinfo->CurrentColour = 1; + + LoadLibrary("riched20.dll"); + + Cinfo->hwndOutput = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, "", + WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_NOHIDESEL | WS_VSCROLL | ES_READONLY, + 6,145,290,130, hConsole, NULL, hInst, NULL); + + // Register for Mouse Events for Copy/Paste + + SendMessage(Cinfo->hwndOutput, EM_SETEVENTMASK, (WPARAM)0, (LPARAM)ENM_MOUSEEVENTS | ENM_SCROLLEVENTS | ENM_KEYEVENTS); + SendMessage(Cinfo->hwndOutput, EM_EXLIMITTEXT, 0, MAXLINES * LINELEN); + + Cinfo->hwndInput = GetDlgItem(hConsole, 118); + + // Set our own WndProcs for the controls. + + Cinfo->wpOrigInputProc = (WNDPROC) SetWindowLong(Cinfo->hwndInput, GWL_WNDPROC, (LONG) InputProc); + + if (cfgMinToTray) + if (Stream == -1) + AddTrayMenuItem(hConsole, "BBS Console"); + else + AddTrayMenuItem(hConsole, "Chat Console"); + + if (Stream == -1) + SetWindowText(hConsole, "BBS Console"); + else + SetWindowText(hConsole, "Chat Console"); + + ShowWindow(hConsole, SW_SHOWNORMAL); + + if (Cinfo->ConsoleRect.right < 100 || Cinfo->ConsoleRect.bottom < 100) + { + GetWindowRect(hConsole, &Cinfo->ConsoleRect); + } + + MoveWindow(hConsole, Cinfo->ConsoleRect.left, Cinfo->ConsoleRect.top, + Cinfo->ConsoleRect.right-Cinfo->ConsoleRect.left, + Cinfo->ConsoleRect.bottom-Cinfo->ConsoleRect.top, TRUE); + + Cinfo->hConsole = hConsole; + + MoveWindows(Cinfo); + + Cinfo->Console = zalloc(sizeof(CIRCUIT)); + + Cinfo->Console->Active = TRUE; + Cinfo->Console->BPQStream = Stream; + + strcpy(Cinfo->Console->Callsign, SYSOPCall); + + user = LookupCall(SYSOPCall); + + if (user == NULL) + { + user = AllocateUserRecord(SYSOPCall); + + if (user == NULL) return 0; // Cant happen?? + + user->Temp = zalloc(sizeof (struct TempUserInfo)); + } + + time((time_t *)&user->TimeLastConnected); + user->Total.ConnectsIn++; + + Cinfo->Console->UserPointer = user; + Cinfo->Console->lastmsg = user->lastmsg; + Cinfo->Console->paclen=236; + Cinfo->Console->sysop = TRUE; + + Cinfo->Console->PageLen = user->PageLen; + Cinfo->Console->Paging = (user->PageLen > 0); + + nodeprintf(Cinfo->Console, BBSSID, "BPQ-", Ver[0], Ver[1], Ver[2], Ver[3], "B", "", "", "", "F"); + + if (user->Name[0] == 0) + { + Cinfo->Console->Flags |= GETTINGUSER; + SendUnbuffered(-1, NewUserPrompt, strlen(NewUserPrompt)); + } + else + { + if (Stream == -2) + { + if(ChatApplMask == 0) + { + BBSputs(Cinfo->Console, "Chat Node is disabled\r"); + SendPrompt(Cinfo->Console, user); + return TRUE; + } + } + else + SendWelcomeMsg(-1, Cinfo->Console, user); + } + return TRUE; + +} + + +VOID CloseConsole(int Stream) +{ + struct ConsoleInfo * Cinfo; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->Console) + { + if (Cinfo->BPQStream == Stream) + { + CloseConsoleSupport(Cinfo); + return; + } + } + } +} + + + +VOID CloseConsoleSupport(struct ConsoleInfo * Cinfo) +{ + GetWindowRect(Cinfo->hConsole, &ConsoleRect); + + if (Cinfo->CloseWindowOnBye) + { +// PostMessage(hConsole, WM_DESTROY, 0, 0); + DestroyWindow(Cinfo->hConsole); + } +} + +void MoveWindows(struct ConsoleInfo * Cinfo) +{ + RECT rcClient; + int ClientWidth; + + GetClientRect(Cinfo->hConsole, &rcClient); + + if (rcClient.bottom == 0) // Minimised + return; + + Cinfo->ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + MoveWindow(Cinfo->hwndOutput,2, 2, ClientWidth-4, Cinfo->ClientHeight-InputBoxHeight-4, TRUE); + MoveWindow(Cinfo->hwndInput,2, Cinfo->ClientHeight-InputBoxHeight-2, ClientWidth-4, InputBoxHeight, TRUE); + + GetClientRect(Cinfo->hwndOutput, &rcClient); + + Cinfo->ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + Cinfo->WarnLen = ClientWidth/8 - 1; + Cinfo->WrapLen = Cinfo->WarnLen; + Cinfo->maxlinelen = Cinfo->WarnLen; + +} + +LRESULT CALLBACK ConsWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + LPRECT lprc; + int i; + struct ConsoleInfo * Cinfo; + ConnectionInfo * conn; + + UCHAR tchBuffer[100000]; + UCHAR * buf = tchBuffer; + TEXTMETRIC tm; + int y; + LPMEASUREITEMSTRUCT lpmis; + LPDRAWITEMSTRUCT lpdis; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->hConsole == hWnd) + break; + } + + if (Cinfo == NULL) + Cinfo = InitHeader; + + switch (message) { + + case WM_CTLCOLOREDIT: + + if (Cinfo->Scrolled) + { + HDC hdcStatic = (HDC)wParam; + SetBkMode(hdcStatic, TRANSPARENT); + + return (LONG)GetStockObject(LTGRAY_BRUSH); + } + return (DefWindowProc(hWnd, message, wParam, lParam)); + + + case WM_VSCROLL: + break; + + case WM_NOTIFY: + { + const MSGFILTER * pF = (MSGFILTER *)lParam; + POINT pos; + CHARRANGE Range; + + if(pF->nmhdr.hwndFrom == Cinfo->hwndOutput) + { + if(pF->msg == WM_VSCROLL) + { +// int Command = LOWORD(pF->wParam); +// int Pos = HIWORD(pF->wParam); + +// Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + + DoRefresh(Cinfo); + break; + } + + if(pF->msg == WM_KEYUP) + { + if (pF->wParam == VK_PRIOR || pF->wParam == VK_NEXT) + { +// Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + DoRefresh(Cinfo); + } + } + + if(pF->msg == WM_RBUTTONDOWN) + { + // Only allow popup if something is selected + + SendMessage(Cinfo->hwndOutput, EM_EXGETSEL , 0, (WPARAM)&Range); + if (Range.cpMin == Range.cpMax) + return TRUE; + + GetCursorPos(&pos); + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, hWnd, 0); + return TRUE; + } + } + break; + } + + case WM_MEASUREITEM: + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + + // Set the height of the list box items. + + lpmis->itemHeight = 15; + return TRUE; + + case WM_DRAWITEM: + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + // If there are no list box items, skip this message. + + if (lpdis->itemID == -1) + { + return TRUE; + } + + switch (lpdis->itemAction) + { + case ODA_SELECT: + case ODA_DRAWENTIRE: + + // if Chat Console, and message has a colour eacape, action it + + SendMessage(lpdis->hwndItem, LB_GETTEXT, lpdis->itemID, (LPARAM) tchBuffer); + + GetTextMetrics(lpdis->hDC, &tm); + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2; + + if ((Cinfo->BPQStream == -2) && (tchBuffer[0] == 0x1b)) + { + SetTextColor(lpdis->hDC, Colours[tchBuffer[1] - 10]); + buf += 2; + } +// SetBkColor(lpdis->hDC, 0); + + TextOut(lpdis->hDC, + 6, + y, + buf, + strlen(buf)); + + // SetTextColor(lpdis->hDC, OldColour); + + break; + } + + return TRUE; + + + case WM_ACTIVATE: + + SetFocus(Cinfo->hwndInput); + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + if (wmId >= BBSUSERCHAT && wmId < BBSUSERCHAT + 63) + { + // Chat to user + + conn=&Connections[wmId-BBSUSERCHAT]; + + if (conn->Active) + { + conn->BBSFlags |= SYSOPCHAT; + Cinfo->Console->SysopChatStream = conn; + SendUnbuffered(conn->BPQStream, chatMsg, strlen(chatMsg)); + + +// Disconnect(conn->BPQStream); + } + } + + switch (wmId) { + + case ENDUSERCHAT: + + if (Cinfo->Console->SysopChatStream) + { + SendUnbuffered(Cinfo->Console->SysopChatStream->BPQStream, endChatMsg, strlen(endChatMsg)); + Cinfo->Console->SysopChatStream->BBSFlags &= ~SYSOPCHAT; + SendPrompt(Cinfo->Console->SysopChatStream, Cinfo->Console->SysopChatStream->UserPointer); + SendPrompt(Cinfo->Console, Cinfo->Console->UserPointer); + Cinfo->Console->SysopChatStream = 0; + } + + break; + + + case 40000: + { + int len=0; + HGLOBAL hMem; + char * ptr; + CHARRANGE Range; + + // Copy Rich Text Selection to Clipboard + + SendMessage(Cinfo->hwndOutput, EM_EXGETSEL , 0, (WPARAM)&Range); + + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, Range.cpMax - Range.cpMin + 1); + + if (hMem != 0) + { + ptr=GlobalLock(hMem); + + if (OpenClipboard(Cinfo->hConsole)) + { + len = SendMessage(Cinfo->hwndOutput, EM_GETSELTEXT , 0, (WPARAM)ptr); + + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + } + else + GlobalFree(hMem); + + SetFocus(Cinfo->hwndInput); + break; + } + + case BPQBELLS: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->Bells, BPQBELLS); + Bells = Cinfo->Bells; + break; + + case BPQFLASHONBELL: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->FlashOnBell, BPQFLASHONBELL); + FlashOnBell = Cinfo->FlashOnBell; + break; + + case BPQStripLF: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->StripLF, BPQStripLF); + StripLF = Cinfo->StripLF; + break; + + case IDM_WARNINPUT: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->WarnWrap, IDM_WARNINPUT); + WarnWrap = Cinfo->WarnWrap; + break; + + + case IDM_WRAPTEXT: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->WrapInput, IDM_WRAPTEXT); + Cinfo->WrapInput = WrapInput; + break; + + case IDM_Flash: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->FlashOnConnect, IDM_Flash); + FlashOnConnect = Cinfo->FlashOnConnect; + break; + + case IDM_CLOSEWINDOW: + + ToggleParam(Cinfo->hMenu, hWnd, &Cinfo->CloseWindowOnBye, IDM_CLOSEWINDOW); + CloseWindowOnBye = Cinfo->CloseWindowOnBye; + break; + + case BPQCLEAROUT: + + for (i = 0; i < MAXLINES; i++) + { + Cinfo->OutputScreen[i][0] = 0; + } + + Cinfo->CurrentLine = 0; + DoRefresh(Cinfo); + break; + + + SendMessage(Cinfo->hwndOutput,LB_RESETCONTENT, 0, 0); + break; + + case BPQCOPYOUT: + + CopyRichTextToClipboard(Cinfo->hwndOutput); + break; + + //case BPQHELP: + + // HtmlHelp(hWnd,"BPQTerminal.chm",HH_HELP_FINDER,0); + // break; + + default: + + return 0; + + } + + case WM_SYSCOMMAND: + + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case SC_MINIMIZE: + + if (cfgMinToTray) + return ShowWindow(hWnd, SW_HIDE); + + default: + + return (DefWindowProc(hWnd, message, wParam, lParam)); + } + + + case WM_SIZING: + + lprc = (LPRECT) lParam; + + Cinfo->Height = lprc->bottom-lprc->top; + Cinfo->Width = lprc->right-lprc->left; + + MoveWindows(Cinfo); + + return TRUE; + + case WM_SIZE: + + MoveWindows(Cinfo); + return TRUE; + + case WM_CLOSE: + + + if (Cinfo->Console->SysopChatStream) + { + SendUnbuffered(Cinfo->Console->SysopChatStream->BPQStream, endChatMsg, strlen(endChatMsg)); + Cinfo->Console->SysopChatStream->BBSFlags &= ~SYSOPCHAT; + SendPrompt(Cinfo->Console->SysopChatStream, Cinfo->Console->SysopChatStream->UserPointer); + SendPrompt(Cinfo->Console, Cinfo->Console->UserPointer); + Cinfo->Console->SysopChatStream = 0; + } + + CloseConsoleSupport(Cinfo); + + return (DefWindowProc(hWnd, message, wParam, lParam)); + + case WM_DESTROY: + + // Remove the subclass from the edit control. + + GetWindowRect(hWnd, &ConsoleRect); // For save soutine + + SetWindowLong(Cinfo->hwndInput, GWL_WNDPROC, + (LONG) Cinfo->wpOrigInputProc); + + if (cfgMinToTray) + DeleteTrayMenuItem(hWnd); + + if (Cinfo->Console && Cinfo->Console->Active) + { + ClearQueue(Cinfo->Console); + + Cinfo->Console->Active = FALSE; + RefreshMainWindow(); + + { + SendUnbuffered(Cinfo->Console->BPQStream, SignoffMsg, strlen(SignoffMsg)); + if (Cinfo->Console->lastmsg > user->lastmsg) + { + user->lastmsg = Cinfo->Console->lastmsg; + SaveUserDatabase(); + } + } + } + + // Free Scrollback + + for (i = 0; i < MAXSTACK ; i++) + { + if (Cinfo->KbdStack[i]) + { + free(Cinfo->KbdStack[i]); + Cinfo->KbdStack[i] = NULL; + } + } + + Sleep(500); + + free(Cinfo->readbuff); + Cinfo->readbufflen = 0; + + free(Cinfo->Console); + Cinfo->Console = 0; + Cinfo->hConsole = NULL; + + break; + + + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)hBBSUSERCHAT) + { + // Set up Chat Menu + + CIRCUIT * conn; + char MenuLine[30]; + int n; + + for (n = 0; n <= NumberofStreams-1; n++) + { + conn=&Connections[n]; + + RemoveMenu(hBBSUSERCHAT, BBSUSERCHAT + n, MF_BYCOMMAND); + + if (conn->Active) + { + sprintf_s(MenuLine, 30, "%d %s", conn->BPQStream, conn->Callsign); + AppendMenu(hBBSUSERCHAT, MF_STRING, BBSUSERCHAT + n, MenuLine); + } + } + return TRUE; + } + break; + + + + default: + return (DefWindowProc(hWnd, message, wParam, lParam)); + + } + return (0); +} + + +LRESULT APIENTRY InputProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + int i; + unsigned int TextLen; + struct ConsoleInfo * Cinfo; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->hwndInput == hwnd) + break; + } + + if (Cinfo == NULL) + Cinfo = InitHeader; + + + if (uMsg == WM_KEYUP) + { + unsigned int i; +// Debugprintf("5%x", LOBYTE(HIWORD(lParam))); + + if (LOBYTE(HIWORD(lParam)) == 0x48 && wParam == 0x26) + { + // Scroll up + + if (Cinfo->KbdStack[Cinfo->StackIndex] == NULL) + return TRUE; + + SendMessage(Cinfo->hwndInput, WM_SETTEXT,0,(LPARAM)(LPCSTR) Cinfo->KbdStack[Cinfo->StackIndex]); + + for (i = 0; i < strlen(Cinfo->KbdStack[Cinfo->StackIndex]); i++) + { + SendMessage(Cinfo->hwndInput, WM_KEYDOWN, VK_RIGHT, 0); + SendMessage(Cinfo->hwndInput, WM_KEYUP, VK_RIGHT, 0); + } + + Cinfo->StackIndex++; + if (Cinfo->StackIndex == 20) + Cinfo->StackIndex = 19; + + return TRUE; + } + + if (LOBYTE(HIWORD(lParam)) == 0x50 && wParam == 0x28) + { + // Scroll up + + Cinfo->StackIndex--; + if (Cinfo->StackIndex < 0) + Cinfo->StackIndex = 0; + + if (Cinfo->KbdStack[Cinfo->StackIndex] == NULL) + return TRUE; + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) Cinfo->KbdStack[Cinfo->StackIndex]); + + for (i = 0; i < strlen(Cinfo->KbdStack[Cinfo->StackIndex]); i++) + { + SendMessage(Cinfo->hwndInput, WM_KEYDOWN, VK_RIGHT, 0); + SendMessage(Cinfo->hwndInput, WM_KEYUP, VK_RIGHT, 0); + } + + return TRUE; + } + } + + + if (uMsg == WM_CHAR) + { + TextLen = SendMessage(Cinfo->hwndInput,WM_GETTEXTLENGTH, 0, 0); + + if (TextLen > INPUTLEN-10) Beep(220, 150); + + if(Cinfo->WarnWrap || Cinfo->WrapInput) + { + TextLen = SendMessage(Cinfo->hwndInput,WM_GETTEXTLENGTH, 0, 0); + + if (Cinfo->WarnWrap) + if (TextLen == Cinfo->WarnLen) Beep(220, 150); + + if (Cinfo->WrapInput) + if ((wParam == 0x20) && (TextLen > Cinfo->WrapLen)) + wParam = 13; // Replace space with Enter + + } + + if (wParam == 13) + { + Cinfo->kbptr=SendMessage(Cinfo->hwndInput, WM_GETTEXT, INPUTLEN-1, + (LPARAM) (LPCSTR)Cinfo->kbbuf); + + Cinfo->StackIndex = 0; + + // Stack it + + if (Cinfo->KbdStack[19]) + free(Cinfo->KbdStack[19]); + + for (i = 18; i >= 0; i--) + { + Cinfo->KbdStack[i+1] = Cinfo->KbdStack[i]; + } + + Cinfo->KbdStack[0] = _strdup(Cinfo->kbbuf); + + Cinfo->kbbuf[Cinfo->kbptr]=13; + + // Echo + + if (Cinfo->BPQStream == -2) + { + char Msg[INPUTLEN+4]; + Msg[0] = 0x1b; + Msg[1] = 11; + memcpy(&Msg[2], Cinfo->kbbuf, Cinfo->kbptr+1); + + WritetoConsoleWindow(Cinfo->BPQStream, Msg, Cinfo->kbptr+3); + + } + else + WritetoConsoleWindow(Cinfo->BPQStream, Cinfo->kbbuf, Cinfo->kbptr+1); + + if (Cinfo->Scrolled) + { + POINT Point; + Point.x = 0; + Point.y = 25000; // Should be plenty for any font + + SendMessage(Cinfo->hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + Cinfo->Scrolled = FALSE; + } + + DoRefresh(Cinfo); + + if (Cinfo->Console->SysopChatStream) + SendUnbuffered(Cinfo->Console->SysopChatStream->BPQStream, &Cinfo->kbbuf[0], Cinfo->kbptr+1); + else + ProcessLine(Cinfo->Console, user, &Cinfo->kbbuf[0], Cinfo->kbptr+1); + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) ""); + + return 0; + } + if (wParam == 0x1a) // Ctrl/Z + { + + Cinfo->kbbuf[0]=0x1a; + Cinfo->kbbuf[1]=13; + + ProcessLine(Cinfo->Console, user, &Cinfo->kbbuf[0], 2); + + + SendMessage(Cinfo->hwndInput,WM_SETTEXT,0,(LPARAM)(LPCSTR) ""); + + return 0; + } + + } + + return CallWindowProc(Cinfo->wpOrigInputProc, hwnd, uMsg, wParam, lParam); +} + + + +int WritetoConsoleWindowSupport(struct ConsoleInfo * Cinfo, char * Msg, int len); + +int WritetoConsoleWindow(int Stream, char * Msg, int len) +{ + struct ConsoleInfo * Cinfo; + + for (Cinfo = ConsHeader[0]; Cinfo; Cinfo = Cinfo->next) + { + if (Cinfo->Console) + { + if (Cinfo->BPQStream == Stream) + { + WritetoConsoleWindowSupport(Cinfo, Msg, len); + DoRefresh(Cinfo); + return 0; + } + } + } + return 0; +} + +int WritetoConsoleWindowSupport(struct ConsoleInfo * Cinfo, char * Msg, int len) +{ + char * ptr1, * ptr2; + + if (len + Cinfo->PartLinePtr > Cinfo->readbufflen) + { + Cinfo->readbufflen += len + Cinfo->PartLinePtr; + Cinfo->readbuff = realloc(Cinfo->readbuff, Cinfo->readbufflen); + } + + if (Cinfo->PartLinePtr != 0) + { + Cinfo->CurrentLine--; // Overwrite part line in buffer + if (Cinfo->CurrentLine < 0) + Cinfo->CurrentLine = MAXLINES - 1; + + if (Msg[0] == 0x1b && len > 1) + { + Msg += 2; // Remove Colour Escape + len -= 2; + } + } + + memcpy(&Cinfo->readbuff[Cinfo->PartLinePtr], Msg, len); + + len=len+Cinfo->PartLinePtr; + + ptr1=&Cinfo->readbuff[0]; + Cinfo->readbuff[len]=0; + + if (Cinfo->Bells) + { + do { + + ptr2=memchr(ptr1,7,len); + + if (ptr2) + { + *(ptr2)=32; + + if (Cinfo->FlashOnBell) + FlashWindow(Cinfo->hConsole, TRUE); + else + Beep(440,250); + } + + } while (ptr2); + } + +lineloop: + + if (len > 0) + { + // copy text to control a line at a time + + ptr2=memchr(ptr1,13,len); + + if (ptr2 == 0) + { + // no newline. Move data to start of buffer and Save pointer + + Cinfo->PartLinePtr=len; + memmove(Cinfo->readbuff,ptr1,len); + AddLinetoWindow(Cinfo, ptr1); +// InvalidateRect(Cinfo->hwndOutput, NULL, FALSE); + + return (0); + } + + *(ptr2++)=0; + + // If len is greater that screen with, fold + + if ((ptr2 - ptr1) > Cinfo->maxlinelen) + { + char * ptr3; + char * saveptr1 = ptr1; + int linelen = ptr2 - ptr1; + int foldlen; + char save; + + foldloop: + + ptr3 = ptr1 + Cinfo->maxlinelen; + + while(*ptr3!= 0x20 && ptr3 > ptr1) + { + ptr3--; + } + + foldlen = ptr3 - ptr1 ; + + if (foldlen == 0) + { + // No space before, so split at width + + foldlen = Cinfo->maxlinelen; + ptr3 = ptr1 + Cinfo->maxlinelen; + + } + else + { + ptr3++ ; // Omit space + linelen--; + } + save = ptr1[foldlen]; + ptr1[foldlen] = 0; + AddLinetoWindow(Cinfo, ptr1); + ptr1[foldlen] = save; + linelen -= foldlen; + ptr1 = ptr3; + + if (linelen > Cinfo->maxlinelen) + goto foldloop; + + AddLinetoWindow(Cinfo, ptr1); + + ptr1 = saveptr1; + } + else + AddLinetoWindow(Cinfo, ptr1); + + Cinfo->PartLinePtr=0; + + len-=(ptr2-ptr1); + + ptr1=ptr2; + + if ((len > 0) && Cinfo->StripLF) + { + if (*ptr1 == 0x0a) // Line Feed + { + ptr1++; + len--; + } + } + + goto lineloop; + } + + + return (0); +} + +int ToggleParam(HMENU hMenu, HWND hWnd, BOOL * Param, int Item) +{ + *Param = !(*Param); + + CheckMenuItem(hMenu,Item, (*Param) ? MF_CHECKED : MF_UNCHECKED); + + return (0); +} + +void CopyRichTextToClipboard(HWND hWnd) +{ + int len=0; + HGLOBAL hMem; + char * ptr; + + // Copy Rich Text to Clipboard + + len = SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0); + + hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len + 1); + + if (hMem != 0) + { + ptr=GlobalLock(hMem); + + if (OpenClipboard(MainWnd)) + { + len = SendMessage(hWnd, WM_GETTEXT , len, (LPARAM)ptr); + + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + } + else + GlobalFree(hMem); +} + + +void CopyToClipboard(HWND hWnd) +{ + int i,n, len=0; + HGLOBAL hMem; + char * ptr; + // + // Copy List Box to clipboard + // + + n = SendMessage(hWnd, LB_GETCOUNT, 0, 0); + + for (i=0; iSendHeader) + { + // Return header + + memcpy(lpBuff, RTFHeader, RTFHddrLen); + *pcb = RTFHddrLen; + Cinfo->SendHeader = FALSE; + Cinfo->Finished = FALSE; + Cinfo->Index = 0; + return 0; + } + + if (Cinfo->Finished) + { + *pcb = 0; + return 0; + } + +/* + if (BufferLen > cb) + { + memcpy(lpBuff, &Buffer[Offset], cb); + BufferLen -= cb; + Offset += cb; + *pcb = cb; + return 0; + } + + memcpy(lpBuff, &Buffer[Offset], BufferLen); + + *pcb = BufferLen; +*/ + + // Return 10 line at a time + + for (i = 0; i < 10; i++); + { + Line = Cinfo->Index++ + Cinfo->CurrentLine - MAXLINES; + + if (Line <0) + Line = Line + MAXLINES; + + sprintf(lpBuff, "\\cf%d ", Cinfo->Colourvalue[Line]); + strcat(lpBuff, Cinfo->OutputScreen[Line]); + strcat(lpBuff, "\\line"); + + if (Cinfo->Index == MAXLINES) + { + Cinfo->Finished = TRUE; + strcat(lpBuff, "}"); + i = 10; + } + } + *pcb = strlen(lpBuff); + return 0; +} + +VOID DoRefresh(struct ConsoleInfo * Cinfo) +{ + EDITSTREAM es = {0}; + int Min, Max, Pos; + POINT Point; + SCROLLINFO ScrollInfo; + int LoopTrap = 0; + HWND hwndOutput = Cinfo->hwndOutput; + + if(WINE) + Cinfo->Thumb = 30000; + else + Cinfo->Thumb = SendMessage(Cinfo->hwndOutput, EM_GETTHUMB, 0, 0); + + Pos = Cinfo->Thumb + Cinfo->ClientHeight; + + if ((Cinfo->Thumb + Cinfo->ClientHeight) > Cinfo->RTFHeight - 10) // Don't bother writing to screen if scrolled back + { + es.pfnCallback = (EDITSTREAMCALLBACK)EditStreamCallback; + es.dwCookie = (DWORD_PTR)Cinfo; + Cinfo->SendHeader = TRUE; + SendMessage(hwndOutput, EM_STREAMIN, SF_RTF, (LPARAM)&es); + } + + GetScrollRange(hwndOutput, SB_VERT, &Min, &Max); + ScrollInfo.cbSize = sizeof(ScrollInfo); + ScrollInfo.fMask = SIF_ALL; + + GetScrollInfo(hwndOutput, SB_VERT, &ScrollInfo); + +// Debugprintf("Pos %d Max %d Min %d nMax %d ClientH %d", Pos, Min, Max, ScrollInfo.nMax, Cinfo->ClientHeight); + + if (Cinfo->FirstTime == FALSE) + { + // RTF Controls don't immediately scroll to end - don't know why. + + Cinfo->FirstTime = TRUE; + Point.x = 0; + Point.y = 25000; // Should be plenty for any font + + while (LoopTrap++ < 20) + { + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + } + + GetScrollRange(hwndOutput, SB_VERT, &Min, &Max); // Get Actual Height + Cinfo->RTFHeight = Max; + Point.x = 0; + Point.y = Cinfo->RTFHeight - ScrollInfo.nPage; + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + Cinfo->Thumb = SendMessage(hwndOutput, EM_GETTHUMB, 0, 0); + } + + Point.x = 0; + Point.y = Cinfo->RTFHeight - ScrollInfo.nPage; + + if (Cinfo->Thumb > (Point.y - 10)) // Don't Scroll if user has scrolled back + { + SendMessage(hwndOutput, EM_SETSCROLLPOS, 0, (LPARAM) &Point); + + if (Cinfo->Scrolled) + { + Cinfo->Scrolled = FALSE; + InvalidateRect(Cinfo->hwndInput, NULL, TRUE); + } + return; + } + + if (!Cinfo->Scrolled) + { + Cinfo->Scrolled = TRUE; + InvalidateRect(Cinfo->hwndInput, NULL, TRUE); + } +} + +VOID AddLinetoWindow(struct ConsoleInfo * Cinfo, char * Line) +{ + int Len = strlen(Line); + char * ptr1 = Line; + char * ptr2; + int l, Index; + char LineCopy[LINELEN * 2]; + + if (Len > 199) // Console can't handle long lines + { + Line[198] = 13; + Line[199] = 0; + Len = 199; + } + + if (Line[0] == 0x1b && Len > 1) + { + // Save Colour Char + + Cinfo->CurrentColour = Line[1] - 10; + ptr1 +=2; + Len -= 2; + } + + strcpy(Cinfo->OutputScreen[Cinfo->CurrentLine], ptr1); + + // Look for chars we need to escape (\ { }) + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = strchr(ptr1, '\\'); // Look for Backslash first, as we may add some later + + if (ptr2) + { + while (ptr2) + { + l = ++ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l); // Copy Including found char + Index += l; + LineCopy[Index++] = '\\'; + Len++; + ptr1 = ptr2; + ptr2 = strchr(ptr1, '\\'); + } + strcpy(&LineCopy[Index], ptr1); // Copy in rest + strcpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = strchr(ptr1, '{'); + + if (ptr2) + { + while (ptr2) + { + l = ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l); + Index += l; + LineCopy[Index++] = '\\'; + LineCopy[Index++] = '{'; + Len++; + ptr1 = ++ptr2; + ptr2 = strchr(ptr1, '{'); + } + strcpy(&LineCopy[Index], ptr1); // Copy in rest + strcpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + ptr1 = Cinfo->OutputScreen[Cinfo->CurrentLine]; + Index = 0; + ptr2 = strchr(ptr1, '}'); // Look for Backslash first, as we may add some later + + if (ptr2) + { + while (ptr2) + { + l = ptr2 - ptr1; + memcpy(&LineCopy[Index], ptr1, l); // Copy + Index += l; + LineCopy[Index++] = '\\'; + LineCopy[Index++] = '}'; + Len++; + ptr1 = ++ptr2; + ptr2 = strchr(ptr1, '}'); + } + strcpy(&LineCopy[Index], ptr1); // Copy in rest + strcpy(Cinfo->OutputScreen[Cinfo->CurrentLine], LineCopy); + } + + + Cinfo->Colourvalue[Cinfo->CurrentLine] = Cinfo->CurrentColour; + Cinfo->LineLen[Cinfo->CurrentLine++] = Len; + if (Cinfo->CurrentLine >= MAXLINES) Cinfo->CurrentLine = 0; +} + + +/* +#define XBITMAP 80 +#define YBITMAP 20 + +#define BUFFER MAX_PATH + +HBITMAP hbmpPencil, hbmpCrayon, hbmpMarker, hbmpPen, hbmpFork; +HBITMAP hbmpPicture, hbmpOld; + +void AddItem(HWND hwnd, LPSTR lpstr, HBITMAP hbmp) +{ + int nItem; + + nItem = SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)lpstr); + SendMessage(hwnd, LB_SETITEMDATA, (WPARAM)nItem, (LPARAM)hbmp); +} + +DWORD APIENTRY DlgDrawProc( + HWND hDlg, // window handle to dialog box + UINT message, // type of message + UINT wParam, // message-specific information + LONG lParam) +{ + int nItem; + TCHAR tchBuffer[BUFFER]; + HBITMAP hbmp; + HWND hListBox; + TEXTMETRIC tm; + int y; + HDC hdcMem; + LPMEASUREITEMSTRUCT lpmis; + LPDRAWITEMSTRUCT lpdis; + RECT rcBitmap; + HRESULT hr; + size_t * pcch; + + switch (message) + { + + case WM_INITDIALOG: + + // Load bitmaps. + + hbmpPencil = LoadBitmap(hinst, MAKEINTRESOURCE(700)); + hbmpCrayon = LoadBitmap(hinst, MAKEINTRESOURCE(701)); + hbmpMarker = LoadBitmap(hinst, MAKEINTRESOURCE(702)); + hbmpPen = LoadBitmap(hinst, MAKEINTRESOURCE(703)); + hbmpFork = LoadBitmap(hinst, MAKEINTRESOURCE(704)); + + // Retrieve list box handle. + + hListBox = GetDlgItem(hDlg, IDL_STUFF); + + // Initialize the list box text and associate a bitmap + // with each list box item. + + AddItem(hListBox, "pencil", hbmpPencil); + AddItem(hListBox, "crayon", hbmpCrayon); + AddItem(hListBox, "marker", hbmpMarker); + AddItem(hListBox, "pen", hbmpPen); + AddItem(hListBox, "fork", hbmpFork); + + SetFocus(hListBox); + SendMessage(hListBox, LB_SETCURSEL, 0, 0); + return TRUE; + + case WM_MEASUREITEM: + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + + // Set the height of the list box items. + + lpmis->itemHeight = 20; + return TRUE; + + case WM_DRAWITEM: + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + // If there are no list box items, skip this message. + + if (lpdis->itemID == -1) + { + break; + } + + // Draw the bitmap and text for the list box item. Draw a + // rectangle around the bitmap if it is selected. + + switch (lpdis->itemAction) + { + case ODA_SELECT: + case ODA_DRAWENTIRE: + + // Display the bitmap associated with the item. + + hbmpPicture =(HBITMAP)SendMessage(lpdis->hwndItem, + LB_GETITEMDATA, lpdis->itemID, (LPARAM) 0); + + hdcMem = CreateCompatibleDC(lpdis->hDC); + hbmpOld = SelectObject(hdcMem, hbmpPicture); + + BitBlt(lpdis->hDC, + lpdis->rcItem.left, lpdis->rcItem.top, + lpdis->rcItem.right - lpdis->rcItem.left, + lpdis->rcItem.bottom - lpdis->rcItem.top, + hdcMem, 0, 0, SRCCOPY); + + // Display the text associated with the item. + + SendMessage(lpdis->hwndItem, LB_GETTEXT, + lpdis->itemID, (LPARAM) tchBuffer); + + GetTextMetrics(lpdis->hDC, &tm); + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - + tm.tmHeight) / 2; + + hr = StringCchLength(tchBuffer, BUFFER, pcch); + if (FAILED(hr)) + { + // TODO: Handle error. + } + + TextOut(lpdis->hDC, + XBITMAP + 6, + y, + tchBuffer, + pcch); + + SelectObject(hdcMem, hbmpOld); + DeleteDC(hdcMem); + + // Is the item selected? + + if (lpdis->itemState & ODS_SELECTED) + { + // Set RECT coordinates to surround only the + // bitmap. + + rcBitmap.left = lpdis->rcItem.left; + rcBitmap.top = lpdis->rcItem.top; + rcBitmap.right = lpdis->rcItem.left + XBITMAP; + rcBitmap.bottom = lpdis->rcItem.top + YBITMAP; + + // Draw a rectangle around bitmap to indicate + // the selection. + + DrawFocusRect(lpdis->hDC, &rcBitmap); + } + break; + + case ODA_FOCUS: + + // Do not process focus changes. The focus caret + // (outline rectangle) indicates the selection. + // The IDOK button indicates the final + // selection. + + break; + } + return TRUE; + + case WM_COMMAND: + + switch (LOWORD(wParam)) + { + case IDOK: + // Get the selected item's text. + + nItem = SendMessage(GetDlgItem(hDlg, IDL_STUFF), + LB_GETCURSEL, 0, (LPARAM) 0); + hbmp = SendMessage(GetDlgItem(hDlg, IDL_STUFF), + LB_GETITEMDATA, nItem, 0); + + // If the item is not the correct answer, tell the + // user to try again. + // + // If the item is the correct answer, congratulate + // the user and destroy the dialog box. + + if (hbmp != hbmpFork) + { + MessageBox(hDlg, "Try again!", "Oops", MB_OK); + return FALSE; + } + else + { + MessageBox(hDlg, "You're right!", + "Congratulations.", MB_OK); + + // Fall through. + } + + case IDCANCEL: + + // Destroy the dialog box. + + EndDialog(hDlg, TRUE); + return TRUE; + + default: + + return FALSE; + } + + case WM_DESTROY: + + // Free any resources used by the bitmaps. + + DeleteObject(hbmpPencil); + DeleteObject(hbmpCrayon); + DeleteObject(hbmpMarker); + DeleteObject(hbmpPen); + DeleteObject(hbmpFork); + + return TRUE; + + default: + return FALSE; + + } + return FALSE; +} +*/ diff --git a/.svn/pristine/3f/3f016988f5cd6816e967bb11684382274ca29881.svn-base b/.svn/pristine/3f/3f016988f5cd6816e967bb11684382274ca29881.svn-base new file mode 100644 index 0000000..228ac74 --- /dev/null +++ b/.svn/pristine/3f/3f016988f5cd6816e967bb11684382274ca29881.svn-base @@ -0,0 +1,913 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +// Hacked about a bit for BPQ32. John Wiseman G8BPQ April 2018 + + + +#include + +#ifndef _NTDEF_ +typedef LONG NTSTATUS; +#endif + +#ifdef __MINGW32__ +#include +#include +#endif + +#ifdef __CYGWIN__ +#include +#define _wcsdup wcsdup +#endif + +//#define HIDAPI_USE_DDK + +#ifdef __cplusplus +extern "C" { +#endif + #include + #include + #ifdef HIDAPI_USE_DDK + #include + #endif + + // Copied from inc/ddk/hidclass.h, part of the Windows DDK. + #define HID_OUT_CTL_CODE(id) \ + CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + #define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) + +#ifdef __cplusplus +} // extern "C" +#endif + +#include +#include + + +#include "hidapi.h" + +#ifdef _MSC_VER + // Thanks Microsoft, but I know how to use strncpy(). + #pragma warning(disable:4996) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HIDAPI_USE_DDK + // Since we're not building with the DDK, and the HID header + // files aren't part of the SDK, we have to define all this + // stuff here. In lookup_functions(), the function pointers + // defined below are set. + typedef struct _HIDD_ATTRIBUTES{ + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; + } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + + typedef USHORT USAGE; + typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT fields_not_used_by_hidapi[10]; + } HIDP_CAPS, *PHIDP_CAPS; + typedef char* HIDP_PREPARSED_DATA; + #define HIDP_STATUS_SUCCESS 0x0 + + typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); + typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, HIDP_PREPARSED_DATA **preparsed_data); + typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(HIDP_PREPARSED_DATA *preparsed_data); + typedef BOOLEAN (__stdcall *HidP_GetCaps_)(HIDP_PREPARSED_DATA *preparsed_data, HIDP_CAPS *caps); + + static HidD_GetAttributes_ HidD_GetAttributes; + static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; + static HidD_GetManufacturerString_ HidD_GetManufacturerString; + static HidD_GetProductString_ HidD_GetProductString; + static HidD_SetFeature_ HidD_SetFeature; + static HidD_GetFeature_ HidD_GetFeature; + static HidD_GetIndexedString_ HidD_GetIndexedString; + static HidD_GetPreparsedData_ HidD_GetPreparsedData; + static HidD_FreePreparsedData_ HidD_FreePreparsedData; + static HidP_GetCaps_ HidP_GetCaps; + + static HMODULE lib_handle = NULL; + static BOOLEAN initialized = FALSE; +#endif // HIDAPI_USE_DDK + +struct hid_device_ { + HANDLE device_handle; + BOOL blocking; + int input_report_length; + void *last_error_str; + DWORD last_error_num; + BOOL read_pending; + char *read_buf; + OVERLAPPED ol; +}; + +static hid_device *new_hid_device() +{ + hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); + dev->device_handle = INVALID_HANDLE_VALUE; + dev->blocking = TRUE; + dev->input_report_length = 0; + dev->last_error_str = NULL; + dev->last_error_num = 0; + dev->read_pending = FALSE; + dev->read_buf = NULL; + memset(&dev->ol, 0, sizeof(dev->ol)); + dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL); + + return dev; +} + + +static void register_error(hid_device *device, const char *op) +{ + WCHAR *ptr, *msg; + + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&msg, 0/*sz*/, + NULL); + + // Get rid of the CR and LF that FormatMessage() sticks at the + // end of the message. Thanks Microsoft! + ptr = msg; + while (*ptr) { + if (*ptr == '\r') { + *ptr = 0x0000; + break; + } + ptr++; + } + + // Store the message off in the Device entry so that + // the hid_error() function can pick it up. + LocalFree(device->last_error_str); + device->last_error_str = msg; +} + +#ifndef HIDAPI_USE_DDK +static int lookup_functions() +{ + lib_handle = LoadLibraryA("hid.dll"); + if (lib_handle) { +#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; + RESOLVE(HidD_GetAttributes); + RESOLVE(HidD_GetSerialNumberString); + RESOLVE(HidD_GetManufacturerString); + RESOLVE(HidD_GetProductString); + RESOLVE(HidD_SetFeature); + RESOLVE(HidD_GetFeature); + RESOLVE(HidD_GetIndexedString); + RESOLVE(HidD_GetPreparsedData); + RESOLVE(HidD_FreePreparsedData); + RESOLVE(HidP_GetCaps); +#undef RESOLVE + } + else + return -1; + + return 0; +} +#endif + +static HANDLE open_device(const char *path) +{ + HANDLE handle; + + /* First, try to open with sharing mode turned off. This will make it so + that a HID device can only be opened once. This is to be consistent + with the behavior on the other platforms. */ + handle = CreateFileA(path, + GENERIC_WRITE |GENERIC_READ, + 0, /*share mode*/ + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_NORMAL, + 0); + + if (handle == INVALID_HANDLE_VALUE) { + /* Couldn't open the device. Some devices must be opened + with sharing enabled (even though they are only opened once), + so try it here. */ + handle = CreateFileA(path, + GENERIC_WRITE |GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, /*share mode*/ + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_NORMAL, + 0); + } + + return handle; +} + +int HID_API_EXPORT hid_init(void) +{ +#ifndef HIDAPI_USE_DDK + if (!initialized) { + if (lookup_functions() < 0) { + hid_exit(); + return -1; + } + initialized = TRUE; + } +#endif + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ +#ifndef HIDAPI_USE_DDK + if (lib_handle) + FreeLibrary(lib_handle); + lib_handle = NULL; + initialized = FALSE; +#endif + return 0; +} + +struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + BOOL res; + struct hid_device_info *root = NULL; // return object + struct hid_device_info *cur_dev = NULL; + + // Windows objects for interacting with the driver. + GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; + SP_DEVINFO_DATA devinfo_data; + SP_DEVICE_INTERFACE_DATA device_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; + HDEVINFO device_info_set = INVALID_HANDLE_VALUE; + int device_index = 0; + + if (hid_init() < 0) + return NULL; + + // Initialize the Windows objects. + devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); + device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + // Get information for all the devices belonging to the HID class. + device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + // Iterate over each device in the HID class, looking for the right one. + + for (;;) { + HANDLE write_handle = INVALID_HANDLE_VALUE; + DWORD required_size = 0; + HIDD_ATTRIBUTES attrib; + + res = SetupDiEnumDeviceInterfaces(device_info_set, + NULL, + &InterfaceClassGuid, + device_index, + &device_interface_data); + + if (!res) { + // A return of FALSE from this function means that + // there are no more devices. + break; + } + + // Call with 0-sized detail size, and let the function + // tell us how long the detail struct needs to be. The + // size is put in &required_size. + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + NULL, + 0, + &required_size, + NULL); + + // Allocate a long enough structure for device_interface_detail_data. + device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size); + device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + + // Get the detailed data for this device. The detail data gives us + // the device path for this device, which is then passed into + // CreateFile() to get a handle to the device. + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + device_interface_detail_data, + required_size, + NULL, + NULL); + + if (!res) { + //register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail"); + // Continue to the next device. + goto cont; + } + + //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); + + // Open a handle to the device + write_handle = open_device(device_interface_detail_data->DevicePath); + + // Check validity of write_handle. + if (write_handle == INVALID_HANDLE_VALUE) { + // Unable to open the device. + //register_error(dev, "CreateFile"); + goto cont_close; + } + + + // Get the Vendor ID and Product ID for this device. + attrib.Size = sizeof(HIDD_ATTRIBUTES); + HidD_GetAttributes(write_handle, &attrib); + //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); + + // Check the VID/PID to see if we should add this + // device to the enumeration list. + if ((vendor_id == 0x0 && product_id == 0x0) || + (attrib.VendorID == vendor_id && attrib.ProductID == product_id)) { + + #define WSTR_LEN 512 + const char *str; + struct hid_device_info *tmp; + HIDP_PREPARSED_DATA *pp_data = NULL; + HIDP_CAPS caps; + BOOLEAN res; + NTSTATUS nt_res; + wchar_t wstr[WSTR_LEN]; // TODO: Determine Size + int len; + + /* VID/PID match. Create the record. */ + tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + // Get the Usage Page and Usage for this device. + res = HidD_GetPreparsedData(write_handle, &pp_data); + if (res) { + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res == HIDP_STATUS_SUCCESS) { + cur_dev->usage_page = caps.UsagePage; + cur_dev->usage = caps.Usage; + } + + HidD_FreePreparsedData(pp_data); + } + + /* Fill out the record */ + cur_dev->next = NULL; + str = device_interface_detail_data->DevicePath; + if (str) { + len = (int)strlen(str); + cur_dev->path = (char*) calloc(len+1, sizeof(char)); + strncpy(cur_dev->path, str, len+1); + cur_dev->path[len] = '\0'; + } + else + cur_dev->path = NULL; + + /* Serial Number */ + res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->serial_number = _wcsdup(wstr); + } + + /* Manufacturer String */ + res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->manufacturer_string = _wcsdup(wstr); + } + + /* Product String */ + res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->product_string = _wcsdup(wstr); + } + + /* VID/PID */ + cur_dev->vendor_id = attrib.VendorID; + cur_dev->product_id = attrib.ProductID; + + /* Release Number */ + cur_dev->release_number = attrib.VersionNumber; + + /* Interface Number. It can sometimes be parsed out of the path + on Windows if a device has multiple interfaces. See + http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or + search for "Hardware IDs for HID Devices" at MSDN. If it's not + in the path, it's set to -1. */ + cur_dev->interface_number = -1; + if (cur_dev->path) { + char *interface_component = strstr(cur_dev->path, "&mi_"); + if (interface_component) { + char *hex_str = interface_component + 4; + char *endptr = NULL; + cur_dev->interface_number = strtol(hex_str, &endptr, 16); + if (endptr == hex_str) { + /* The parsing failed. Set interface_number to -1. */ + cur_dev->interface_number = -1; + } + } + } + } + +cont_close: + CloseHandle(write_handle); +cont: + // We no longer need the detail data. It can be freed + free(device_interface_detail_data); + + device_index++; + + } + + // Close the device information handle. + SetupDiDestroyDeviceInfoList(device_info_set); + + return root; + +} + +void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) +{ + // TODO: Merge this with the Linux version. This function is platform-independent. + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + + +HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number) +{ + // TODO: Merge this functions with the Linux version. This function should be platform independent. + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) +{ + hid_device *dev; + HIDP_CAPS caps; + HIDP_PREPARSED_DATA *pp_data = NULL; + BOOLEAN res; + NTSTATUS nt_res; + + if (hid_init() < 0) { + return NULL; + } + + dev = new_hid_device(); + + // Open a handle to the device + dev->device_handle = open_device(path); + + // Check validity of write_handle. + if (dev->device_handle == INVALID_HANDLE_VALUE) { + // Unable to open the device. + register_error(dev, "CreateFile"); + goto err; + } + + // Get the Input Report length for the device. + res = HidD_GetPreparsedData(dev->device_handle, &pp_data); + if (!res) { + register_error(dev, "HidD_GetPreparsedData"); + goto err; + } + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res != HIDP_STATUS_SUCCESS) { + register_error(dev, "HidP_GetCaps"); + goto err_pp_data; + } + dev->input_report_length = caps.InputReportByteLength; + HidD_FreePreparsedData(pp_data); + + dev->read_buf = (char*) malloc(dev->input_report_length); + + return dev; + +err_pp_data: + HidD_FreePreparsedData(pp_data); +err: + CloseHandle(dev->device_handle); + free(dev); + return NULL; +} + +int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + DWORD bytes_written; + BOOL res; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + + res = WriteFile(dev->device_handle, data, (int)length, NULL, &ol); + + if (!res) + { + int err = GetLastError(); + + if (GetLastError() != ERROR_IO_PENDING) { + // WriteFile() failed. Return error. + register_error(dev, "WriteFile"); + return -1; + } + } + + // Wait here until the write is done. This makes + // hid_write() synchronous. + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); + if (!res) { + // The Write operation failed. + register_error(dev, "WriteFile"); + return -1; + } + + return bytes_written; +} + +int HID_API_EXPORT HID_API_CALL hid_set_ptt(int state) +{ + int res; + hid_device *handle; + unsigned char buf[16]; + + handle = hid_open(0xd8c, 0x8, NULL); + if (!handle) { + printf("unable to open device\n"); + return 1; + } + + + // Toggle PTT + + buf[0] = 0; + buf[1] = 0; + buf[2]= 1 << (3 - 1); + buf[3] = state << (3 - 1); + buf[4] = 0; + + + res = hid_write(handle, buf, 5); + if (res < 0) + { + printf("Unable to write()\n"); + printf("Error: %ls\n", hid_error(handle)); + } + + hid_close(handle); + return res; +} + + +int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + DWORD bytes_read = 0; + BOOL res; + + // Copy the handle for convenience. + HANDLE ev = dev->ol.hEvent; + + if (!dev->read_pending) { + // Start an Overlapped I/O read. + dev->read_pending = TRUE; + ResetEvent(ev); + res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + // ReadFile() has failed. + // Clean up and return error. + CancelIo(dev->device_handle); + dev->read_pending = FALSE; + goto end_of_function; + } + } + } + + if (milliseconds >= 0) { + // See if there is any data yet. + res = WaitForSingleObject(ev, milliseconds); + if (res != WAIT_OBJECT_0) { + // There was no data this time. Return zero bytes available, + // but leave the Overlapped I/O running. + return 0; + } + } + + // Either WaitForSingleObject() told us that ReadFile has completed, or + // we are in non-blocking mode. Get the number of bytes read. The actual + // data has been copied to the data[] array which was passed to ReadFile(). + res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); + + // Set pending back to false, even if GetOverlappedResult() returned error. + dev->read_pending = FALSE; + + if (res && bytes_read > 0) { + if (dev->read_buf[0] == 0x0) { + /* If report numbers aren't being used, but Windows sticks a report + number (0x0) on the beginning of the report anyway. To make this + work like the other platforms, and to make it work more like the + HID spec, we'll skip over this byte. */ + bytes_read--; + memcpy(data, dev->read_buf+1, length); + } + else { + /* Copy the whole buffer, report number and all. */ + memcpy(data, dev->read_buf, length); + } + } + +end_of_function: + if (!res) { + register_error(dev, "GetOverlappedResult"); + return -1; + } + + return bytes_read; +} + +int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) +{ + dev->blocking = !nonblock; + return 0; /* Success */ +} + +int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, (int)length); + if (!res) { + register_error(dev, "HidD_SetFeature"); + return -1; + } + + return (int)length; +} + + +int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + BOOL res; +#if 0 + res = HidD_GetFeature(dev->device_handle, data, length); + if (!res) { + register_error(dev, "HidD_GetFeature"); + return -1; + } + return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ +#else + DWORD bytes_returned; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + + res = DeviceIoControl(dev->device_handle, + IOCTL_HID_GET_FEATURE, + data, (int)length, + data, (int)length, + &bytes_returned, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + // DeviceIoControl() failed. Return error. + register_error(dev, "Send Feature Report DeviceIoControl"); + return -1; + } + } + + // Wait here until the write is done. This makes + // hid_get_feature_report() synchronous. + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); + if (!res) { + // The operation failed. + register_error(dev, "Send Feature Report GetOverLappedResult"); + return -1; + } + return bytes_returned; +#endif +} + +void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) +{ + if (!dev) + return; + CancelIo(dev->device_handle); + CloseHandle(dev->ol.hEvent); + CloseHandle(dev->device_handle); + LocalFree(dev->last_error_str); + free(dev->read_buf); + free(dev); +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetManufacturerString(dev->device_handle, string, 2 * (int)maxlen); + if (!res) { + register_error(dev, "HidD_GetManufacturerString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetProductString(dev->device_handle, string, 2 * (int)maxlen); + if (!res) { + register_error(dev, "HidD_GetProductString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetSerialNumberString(dev->device_handle, string, 2 * (int)maxlen); + if (!res) { + register_error(dev, "HidD_GetSerialNumberString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetIndexedString(dev->device_handle, string_index, string, 2 * (int)maxlen); + if (!res) { + register_error(dev, "HidD_GetIndexedString"); + return -1; + } + + return 0; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return (wchar_t*)dev->last_error_str; +} + + +//#define PICPGM +//#define S11 +#define P32 +#ifdef S11 + unsigned short VendorID = 0xa0a0; + unsigned short ProductID = 0x0001; +#endif + +#ifdef P32 + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x3f; +#endif + + +#ifdef PICPGM + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x0033; +#endif + + +#if 0 +int __cdecl main(int argc, char* argv[]) +{ + int res; + unsigned char buf[65]; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + // Set up the command buffer. + memset(buf,0x00,sizeof(buf)); + buf[0] = 0; + buf[1] = 0x81; + + + // Open the device. + int handle = open(VendorID, ProductID, L"12345"); + if (handle < 0) + printf("unable to open device\n"); + + + // Toggle LED (cmd 0x80) + buf[1] = 0x80; + res = write(handle, buf, 65); + if (res < 0) + printf("Unable to write()\n"); + + // Request state (cmd 0x81) + buf[1] = 0x81; + write(handle, buf, 65); + if (res < 0) + printf("Unable to write() (2)\n"); + + // Read requested state + read(handle, buf, 65); + if (res < 0) + printf("Unable to read()\n"); + + // Print out the returned buffer. + for (int i = 0; i < 4; i++) + printf("buf[%d]: %d\n", i, buf[i]); + + return 0; +} +#endif + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/.svn/pristine/3f/3fe6dbbc01cc9eb228336b43ed145ca34bc7c6c0.svn-base b/.svn/pristine/3f/3fe6dbbc01cc9eb228336b43ed145ca34bc7c6c0.svn-base new file mode 100644 index 0000000..e8ad2ca --- /dev/null +++ b/.svn/pristine/3f/3fe6dbbc01cc9eb228336b43ed145ca34bc7c6c0.svn-base @@ -0,0 +1,386 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +#ifdef _WIN32 +// #define HID_API_EXPORT __declspec(dllexport) // BPQ + #define HID_API_EXPORT + #define HID_API_CALL +#else + #define HID_API_EXPORT /**< API export macro */ + #define HID_API_CALL /**< API call macro */ +#endif + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +#ifdef __cplusplus +extern "C" { +#endif + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac only). */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac only).*/ + unsigned short usage; + /** The USB interface which this logical device + represents. Valid on both Linux implementations + in all cases, and valid on the Windows implementation + only if the device contains more than one interface. */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device, containing information about the HID devices + attached to the system, or NULL in the case of failure. Free + this linked list by calling hid_free_enumeration(). + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param device A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Make sure to set the first byte of @p data[] to the Report + ID of the report to be read. Make sure to allow space for + this extra byte in @p data[]. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a string describing the last error which occurred. + + @ingroup API + @param device A device handle returned from hid_open(). + + @returns + This function returns a string containing the last error + which occurred or NULL if none has occurred. + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); + + int HID_API_EXPORT HID_API_CALL hid_set_ptt(int state); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/.svn/pristine/40/40d12cdb8ae964f75a7534193b4fec7e4e89a309.svn-base b/.svn/pristine/40/40d12cdb8ae964f75a7534193b4fec7e4e89a309.svn-base new file mode 100644 index 0000000..19532de --- /dev/null +++ b/.svn/pristine/40/40d12cdb8ae964f75a7534193b4fec7e4e89a309.svn-base @@ -0,0 +1,10505 @@ +/* +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 +*/ + +// +// Rig Control Module +// + +// Dec 29 2009 + +// Add Scan Control for SCS + +// August 2010 + +// Fix logic error in Port Initialisation (wasn't always raising RTS and DTR +// Clear RTS and DTR on close + +// Fix Kenwood processing of multiple messages in one packet. + +// Fix reporting of set errors in scan to the wrong session + + +// Yaesu List + +// FT990 define as FT100 + + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + + +#include +#include +#include "time.h" + +#include "cheaders.h" +#include "tncinfo.h" +#ifdef WIN32 +#include +#else +char *fcvt(double number, int ndigits, int *decpt, int *sign); +#include +#include +#include +#endif +#include "bpq32.h" + +#include "hidapi.h" + +int Row = -20; + +extern struct PORTCONTROL * PORTTABLE; + +VOID __cdecl Debugprintf(const char * format, ...); + +struct RIGINFO * RigConfig(struct TNCINFO * TNC, char * buf, int Port); +struct RIGPORTINFO * CreateTTYInfo(int port, int speed); +BOOL RigCloseConnection(struct RIGPORTINFO * PORT); +BOOL RigWriteCommBlock(struct RIGPORTINFO * PORT); +BOOL DestroyTTYInfo(int port); +void CheckRX(struct RIGPORTINFO * PORT); +static int OpenRigCOMMPort(struct RIGPORTINFO * PORT, VOID * Port, int Speed); +VOID ICOMPoll(struct RIGPORTINFO * PORT); +VOID ProcessFrame(struct RIGPORTINFO * PORT, UCHAR * rxbuff, int len); +VOID ProcessICOMFrame(struct RIGPORTINFO * PORT, UCHAR * rxbuffer, int Len); +int SendResponse(int Stream, char * Msg); +VOID ProcessYaesuFrame(struct RIGPORTINFO * PORT); +VOID YaesuPoll(struct RIGPORTINFO * PORT); +VOID ProcessYaesuCmdAck(struct RIGPORTINFO * PORT); +VOID ProcessKenwoodFrame(struct RIGPORTINFO * PORT, int Length); +VOID KenwoodPoll(struct RIGPORTINFO * PORT); +VOID DummyPoll(struct RIGPORTINFO * PORT); +VOID SwitchAntenna(struct RIGINFO * RIG, char Antenna); +VOID DoBandwidthandAntenna(struct RIGINFO *RIG, struct ScanEntry * ptr); +VOID SetupScanInterLockGroups(struct RIGINFO *RIG); +VOID ProcessFT100Frame(struct RIGPORTINFO * PORT); +VOID ProcessFT990Frame(struct RIGPORTINFO * PORT); +VOID ProcessFT1000Frame(struct RIGPORTINFO * PORT); +VOID AddNMEAChecksum(char * msg); +VOID ProcessNMEA(struct RIGPORTINFO * PORT, char * NMEAMsg, int len); +VOID COMSetDTR(HANDLE fd); +VOID COMClearDTR(HANDLE fd); +VOID COMSetRTS(HANDLE fd); +VOID COMClearRTS(HANDLE fd); +void CM108_set_ptt(struct RIGINFO *RIG, int PTTState); +BOOL OpenHIDPort(struct RIGPORTINFO * PORT, VOID * Port, int Speed); +int HID_Read_Block(struct RIGPORTINFO * PORT); +int HID_Write_Block(struct RIGPORTINFO * PORT); +HANDLE rawhid_open(char * Device); +int rawhid_recv(int num, void *buf, int len, int timeout); +int rawhid_send(int num, void *buf, int len, int timeout); +void rawhid_close(int num); +VOID ConnecttoHAMLIB(struct RIGPORTINFO * PORT); +VOID ConnecttoFLRIG(struct RIGPORTINFO * PORT); +int DecodeHAMLIBAddr(struct RIGPORTINFO * PORT, char * ptr); +void ProcessHAMLIBFrame(struct RIGPORTINFO * PORT, int Length); +VOID HAMLIBPoll(struct RIGPORTINFO * PORT); +void HAMLIBSlaveThread(struct RIGINFO * RIG); +void CheckAndProcessRTLUDP(struct RIGPORTINFO * PORT); +VOID RTLUDPPoll(struct RIGPORTINFO * PORT); +VOID ConnecttoRTLUDP(struct RIGPORTINFO * PORT); +VOID FLRIGPoll(struct RIGPORTINFO * PORT); +void ProcessFLRIGFrame(struct RIGPORTINFO * PORT); +VOID FLRIGSendCommand(struct RIGPORTINFO * PORT, char * Command, char * Value); + +VOID ProcessSDRRadioFrame(struct RIGPORTINFO * PORT, int Length); +VOID SDRRadioPoll(struct RIGPORTINFO * PORT); + +VOID SetupPortRIGPointers(); +VOID PTTCATThread(struct RIGINFO *RIG); +VOID ConnecttoHAMLIB(struct RIGPORTINFO * PORT); + +// ----- G7TAJ ---- +VOID ConnecttoSDRANGEL(struct RIGPORTINFO * PORT); +VOID SDRANGELPoll(struct RIGPORTINFO * PORT); +void ProcessSDRANGELFrame(struct RIGPORTINFO * PORT); +VOID SDRANGELSendCommand(struct RIGPORTINFO * PORT, char * Command, char * Value); +void SDRANGELProcessMessage(struct RIGPORTINFO * PORT); + +// ----- G7TAJ ---- + + +int SendPTCRadioCommand(struct TNCINFO * TNC, char * Block, int Length); +int GetPTCRadioCommand(struct TNCINFO * TNC, char * Block); +int BuildRigCtlPage(char * _REPLYBUFFER); +void SendRigWebPage(); + +extern TRANSPORTENTRY * L4TABLE; +HANDLE hInstance; + +VOID APIENTRY CreateOneTimePassword(char * Password, char * KeyPhrase, int TimeOffset); +BOOL APIENTRY CheckOneTimePassword(char * Password, char * KeyPhrase); + +char * GetApplCallFromName(char * App); + +char Modes[25][6] = {"LSB", "USB", "AM", "CW", "RTTY", "FM", "WFM", "CW-R", "RTTY-R", + "????","????","????","????","????","????","????","????","DV", "LSBD1", + "USBD1", "LSBD2","USBD2", "LSBD3","USBD3", "????"}; + +/* +DV = 17 +F8101 +(0000=LSB, 0001=USB, 0002=AM, +0003=CW, 0004=RTTY, +0018=LSB D1, 0019=USB D1, +0020=LSB D2, 0021=USB D2, +0022=LSB D3, 0023=USB D3 +*/ +// 0 1 2 3 4 5 6 7 8 9 0A 0B 0C 88 + +char YaesuModes[16][6] = {"LSB", "USB", "CW", "CWR", "AM", "", "", "", "FM", "", "DIG", "", "PKT", "FMN", "????"}; + +char FT100Modes[9][6] = {"LSB", "USB", "CW", "CWR", "AM", "DIG", "FM", "WFM", "????"}; + +char FT990Modes[13][6] = {"LSB", "USB", "CW2k4", "CW500", "AM6k", "AM2k4", "FM", "FM", "RTTYL", "RTTYU", "PKTL", "PKTFM", "????"}; + +char FT1000Modes[13][6] = {"LSB", "USB", "CW", "CWR", "AM", "AMS", "FM", "WFM", "RTTYL", "RTTYU", "PKTL", "PKTF", "????"}; + +char FTRXModes[8][6] = {"LSB", "USB", "CW", "AM", "FM", "RTTY", "PKT", ""}; + +char KenwoodModes[16][6] = {"????", "LSB", "USB", "CW", "FM", "AM", "FSK", "????"}; + +char FT2000Modes[16][6] = {"????", "LSB", "USB", "CW", "FM", "AM", "FSK", "CW-R", "PKT-L", "FSK-R", "PKT-FM", "FM-N", "PKT-U", "????"}; + +char FTDX10Modes[16][9] = {"????", "LSB", "USB", "CW-U", "FM", "AM", "RTTY-L", "CW-L", "DATA-L", "RTTY-U", "DATA-FM", "FM-N", "DATA-U", "AM-N", "PSK", "DATA-FM-N"}; + +char FT991AModes[16][9] = {"????", "LSB", "USB", "CW-U", "FM", "AM", "RTTY-LSB", "CW-L", "DATA-LSB", "RTTY-USB", "DATA-FM", "FM-N", "DATA-USB", "AM-N", "C4FM", "????"}; + +char FLEXModes[16][6] = {"LSB", "USB", "DSB", "CWL", "CWU", "FM", "AM", "DIGU", "SPEC", "DIGL", "SAM", "DRM"}; + +char AuthPassword[100] = ""; + +char LastPassword[17]; + +int NumberofPorts = 0; + +BOOL EndPTTCATThread = FALSE; + +int HAMLIBMasterRunning = 0; +int HAMLIBSlaveRunning = 0; +int FLRIGRunning = 0; + +// ---- G7TAJ ---- +int SDRANGELRunning = 0; +// ---- G7TAJ ---- + +char * RigWebPage = 0; +int RigWebPageLen = 0; + + +struct RIGPORTINFO * PORTInfo[MAXBPQPORTS + 2] = {NULL}; // Records are Malloc'd + +struct RIGINFO * DLLRIG = NULL; // Rig record for dll PTT interface (currently only for UZ7HO); + + +struct TimeScan * AllocateTimeRec(struct RIGINFO * RIG) +{ + struct TimeScan * Band = zalloc(sizeof (struct TimeScan)); + + RIG->TimeBands = realloc(RIG->TimeBands, (++RIG->NumberofBands+2) * sizeof(void *)); + RIG->TimeBands[RIG->NumberofBands] = Band; + RIG->TimeBands[RIG->NumberofBands+1] = NULL; + + return Band; +} + +struct ScanEntry ** CheckTimeBands(struct RIGINFO * RIG) +{ + int i = 0; + time_t NOW = time(NULL) % 86400; + + // Find TimeBand + + while (i < RIG->NumberofBands) + { + if (RIG->TimeBands[i + 1]->Start > NOW) + { + break; + } + i++; + } + + RIG->FreqPtr = RIG->TimeBands[i]->Scanlist; + + return RIG->FreqPtr; +} + +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC); + +VOID Rig_PTT(struct TNCINFO * TNC, BOOL PTTState) +{ + if (TNC == NULL) return; + + if (TNC->TXRIG) + Rig_PTTEx(TNC->TXRIG, PTTState, TNC); + else + Rig_PTTEx(TNC->RIG, PTTState, TNC); +} + +VOID Rig_PTTEx(struct RIGINFO * RIG, BOOL PTTState, struct TNCINFO * TNC) +{ + struct RIGPORTINFO * PORT; + int i, Len; + char cmd[32]; + char onString[128]; // Actual CAT strings to send. May be modified for QSY on PTT + char offString[128]; + int onLen = 0, offLen = 0; + + if (RIG == NULL) return; + + PORT = RIG->PORT; + + if (PORT == NULL) + return; + + // CAT string defaults to that set up by RIGConfig in RIG->PTTOn and RIG->PTTOff, + // but can be overidden by Port specify strings from TNC->PTTOn and TNC->PTTOff. + // If PTTSetsFreq is set on a Rig, that overrides the RIG->PTTOn but not TNC->PTTOn + + + if (PTTState) + { + MySetWindowText(RIG->hPTT, "T"); + RIG->WEB_PTT = 'T'; + RIG->PTTTimer = PTTLimit; + RIG->repeatPTTOFFTimer = 0; // Cancel repeated off command + + if (TNC && TNC->PTTOn[0]) + { + memcpy(onString, TNC->PTTOn, TNC->PTTOnLen); + onLen = TNC->PTTOnLen; + } + else + { + memcpy(onString, RIG->PTTOn, RIG->PTTOnLen); + onLen = RIG->PTTOnLen; + + // If PTT_SETS_FREQ set calculate TX Freq and see if changed + + // Freq can be set on the TNC, the RIG, or calculated from current rx freq + pttOffset + + if (TNC && RIG->PTTSetsFreq) + { + long long txfreq = 0; + + if (TNC->TXFreq) + txfreq = TNC->TXFreq + TNC->TXOffset + RIG->txError; + else if (TNC->RIG && TNC->RIG->txFreq) + txfreq = RIG->txFreq; // Used if not associated with a TNC port - eg HAMLIB + WSJT + else if (TNC->RIG && TNC->RIG->RigFreq != 0.0) + { + // Use RigFreq + pttOffset, so TX Tracks RX + + long long rxfreq = (long long)(TNC->RIG->RigFreq * 1000000.0) - TNC->RIG->rxOffset; + txfreq = rxfreq + RIG->pttOffset + RIG->txError; + txfreq += RIG->rxError; + } + + if (txfreq) + { + if (RIG->lastSetFreq != txfreq) + { + char FreqString[80]; + char * CmdPtr = onString; + UCHAR * Poll = PORT->TXBuffer; + + + RIG->lastSetFreq = txfreq; + + // Convert to CAT string + + sprintf(FreqString, "%012lld", txfreq); + + switch (PORT->PortType) + { + case ICOM: + + // CI-V must send all commands as one string, or Radio will start to ack them and + // collide with rest of command + + // Set Freq is sent before set PTT, so set up QSY string then copy PTT string to buffer + // Need to convert two chars to bcd digit + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x5; // Set frequency command + + *(CmdPtr++) = (FreqString[11] - 48) | ((FreqString[10] - 48) << 4); + *(CmdPtr++) = (FreqString[9] - 48) | ((FreqString[8] - 48) << 4); + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + + if (RIG->IC735) + { + *(CmdPtr++) = 0xFD; + onLen = 10; + } + else + { + *(CmdPtr++) = (FreqString[3] - 48); + *(CmdPtr++) = 0xFD; + onLen = 11; + } + + // Now add PTT String + + memcpy(&onString[onLen], RIG->PTTOn, RIG->PTTOnLen); + onLen += RIG->PTTOnLen; + + break; + + case YAESU: + + *(Poll++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(Poll++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(Poll++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(Poll++) = (FreqString[10] - 48) | ((FreqString[9] - 48) << 4); + *(Poll++) = 1; // Set Freq + + PORT->TXLen = 5; + RigWriteCommBlock(PORT); + + + if (RIG->PTTMode & PTTCI_V) + { + Sleep(150); + Poll = PORT->TXBuffer; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + + *(Poll++) = 0; + *(Poll++) = PTTState ? 0x08 : 0x88; // CMD = 08 : PTT ON CMD = 88 : PTT OFF + + PORT->TXLen = 5; + RigWriteCommBlock(PORT); + } + + PORT->Retries = 1; + PORT->Timeout = 0; + + return; + + + + case HAMLIB: + + // Dont need to save, as we can send strings separately + + Len = sprintf(cmd, "F %lld\n", txfreq); + i = send(PORT->remoteSock, cmd, Len, 0); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + + break; + + } + } + } + } + } + } + else + { + // Drop PTT + + MySetWindowText(RIG->hPTT, " "); + RIG->WEB_PTT = ' '; + RIG->PTTTimer = 0; + if (PORT->PortType == ICOM) + RIG->repeatPTTOFFTimer = 300; // set 30 second repeated off command + + if (TNC && TNC->PTTOff[0]) + { + memcpy(offString, TNC->PTTOff, TNC->PTTOffLen); + offLen = TNC->PTTOffLen; + RIG->lastSetFreq = 0; + } + else + { + memcpy(offString, RIG->PTTOff, RIG->PTTOffLen); + offLen = RIG->PTTOffLen; + + // If PTT_SETS_FREQ set calculate TX Freq and see if changed + + if (PTTState == 0 && RIG->PTTSetsFreq && RIG->defaultFreq) + { + // Dropped PTT. See if need to set freq back to default + + long long txfreq = RIG->defaultFreq + RIG->txError; + + if (RIG->lastSetFreq != txfreq) + { + char FreqString[80]; + char * CmdPtr = offString; + UCHAR * Poll = PORT->TXBuffer; + + RIG->lastSetFreq = txfreq; + + // Convert to CAT string + + sprintf(FreqString, "%012lld", txfreq); + + switch (PORT->PortType) + { + case ICOM: + + // CI-V must send all commands as one string, or Radio will start to ack them and + // collide with rest of command + + // Set Freq is sent after drop PTT, so copy PTT string to buffer then set up QSY string + // Need to convert two chars to bcd digit + + // We copied off string earlier, so just append QSY string + + CmdPtr += offLen; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x5; // Set frequency command + + *(CmdPtr++) = (FreqString[11] - 48) | ((FreqString[10] - 48) << 4); + *(CmdPtr++) = (FreqString[9] - 48) | ((FreqString[8] - 48) << 4); + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + if (RIG->IC735) + { + *(CmdPtr++) = 0xFD; + offLen += 10; + } + else + { + *(CmdPtr++) = (FreqString[3] - 48); + *(CmdPtr++) = 0xFD; + offLen += 11; + } + + case FLRIG: + + sprintf(cmd, "%lld", txfreq); + FLRIGSendCommand(PORT, "rig.set_vfo", cmd); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + + case YAESU: + + // Easier to add PTT string, send and return; + + Len = 0; + + if (RIG->PTTMode & PTTCI_V) + { + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = PTTState ? 0x08 : 0x88; // CMD = 08 : PTT ON CMD = 88 : PTT OFF + + PORT->TXLen = 5; + RigWriteCommBlock(PORT); + Poll = PORT->TXBuffer; + Sleep(100); + } + + *(Poll++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(Poll++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(Poll++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(Poll++) = (FreqString[10] - 48) | ((FreqString[9] - 48) << 4); + *(Poll++) = 1; // Set Freq + + PORT->TXLen = 5; + RigWriteCommBlock(PORT); + + PORT->Retries = 1; + PORT->Timeout = 0; + + return; + + case HAMLIB: + + // Dont need to save, as we can send strings separately + + Len = sprintf(cmd, "F %lld\n", txfreq); + send(PORT->remoteSock, cmd, Len, 0); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + } + } + } + } + } + + // Now send the command + + if (RIG->PTTMode & PTTCI_V) + { + UCHAR * Poll = PORT->TXBuffer; + + // Don't read for 10 secs to avoid clash with PTT OFF + // Should do this for all rigs on port + + for (i = 0; i< PORT->ConfiguredRigs; i++) + PORT->Rigs[i].PollCounter = 100; + + PORT->AutoPoll = TRUE; + + switch (PORT->PortType) + { + case ICOM: + case KENWOOD: + case FT2000: + case FTDX10: + case FT991A: + case FLEX: + case NMEA: + + if (PTTState) + { + memcpy(Poll, onString, onLen); + PORT->TXLen = onLen; + } + else + { + memcpy(Poll, offString, offLen); + PORT->TXLen = offLen; + } + + RigWriteCommBlock(PORT); + + if (PORT->PortType == ICOM && !PTTState) + RigWriteCommBlock(PORT); // Send ICOM PTT OFF Twice + + PORT->Retries = 1; + + if (PORT->PortType != ICOM) + PORT->Timeout = 0; + + return; + + case FT100: + case FT990: + case FT1000: + + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = PTTState; // OFF/ON + *(Poll++) = 15; + + PORT->TXLen = 5; + RigWriteCommBlock(PORT); + + PORT->Retries = 1; + PORT->Timeout = 0; + + return; + + case YAESU: // 897 - maybe others + + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = PTTState ? 0x08 : 0x88; // CMD = 08 : PTT ON CMD = 88 : PTT OFF + + PORT->TXLen = 5; + RigWriteCommBlock(PORT); + + PORT->Retries = 1; + PORT->Timeout = 0; + + return; + + case FLRIG: + + sprintf(cmd, "%d", PTTState); + FLRIGSendCommand(PORT, "rig.set_ptt", cmd); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + + return; + + case HAMLIB: + + Len = sprintf(cmd, "T %d\n", PTTState); + send(PORT->remoteSock, cmd, Len, 0); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + + return; + } + } + + if (RIG->PTTMode & PTTRTS) + if (PTTState) + COMSetRTS(PORT->hPTTDevice); + else + COMClearRTS(PORT->hPTTDevice); + + if (RIG->PTTMode & PTTDTR) + if (PTTState) + COMSetDTR(PORT->hPTTDevice); + else + COMClearDTR(PORT->hPTTDevice); + + if (RIG->PTTMode & PTTCM108) + CM108_set_ptt(RIG, PTTState); + + if (RIG->PTTMode & PTTHAMLIB) + { + char Msg[16]; + int Len = sprintf(Msg, "T %d\n", PTTState); + + Len = send(PORT->remoteSock, Msg, Len, 0); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + } + if (RIG->PTTMode & PTTFLRIG) + { + char cmd[32]; + + sprintf(cmd, "%d", PTTState); + FLRIGSendCommand(PORT, "rig.set_ptt", cmd); + RIG->PollCounter = 100; // Don't read for 10 secs to avoid clash with PTT OFF + } +} + +void saveNewFreq(struct RIGINFO * RIG, double Freq, char * Mode) +{ + if (Freq > 0.0) + { + _gcvt((Freq + RIG->rxOffset) / 1000000.0, 9, RIG->Valchar); + strcpy(RIG->WEB_FREQ, RIG->Valchar); + MySetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + if (Mode[0]) + { + strcpy(RIG->ModeString, Mode); + MySetWindowText(RIG->hMODE, Mode); + } + +} + +// Need version that doesn't need Port Number + +int Rig_CommandEx(struct RIGPORTINFO * PORT, struct RIGINFO * RIG, TRANSPORTENTRY * Session, char * Command); + +int Rig_Command(TRANSPORTENTRY * Session, char * Command) +{ + char * ptr; + int i, n, p, Port; + TRANSPORTENTRY * L4 = L4TABLE; + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + + // Only Allow RADIO from Secure Applications + + _strupr(Command); + + ptr = strchr(Command, 13); + if (ptr) *(ptr) = 0; // Null Terminate + + if (memcmp(Command, "AUTH ", 5) == 0) + { + if (AuthPassword[0] && (memcmp(LastPassword, &Command[5], 16) != 0)) + { + if (CheckOneTimePassword(&Command[5], AuthPassword)) + { + Session->Secure_Session = 1; + + sprintf(Command, "Ok\r"); + + memcpy(LastPassword, &Command[5], 16); // Save + + return FALSE; + } + } + + sprintf(Command, "Sorry AUTH failed\r"); + return FALSE; + } + + if (Session != (TRANSPORTENTRY *) -1) // Used for internal Stop/Start + { + if (Session->Secure_Session == 0) + { + sprintf(Command, "Sorry - you are not allowed to use this command\r"); + return FALSE; + } + } + if (NumberofPorts == 0) + { + sprintf(Command, "Sorry - Rig Control not configured\r"); + return FALSE; + } + + // if Port starts with 'R' then select Radio (was Interlock) number, not BPQ Port + + if (Command[0] == 'R') + { + n = sscanf(&Command[1],"%d ", &Port); + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + if (RIG->Interlock == Port) + goto portok; + } + } + + sprintf(Command, "Sorry - Port not found\r"); + return FALSE; + } + + n = sscanf(Command,"%d ", &Port); + + // Look for the port + + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + if (RIG->BPQPort & ((uint64_t)1 << Port)) + goto portok; + } + } + + sprintf(Command, "Sorry - Port not found\r"); + return FALSE; + +portok: + + return Rig_CommandEx(PORT, RIG, Session, Command); +} + + + +static char MsgHddr[] = "POST /RPC2 HTTP/1.1\r\n" + "User-Agent: XMLRPC++ 0.8\r\n" + "Host: 127.0.0.1:7362\r\n" + "Content-Type: text/xml\r\n" + "Content-length: %d\r\n" + "\r\n%s"; + +static char Req[] = "\r\n" + "%s\r\n" + "%s" + "\r\n"; + + +// ---- G7TAJ ---- +static char SDRANGEL_MsgHddr[] = "PATCH HTTP/1.1\r\n" + "User-Agent: BPQ32\r\n" + "Host: %s\r\n" + "accept: application/json" + "Content-Type: application/json" + "Content-length: %d\r\n" + "\r\n%s"; + +static char SDRANGEL_FREQ_DATA[] = "{" + "\"deviceHwType\": \"%s\", " + "\"direction\": 0," + "\"rtlSdrSettings\": {" + " \"centerFrequency\": \"%s\"" + "}}"; + +//freq = 10489630000 + +// ---- G7TAJ ---- + + + +int Rig_CommandEx(struct RIGPORTINFO * PORT, struct RIGINFO * RIG, TRANSPORTENTRY * Session, char * Command) +{ + int n, ModeNo, Filter, Port = 0; + double Freq = 0.0; + char FreqString[80]="", FilterString[80]="", Mode[80]="", Data[80] = "", Dummy[80] = ""; + struct MSGWITHOUTLEN * buffptr; + UCHAR * Poll; + + int i; + TRANSPORTENTRY * L4 = L4TABLE; + char * ptr; + int Split, DataFlag, Bandwidth, Antenna; + struct ScanEntry * FreqPtr; + char * CmdPtr; + int Len; + char MemoryBank = 0; // For Memory Scanning + int MemoryNumber = 0; + + // Only Allow RADIO from Secure Applications + + _strupr(Command); + + ptr = strchr(Command, 13); + if (ptr) *(ptr) = 0; // Null Terminate + + if (memcmp(Command, "AUTH ", 5) == 0) + { + if (AuthPassword[0] && (memcmp(LastPassword, &Command[5], 16) != 0)) + { + if (CheckOneTimePassword(&Command[5], AuthPassword)) + { + Session->Secure_Session = 1; + + sprintf(Command, "Ok\r"); + + memcpy(LastPassword, &Command[5], 16); // Save + + return FALSE; + } + } + + sprintf(Command, "Sorry AUTH failed\r"); + return FALSE; + } + + if (Session != (TRANSPORTENTRY *) -1) // Used for internal Stop/Start + { + if (Session->Secure_Session == 0) + { + sprintf(Command, "Sorry - you are not allowed to use this command\r"); + return FALSE; + } + } + if (NumberofPorts == 0) + { + sprintf(Command, "Sorry - Rig Control not configured\r"); + return FALSE; + } + + // if Port starts with 'R' then select Radio (was Interlock) number, not BPQ Port + + if (Command[0] == 'R') + n = sscanf(Command,"%s %s %s %s %s", &Dummy[0], &FreqString[0], &Mode[0], &FilterString[0], &Data[0]); + else + n = sscanf(Command,"%d %s %s %s %s", &Port, &FreqString[0], &Mode[0], &FilterString[0], &Data[0]); + + if (_stricmp(FreqString, "CLOSE") == 0) + { + PORT->Closed = 1; + RigCloseConnection(PORT); + + MySetWindowText(RIG->hSCAN, "C"); + RIG->WEB_SCAN = 'C'; + + sprintf(Command, "Ok\r"); + return FALSE; + } + + if (_stricmp(FreqString, "OPEN") == 0) + { + PORT->ReopenDelay = 300; + PORT->Closed = 0; + + MySetWindowText(RIG->hSCAN, ""); + RIG->WEB_SCAN = ' '; + + sprintf(Command, "Ok\r"); + return FALSE; + } + + if (n > 1) + { + if (_stricmp(FreqString, "SCANSTART") == 0) + { + if (RIG->NumberofBands) + { + RIG->ScanStopped &= (0xffffffffffffffff ^ ((uint64_t)1 << Port)); + + if (Session != (TRANSPORTENTRY *) -1) // Used for internal Stop/Start + RIG->ScanStopped &= 0xfffffffffffffffe; // Clear Manual Stopped Bit + + if (n > 2) + RIG->ScanCounter = atoi(Mode) * 10; //Start Delay + else + RIG->ScanCounter = 10; + + RIG->WaitingForPermission = FALSE; // In case stuck + + if (RIG->ScanStopped == 0) + { + SetWindowText(RIG->hSCAN, "S"); + RIG->WEB_SCAN = 'S'; + } + sprintf(Command, "Ok\r"); + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 SCANSTART Port %d", Port); + } + else + sprintf(Command, "Sorry no Scan List defined for this port\r"); + + return FALSE; + } + + if (_stricmp(FreqString, "SCANSTOP") == 0) + { + RIG->ScanStopped |= ((uint64_t)1 << Port); + + if (Session != (TRANSPORTENTRY *) -1) // Used for internal Stop/Start + RIG->ScanStopped |= 1; // Set Manual Stopped Bit + + MySetWindowText(RIG->hSCAN, ""); + RIG->WEB_SCAN = ' '; + + sprintf(Command, "Ok\r"); + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 SCANSTOP Port %d", Port); + + RIG->PollCounter = 50 / RIG->PORT->ConfiguredRigs; // Dont read freq for 5 secs + + return FALSE; + } + } + + + + if (RIG->RIGOK == 0) + { + if (Session != (TRANSPORTENTRY *) -1) + { + if (PORT->Closed) + sprintf(Command, "Sorry - Radio port closed\r"); + else + sprintf(Command, "Sorry - Radio not responding\r"); + } + return FALSE; + } + + if (n == 2 && _stricmp(FreqString, "FREQ") == 0) + { + if (RIG->Valchar[0]) + sprintf(Command, "Frequency is %s MHz\r", RIG->Valchar); + else + sprintf(Command, "Frequency not known\r"); + + return FALSE; + } + + if (n == 2 && _stricmp(FreqString, "PTT") == 0) + { + Rig_PTTEx(RIG, TRUE, NULL); + RIG->PTTTimer = 10; // 1 sec + sprintf(Command, "Ok\r"); + return FALSE; + } + + if (Session != (void *)-1) + { + if (Session->CIRCUITINDEX == 255) + RIG->Session = -1; + else + RIG->Session = Session->CIRCUITINDEX; // BPQ Stream + + RIG->PollCounter = 50; // Dont read freq for 5 secs in case clash with Poll + } + + if (_stricmp(FreqString, "TUNE") == 0) + { + char ReqBuf[256]; + char SendBuff[256]; + char FLPoll[80]; + + switch (PORT->PortType) + { + case ICOM: + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + CmdPtr = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + FreqPtr->Cmd2 = NULL; + FreqPtr->Cmd3 = NULL; + + // IC7100 Tune Fe fe 88 e0 1c 01 02 fd + + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1C; + *(CmdPtr++) = 0x01; + *(CmdPtr++) = 0x02; + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 8; + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case KENWOOD: + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + FreqPtr->Cmd1Len = sprintf(Poll, "AC111;AC;"); + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + return TRUE; + + case FLRIG: + + strcpy(FLPoll, "rig.tune"); + + Len = sprintf(ReqBuf, Req, FLPoll, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + if (PORT->CONNECTED) + { + if (send(PORT->remoteSock, SendBuff, Len, 0) != Len) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + } + } + sprintf(Command, "Ok\r"); + return FALSE; + } + + sprintf(Command, "Sorry - TUNE not supported on your radio\r"); + return FALSE; + } + + if (_stricmp(FreqString, "POWER") == 0) + { + char PowerString[16] = ""; + int Power = atoi(Mode); + int len; + char cmd[80]; + + switch (PORT->PortType) + { + case ICOM: + + if (n != 3 || Power > 255) + { + strcpy(Command, "Sorry - Invalid Format - should be POWER Level (0 - 255)\r"); + return FALSE; + } + + sprintf(PowerString, "%04d", Power); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + CmdPtr = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + FreqPtr->Cmd2 = NULL; + FreqPtr->Cmd3 = NULL; + + // IC7100 Set Power Fe fe 88 e0 14 0a xx xx fd + + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x14; + *(CmdPtr++) = 0x0A; + + // Need to convert param to decimal digits + + *(CmdPtr++) = (PowerString[1] - 48) | ((PowerString[0] - 48) << 4); + *(CmdPtr++) = (PowerString[3] - 48) | ((PowerString[2] - 48) << 4); + + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 9; + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case KENWOOD: + + if (n != 3 || Power > 200 || Power < 5) + { + strcpy(Command, "Sorry - Invalid Format - should be POWER Level (5 - 200)\r"); + return FALSE; + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + FreqPtr->Cmd1Len = sprintf(Poll, "PC%03d;PC;", Power); + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + return TRUE; + + case FLRIG: + + len = sprintf(cmd, "%d", Power); + + FLRIGSendCommand(PORT, "rig.set_power", cmd); + + sprintf(Command, "Ok\r"); + return FALSE; + } + + sprintf(Command, "Sorry - POWER not supported on your Radio\r"); + return FALSE; + } + + if (_stricmp(FreqString, "CMD") == 0) + { + // Send arbitrary command to radio + + char c; + int val; + char * ptr1; + char * ptr2; + int Len; + + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be CMD Params\r"); + return FALSE; + } + + buffptr = GetBuff(); + + if (buffptr == NULL) + return FALSE; + + ptr1 = strstr(Command, "CMD"); + + if (ptr1 == NULL) + return FALSE; + + ptr1 += 4; + + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + CmdPtr = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + FreqPtr->Cmd2 = NULL; + FreqPtr->Cmd3 = NULL; + + + switch (PORT->PortType) + { + case ICOM: + + // String is in Hex + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + while (c = *(ptr1++)) + { + if (c == ' ') continue; // Allow space between pairs + + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(CmdPtr++) = val; + } + + *(CmdPtr++) = 0xFD; + *(CmdPtr) = 0; + + Len = (int)(CmdPtr - (char *)&buffptr[30]); + break; + + case KENWOOD: + case FT991A: + case FT2000: + case FTDX10: + case FLEX: + case NMEA: + + // use text command + + Len = sprintf(CmdPtr, "%s", ptr1); + break; + + case YAESU: + + // String is in Hex (5 values) + + while (c = *(ptr1++)) + { + if (c == ' ') continue; // Allow space between pairs + + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(CmdPtr++) = val; + } + + *(CmdPtr) = 0; + + Len = 5; + break; + + case FLRIG: + + // Two string params - command and params eg rig.set_ptt 1" + // or maybe param could be format data - eg i 1 + + _strlwr(ptr1); + + ptr2 = strlop(ptr1, ' '); + + if (ptr2 == 0) + { + strcpy(Command, "Sorry - Invalid Format - should be CMD cmd params\r"); + return FALSE; + } + + strlop(ptr2, ' '); + + FLRIGSendCommand(PORT, ptr1, ptr2); + + return FALSE; + + + + + + default: + sprintf(Command, "Sorry - CMD not supported on your Radio\r"); + return FALSE; + } + + FreqPtr[0].Cmd1Len = Len; // for ICOM + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + sprintf(Command, "Ok\r"); + return FALSE; + } + + if (_memicmp(FreqString, "Chan", 4) == 0) + { + if (strchr(FreqString, '/') ) // Bank/Chan + { + MemoryBank = FreqString[4]; + MemoryNumber = atoi(&FreqString[6]); + } + else + MemoryNumber = atoi(&FreqString[4]); // Just Chan + + Freq = 0.0; + } + else + { + Freq = atof(FreqString); + + if (Freq < 0.1 && PORT->PortType != FLRIG) + { + strcpy(Command, "Sorry - Invalid Frequency\r"); + return FALSE; + } + } + + Freq = Freq * 1000000.0; + + sprintf(FreqString, "%09.0f", Freq); + + if (PORT->PortType != ICOM) + strcpy(Data, FilterString); // Others don't have a filter. + + Split = DataFlag = Bandwidth = Antenna = 0; + + _strupr(Data); + + if (strchr(Data, '+')) + Split = '+'; + else if (strchr(Data, '-')) + Split = '-'; + else if (strchr(Data, 'S')) + Split = 'S'; + else if (strchr(Data, 'D')) + DataFlag = 1; + + if (strchr(Data, 'W')) + Bandwidth = 'W'; + else if (strchr(Data, 'N')) + Bandwidth = 'N'; + + if (strstr(Data, "A1")) + Antenna = '1'; + else if (strstr(Data, "A2")) + Antenna = '2'; + else if (strstr(Data, "A3")) + Antenna = '3'; + else if (strstr(Data, "A4")) + Antenna = '4'; + else if (strstr(Data, "A5")) + Antenna = '5'; + else if (strstr(Data, "A6")) + Antenna = '6'; + + switch (PORT->PortType) + { + case ICOM: + + if (n == 2) + // Set Freq Only + + ModeNo = -1; + else + { + if (n < 4 && RIG->ICF8101 == 0) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode Filter Width\r"); + return FALSE; + } + + Filter = atoi(FilterString); + + for (ModeNo = 0; ModeNo < 24; ModeNo++) + { + if (_stricmp(Modes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 24) + { + sprintf(Command, "Sorry - Invalid Mode\r"); + return FALSE; + } + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + FreqPtr->Dwell = 51; + + CmdPtr = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + FreqPtr->Cmd2 = NULL; + FreqPtr->Cmd3 = NULL; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + + if (MemoryNumber) + { + // Set Memory Channel instead of Freq, Mode, etc + + char ChanString[5]; + + // Send Set Memory, then Channel + + *(CmdPtr++) = 0x08; + *(CmdPtr++) = 0xFD; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + sprintf(ChanString, "%04d", MemoryNumber); + + *(CmdPtr++) = 0x08; + *(CmdPtr++) = (ChanString[1] - 48) | ((ChanString[0] - 48) << 4); + *(CmdPtr++) = (ChanString[3] - 48) | ((ChanString[2] - 48) << 4); + *(CmdPtr++) = 0xFD; + + FreqPtr[0].Cmd1Len = 14; + + if (MemoryBank) + { + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x08; + *(CmdPtr++) = 0xA0; + *(CmdPtr++) = MemoryBank - 0x40; + *(CmdPtr++) = 0xFD; + + FreqPtr[0].Cmd1Len += 8; + } + } + else + { + if (RIG->ICF8101) + { + // Set Freq is 1A 35 and set Mode 1A 36 + + *(CmdPtr++) = 0x1A; + *(CmdPtr++) = 0x35; // Set frequency command + + // Need to convert two chars to bcd digit + + *(CmdPtr++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(CmdPtr++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(CmdPtr++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(CmdPtr++) = (FreqString[2] - 48) | ((FreqString[1] - 48) << 4); + *(CmdPtr++) = (FreqString[0] - 48); + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 12; + + if (ModeNo != -1) // Dont Set + { + CmdPtr = FreqPtr->Cmd2 = FreqPtr->Cmd2Msg; + FreqPtr[0].Cmd2Len = 9; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1A; + *(CmdPtr++) = 0x36; // Set mode command + *(CmdPtr++) = 0; + if (ModeNo > 19) + *(CmdPtr++) = ModeNo + 12; + else if (ModeNo > 9) + *(CmdPtr++) = ModeNo + 6; + else + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = 0xFD; + +// Mode = (Msg[7] >> 4); +// Mode *= 10; +// Mode += Msg[7] & 0xf; + + + } + } + else + { + + *(CmdPtr++) = 0x5; // Set frequency command + + // Need to convert two chars to bcd digit + + *(CmdPtr++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(CmdPtr++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(CmdPtr++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(CmdPtr++) = (FreqString[2] - 48) | ((FreqString[1] - 48) << 4); + if (RIG->IC735) + { + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 10; + } + else + { + *(CmdPtr++) = (FreqString[0] - 48); + *(CmdPtr++) = 0xFD; + FreqPtr[0].Cmd1Len = 11; + } + + // Send Set VFO in case last chan was memory + + // *(CmdPtr++) = 0xFE; + // *(CmdPtr++) = 0xFE; + // *(CmdPtr++) = RIG->RigAddr; + // *(CmdPtr++) = 0xE0; + + // *(CmdPtr++) = 0x07; + // *(CmdPtr++) = 0xFD; + + // FreqPtr[0].Cmd1Len = 17; + + if (ModeNo != -1) // Dont Set + { + CmdPtr = FreqPtr->Cmd2 = FreqPtr->Cmd2Msg; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x6; // Set Mode + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = Filter; + *(CmdPtr++) = 0xFD; + + FreqPtr->Cmd2Len = 8; + + if (Split) + { + CmdPtr = FreqPtr->Cmd3 = FreqPtr->Cmd3Msg; + FreqPtr->Cmd3Len = 7; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0xF; // Set Mode + if (Split == 'S') + *(CmdPtr++) = 0x10; + else + if (Split == '+') + *(CmdPtr++) = 0x12; + else + if (Split == '-') + *(CmdPtr++) = 0x11; + + *(CmdPtr++) = 0xFD; + } + else if (DataFlag) + { + CmdPtr = FreqPtr->Cmd3 = FreqPtr->Cmd3Msg; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1a; + + if ((strcmp(RIG->RigName, "IC7100") == 0) || + (strcmp(RIG->RigName, "IC7410") == 0) || + (strcmp(RIG->RigName, "IC7600") == 0) || + (strcmp(RIG->RigName, "IC7610") == 0) || + (strcmp(RIG->RigName, "IC7300") == 0)) + { + FreqPtr[0].Cmd3Len = 9; + *(CmdPtr++) = 0x6; // Send/read DATA mode with filter set + *(CmdPtr++) = 0x1; // Data On + *(CmdPtr++) = Filter; //Filter + } + else if (strcmp(RIG->RigName, "IC7200") == 0) + { + FreqPtr[0].Cmd3Len = 9; + *(CmdPtr++) = 0x4; // Send/read DATA mode with filter set + *(CmdPtr++) = 0x1; // Data On + *(CmdPtr++) = Filter; // Filter + } + else + { + FreqPtr[0].Cmd3Len = 8; + *(CmdPtr++) = 0x6; // Set Data + *(CmdPtr++) = 0x1; //On + } + + *(CmdPtr++) = 0xFD; + } + } + + if (Antenna == '5' || Antenna == '6') + { + // Antenna select for 746 and maybe others + + // Could be going in cmd2 3 or 4 + + if (FreqPtr[0].Cmd2 == NULL) + { + CmdPtr = FreqPtr->Cmd2 = FreqPtr->Cmd2Msg; + FreqPtr[0].Cmd2Len = 7; + } + else if (FreqPtr[0].Cmd3 == NULL) + { + CmdPtr = FreqPtr->Cmd3 = FreqPtr->Cmd3Msg; + FreqPtr[0].Cmd3Len = 7; + } + else + { + CmdPtr = FreqPtr->Cmd4 = FreqPtr->Cmd4Msg; + FreqPtr[0].Cmd4Len = 7; + } + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x12; // Set Antenna + *(CmdPtr++) = Antenna - '5'; // 0 for A5 1 for A6 + *(CmdPtr++) = 0xFD; + } + } + } + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case YAESU: + + if (n == 2) // Just set freq + { + ModeNo = -1; + } + else + { + for (ModeNo = 0; ModeNo < 15; ModeNo++) + { + if (_stricmp(YaesuModes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 15) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + // Send Mode then Freq - setting Mode seems to change frequency + + FreqPtr->Cmd1Len = 0; + + if (ModeNo != -1) // Set freq only + { + *(Poll++) = ModeNo; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 7; // Set Mode + FreqPtr->Cmd1Len = 5; + } + + *(Poll++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(Poll++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(Poll++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(Poll++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(Poll++) = 1; // Set Freq + + FreqPtr->Cmd1Len += 5; + + if (strcmp(PORT->Rigs[0].RigName, "FT847") == 0) + { + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 3; // Status Poll + + FreqPtr->Cmd1Len = 15; + } + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + saveNewFreq(RIG, Freq, Mode); + + return TRUE; + + + case FT100: + case FT990: + case FT1000: + + if (n == 2) // Set Freq Only + ModeNo = -1; + else + { + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode\r"); + return FALSE; + } + + if (PORT->PortType == FT100) + { + for (ModeNo = 0; ModeNo < 8; ModeNo++) + { + if (_stricmp(FT100Modes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 8) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + } + else if (PORT->PortType == FT990) + { + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (_stricmp(FT990Modes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 12) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + } + else + { + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (_stricmp(FT1000Modes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo == 12) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + } + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + // Send Mode then Freq - setting Mode seems to change frequency + + if (ModeNo == -1) // Don't set Mode + { + // Changing the length messes up a lot of code, + // so set freq twice instead of omitting entry + + *(Poll++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(Poll++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(Poll++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(Poll++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(Poll++) = 10; // Set Freq + } + else + { + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = ModeNo; + *(Poll++) = 12; // Set Mode + } + + *(Poll++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(Poll++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(Poll++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(Poll++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(Poll++) = 10; // Set Freq + + *(Poll++) = 0; + *(Poll++) = 0; + *(Poll++) = 0; + if (PORT->PortType == FT990 || PORT->YaesuVariant == FT1000D) + *(Poll++) = 3; + else + *(Poll++) = 2; // 100 or 1000MP + + *(Poll++) = 16; // Status Poll + + FreqPtr->Cmd1Len = 15; + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case KENWOOD: + case FT2000: + case FTDX10: + case FT991A: + case FLEX: + + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode\r"); + return FALSE; + } + + for (ModeNo = 0; ModeNo < 16; ModeNo++) + { + if (PORT->PortType == FT2000) + if (_stricmp(FT2000Modes[ModeNo], Mode) == 0) + break; + + if (PORT->PortType == FTDX10) + if (_stricmp(FTDX10Modes[ModeNo], Mode) == 0) + break; + + if (PORT->PortType == FT991A) + if (_stricmp(FT991AModes[ModeNo], Mode) == 0) + break; + + if (PORT->PortType == KENWOOD) + if (_stricmp(KenwoodModes[ModeNo], Mode) == 0) + break; + if (PORT->PortType == FLEX) + if (_stricmp(FLEXModes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo > 15) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + if (PORT->PortType == FT2000) + FreqPtr->Cmd1Len = sprintf(Poll, "FA%s;MD0%X;FA;MD;", &FreqString[1], ModeNo); + else + if (PORT->PortType == FT991A || PORT->PortType == FTDX10) + FreqPtr->Cmd1Len = sprintf(Poll, "FA%s;MD0%X;FA;MD;", FreqString, ModeNo); + else + if (PORT->PortType == FLEX) + FreqPtr->Cmd1Len = sprintf(Poll, "ZZFA00%s;ZZMD%02d;ZZFA;ZZMD;", &FreqString[1], ModeNo); + else + { + if (Antenna == '5' || Antenna == '6') + FreqPtr->Cmd1Len = sprintf(Poll, "FA00%s;MD%d;AN%c;FA;MD;", FreqString, ModeNo, Antenna - 4); + else + FreqPtr->Cmd1Len = sprintf(Poll, "FA00%s;MD%d;FA;MD;", FreqString, ModeNo); + } + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + + case SDRRADIO: + + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode\r"); + return FALSE; + } + + for (ModeNo = 0; ModeNo < 16; ModeNo++) + { + if (_stricmp(KenwoodModes[ModeNo], Mode) == 0) + break; + } + + if (ModeNo > 15) + { + sprintf(Command, "Sorry -Invalid Mode\r"); + return FALSE; + } + + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + FreqPtr->Cmd1Len = sprintf(Poll, "F%c00%s;MD%d;F%c;MD;", RIG->RigAddr, FreqString, ModeNo, RIG->RigAddr); + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + + case NMEA: + + if (n < 3) + { + strcpy(Command, "Sorry - Invalid Format - should be Port Freq Mode\r"); + return FALSE; + } + buffptr = GetBuff(); + + if (buffptr == 0) + { + sprintf(Command, "Sorry - No Buffers available\r"); + return FALSE; + } + + // Build a ScanEntry in the buffer + + FreqPtr = (struct ScanEntry *)buffptr->Data; + memset(FreqPtr, 0, sizeof(struct ScanEntry)); + + FreqPtr->Freq = Freq; + FreqPtr->Bandwidth = Bandwidth; + FreqPtr->Antenna = Antenna; + + Poll = FreqPtr->Cmd1 = FreqPtr->Cmd1Msg; + + i = sprintf(Poll, "$PICOA,90,%02x,RXF,%.6f*xx\r\n", RIG->RigAddr, Freq/1000000.); + AddNMEAChecksum(Poll); + Len = i; + i = sprintf(Poll + Len, "$PICOA,90,%02x,TXF,%.6f*xx\r\n", RIG->RigAddr, Freq/1000000.); + AddNMEAChecksum(Poll + Len); + Len += i; + i = sprintf(Poll + Len, "$PICOA,90,%02x,MODE,%s*xx\r\n", RIG->RigAddr, Mode); + AddNMEAChecksum(Poll + Len); + + FreqPtr->Cmd1Len = i + Len; + + C_Q_ADD(&RIG->BPQtoRADIO_Q, buffptr); + + return TRUE; + + case HAMLIB: + { + char cmd[200]; + + int len = sprintf(cmd, "F %s\n+f\nM %s %d\n+m\n", + FreqString, Mode, atoi(Data)); + + send(PORT->remoteSock, cmd, len, 0); + sprintf(Command, "Ok\r"); + + saveNewFreq(RIG, Freq, Mode); + + return FALSE; + } + + case FLRIG: + { + char cmd[80]; + + int len = sprintf(cmd, "%.0f", Freq); + + strcpy(PORT->ScanEntry.Cmd2Msg, Mode); + strcpy(PORT->ScanEntry.Cmd3Msg, FilterString); + + if (Freq > 0.0) + FLRIGSendCommand(PORT, "rig.set_vfo", cmd); + + else if (PORT->ScanEntry.Cmd2Msg[0] && Mode[0] != '*') + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd2Msg); + FLRIGSendCommand(PORT, "rig.set_mode", cmd); + } + + else if (PORT->ScanEntry.Cmd3Msg[0] && strcmp(PORT->ScanEntry.Cmd3Msg, "0") != 0) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd3Msg); + FLRIGSendCommand(PORT, "rig.set_bandwidth", cmd); + } + else + { + sprintf(Command, "Sorry - Nothing to do\r"); + return FALSE; + } + + PORT->AutoPoll = 0; + + saveNewFreq(RIG, Freq, Mode); + return TRUE; + } + + + case RTLUDP: + { + char cmd[80]; + int len = 5; + int FreqInt = (int)Freq; + int FM = 0; + int AM = 1; + int USB = 2; + int LSB = 3; + + cmd[0] = 0; + cmd[1] = FreqInt & 0xff; + cmd[2] = (FreqInt >> 8) & 0xff; + cmd[3] = (FreqInt >> 16) & 0xff; + cmd[4] = (FreqInt >> 24) & 0xff; + + len = sendto(PORT->remoteSock, cmd, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + if (Mode[0]) + { + if (strcmp(Mode, "FM") == 0) + memcpy(&cmd[1], &FM, 4); + else if (strcmp(Mode, "AM") == 0) + memcpy(&cmd[1], &AM, 4); + else if (strcmp(Mode, "USB") == 0) + memcpy(&cmd[1], &USB, 4); + else if (strcmp(Mode, "LSB") == 0) + memcpy(&cmd[1], &LSB, 4); + + cmd[0] = 1; + len = sendto(PORT->remoteSock, cmd, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + } + + saveNewFreq(RIG, Freq, Mode); + + sprintf(Command, "Ok\r"); + return FALSE; + } +// --- G7TAJ ---- + case SDRANGEL: + { + char cmd[80]; + int len = sprintf(cmd, "%.0f", Freq); + + strcpy(PORT->ScanEntry.Cmd2Msg, Mode); + strcpy(PORT->ScanEntry.Cmd3Msg, FilterString); + + if (Freq > 0.0) + { + SDRANGELSendCommand(PORT, "FREQSET", cmd); + sprintf(Command, "Ok\r"); + return FALSE; + } +//TODO +/* else if (PORT->ScanEntry.Cmd2Msg[0] && Mode[0] != '*') + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd2Msg); + FLRIGSendCommand(PORT, "rig.set_mode", cmd); + } + + else if (PORT->ScanEntry.Cmd3Msg[0] && strcmp(PORT->ScanEntry.Cmd3Msg, "0") != 0) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd3Msg); + FLRIGSendCommand(PORT, "rig.set_bandwidth", cmd); + } +*/ + else + { + sprintf(Command, "Sorry - Nothing to do\r"); + return FALSE; + } + + PORT->AutoPoll = 0; + } +// --- G7TAJ ---- + + + + + } + return TRUE; +} + +int BittoInt(UINT BitMask) +{ + // Returns bit position of first 1 bit in BitMask + + int i = 0; + while ((BitMask & 1) == 0) + { + BitMask >>= 1; + i ++; + } + return i; +} + +extern char * RadioConfigMsg[36]; + +struct TNCINFO RIGTNC; // Dummy TNC Record for Rigcontrol without a corresponding TNC + +DllExport BOOL APIENTRY Rig_Init() +{ + struct RIGPORTINFO * PORT; + int i, p, port; + struct RIGINFO * RIG; + struct TNCINFO * TNC = &RIGTNC; + HWND hDlg; +#ifndef LINBPQ + int RigRow = 0; +#endif + int NeedRig = 0; + int Interlock = 0; + + memset(&RIGTNC, 0, sizeof(struct TNCINFO)); + + TNCInfo[70] = TNC; + + // Get config info + + NumberofPorts = 0; + + for (port = 0; port < MAXBPQPORTS; port++) + PORTInfo[port] = NULL; + + // See if any rigcontrol defined (either RADIO or RIGCONTROL lines) + + for (port = 0; port < MAXBPQPORTS; port++) + { + if (RadioConfigMsg[port]) + NeedRig++; + } + + if (NeedRig == 0) + { + SetupPortRIGPointers(); // Sets up Dummy Rig Pointer + return FALSE; + } + + +#ifndef LINBPQ + + TNC->Port = 70; + CreatePactorWindow(TNC, "RIGCONTROL", "RigControl", 10, PacWndProc, 550, NeedRig * 20 + 60, NULL); + hDlg = TNC->hDlg; + +#endif + + TNC->ClientHeight = NeedRig * 20 + 60; + TNC->ClientWidth = 550; + + for (port = 0; port < MAXBPQPORTS; port++) + { + if (RadioConfigMsg[port]) + { + char msg[1000]; + + char * SaveRigConfig = _strdup(RadioConfigMsg[port]); + char * RigConfigMsg1 = _strdup(RadioConfigMsg[port]); + + Interlock = atoi(&RigConfigMsg1[5]); + + RIG = RigConfig(TNC, RigConfigMsg1, port); + + if (RIG == NULL) + { + // Report Error + + sprintf(msg,"Port %d Invalid Rig Config %s", port, SaveRigConfig); + WritetoConsole(msg); + free(SaveRigConfig); + free(RigConfigMsg1); + continue; + } + + RIG->Interlock = Interlock; + + if (RIG->PORT->IOBASE[0] == 0) + { + // We have to find the SCS TNC in this scan group as TNC is now a dummy + + struct TNCINFO * PTCTNC; + int n; + + for (n = 1; n < MAXBPQPORTS; n++) + { + PTCTNC = TNCInfo[n]; + + if (PTCTNC == NULL || (PTCTNC->Hardware != H_SCS && PTCTNC->Hardware != H_ARDOP)) + continue; + + if (PTCTNC->RXRadio != Interlock) + continue; + + RIG->PORT->PTC = PTCTNC; + } + } + +#ifndef LINBPQ + + // Create line for this rig + + RIG->hLabel = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 10, RigRow, 80,20, hDlg, NULL, hInstance, NULL); + + // RIG->hCAT = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + // 90, RigRow, 40,20, hDlg, NULL, hInstance, NULL); + + RIG->hFREQ = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 95, RigRow, 100,20, hDlg, NULL, hInstance, NULL); + + RIG->hMODE = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 200, RigRow, 100,20, hDlg, NULL, hInstance, NULL); + + RIG->hSCAN = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 300, RigRow, 20,20, hDlg, NULL, hInstance, NULL); + + RIG->hPTT = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 320, RigRow, 20,20, hDlg, NULL, hInstance, NULL); + + RIG->hPORTS = CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, + 340, RigRow, 210, 20, hDlg, NULL, hInstance, NULL); + + RigRow += 20; + + //if (PORT->PortType == ICOM) + //{ + // sprintf(msg,"%02X", PORT->Rigs[i].RigAddr); + // SetWindowText(RIG->hCAT, msg); + //} + SetWindowText(RIG->hLabel, RIG->RigName); +#endif + + RIG->WEB_Label = _strdup(RIG->RigName); + // RIG->WEB_CAT; + RIG->WEB_FREQ = zalloc(80); + RIG->WEB_MODE = zalloc(80); + RIG->WEB_PORTS = zalloc(80); + strcpy(RIG->WEB_FREQ, "-----------"); + strcpy(RIG->WEB_MODE, "------"); + + RIG->WEB_PTT = ' '; + RIG->WEB_SCAN = ' '; + } + } + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + if (PORT->PortType == HAMLIB) + { + HAMLIBMasterRunning = 1; + ConnecttoHAMLIB(PORT); + } + else if (PORT->PortType == FLRIG) + { + FLRIGRunning = 1; + ConnecttoFLRIG(PORT); + } + else if (PORT->PortType == RTLUDP) + ConnecttoRTLUDP(PORT); +//---- G7TAJ ---- + else if (PORT->PortType == SDRANGEL) + { + SDRANGELRunning = 1; + ConnecttoSDRANGEL(PORT); + } +//---- G7TAJ ---- + else if (PORT->HIDDevice) // This is RAWHID, Not CM108 + OpenHIDPort(PORT, PORT->IOBASE, PORT->SPEED); + else if (PORT->PTC == 0 && _stricmp(PORT->IOBASE, "CM108") != 0) + OpenRigCOMMPort(PORT, PORT->IOBASE, PORT->SPEED); + + if (PORT->PTTIOBASE[0]) // Using separate port for PTT? + { + if (PORT->PTTIOBASE[3] == '=') + PORT->hPTTDevice = OpenCOMPort(&PORT->PTTIOBASE[4], PORT->SPEED, FALSE, FALSE, FALSE, 0); + else + PORT->hPTTDevice = OpenCOMPort(&PORT->PTTIOBASE[3], PORT->SPEED, FALSE, FALSE, FALSE, 0); + } + else + PORT->hPTTDevice = PORT->hDevice; // Use same port for PTT + } + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i < PORT->ConfiguredRigs; i++) + { + int j; + int k = 0; + uint64_t BitMask; + struct _EXTPORTDATA * PortEntry; + + RIG = &PORT->Rigs[i]; + + SetupScanInterLockGroups(RIG); + + // Get record for each port in Port Bitmap + + // The Scan "Request Permission to Change" code needs the Port Records in order - + // Those with active connect lock (eg SCS) first, then those with just a connect pending lock (eg WINMOR) + // then those with neither + + BitMask = RIG->BPQPort; + for (j = 0; j < MAXBPQPORTS; j++) + { + if (BitMask & 1) + { + PortEntry = (struct _EXTPORTDATA *)GetPortTableEntryFromPortNum(j); // BPQ32 port record for this port + if (PortEntry) + if (PortEntry->SCANCAPABILITIES == CONLOCK) + RIG->PortRecord[k++] = PortEntry; + } + BitMask >>= 1; + } + + BitMask = RIG->BPQPort; + for (j = 0; j < MAXBPQPORTS; j++) + { + if (BitMask & 1) + { + PortEntry = (struct _EXTPORTDATA *)GetPortTableEntryFromPortNum(j); // BPQ32 port record for this port + if (PortEntry) + if (PortEntry->SCANCAPABILITIES == SIMPLE) + RIG->PortRecord[k++] = PortEntry; + } + BitMask >>= 1; + } + + BitMask = RIG->BPQPort; + for (j = 0; j < MAXBPQPORTS; j++) + { + if (BitMask & 1) + { + PortEntry = (struct _EXTPORTDATA *)GetPortTableEntryFromPortNum(j); // BPQ32 port record for this port + if (PortEntry) + if (PortEntry->SCANCAPABILITIES == NONE) + RIG->PortRecord[k++] = PortEntry; + } + BitMask >>= 1; + } + + RIG->PORT = PORT; // For PTT + + if (RIG->NumberofBands) + CheckTimeBands(RIG); // Set initial timeband + +#ifdef WIN32 + if (RIG->PTTCATPort[0]) // Serial port RTS to CAT + _beginthread(PTTCATThread,0,RIG); +#endif + if (RIG->HAMLIBPORT) + { + // Open listening socket + + struct sockaddr_in local_sin; /* Local socket - internet style */ + struct sockaddr_in * psin; + SOCKET sock = 0; + u_long param=1; + + char szBuff[80]; + + psin=&local_sin; + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = INADDR_ANY; + + sock = socket(AF_INET, SOCK_STREAM, 0); + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *)¶m,4); + + psin->sin_port = htons(RIG->HAMLIBPORT); // Convert to network ordering + + if (bind( sock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) + { + sprintf(szBuff, "bind(sock) failed port %d Error %d\n", port, WSAGetLastError()); + WritetoConsoleLocal(szBuff); + closesocket(sock); + } + else + { + if (listen(sock, 5) < 0) + { + sprintf(szBuff, "listen(sock) failed port %d Error %d\n", port, WSAGetLastError()); + WritetoConsoleLocal(szBuff); + } + else + { + ioctl(sock, FIONBIO, ¶m); + RIG->ListenSocket = sock; + _beginthread(HAMLIBSlaveThread, 0, RIG); + } + } + } + Rig_PTTEx(RIG, 0, NULL); // Send initial PTT Off (Mainly to set input mux on soundcard rigs) + } + } + + // MoveWindow(hDlg, Rect.left, Rect.top, Rect.right - Rect.left, Row + 100, TRUE); + + SetupPortRIGPointers(); + + RigWebPage = zalloc(10); + + WritetoConsole("\nRig Control Enabled\n"); + + return TRUE; +} + +DllExport BOOL APIENTRY Rig_Close() +{ + struct RIGPORTINFO * PORT; + struct TNCINFO * TNC; + int n, p; + + HAMLIBMasterRunning = 0; // Close HAMLIB thread(s) + HAMLIBSlaveRunning = 0; // Close HAMLIB thread(s) + FLRIGRunning = 0; // Close FLRIG thread(s) +// ---- G7TAJ ---- + SDRANGELRunning = 0; // Close SDRANGEL thread(s) +// ---- G7TAJ ---- + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + if (PORT->PortType == NMEA) + { + // Send Remote OFF + + int i; + char REMOFF[80]; + + i = sprintf(REMOFF, "$PICOA,90,%02x,REMOTE,OFF*xx\r\n", PORT->Rigs[0].RigAddr); + AddNMEAChecksum(REMOFF); + + WriteCOMBlock(PORT->hDevice, REMOFF, i); + Sleep(200); + } + + if (PORT->PortType != HAMLIB && PORT->PortType != RTLUDP && PORT->PortType != FLRIG && strcmp(PORT->IOBASE, "RAWHID") != 0) + { + if (PORT->hPTTDevice != PORT->hDevice) + CloseCOMPort(PORT->hPTTDevice); + + CloseCOMPort(PORT->hDevice); + } + + PORT->hDevice = 0; + PORT->hPTTDevice = 0; + + // Free the RIG and Port Records + + for (n = 0; n < PORT->ConfiguredRigs; n++) + { + struct RIGINFO * RIG = &PORT->Rigs[n]; + struct HAMLIBSOCK * Entry = RIG->Sockets; + struct HAMLIBSOCK * Save; + + // if RIG has any HAMLIB slave connections, close them + + Entry = RIG->Sockets; + + while (Entry) + { + closesocket(Entry->Sock); + Save = Entry; + Entry = Entry->Next; + free(Save); + } + + RIG->Sockets = 0; + + if (RIG->TimeBands) + free (RIG->TimeBands[1]->Scanlist); + + if (RIG->PTTCATPort[0]) + { + Rig_PTTEx(RIG, FALSE, NULL); // Make sure PTT is down + EndPTTCATThread = TRUE; + } + } + + free (PORT); + PORTInfo[p] = NULL; + } + + NumberofPorts = 0; // For possible restart + + // And free the TNC config info + + for (p = 1; p < MAXBPQPORTS; p++) + { + TNC = TNCInfo[p]; + + if (TNC == NULL) + continue; + + TNC->RIG = NULL; + + // memset(TNC->WL2KInfoList, 0, sizeof(TNC->WL2KInfoList)); + + } + + return TRUE; +} + + + +BOOL Rig_Poll() +{ + int p, i, Len; + char RigWeb[16384]; + + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + if (PORT->PortType == DUMMY) + { + DummyPoll(PORT); + return TRUE; + } + + if (PORT->hDevice == 0) + { + // Try to reopen every 15 secs + + PORT->ReopenDelay++; + + if (PORT->ReopenDelay > 150) + { + PORT->ReopenDelay = 0; + + if (PORT->PortType == HAMLIB) + ConnecttoHAMLIB(PORT); + else if (PORT->PortType == FLRIG) + ConnecttoFLRIG(PORT); + else if (PORT->PortType == RTLUDP) + ConnecttoRTLUDP(PORT); +// ---- G7TAJ ---- + else if (PORT->PortType == SDRANGEL) + ConnecttoSDRANGEL(PORT); +// ---- G7TAJ ---- + else if (PORT->HIDDevice) + OpenHIDPort(PORT, PORT->IOBASE, PORT->SPEED); + else if (PORT->PTC == 0 + && _stricmp(PORT->IOBASE, "REMOTE") != 0 + && _stricmp(PORT->IOBASE, "CM108") != 0) + { + if (PORT->Closed == 0) + { + OpenRigCOMMPort(PORT, PORT->IOBASE, PORT->SPEED); + if (PORT->IOBASE && PORT->PTTIOBASE[0] == 0) // Using separate port for PTT? + PORT->hPTTDevice = PORT->hDevice; + } + } + } + } + + if (PORT == NULL || (PORT->hDevice == 0 && PORT->PTC == 0 && PORT->remoteSock == 0)) + continue; + + // Check PTT Timers + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + // Active PTT command + + if (RIG->PTTTimer) + { + RIG->PTTTimer--; + if (RIG->PTTTimer == 0) + Rig_PTTEx(RIG, FALSE, NULL); + } + + // repeated off timer + + if (RIG->repeatPTTOFFTimer) + { + RIG->repeatPTTOFFTimer--; + if (RIG->repeatPTTOFFTimer == 0) + { + Rig_PTTEx(RIG, FALSE, NULL); + RIG->repeatPTTOFFTimer = 0; // Don't repeat repeat! + } + } + } + + CheckRX(PORT); + + switch (PORT->PortType) + { + case ICOM: + + ICOMPoll(PORT); + break; + + case YAESU: + case FT100: + case FT990: + case FT1000: + + YaesuPoll(PORT); + break; + + case KENWOOD: + case FT2000: + case FTDX10: + case FT991A: + case FLEX: + case NMEA: + + KenwoodPoll(PORT); + break; + + + case SDRRADIO: + SDRRadioPoll(PORT); + break; + + case HAMLIB: + HAMLIBPoll(PORT); + break; + + case RTLUDP: + RTLUDPPoll(PORT); + break; + + case FLRIG: + FLRIGPoll(PORT); + break; +// ---- G7TAJ ---- + case SDRANGEL: + SDRANGELPoll(PORT); + break; } +// ---- G7TAJ ---- + } + + // Build page for Web Display + + Len = BuildRigCtlPage(RigWeb); + + if (strcmp(RigWebPage, RigWeb) == 0) + return TRUE; // No change + + free(RigWebPage); + + RigWebPage = _strdup(RigWeb); + + SendRigWebPage(); + + return TRUE; +} + + +BOOL RigCloseConnection(struct RIGPORTINFO * PORT) +{ + // disable event notification and wait for thread + // to halt + + CloseCOMPort(PORT->hDevice); + PORT->hDevice = 0; + return TRUE; + +} // end of CloseConnection() + +#ifndef WIN32 +#define ONESTOPBIT 0 +#define ONE5STOPBITS 1 +#define TWOSTOPBITS 2 +#endif + +int OpenRigCOMMPort(struct RIGPORTINFO * PORT, VOID * Port, int Speed) +{ + if (PORT->remoteSock) // Using WINMORCONTROL + return TRUE; + + if (PORT->PortType == FT2000 || PORT->PortType == FT991A || PORT->PortType == FTDX10 || strcmp(PORT->Rigs[0].RigName, "FT847") == 0) // FT2000 and similar seem to need two stop bits + PORT->hDevice = OpenCOMPort((VOID *)Port, Speed, FALSE, FALSE, PORT->Alerted, TWOSTOPBITS); + else if (PORT->PortType == NMEA) + PORT->hDevice = OpenCOMPort((VOID *)Port, Speed, FALSE, FALSE, PORT->Alerted, ONESTOPBIT); + else + PORT->hDevice = OpenCOMPort((VOID *)Port, Speed, FALSE, FALSE, PORT->Alerted, TWOSTOPBITS); + + if (PORT->hDevice == 0) + { + PORT->Alerted = TRUE; + return FALSE; + } + + PORT->Alerted = FALSE; + + if (PORT->PortType == PTT || (PORT->Rigs[0].PTTMode & PTTRTS)) + COMClearRTS(PORT->hDevice); + else + COMSetRTS(PORT->hDevice); + + if (PORT->PortType == PTT || (PORT->Rigs[0].PTTMode & PTTDTR)) + COMClearDTR(PORT->hDevice); + else + COMSetDTR(PORT->hDevice); + if (strcmp(PORT->Rigs[0].RigName, "FT847") == 0) + { + // Looks like FT847 Needa a "Cat On" Command + + UCHAR CATON[6] = {0,0,0,0,0}; + + WriteCOMBlock(PORT->hDevice, CATON, 5); + } + + + if (PORT->PortType == NMEA) + { + // Looks like NMEA Needs Remote ON + + int i; + char REMON[80]; + + i = sprintf(REMON, "$PICOA,90,%02x,REMOTE,ON*xx\r\n", PORT->Rigs[0].RigAddr); + AddNMEAChecksum(REMON); + + WriteCOMBlock(PORT->hDevice, REMON, i); + } + + + return TRUE; +} + +void CheckRX(struct RIGPORTINFO * PORT) +{ + int Length; + char NMEAMsg[100]; + unsigned char * ptr; + int len; + + if (PORT->PortType == FLRIG) + { + // Data received in thread + + return; + } + + if (PORT->PortType == HAMLIB) + { + // Data received in thread (do we need thread?? + + Length = PORT->RXLen; + PORT->RXLen = 0; + } + + else if (PORT->PortType == RTLUDP) + { + CheckAndProcessRTLUDP(PORT); + Length = 0; + PORT->RXLen = 0; + return; + } + + else if (PORT->HIDDevice) + Length = HID_Read_Block(PORT); + else if (PORT->PTC) + Length = GetPTCRadioCommand(PORT->PTC, &PORT->RXBuffer[PORT->RXLen]); + else if (PORT->remoteSock) + { + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + + Length = recvfrom(PORT->remoteSock, PORT->RXBuffer, 500, 0, (struct sockaddr *)&rxaddr, &addrlen); + if (Length == -1) + Length = 0; + } + else + { + if (PORT->hDevice == 0) + return; + + // only try to read number of bytes in queue + + if (PORT->RXLen == 500) + PORT->RXLen = 0; + + Length = 500 - (DWORD)PORT->RXLen; + + Length = ReadCOMBlock(PORT->hDevice, &PORT->RXBuffer[PORT->RXLen], Length); + } + + if (Length == 0) + return; // Nothing doing + + PORT->RXLen += Length; + + Length = PORT->RXLen; + + switch (PORT->PortType) + { + case ICOM: + + if (Length < 6) // Minimum Frame Sise + return; + + if (PORT->RXBuffer[Length-1] != 0xfd) + return; // Echo + + ProcessICOMFrame(PORT, PORT->RXBuffer, Length); // Could have multiple packets in buffer + + PORT->RXLen = 0; // Ready for next frame + return; + + case YAESU: + + // Possible responses are a single byte ACK/NAK or a 5 byte info frame + + if (Length == 1 && PORT->CmdSent > 0) + { + ProcessYaesuCmdAck(PORT); + return; + } + + if (Length < 5) // Frame Sise + return; + + if (Length > 5) // Frame Sise + { + PORT->RXLen = 0; // Corruption - reset and wait for retry + return; + } + + ProcessYaesuFrame(PORT); + + PORT->RXLen = 0; // Ready for next frame + return; + + case FT100: + + // Only response should be a 16 byte info frame + + if (Length < 32) // Frame Sise why??????? + return; + + if (Length > 32) // Frame Sise + { + PORT->RXLen = 0; // Corruption - reset and wait for retry + return; + } + + ProcessFT100Frame(PORT); + + PORT->RXLen = 0; // Ready for next frame + return; + + case FT990: + + // Only response should be a 32 byte info frame + + if (Length < 32) // Frame Sise + return; + + if (Length > 32) // Frame Sise + { + PORT->RXLen = 0; // Corruption - reset and wait for retry + return; + } + + ProcessFT990Frame(PORT); + PORT->RXLen = 0; // Ready for next frame + return; + + + case FT1000: + + // Only response should be a 16 byte info frame + + ptr = PORT->RXBuffer; + + if (Length < 16) // Frame Sise + return; + + if (Length > 16) // Frame Sise + { + PORT->RXLen = 0; // Corruption - reset and wait for retry + return; + } + + ProcessFT1000Frame(PORT); + + PORT->RXLen = 0; // Ready for next frame + return; + + case KENWOOD: + case FT2000: + case FTDX10: + case FT991A: + case FLEX: + + if (Length < 2) // Minimum Frame Sise + return; + + if (Length > 50) // Garbage + { + PORT->RXLen = 0; // Ready for next frame + return; + } + + if (PORT->RXBuffer[Length-1] != ';') + return; + + ProcessKenwoodFrame(PORT, Length); + + PORT->RXLen = 0; // Ready for next frame + return; + + case SDRRADIO: + + if (Length < 2) // Minimum Frame Sise + return; + + if (Length > 50) // Garbage + { + PORT->RXLen = 0; // Ready for next frame + return; + } + + if (PORT->RXBuffer[Length-1] != ';') + return; + + ProcessSDRRadioFrame(PORT, Length); + + PORT->RXLen = 0; // Ready for next frame + return; + + case NMEA: + + ptr = memchr(PORT->RXBuffer, 0x0a, Length); + + while (ptr != NULL) + { + ptr++; // include lf + len = (int)(ptr - &PORT->RXBuffer[0]); + + memcpy(NMEAMsg, PORT->RXBuffer, len); + + NMEAMsg[len] = 0; + +// if (Check0183CheckSum(NMEAMsg, len)) + ProcessNMEA(PORT, NMEAMsg, len); + + Length -= len; // bytes left + + if (Length > 0) + { + memmove(PORT->RXBuffer, ptr, Length); + ptr = memchr(PORT->RXBuffer, 0x0a, Length); + } + else + ptr=0; + } + + PORT->RXLen = Length; + return; + + case HAMLIB: + + ProcessHAMLIBFrame(PORT, Length); + PORT->RXLen = 0; + return; + } +} + +VOID ProcessICOMFrame(struct RIGPORTINFO * PORT, UCHAR * rxbuffer, int Len) +{ + UCHAR * FendPtr; + int NewLen; + + // Split into Packets. By far the most likely is a single frame + // so treat as special case + + FendPtr = memchr(rxbuffer, 0xfd, Len); + + if (FendPtr == &rxbuffer[Len-1]) + { + ProcessFrame(PORT, rxbuffer, Len); + return; + } + + // Process the first Packet in the buffer + + NewLen = (int)(FendPtr - rxbuffer + 1); + + ProcessFrame(PORT, rxbuffer, NewLen); + + // Loop Back + + ProcessICOMFrame(PORT, FendPtr+1, Len - NewLen); + return; +} + + +BOOL RigWriteCommBlock(struct RIGPORTINFO * PORT) +{ + // if using a PTC radio interface send to the SCSPactor Driver, else send to COM port + + int ret; + + if (PORT->HIDDevice) + HID_Write_Block(PORT); + else + if (PORT->PTC) + SendPTCRadioCommand(PORT->PTC, PORT->TXBuffer, PORT->TXLen); + else if (PORT->remoteSock) + ret = sendto(PORT->remoteSock, PORT->TXBuffer, PORT->TXLen, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + else + { + BOOL fWriteStat; + DWORD BytesWritten; + +#ifndef WIN32 + BytesWritten = write(PORT->hDevice, PORT->TXBuffer, PORT->TXLen); +#else + DWORD Mask = 0; + int Err; + + Err = GetCommModemStatus(PORT->hDevice, &Mask); + + if (Mask == 0) // trap com0com other end not open + return TRUE; + + fWriteStat = WriteFile(PORT->hDevice, PORT->TXBuffer, PORT->TXLen, &BytesWritten, NULL ); +#endif + if (PORT->TXLen != BytesWritten) + { + struct RIGINFO * RIG = &PORT->Rigs[PORT->CurrentRig]; // Only one on Yaseu + + if (RIG->RIGOK) + { + SetWindowText(RIG->hFREQ, "Port Closed"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + } + + RIG->RIGOK = FALSE; + + if (PORT->hDevice) + CloseCOMPort(PORT->hDevice); + + OpenRigCOMMPort(PORT, PORT->IOBASE, PORT->SPEED); + + if (PORT->IOBASE && PORT->PTTIOBASE[0] == 0) // Using separate port for PTT? + PORT->hPTTDevice = PORT->hDevice; + + if (PORT->hDevice) + { + // Try Again + +#ifndef WIN32 + BytesWritten = write(PORT->hDevice, PORT->TXBuffer, PORT->TXLen); +#else + fWriteStat = WriteFile(PORT->hDevice, PORT->TXBuffer, PORT->TXLen, &BytesWritten, NULL ); +#endif + } + } + } + + ret = GetLastError(); + + PORT->Timeout = 100; // 2 secs + return TRUE; +} + +VOID ReleasePermission(struct RIGINFO *RIG) +{ + int i = 0; + struct _EXTPORTDATA * PortRecord; + + while (RIG->PortRecord[i]) + { + PortRecord = RIG->PortRecord[i]; + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, (PDATAMESSAGE)3); // Release Perrmission + i++; + } +} + +int GetPermissionToChange(struct RIGPORTINFO * PORT, struct RIGINFO *RIG) +{ + struct ScanEntry ** ptr; + struct _EXTPORTDATA * PortRecord; + int i = 0; + + // Get Permission to change + + if (RIG->RIG_DEBUG) + Debugprintf("GetPermissionToChange - WaitingForPermission = %d", RIG->WaitingForPermission); + + // See if any two stage (CONLOCK) ports + + if (RIG->PortRecord[0] && RIG->PortRecord[0]->SCANCAPABILITIES != CONLOCK) + goto CheckOtherPorts; + + + if (RIG->WaitingForPermission) + { + // This code should only be called if port needs two stage + // eg Request then confirm. only SCS Pactor at the moment. + + // TNC has been asked for permission, and we are waiting respoonse + // Only SCS pactor returns WaitingForPrmission, so check shouldn't be called on others + + RIG->OKtoChange = (int)(intptr_t)RIG->PortRecord[0]->PORT_EXT_ADDR(6, RIG->PortRecord[0]->PORTCONTROL.PORTNUMBER, (PDATAMESSAGE)2); // Get Ok Flag + + if (RIG->OKtoChange == 1) + { + if (RIG->RIG_DEBUG) + Debugprintf("Check Permission returned OK to change"); + + i = 1; + goto CheckOtherPorts; + } + + if (RIG->OKtoChange == -1) + { + // Permission Refused. Wait Scan Interval and try again + + if (RIG->RIG_DEBUG) + Debugprintf("Scan Debug %s Refused permission - waiting ScanInterval %d", + RIG->PortRecord[0]->PORT_DLL_NAME, PORT->FreqPtr->Dwell ); + + RIG->WaitingForPermission = FALSE; + SetWindowText(RIG->hSCAN, "-"); + RIG->WEB_SCAN = '='; + + RIG->ScanCounter = PORT->FreqPtr->Dwell; + + if (RIG->ScanCounter == 0) // ? After manual change + RIG->ScanCounter = 50; + + return FALSE; + } + + if (RIG->RIG_DEBUG) + Debugprintf("Scan Debug %s Still Waiting", RIG->PortRecord[0]->PORT_DLL_NAME); + + + return FALSE; // Haven't got reply yet. Will re-enter next tick + } + else + { + // not waiting for permission, so must be first call of a cycle + + if (RIG->PortRecord[0] && RIG->PortRecord[0]->PORT_EXT_ADDR) + RIG->WaitingForPermission = (int)(intptr_t)RIG->PortRecord[0]->PORT_EXT_ADDR(6, RIG->PortRecord[0]->PORTCONTROL.PORTNUMBER, (PDATAMESSAGE)1); // Request Perrmission + + // If it returns zero there is no need to wait. + // Normally SCS Returns True for first call, but returns 0 if Link not running + + if (RIG->WaitingForPermission) + return FALSE; // Wait till driver has status + + if (RIG->RIG_DEBUG) + Debugprintf("First call to %s returned OK to change", RIG->PortRecord[0]->PORT_DLL_NAME); + + i = 1; + } + +CheckOtherPorts: + + // Either first TNC gave permission or there are no SCS like ports. + // Ask any others (these are assumed to give immediate yes/no + + while (RIG->PortRecord[i]) + { + PortRecord = RIG->PortRecord[i]; + + if (PortRecord->PORT_EXT_ADDR && PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, (PDATAMESSAGE)1)) + { + // 1 means can't change - release all + + if (RIG->RIG_DEBUG && PORT->FreqPtr) + Debugprintf("Scan Debug %s Refused permission - waiting ScanInterval %d", + PortRecord->PORT_DLL_NAME, PORT->FreqPtr->Dwell); + + RIG->WaitingForPermission = FALSE; + MySetWindowText(RIG->hSCAN, "-"); + RIG->WEB_SCAN = '-'; + + if (PORT->FreqPtr) + RIG->ScanCounter = PORT->FreqPtr->Dwell; + + if (RIG->ScanCounter == 0) // ? After manual change + RIG->ScanCounter = 50; + + ReleasePermission(RIG); + return FALSE; + } + else + if (RIG->RIG_DEBUG) + Debugprintf("Scan Debug %s gave permission", PortRecord->PORT_DLL_NAME); + + i++; + } + + + RIG->WaitingForPermission = FALSE; + + // Update pointer to next frequency + + RIG->FreqPtr++; + + ptr = RIG->FreqPtr; + + if (ptr == NULL) + { + Debugprintf("Scan Debug - No freqs - quitting"); + return FALSE; // No Freqs + } + + if (ptr[0] == (struct ScanEntry *)0) // End of list - reset to start + { + ptr = CheckTimeBands(RIG); + + if (ptr[0] == (struct ScanEntry *)0) + { + // Empty Timeband - delay 60 secs (we need to check for timeband change sometimes) + + RIG->ScanCounter = 600; + ReleasePermission(RIG); + return FALSE; + } + } + + PORT->FreqPtr = ptr[0]; // Save Scan Command Block + + RIG->ScanCounter = PORT->FreqPtr->Dwell; + + if (RIG->ScanCounter == 0) // ? After manual change + RIG->ScanCounter = 150; + + MySetWindowText(RIG->hSCAN, "S"); + RIG->WEB_SCAN = 'S'; + + // Do Bandwidth and antenna switches (if needed) + + DoBandwidthandAntenna(RIG, ptr[0]); + + return TRUE; +} + +VOID DoBandwidthandAntenna(struct RIGINFO *RIG, struct ScanEntry * ptr) +{ + // If Bandwidth Change needed, do it + + int i; + struct _EXTPORTDATA * PortRecord; + + if (ptr->Bandwidth || ptr->RPacketMode || ptr->HFPacketMode + || ptr->PMaxLevel || ptr->ARDOPMode[0] || ptr->VARAMode) + { + i = 0; + + while (RIG->PortRecord[i]) + { + PortRecord = RIG->PortRecord[i]; + + RIG->CurrentBandWidth = ptr->Bandwidth; + + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, (PDATAMESSAGE)ptr); + +/* if (ptr->Bandwidth == 'R') // Robust Packet + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, 6); // Set Robust Packet + else + + if (ptr->Bandwidth == 'W') + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, 4); // Set Wide Mode + else + PortRecord->PORT_EXT_ADDR(6, PortRecord->PORTCONTROL.PORTNUMBER, 5); // Set Narrow Mode +*/ + i++; + } + } + + // If Antenna Change needed, do it + + // 5 and 6 are used for CI-V switching so ignore here + + if (ptr->Antenna && ptr->Antenna < '5') + { + SwitchAntenna(RIG, ptr->Antenna); + } + + return; +} + +VOID ICOMPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + int i; + + struct RIGINFO * RIG; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + } + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + PORT->Retries--; + + if(PORT->Retries) + { + RigWriteCommBlock(PORT); // Retransmit Block + return; + } + + RIG = &PORT->Rigs[PORT->CurrentRig]; + + + SetWindowText(RIG->hFREQ, "-----------"); + strcpy(RIG->WEB_FREQ, "-----------"); + SetWindowText(RIG->hMODE, "------"); + strcpy(RIG->WEB_MODE, "------"); + +// SetWindowText(RIG->hFREQ, "145.810000"); +// SetWindowText(RIG->hMODE, "RTTY/1"); + + PORT->Rigs[PORT->CurrentRig].RIGOK = FALSE; + + return; + + } + + // Send Data if avail, else send poll + + PORT->CurrentRig++; + + if (PORT->CurrentRig >= PORT->ConfiguredRigs) + PORT->CurrentRig = 0; + + RIG = &PORT->Rigs[PORT->CurrentRig]; + +/* + RIG->DebugDelay ++; + + if (RIG->DebugDelay > 600) + { + RIG->DebugDelay = 0; + Debugprintf("Scan Debug %d %d %d %d %d %d", PORT->CurrentRig, + RIG->NumberofBands, RIG->RIGOK, RIG->ScanStopped, RIG->ScanCounter, + RIG->WaitingForPermission); + } +*/ + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq/1000000.0); + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + + if (PORT->FreqPtr->Freq > 0.0) + { + _gcvt((PORT->FreqPtr->Freq + RIG->rxOffset) / 1000000.0, 9, RIG->Valchar); // For MH + strcpy(RIG->WEB_FREQ, RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->AutoPoll = TRUE; + + return; + } + } + } + + if (RIG->RIGOK && RIG->BPQtoRADIO_Q) + { + struct MSGWITHOUTLEN * buffptr; + + buffptr = Q_REM(&RIG->BPQtoRADIO_Q); + + // Copy the ScanEntry struct from the Buffer to the PORT Scanentry + + memcpy(&PORT->ScanEntry, buffptr->Data, sizeof(struct ScanEntry)); + + PORT->FreqPtr = &PORT->ScanEntry; // Block we are currently sending. + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Manual Change Freq to %9.4f", PORT->FreqPtr->Freq/1000000.0); + + + memcpy(Poll, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + + DoBandwidthandAntenna(RIG, &PORT->ScanEntry); + + if (PORT->FreqPtr->Freq > 0.0) + { + _gcvt((PORT->FreqPtr->Freq + RIG->rxOffset) / 1000000.0, 9, RIG->Valchar); // For MH + strcpy(RIG->WEB_FREQ, RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + PORT->TXLen = PORT->FreqPtr->Cmd1Len; // First send the set Freq + RigWriteCommBlock(PORT); + PORT->Retries = 2; + + ReleaseBuffer(buffptr); + + PORT->AutoPoll = FALSE; + + return; + } + + + if (RIG->RIGOK && RIG->ScanStopped == 0 && RIG->NumberofBands && + RIG->ScanCounter && RIG->ScanCounter < 30) // no point in reading freq if we are about to change + return; + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter) + return; + } + + // Need to make sure we don't poll multiple rigs on port at the same time + + if (RIG->RIGOK) + { + PORT->Retries = 2; + RIG->PollCounter = 10 / PORT->ConfiguredRigs; // Once Per Sec + } + else + { + PORT->Retries = 1; + RIG->PollCounter = 100 / PORT->ConfiguredRigs; // Slow Poll if down + } + + RIG->PollCounter += PORT->CurrentRig * 3; + + PORT->AutoPoll = TRUE; + + // Read Frequency + + Poll[0] = 0xFE; + Poll[1] = 0xFE; + Poll[2] = RIG->RigAddr; + Poll[3] = 0xE0; + Poll[4] = 0x3; // Get frequency command + Poll[5] = 0xFD; + + PORT->TXLen = 6; + + RigWriteCommBlock(PORT); + + PORT->Retries = 1; + PORT->Timeout = 10; + + return; +} + + +VOID ProcessFrame(struct RIGPORTINFO * PORT, UCHAR * Msg, int framelen) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG; + int i; + int cmdSent = PORT->TXBuffer[4]; + + if (Msg[0] != 0xfe || Msg[1] != 0xfe) + + // Duff Packet - return + + return; + + if (Msg[2] != 0xe0 && Msg[2] != 0) + { + // Echo - Proves a CI-V interface is attached + + if (PORT->PORTOK == FALSE) + { + // Just come up + PORT->PORTOK = TRUE; + Debugprintf("Cat Port OK"); + } + return; + } + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + if (Msg[3] == RIG->RigAddr) + goto ok; + } + + return; + +ok: + + if (Msg[4] == 0xFB) + { + // Accept + + if (cmdSent == 0x1C && PORT->TXBuffer[5] == 1) // Tune + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Tune OK"); + + PORT->Timeout = 0; + return; + } + + if (cmdSent == 0x14 && PORT->TXBuffer[5] == 0x0A) // Power + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Set Power OK"); + + PORT->Timeout = 0; + return; + } + + // if it was the set freq, send the set mode + + if (RIG->ICF8101) + { + if (cmdSent == 0x1A && PORT->TXBuffer[5] == 0x35) + { + if (PORT->FreqPtr->Cmd2) + { + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd2, PORT->FreqPtr->Cmd2Len); + PORT->TXLen = PORT->FreqPtr->Cmd2Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + } + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Frequency Set OK"); + + PORT->Timeout = 0; + } + return; + } + + if (cmdSent == 0x1a && PORT->TXBuffer[5] == 0x36) + { + // Set Mode Response - if scanning read freq, else return OK to user + + if (RIG->ScanStopped == 0) + { + ReleasePermission(RIG); // Release Perrmission + + Poll[0] = 0xFE; + Poll[1] = 0xFE; + Poll[2] = RIG->RigAddr; + Poll[3] = 0xE0; + Poll[4] = 0x3; // Get frequency command + Poll[5] = 0xFD; + + PORT->TXLen = 6; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Frequency and Mode Set OK"); + + RIG->PollCounter = 30 / RIG->PORT->ConfiguredRigs; // Dont read freq for 3 secs + } + } + + PORT->Timeout = 0; + return; + } + + if (cmdSent == 5 || cmdSent == 7) // Freq or VFO + { + if (PORT->FreqPtr && PORT->FreqPtr->Cmd2) + { + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd2, PORT->FreqPtr->Cmd2Len); + PORT->TXLen = PORT->FreqPtr->Cmd2Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + } + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Frequency Set OK"); + + PORT->Timeout = 0; + } + return; + } + + if (cmdSent == 6) // Set Mode + { + if (PORT->FreqPtr && PORT->FreqPtr->Cmd3) + { + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd3, PORT->FreqPtr->Cmd3Len); + PORT->TXLen = PORT->FreqPtr->Cmd3Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + + goto SetFinished; + } + + if (cmdSent == 0x08) + { + // Memory Chan + + cmdSent = 0; // So we only do it once + + goto SetFinished; + } + + if (cmdSent == 0x12) + goto SetFinished; // Antenna always sent last + + + if (cmdSent == 0x0f || cmdSent == 0x01a) // Set DUP or Data + { + // These are send from cmd3. There may be a cmd4 + // for antenna switching + + if (PORT->FreqPtr && PORT->FreqPtr->Cmd4) + { + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd4, PORT->FreqPtr->Cmd4Len); + PORT->TXLen = PORT->FreqPtr->Cmd4Len; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + +SetFinished: + + // Set Mode Response - if scanning read freq, else return OK to user + + if (RIG->ScanStopped == 0 && PORT->AutoPoll) + { + ReleasePermission(RIG); // Release Perrmission + + Poll[0] = 0xFE; + Poll[1] = 0xFE; + Poll[2] = RIG->RigAddr; + Poll[3] = 0xE0; + Poll[4] = 0x3; // Get frequency command + Poll[5] = 0xFD; + + PORT->TXLen = 6; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Frequency and Mode Set OK"); + + RIG->PollCounter = 30 / RIG->PORT->ConfiguredRigs; // Dont read freq for 3 secs + } + } + + PORT->Timeout = 0; + return; + } + + if (Msg[4] == 0xFA) + { + // Reject + + PORT->Timeout = 0; + + if (!PORT->AutoPoll) + { + if (cmdSent == 5) + SendResponse(RIG->Session, "Sorry - Set Frequency Command Rejected"); + else + if (cmdSent == 6) + SendResponse(RIG->Session, "Sorry - Set Mode Command Rejected"); + else + if (cmdSent == 0x0f) + SendResponse(RIG->Session, "Sorry - Set Shift Command Rejected"); + else + if (cmdSent == 0x12) + SendResponse(RIG->Session, "Sorry - Set Antenna Command Rejected"); + else + if (cmdSent == 0x1C && PORT->TXBuffer[5] == 1) // Tune + SendResponse(RIG->Session, "Sorry - Tune Command Rejected"); + else + if (cmdSent == 0x14 && PORT->TXBuffer[5] == 0x0a) // Power + SendResponse(RIG->Session, "Sorry - Power Command Rejected"); + else + SendResponse(RIG->Session, "Sorry - Command Rejected"); + + } + return; + } + + if (Msg[4] == PORT->TXBuffer[4]) + { + // Response to our command + + // Any valid frame is an ACK + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + if (!PORT->AutoPoll) + { + // Manual response probably to CMD Query. Return response + + char reply[256] = "CMD Response "; + char * p1 = &reply[13]; + UCHAR * p2 = &Msg[4]; + int n = framelen - 4; + + while (n > 0) + { + sprintf(p1, "%02X ", *(p2)); + p1 += 3; + p2++; + n--; + } + + SendResponse(RIG->Session, reply); + + } + + } + else + return; // What does this mean?? + + + if (PORT->PORTOK == FALSE) + { + // Just come up +// char Status[80]; + + PORT->PORTOK = TRUE; +// sprintf(Status,"COM%d PORT link OK", PORT->IOBASE); +// SetWindowText(PORT->hStatus, Status); + } + + if (Msg[4] == 3) + { + // Rig Frequency + int n, j, decdigit; + long long Freq = 0; + int start = 9; + long long MHz, Hz; + char CharMHz[16] = ""; + char CharHz[16] = ""; + + int i; + + + if (RIG->IC735) + start = 8; // shorted msg + + for (j = start; j > 4; j--) + { + n = Msg[j]; + decdigit = (n >> 4); + decdigit *= 10; + decdigit += n & 0xf; + Freq = (Freq *100 ) + decdigit; + } + + Freq += RIG->rxOffset; + + RIG->RigFreq = Freq / 1000000.0; + + // If we convert to float to display we get rounding errors, so convert to MHz and Hz to display + + MHz = Freq / 1000000; + + Hz = Freq - MHz * 1000000; + + sprintf(CharMHz, "%lld", MHz); + sprintf(CharHz, "%06lld", Hz); + + for (i = 5; i > 2; i--) + { + if (CharHz[i] == '0') + CharHz[i] = 0; + else + break; + } + + sprintf(RIG->WEB_FREQ,"%lld.%s", MHz, CharHz); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + strcpy(RIG->Valchar, RIG->WEB_FREQ); + + + // Now get Mode + + Poll[0] = 0xFE; + Poll[1] = 0xFE; + Poll[2] = RIG->RigAddr; + Poll[3] = 0xE0; + + if (RIG->ICF8101) + { + Poll[4] = 0x1A; // Get Mode + Poll[5] = 0x34; + Poll[6] = 0xFD; + PORT->TXLen = 7; + } + else + { + Poll[4] = 0x4; // Get Mode + Poll[5] = 0xFD; + + PORT->TXLen = 6; + } + + RigWriteCommBlock(PORT); + PORT->Retries = 2; + return; + } + + if (RIG->ICF8101) + { + if (cmdSent == 0x1A && PORT->TXBuffer[5] == 0x34) + { + // Get Mode + + unsigned int Mode; + + Mode = (Msg[7] >> 4); + Mode *= 10; + Mode += Msg[7] & 0xf; + + if (Mode > 24) Mode = 24; + + sprintf(RIG->WEB_MODE,"%s", Modes[Mode]); + + strcpy(RIG->ModeString, Modes[Mode]); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + + + return; + } + } + + + if (Msg[4] == 4) + { + // Mode + + unsigned int Mode; + + Mode = (Msg[5] >> 4); + Mode *= 10; + Mode += Msg[5] & 0xf; + + if (Mode > 24) Mode = 24; + + if (RIG->IC735) + sprintf(RIG->WEB_MODE,"%s", Modes[Mode]); + else + sprintf(RIG->WEB_MODE,"%s/%d", Modes[Mode], Msg[6]); + + strcpy(RIG->ModeString, Modes[Mode]); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + } +} + +int SendResponse(int Session, char * Msg) +{ + PDATAMESSAGE Buffer; + BPQVECSTRUC * VEC; + TRANSPORTENTRY * L4 = L4TABLE; + + if (Session == -1) + return 0; + + Buffer = GetBuff(); + + L4 += Session; + + Buffer->PID = 0xf0; + Buffer->LENGTH = MSGHDDRLEN + 1; // Includes PID + Buffer->LENGTH += sprintf(Buffer->L2DATA, "%s\r", Msg); + + VEC = L4->L4TARGET.HOST; + + C_Q_ADD(&L4->L4TX_Q, (UINT *)Buffer); + +#ifndef LINBPQ + + if (VEC) + PostMessage(VEC->HOSTHANDLE, BPQMsg, VEC->HOSTSTREAM, 2); +#endif + return 0; +} + +VOID ProcessFT100Frame(struct RIGPORTINFO * PORT) +{ + // Only one we should see is a Status Message + + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + int Freq; + double FreqF; + unsigned int Mode; + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + // Bytes 0 is Band + // 1 - 4 is Freq in binary in units of 1.25 HZ (!) + // Byte 5 is Mode + + Freq = (Msg[1] << 24) + (Msg[2] << 16) + (Msg[3] << 8) + Msg[4]; + + FreqF = (Freq * 1.25) / 1000000; + + if (PORT->YaesuVariant == FT1000MP) + FreqF = FreqF / 2; // No idea why! + + RIG->RigFreq = FreqF; + + _gcvt(FreqF, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + if (PORT->PortType == FT100) + { + Mode = Msg[5] & 15; + if (Mode > 8) Mode = 8; + sprintf(RIG->WEB_MODE,"%s", FT100Modes[Mode]); + } + else // FT1000 + { + Mode = Msg[7] & 7; + sprintf(RIG->WEB_MODE,"%s", FTRXModes[Mode]); + } + + strcpy(RIG->ModeString, RIG->WEB_MODE); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + else + if (PORT->TXLen > 5) // Poll is 5 Change is more + ReleasePermission(RIG); // Release Perrmission to change +} + + + +VOID ProcessFT990Frame(struct RIGPORTINFO * PORT) +{ + // Only one we should see is a Status Message + + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + int Freq; + double FreqF; + unsigned int Mode; + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + // Bytes 0 is Band + // 1 - 4 is Freq in units of 10Hz (I think!) + // Byte 5 is Mode + + Freq = (Msg[1] << 16) + (Msg[2] << 8) + Msg[3]; + + FreqF = (Freq * 10.0) / 1000000; + + RIG->RigFreq = FreqF; + + _gcvt(FreqF, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + Mode = Msg[7] & 7; + sprintf(RIG->WEB_MODE,"%s", FTRXModes[Mode]); + + strcpy(RIG->ModeString, RIG->WEB_MODE); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + else + if (PORT->TXLen > 5) // Poll is 5 change is more + ReleasePermission(RIG); // Release Perrmission to change +} + +VOID ProcessFT1000Frame(struct RIGPORTINFO * PORT) +{ + // Only one we should see is a Status Message + + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + int Freq; + double FreqF; + unsigned int Mode; + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + // I think the FT1000/1000D is same as 990 + // FT1000MP is similar to FT100, but steps on .625 Hz (despite manual) + // Bytes 0 is Band + // 1 - 4 is Freq in binary in units of 1.25 HZ (!) + // Byte 5 is Mode + + if (PORT->YaesuVariant == FT1000MP) + { + Freq = (Msg[1] << 24) + (Msg[2] << 16) + (Msg[3] << 8) + Msg[4]; + FreqF = (Freq * 1.25) / 1000000; + FreqF = FreqF / 2; // No idea why! + } + else + { + Freq = (Msg[1] << 16) + (Msg[2] << 8) + Msg[3]; + FreqF = (Freq * 10.0) / 1000000; + } + + RIG->RigFreq = FreqF; + + _gcvt(FreqF, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + if (PORT->PortType == FT100) + { + Mode = Msg[5] & 15; + if (Mode > 8) Mode = 8; + sprintf(RIG->WEB_MODE,"%s", FT100Modes[Mode]); + } + else // FT1000 + { + Mode = Msg[7] & 7; + sprintf(RIG->WEB_MODE,"%s", FTRXModes[Mode]); + } + + strcpy(RIG->ModeString, RIG->WEB_MODE); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + else + if (PORT->TXLen > 5) // Poll is 5 change is more + ReleasePermission(RIG); // Release Perrmission to change +} + + + + + +VOID ProcessYaesuCmdAck(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + + PORT->Timeout = 0; + PORT->RXLen = 0; // Ready for next frame + + if (PORT->CmdSent == 1) // Set Freq + { + ReleasePermission(RIG); // Release Perrmission + + if (Msg[0]) + { + // I think nonzero is a Reject + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Sorry - Set Frequency Rejected"); + + return; + } + else + { + if (RIG->ScanStopped == 0) + { + // Send a Get Freq - We Don't Poll when scanning + + Poll[0] = Poll[1] = Poll[2] = Poll[3] = 0; + Poll[4] = 0x3; // Get frequency amd mode command + + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->CmdSent = 0; + } + else + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + + return; + } + } + + if (PORT->CmdSent == 7) // Set Mode + { + if (Msg[0]) + { + // I think nonzero is a Reject + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Sorry - Set Mode Rejected"); + + return; + } + else + { + // Send the Frequency + + memcpy(Poll, &Poll[5], 5); + RigWriteCommBlock(PORT); + PORT->CmdSent = Poll[4]; + PORT->Retries = 2; + + return; + } + } + +} +VOID ProcessYaesuFrame(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + int n, j, Freq = 0, decdigit; + double FreqF; + unsigned int Mode; + + // I'm not sure we get anything but a Command Response, + // and the only command we send is Get Rig Frequency and Mode + + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + for (j = 0; j < 4; j++) + { + n = Msg[j]; + decdigit = (n >> 4); + decdigit *= 10; + decdigit += n & 0xf; + Freq = (Freq *100 ) + decdigit; + } + + FreqF = Freq / 100000.0; + + RIG->RigFreq = FreqF; + +// Valchar = _fcvt(FreqF, 6, &dec, &sign); + _gcvt(FreqF, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + Mode = Msg[4]; + + if (Mode > 15) Mode = 15; + + sprintf(RIG->WEB_MODE,"%s", YaesuModes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + // FT847 Manual Freq Change response ends up here + + if (strcmp(RIG->RigName, "FT847") == 0) + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + + if (PORT->CmdSent == -1) + ReleasePermission(RIG); // Release Perrmission to change + } +} + +VOID YaesuPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + PORT->Retries--; + + if(PORT->Retries) + { + RigWriteCommBlock(PORT); // Retransmit Block + return; + } + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + + PORT->Rigs[PORT->CurrentRig].RIGOK = FALSE; + + return; + + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, 24); + + if (PORT->PortType == YAESU) + { + if (strcmp(PORT->Rigs[0].RigName, "FT847") == 0) + { + PORT->TXLen = 15; // No Cmd ACK, so send Mode, Freq and Poll + PORT->CmdSent = -1; + } + else + { + PORT->TXLen = 5; + PORT->CmdSent = Poll[4]; + } + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->AutoPoll = TRUE; + return; + } + + // FT100 + + PORT->TXLen = 15; // Set Mode, Set Freq, Poll + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->AutoPoll = TRUE; + } + } + } + + if (RIG->RIGOK && RIG->BPQtoRADIO_Q) + { + struct MSGWITHOUTLEN * buffptr; + + buffptr = Q_REM(&RIG->BPQtoRADIO_Q); + + // Copy the ScanEntry struct from the Buffer to the PORT Scanentry + + memcpy(&PORT->ScanEntry, buffptr->Data, sizeof(struct ScanEntry)); + + PORT->FreqPtr = &PORT->ScanEntry; // Block we are currently sending. + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + DoBandwidthandAntenna(RIG, &PORT->ScanEntry); + + memcpy(Poll, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + + if (PORT->PortType == YAESU) + { + if (strcmp(PORT->Rigs[0].RigName, "FT847") == 0) + { + PORT->TXLen = 15; // Send all + PORT->CmdSent = -1; + } + else + { + PORT->TXLen = 5; // First send the set Freq + PORT->CmdSent = Poll[4]; + } + } + else + PORT->TXLen = 15; // Send all + + RigWriteCommBlock(PORT); + PORT->Retries = 2; + + ReleaseBuffer(buffptr); + PORT->AutoPoll = FALSE; + + return; + } + + if (RIG->RIGOK && RIG->ScanStopped == 0 && RIG->NumberofBands && + RIG->ScanCounter && RIG->ScanCounter < 30) // no point in reading freq if we are about to change + return; + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter) + return; + } + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + Poll[0] = 0; + Poll[1] = 0; + Poll[2] = 0; + + if (PORT->PortType == FT990 || PORT->PortType == YAESU || PORT->YaesuVariant == FT1000D) + Poll[3] = 3; + else + Poll[3] = 2; + + if (PORT->PortType == YAESU) + Poll[4] = 0x3; // Get frequency amd mode command + else + Poll[4] = 16; // FT100/990/1000 Get frequency amd mode command + + PORT->TXLen = 5; + RigWriteCommBlock(PORT); + PORT->Retries = 2; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID ProcessNMEA(struct RIGPORTINFO * PORT, char * Msg, int Length) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + + Msg[Length] = 0; + + if (PORT->PORTOK == FALSE) + { + // Just come up + PORT->PORTOK = TRUE; + } + + RIG->RIGOK = TRUE; + PORT->Timeout = 0; + + if (!PORT->AutoPoll) + { + // Response to a RADIO Command + + if (Msg[0] == '?') + SendResponse(RIG->Session, "Sorry - Command Rejected"); + else + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + + PORT->AutoPoll = TRUE; + } + + if (memcmp(&Msg[13], "RXF", 3) == 0) + { + if (Length < 24) + return; + + RIG->RigFreq = atof(&Msg[17]); + + sprintf(RIG->WEB_FREQ,"%f", RIG->RigFreq); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + strcpy(RIG->Valchar, RIG->WEB_FREQ); + + return; + } + + if (memcmp(&Msg[13], "MODE", 3) == 0) + { + char * ptr; + + if (Length < 24) + return; + + ptr = strchr(&Msg[18], '*'); + if (ptr) *(ptr) = 0; + + SetWindowText(RIG->hMODE, &Msg[18]); + strcpy(RIG->WEB_MODE, &Msg[18]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + return; + } + + +} + + +//FA00014103000;MD2; + + +VOID ProcessSDRRadioFrame(struct RIGPORTINFO * PORT, int Length) +{ + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; + UCHAR * ptr; + int CmdLen; + int i; + + Msg[Length] = 0; + + if (PORT->PORTOK == FALSE) + { + // Just come up + PORT->PORTOK = TRUE; + } + + if (!PORT->AutoPoll) + { + // Response to a RADIO Command + + if (Msg[0] == '?') + SendResponse(RIG->Session, "Sorry - Command Rejected"); + else if (Msg[0] == 'A' && Msg[1] == 'C') + SendResponse(RIG->Session, "TUNE OK"); + else if (Msg[0] == 'P' && Msg[1] == 'C') + SendResponse(RIG->Session, "Power Set OK"); + else + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + + PORT->AutoPoll = TRUE; + return; + } + + +LoopR: + + ptr = strchr(Msg, ';'); + CmdLen = (int)(ptr - Msg + 1); + + // Find Device. FA to FF for frequency + + // Note. SDRConsole reports the same mode for all receivers, so don't rely on reported mode + + if (Msg[0] == 'F') + { + for (i = 0; i < PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + if (Msg[1] == RIG->RigAddr) + goto ok; + } + return; +ok: + + if (CmdLen > 9) + { + char CharMHz[16] = ""; + char CharHz[16] = ""; + + int i; + long long Freq; + long long MHz, Hz; + + RIG->RIGOK = TRUE; + + Freq = strtoll(&Msg[2], NULL, 10) + RIG->rxOffset; + + RIG->RigFreq = Freq / 1000000.0; + + // If we convert to float to display we get rounding errors, so convert to MHz and Hz to display + + MHz = Freq / 1000000; + + Hz = Freq - MHz * 1000000; + + sprintf(CharMHz, "%lld", MHz); + sprintf(CharHz, "%06lld", Hz); + + for (i = 5; i > 2; i--) + { + if (CharHz[i] == '0') + CharHz[i] = 0; + else + break; + } + + + sprintf(RIG->WEB_FREQ,"%lld.%s", MHz, CharHz); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + strcpy(RIG->Valchar, RIG->WEB_FREQ); + + PORT->Timeout = 0; + } + } + else if (Msg[0] == 'M' && Msg[1] == 'D') + { + int Mode; + + Mode = Msg[2] - 48; + if (Mode > 7) Mode = 7; + SetWindowText(RIG->hMODE, KenwoodModes[Mode]); + strcpy(RIG->WEB_MODE, KenwoodModes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + + } + + if (CmdLen < Length) + { + // Another Message in Buffer + + ptr++; + Length -= (int)(ptr - Msg); + + if (Length <= 0) + return; + + memmove(Msg, ptr, Length +1); + + goto LoopR; + } +} + + + +VOID ProcessKenwoodFrame(struct RIGPORTINFO * PORT, int Length) +{ + UCHAR * Poll = PORT->TXBuffer; + UCHAR * Msg = PORT->RXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + UCHAR * ptr; + int CmdLen; + + Msg[Length] = 0; + + if (PORT->PORTOK == FALSE) + { + // Just come up + PORT->PORTOK = TRUE; + } + + RIG->RIGOK = TRUE; + + if (!PORT->AutoPoll) + { + // Response to a RADIO Command + + if (Msg[0] == '?') + SendResponse(RIG->Session, "Sorry - Command Rejected"); + else if (Msg[0] == 'A' && Msg[1] == 'C') + SendResponse(RIG->Session, "TUNE OK"); + else if (Msg[0] == 'P' && Msg[1] == 'C') + SendResponse(RIG->Session, "Power Set OK"); + else + SendResponse(RIG->Session, "Mode and Frequency Set OK"); + + PORT->AutoPoll = TRUE; + return; + } + +Loop: + + if (PORT->PortType == FLEX) + { + Msg += 2; // Skip ZZ + Length -= 2; + } + + ptr = strchr(Msg, ';'); + CmdLen = (int)(ptr - Msg + 1); + + if (Msg[0] == 'F' && Msg[1] == 'A' && CmdLen > 9) + { + char CharMHz[16] = ""; + char CharHz[16] = ""; + + int i; + long long Freq; + long long MHz, Hz; + + Freq = strtoll(&Msg[2], NULL, 10) + RIG->rxOffset; + + RIG->RigFreq = Freq / 1000000.0; + + // If we convert to float to display we get rounding errors, so convert to MHz and Hz to display + + MHz = Freq / 1000000; + + Hz = Freq - MHz * 1000000; + + sprintf(CharMHz, "%lld", MHz); + sprintf(CharHz, "%06lld", Hz); + +/* + if (PORT->PortType == FT2000) + { + memcpy(FreqDecimal,&Msg[4], 6); + Msg[4] = 0; + } + else if (PORT->PortType == FT991A) + { + memcpy(FreqDecimal,&Msg[5], 6); + Msg[5] = 0; + } + + else + { + memcpy(FreqDecimal,&Msg[7], 6); + Msg[7] = 0; + } + FreqDecimal[6] = 0; +*/ + + for (i = 5; i > 2; i--) + { + if (CharHz[i] == '0') + CharHz[i] = 0; + else + break; + } + + + sprintf(RIG->WEB_FREQ,"%lld.%s", MHz, CharHz); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + strcpy(RIG->Valchar, RIG->WEB_FREQ); + + PORT->Timeout = 0; + } + else if (Msg[0] == 'M' && Msg[1] == 'D') + { + int Mode; + + if (PORT->PortType == FT2000) + { + Mode = Msg[3] - 48; + if (Mode > 13) Mode = 13; + SetWindowText(RIG->hMODE, FT2000Modes[Mode]); + strcpy(RIG->WEB_MODE, FT2000Modes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + } + else if (PORT->PortType == FT991A) + { + Mode = Msg[3] - 48; + if (Mode > 16) + Mode -= 7; + + if (Mode > 15) Mode = 13; + SetWindowText(RIG->hMODE, FT991AModes[Mode]); + strcpy(RIG->WEB_MODE, FT991AModes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + } + else if (PORT->PortType == FTDX10) + { + Mode = Msg[3] - 48; + if (Mode > 16) + Mode -= 7; + + if (Mode > 15) Mode = 15; + SetWindowText(RIG->hMODE, FTDX10Modes[Mode]); + strcpy(RIG->WEB_MODE, FTDX10Modes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + } + else if (PORT->PortType == FLEX) + { + Mode = atoi(&Msg[3]); + if (Mode > 12) Mode = 12; + SetWindowText(RIG->hMODE, FLEXModes[Mode]); + strcpy(RIG->WEB_MODE, FLEXModes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + } + else + { + Mode = Msg[2] - 48; + if (Mode > 7) Mode = 7; + SetWindowText(RIG->hMODE, KenwoodModes[Mode]); + strcpy(RIG->WEB_MODE, KenwoodModes[Mode]); + strcpy(RIG->ModeString, RIG->WEB_MODE); + } + } + + if (CmdLen < Length) + { + // Another Message in Buffer + + ptr++; + Length -= (int)(ptr - Msg); + + if (Length <= 0) + return; + + memmove(Msg, ptr, Length +1); + + goto Loop; + } +} + + +VOID KenwoodPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Kenwood + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + PORT->Retries--; + + if(PORT->Retries) + { + RigWriteCommBlock(PORT); // Retransmit Block + return; + } + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + + return; + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + RigWriteCommBlock(PORT); + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 0; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + + return; + } + } + } + + if (RIG->RIGOK && RIG->BPQtoRADIO_Q) + { + struct MSGWITHOUTLEN * buffptr; + + buffptr = Q_REM(&RIG->BPQtoRADIO_Q); + + // Copy the ScanEntry struct from the Buffer to the PORT Scanentry + + memcpy(&PORT->ScanEntry, buffptr->Data, sizeof(struct ScanEntry)); + + PORT->FreqPtr = &PORT->ScanEntry; // Block we are currently sending. + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + DoBandwidthandAntenna(RIG, &PORT->ScanEntry); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + memcpy(Poll, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + RigWriteCommBlock(PORT); + PORT->CmdSent = Poll[4]; + PORT->Timeout = 0; + RIG->PollCounter = 10; + + ReleaseBuffer(buffptr); + PORT->AutoPoll = FALSE; + + return; + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && RIG->ScanStopped == 0 && RIG->NumberofBands && + RIG->ScanCounter && RIG->ScanCounter < 30) + return; // no point in reading freq if we are about to change it + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + PORT->TXLen = RIG->PollLen; + strcpy(Poll, RIG->Poll); + + RigWriteCommBlock(PORT); + PORT->Retries = 1; + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID SDRRadioPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG; + int i; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + } + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + PORT->Retries--; + + if(PORT->Retries) + { + RigWriteCommBlock(PORT); // Retransmit Block + return; + } + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + + return; + } + + // Send Data if avail, else send poll + + + PORT->CurrentRig++; + + if (PORT->CurrentRig >= PORT->ConfiguredRigs) + PORT->CurrentRig = 0; + + RIG = &PORT->Rigs[PORT->CurrentRig]; + + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + RigWriteCommBlock(PORT); + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 0; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + + return; + } + } + } + + if (RIG->RIGOK && RIG->BPQtoRADIO_Q) + { + struct MSGWITHOUTLEN * buffptr; + + buffptr = Q_REM(&RIG->BPQtoRADIO_Q); + + // Copy the ScanEntry struct from the Buffer to the PORT Scanentry + + memcpy(&PORT->ScanEntry, buffptr->Data, sizeof(struct ScanEntry)); + + PORT->FreqPtr = &PORT->ScanEntry; // Block we are currently sending. + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + DoBandwidthandAntenna(RIG, &PORT->ScanEntry); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + memcpy(Poll, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + RigWriteCommBlock(PORT); + PORT->CmdSent = Poll[4]; + PORT->Timeout = 0; + RIG->PollCounter = 10; + + ReleaseBuffer(buffptr); + PORT->AutoPoll = FALSE; + + return; + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && RIG->ScanStopped == 0 && RIG->NumberofBands && + RIG->ScanCounter && RIG->ScanCounter < 30) + return; // no point in reading freq if we are about to change it + + // Need to make sure we don't poll multiple rigs on port at the same time + + if (RIG->RIGOK) + { + PORT->Retries = 2; + RIG->PollCounter = 10 / PORT->ConfiguredRigs; // Once Per Sec + } + else + { + PORT->Retries = 1; + RIG->PollCounter = 100 / PORT->ConfiguredRigs; // Slow Poll if down + } + + RIG->PollCounter += PORT->CurrentRig * 3; + + // Read Frequency + + PORT->TXLen = RIG->PollLen; + strcpy(Poll, RIG->Poll); + + RigWriteCommBlock(PORT); + PORT->Retries = 1; + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID DummyPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + PORT->TXLen = PORT->FreqPtr->Cmd1Len; +/* + RigWriteCommBlock(PORT); + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 0; + PORT->AutoPoll = TRUE; +*/ + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + + return; + } + } + } + + return; +} + +VOID SwitchAntenna(struct RIGINFO * RIG, char Antenna) +{ + struct RIGPORTINFO * PORT; + char Ant[3]=" "; + + if (RIG == NULL) return; + + PORT = RIG->PORT; + + Ant[1] = Antenna; + + SetWindowText(RIG->hPTT, Ant); + + switch (Antenna) + { + case '1': + COMClearDTR(PORT->hDevice); + COMClearRTS(PORT->hDevice); + break; + case '2': + COMSetDTR(PORT->hDevice); + COMClearRTS(PORT->hDevice); + break; + case '3': + COMClearDTR(PORT->hDevice); + COMSetRTS(PORT->hDevice); + break; + case '4': + COMSetDTR(PORT->hDevice); + COMSetRTS(PORT->hDevice); + break; + } +} + +BOOL DecodeModePtr(char * Param, double * Dwell, double * Freq, char * Mode, + char * PMinLevel, char * PMaxLevel, char * PacketMode, + char * RPacketMode, char * Split, char * Data, char * WinmorMode, + char * Antenna, BOOL * Supress, char * Filter, char * Appl, + char * MemoryBank, int * MemoryNumber, char * ARDOPMode, char * VARAMode, int * BandWidth, int * Power) +{ + char * Context; + char * ptr; + + *Filter = '1'; // Defaults + *PMinLevel = 1; + *MemoryBank = 0; + *MemoryNumber = 0; + *Mode = 0; + *ARDOPMode = 0; + *VARAMode = 0; + *Power = 0; + + + ptr = strtok_s(Param, ",", &Context); + + if (ptr == NULL) + return FALSE; + + // "New" format - Dwell, Freq, Params. + + // Each param is a 2 char pair, separated by commas + + // An - Antenna + // Pn - Pactor + // Wn - Winmor + // Pn - Packet + // Fn - Filter + // Sx - Split + + // 7.0770/LSB,F1,A3,WN,P1,R1 + + *Dwell = atof(ptr); + + ptr = strtok_s(NULL, ",", &Context); + + if (ptr == NULL) + return FALSE; + + + // May be a frequency or a Memory Bank/Channel + + if (_memicmp(ptr, "Chan", 4) == 0) + { + if (strchr(ptr, '/')) // Bank/Chan + { + memcpy(MemoryBank, &ptr[4], 1); + *MemoryNumber = atoi(&ptr[6]); + } + else + *MemoryNumber = atoi(&ptr[4]); // Just Chan + + *Freq = 0.0; + } + else + *Freq = atof(ptr); + + ptr = strtok_s(NULL, ",", &Context); + + if (ptr == NULL || strlen(ptr) > 8) + return FALSE; + + // If channel, dont need mode + + if (*MemoryNumber == 0) + { + strcpy(Mode, ptr); + ptr = strtok_s(NULL, ",", &Context); + } + + while (ptr) + { + if (isdigit(ptr[0])) + *BandWidth = atoi(ptr); + + else if (memcmp(ptr, "APPL=", 5) == 0) + strcpy(Appl, ptr + 5); + + else if (memcmp(ptr, "POWER=", 6) == 0) + *Power = atoi(ptr + 6); + + else if (ptr[0] == 'A' && (ptr[1] == 'S' || ptr[1] == '0') && strlen(ptr) < 7) + strcpy(ARDOPMode, "S"); + + else if (ptr[0] == 'A' && strlen(ptr) > 2 && strlen(ptr) < 7) + strcpy(ARDOPMode, &ptr[1]); + + else if (ptr[0] == 'A' && strlen(ptr) == 2) + *Antenna = ptr[1]; + + else if (ptr[0] == 'F') + *Filter = ptr[1]; + + else if (ptr[0] == 'R') + *RPacketMode = ptr[1]; + + else if (ptr[0] == 'H') + *PacketMode = ptr[1]; + + else if (ptr[0] == 'N') + *PacketMode = ptr[1]; + + else if (ptr[0] == 'P') + { + *PMinLevel = ptr[1]; + *PMaxLevel = ptr[strlen(ptr) - 1]; + } + else if (ptr[0] == 'W') + { + *WinmorMode = ptr[1]; + if (*WinmorMode == '0') + *WinmorMode = 'X'; + else if (*WinmorMode == '1') + *WinmorMode = 'N'; + else if (*WinmorMode == '2') + *WinmorMode = 'W'; + } + + else if (ptr[0] == 'V') + { + *VARAMode = ptr[1]; + // W N S T (skip) or 0 (also Skip) + if (ptr[1] == '0') + *VARAMode = 'S'; + } + else if (ptr[0] == '+') + *Split = '+'; + else if (ptr[0] == '-') + *Split = '-'; + else if (ptr[0] == 'S') + *Split = 'S'; + else if (ptr[0] == 'D') + *Data = 1; + else if (ptr[0] == 'X') + *Supress = TRUE; + + ptr = strtok_s(NULL, ",", &Context); + } + return TRUE; +} + +VOID AddNMEAChecksum(char * msg) +{ + UCHAR CRC = 0; + + msg++; // Skip $ + + while (*(msg) != '*') + { + CRC ^= *(msg++); + } + + sprintf(++msg, "%02X\r\n", CRC); +} + +void DecodeRemote(struct RIGPORTINFO * PORT, char * ptr) +{ + // Param is IPHOST:PORT for use with WINMORCONTROL + + struct sockaddr_in * destaddr = (SOCKADDR_IN * )&PORT->remoteDest; + UCHAR param = 1; + u_long ioparam = 1; + + char * port = strlop(ptr, ':'); + + PORT->remoteSock = socket(AF_INET,SOCK_DGRAM,0); + + if (PORT->remoteSock == INVALID_SOCKET) + return; + + setsockopt (PORT->remoteSock, SOL_SOCKET, SO_BROADCAST, ¶m, 1); + + if (port == NULL) + return; + + ioctl (PORT->remoteSock, FIONBIO, &ioparam); + + destaddr->sin_family = AF_INET; + destaddr->sin_addr.s_addr = inet_addr(ptr); + destaddr->sin_port = htons(atoi(port)); + + if (destaddr->sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname(ptr); + + if (!HostEnt) + return; // Resolve failed + + memcpy(&destaddr->sin_addr.s_addr,HostEnt->h_addr,4); + } + + return; +} + + +char * CM108Device = NULL; + +void DecodeCM108(int Port, char * ptr) +{ + // Called if Device Name or PTT = Param is CM108 + +#ifdef WIN32 + + // Next Param is VID and PID - 0xd8c:0x8 or Full device name + // On Windows device name is very long and difficult to find, so + // easier to use VID/PID, but allow device in case more than one needed + + char * next; + int32_t VID = 0, PID = 0; + char product[256]; + char sernum[256] = "NULL"; + + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + if (strlen(ptr) > 16) + CM108Device = _strdup(ptr); + else + { + VID = strtol(ptr, &next, 0); + if (next) + PID = strtol(++next, &next, 0); + + // Look for Device + + devs = hid_enumerate(0,0); // so we list devices(USHORT)VID, (USHORT)PID); + cur_dev = devs; + while (cur_dev) + { + wcstombs(product, cur_dev->product_string, 255); + if (cur_dev->serial_number) + wcstombs(sernum, cur_dev->serial_number, 255); + + if (product) + Debugprintf("HID Device %s VID %X PID %X Ser %s %s", product, cur_dev->vendor_id, cur_dev->product_id, sernum, cur_dev->path); + else + Debugprintf("HID Device %s VID %X PID %X Ser %s %s", "Missing Product", cur_dev->vendor_id, cur_dev->product_id, sernum, cur_dev->path); + + if (cur_dev->vendor_id == VID && cur_dev->product_id == PID) + path_to_open = cur_dev->path; + + cur_dev = cur_dev->next; + } + + if (path_to_open) + { + handle = hid_open_path(path_to_open); + + if (handle) + { + hid_close(handle); + CM108Device = _strdup(path_to_open); + } + else + { + char msg[128]; + sprintf(msg,"Port %d Unable to open CM108 device %x %x", Port, VID, PID); + WritetoConsole(msg); + } + } + hid_free_enumeration(devs); + } +#else + + // Linux - Next Param HID Device, eg /dev/hidraw0 + + CM108Device = _strdup(ptr); +#endif +} + + + +// Called by Port Driver .dll to add/update rig info + +// RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + + +struct RIGINFO * RigConfig(struct TNCINFO * TNC, char * buf, int Port) +{ + int i; + char * ptr; + char * COMPort = NULL; + char * RigName; + int RigAddr; + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + struct ScanEntry ** FreqPtr; + char * CmdPtr; + char * Context; + struct TimeScan * SaveBand; + char PTTRigName[] = "PTT"; + double ScanFreq; + double Dwell; + char MemoryBank; // For Memory Scanning + int MemoryNumber; + BOOL RIG_DEBUG = FALSE; + + BOOL PTTControlsInputMUX = FALSE; + BOOL DataPTT = FALSE; + int DataPTTOffMode = 1; // ACC + int ICF8101Mode = 8; // USB (as in upper sideband) + + char onString[256] = ""; + char offString[256] = ""; + int onLen = 0; + int offLen = 0; + + CM108Device = NULL; + Debugprintf("Processing RIG line %s", buf); + + // Starts RADIO Interlockgroup + + ptr = strtok_s(&buf[5], " \t\n\r", &Context); // Skip Interlock + if (ptr == NULL) return FALSE; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) return FALSE; + + if (_memicmp(ptr, "DEBUG", 5) == 0) + { + ptr = strtok_s(NULL, " \t\n\r", &Context); + RIG_DEBUG = TRUE; + } + + if (_memicmp(ptr, "AUTH", 4) == 0) + { + ptr = strtok_s(NULL, " \t\n\r", &Context); + if (ptr == NULL) return FALSE; + if (strlen(ptr) > 100) return FALSE; + + strcpy(AuthPassword, ptr); + ptr = strtok_s(NULL, " \t\n\r", &Context); + } + + if (ptr == NULL || ptr[0] == 0) + return FALSE; + + if (_memicmp(ptr, "DUMMY", 5) == 0) + { + // Dummy to allow PTC application scanning + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + PORT->PortType = DUMMY; + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + RIG->RIGOK = TRUE; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + goto CheckScan; + } + + if (_memicmp(ptr, "FLRIG", 5) == 0) + { + // Use FLRIG + + // Need parameter - Host:Port + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL || strlen(ptr) > 79) return FALSE; + + // Have a parameter to define port. Will decode it later + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + PORT->PortType = FLRIG; + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + RIG->RIGOK = TRUE; + RIG->PORT = PORT; + + strcpy(PORT->IOBASE, ptr); + strcpy(RIG->RigName, "FLRIG"); + + // Decode host + + DecodeHAMLIBAddr(PORT, ptr); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + // look for other config statementes and scan params + + goto CheckOtherParams; + } + + if (_memicmp(ptr, "HAMLIB", 5) == 0) + { + // Use rigctld + + // Need parameter - Host:Port + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL || strlen(ptr) > 79) return FALSE; + + // Have a parameter to define port. Will decode it later + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + PORT->PortType = HAMLIB; + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + RIG->RIGOK = TRUE; + RIG->PORT = PORT; + + strcpy(PORT->IOBASE, ptr); + strcpy(RIG->RigName, "HAMLIB"); + + // Decode host + + DecodeHAMLIBAddr(PORT, ptr); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + // look for scan params + + goto CheckOtherParams; + } + + if (_memicmp(ptr, "rtludp", 5) == 0) + { + // rtl_fm with udp freq control + + // Need parameter - Host:Port + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL || strlen(ptr) > 79) return FALSE; + + // Have a parameter to define port. Will decode it later + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + PORT->PortType = RTLUDP; + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + RIG->RIGOK = TRUE; + RIG->PORT = PORT; + + strcpy(PORT->IOBASE, ptr); + strcpy(RIG->RigName, "RTLUDP"); + + // Decode host + + DecodeHAMLIBAddr(PORT, ptr); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + // look for scan params + + goto CheckOtherParams; + } + +// ---- G7TAJ ---- + + if (_memicmp(ptr, "sdrangel", 5) == 0) + { + // each instance (ip addr/port) of sdrangle can have one or more sampling devices (eg rltsdr) each with one ot + // more channels (eg ssb demod, ssb mod). each set of sampling device = channel(s) is a device set. + + // We poll all devices/channels at once. we one PORT record plus a RIG record for each channel + + // Need parameters - Host:Port device channel. Device and Channel will default to zero + + int device = 0, channel = 0; + char * Name; + char * nptr1; + char * nptr2; + + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL || strlen(ptr) > 79) return FALSE; + + Name = strtok_s(NULL, " \t\n\r", &Context); + nptr1 = strtok_s(NULL, " \t\n\r", &Context); + nptr2 = strtok_s(NULL, " \t\n\r", &Context); + + if (nptr1 == 0 || nptr2 == 0 || Name == NULL || strlen(Name) > 9) + return FALSE; + + device = atoi(nptr1); + channel = atoi(nptr2); + + // Have a parameter to define port. Will decode it later + + // See if already defined. PORT->IOBASE has Host:Port + + for (i = 0; i < NumberofPorts; i++) + { + PORT = PORTInfo[i]; + + if (strcmp(PORT->IOBASE, ptr) == 0) + goto AngelRigFound; + } + + // New Port + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + PORT->PortType = SDRANGEL; + PORT->ConfiguredRigs = 0; + strcpy(PORT->IOBASE, ptr); + + // Decode host + + DecodeHAMLIBAddr(PORT, ptr); + + +AngelRigFound: + + RIG = &PORT->Rigs[PORT->ConfiguredRigs++]; + RIG->RIGOK = TRUE; + RIG->PORT = PORT; + RIG->RigAddr = device; + RIG->Channel = channel; + + strcpy(RIG->RigName, Name); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + // look for scan params + + goto CheckOtherParams; + } +// ---- G7TAJ ---- + + if ((_memicmp(ptr, "VCOM", 4) == 0) && TNC->Hardware == H_SCS) // Using Radio Port on PTC + COMPort = 0; + else if (_memicmp(ptr, "PTCPORT", 7) == 0) + COMPort = 0; + else + COMPort = ptr; + + // See if port is already defined. We may be adding another radio (ICOM or SDRRADIO only) or updating an existing one + + // Unless CM108 - they must be on separate Ports + + if (COMPort && _stricmp("CM108", COMPort) != 0) + { + for (i = 0; i < NumberofPorts; i++) + { + PORT = PORTInfo[i]; + + if (COMPort) + if (strcmp(PORT->IOBASE, COMPort) == 0) + goto PortFound; + + if (COMPort == 0) + if (PORT->IOBASE == COMPort) + goto PortFound; + } + } + + // Allocate a new one + + PORT = PORTInfo[NumberofPorts++] = zalloc(sizeof(struct RIGPORTINFO)); + + if (COMPort) + strcpy(PORT->IOBASE, COMPort); + +PortFound: + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) return (FALSE); + + if (_stricmp(PORT->IOBASE, "RAWHID") == 0) // HID Addr instead of Speed + { + DecodeCM108(Port, ptr); + if (CM108Device) + PORT->HIDDevice = CM108Device; + else + PORT->HIDDevice = _strdup ("MissingHID"); + + CM108Device = 0; + } + + if ( _stricmp(PORT->IOBASE, "CM108") == 0) // HID Addr instead of Speed + { + DecodeCM108(Port, ptr); + } + + if (_stricmp(PORT->IOBASE, "REMOTE") == 0) // IP Addr/Port + DecodeRemote(PORT, ptr); + else + PORT->SPEED = atoi(ptr); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) return (FALSE); + + if (_memicmp(ptr, "PTTCOM", 6) == 0 || _memicmp(ptr, "PTT=", 4) == 0) + { + if (_stricmp(ptr, "PTT=CM108") == 0) + { + // PTT on CM108 GPIO + + ptr = strtok_s(NULL, " \t\n\r", &Context); + if (ptr == NULL) return (FALSE); + + DecodeCM108(Port, ptr); + } + else + { + strcpy(PORT->PTTIOBASE, ptr); + } + ptr = strtok_s(NULL, " \t\n\r", &Context); + if (ptr == NULL) return (FALSE); + + } + + // if (strcmp(ptr, "ICOM") == 0 || strcmp(ptr, "YAESU") == 0 + // || strcmp(ptr, "KENWOOD") == 0 || strcmp(ptr, "PTTONLY") == 0 || strcmp(ptr, "ANTENNA") == 0) + + // RADIO IC706 4E 5 14.103/U1 14.112/u1 18.1/U1 10.12/l1 + // Read RADIO Lines + + _strupr(ptr); + + + if (strcmp(ptr, "ICOM") == 0) + PORT->PortType = ICOM; + else if (strcmp(ptr, "YAESU") == 0) + PORT->PortType = YAESU; + else if (strcmp(ptr, "KENWOOD") == 0) + PORT->PortType = KENWOOD; + else if (strcmp(ptr, "SDRRADIO") == 0) + PORT->PortType = SDRRADIO; // Varient of KENWOOD that supports multiple devices on one serial port + else if (strcmp(ptr, "FLEX") == 0) + PORT->PortType = FLEX; + else if (strcmp(ptr, "NMEA") == 0) + PORT->PortType = NMEA; + else if (strcmp(ptr, "PTTONLY") == 0) + PORT->PortType = PTT; + else if (strcmp(ptr, "ANTENNA") == 0) + PORT->PortType = ANT; + else + return FALSE; + + Debugprintf("Port type = %d", PORT->PortType); + + _strupr(Context); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr && memcmp(ptr, "HAMLIB=", 7) == 0) + { + // HAMLIB Emulator - param is port to listen on + + if (PORT->PortType == PTT) + { + RIG = &PORT->Rigs[PORT->ConfiguredRigs++]; + strcpy(RIG->RigName, PTTRigName); + + RIG->HAMLIBPORT = atoi(&ptr[7]); + ptr = strtok_s(NULL, " \t\n\r", &Context); + + } + } + + if (ptr && strcmp(ptr, "PTTMUX") == 0) + { + if (PORT->PortType == PTT) + { + RIG = &PORT->Rigs[PORT->ConfiguredRigs++]; + strcpy(RIG->RigName, PTTRigName); + goto domux; // PTTONLY with PTTMUX + } + } + + + if (ptr == NULL) + { + if (PORT->PortType == PTT) + ptr = PTTRigName; + else + return FALSE; + } + + if (strlen(ptr) > 9) return FALSE; + + RigName = ptr; + + Debugprintf("Rigname = *%s*", RigName); + + // FT100 seems to be different to most other YAESU types + + if (strcmp(RigName, "FT100") == 0 && PORT->PortType == YAESU) + { + PORT->PortType = FT100; + } + + // FT990 seems to be different to most other YAESU types + + if (strcmp(RigName, "FT990") == 0 && PORT->PortType == YAESU) + { + PORT->PortType = FT990; + } + + // FT1000 seems to be different to most other YAESU types + + if (strstr(RigName, "FT1000") && PORT->PortType == YAESU) + { + PORT->PortType = FT1000; + + // Subtypes need different code. D and no suffix are same + + if (strstr(RigName, "FT1000MP")) + PORT->YaesuVariant = FT1000MP; + else + PORT->YaesuVariant = FT1000D; + } + + // FT2000 seems to be different to most other YAESU types + + if (strcmp(RigName, "FT2000") == 0 && PORT->PortType == YAESU) + { + PORT->PortType = FT2000; + } + + // FTDX10 seems to be different to most other YAESU types + + if (strcmp(RigName, "FTDX10") == 0 && PORT->PortType == YAESU) + { + PORT->PortType = FTDX10; + } + + // FT991A seems to be different to most other YAESU types + + if (strcmp(RigName, "FT991A") == 0 && PORT->PortType == YAESU) + { + PORT->PortType = FT991A; + } + + + // If ICOM or SDRRADIO, we may be adding a new Rig + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (PORT->PortType == ICOM || PORT->PortType == NMEA || PORT->PortType == SDRRADIO) + { + if (ptr == NULL) return (FALSE); + + if (PORT->PortType == SDRRADIO) + RigAddr = ptr[0]; + else + sscanf(ptr, "%x", &RigAddr); + + // See if already defined + + for (i = 0; i < PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + if (RIG->RigAddr == RigAddr) + goto RigFound; + } + + // Allocate a new one + + RIG = &PORT->Rigs[PORT->ConfiguredRigs++]; + RIG->RigAddr = RigAddr; + +RigFound: + + ptr = strtok_s(NULL, " \t\n\r", &Context); + // if (ptr == NULL) return (FALSE); + } + else + { + // Only allows one RIG + + PORT->ConfiguredRigs = 1; + RIG = &PORT->Rigs[0]; + } + + RIG->RIG_DEBUG = RIG_DEBUG; + RIG->PORT = PORT; + + strcpy(RIG->RigName, RigName); + + RIG->TSMenu = 63; // Default to TS590S + + // IC735 uses shorter freq message + + if (strcmp(RigName, "IC735") == 0 && PORT->PortType == ICOM) + RIG->IC735 = TRUE; + + // IC-F8101 uses a different set of commands + + if (strstr(RigName, "F8101") && PORT->PortType == ICOM) + RIG->ICF8101 = TRUE; + + if (PORT->PortType == KENWOOD) + { + RIG->TSMenu = 63; // Default to TS590S + + if (strcmp(RigName, "TS590SG") == 0) + RIG->TSMenu = 69; + } + + if (PORT->PortType == FT991A) + RIG->TSMenu = 72; //Menu for Data/USB siwtching + +domux: + + RIG->CM108Device = CM108Device; + +CheckOtherParams: + + while (ptr) + { + if (strcmp(ptr, "PTTMUX") == 0) + { + // Ports whose RTS/DTR will be converted to CAT commands (for IC7100/IC7200 etc) + + int PIndex = 0; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + while (memcmp(ptr, "COM", 3) == 0) + { + char * tncport = strlop(ptr, '/'); + + strcpy(RIG->PTTCATPort[PIndex], &ptr[3]); + + if (tncport) + RIG->PTTCATTNC[PIndex] = TNCInfo[atoi(tncport)]; + + if (PIndex < 3) + PIndex++; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) + break; + } + if (ptr == NULL) + break; + else + continue; + } + else if (strcmp(ptr, "PTT_SETS_INPUT") == 0) + { + // Send Select Soundcard as mod source with PTT commands + + PTTControlsInputMUX = TRUE; + + // See if following param is an PTT Off Mode + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) + break; + + if (strcmp(ptr, "MIC") == 0) + DataPTTOffMode = 0; + else if (strcmp(ptr, "AUX") == 0) + DataPTTOffMode = 1; + else if (strcmp(ptr, "MICAUX") == 0) + DataPTTOffMode = 2; + else if (RIG->ICF8101 && strcmp(ptr, "LSB") == 0) + ICF8101Mode = 0x7; + else if (RIG->ICF8101 && strcmp(ptr, "USB") == 0) + ICF8101Mode = 0x8; + else if (RIG->ICF8101 && strcmp(ptr, "LSBD1") == 0) + ICF8101Mode = 0x18; + else if (RIG->ICF8101 && strcmp(ptr, "USBD1") == 0) + ICF8101Mode = 0x19; + else if (RIG->ICF8101 && strcmp(ptr, "LSBD2") == 0) + ICF8101Mode = 0x20; + else if (RIG->ICF8101 && strcmp(ptr, "USBD2") == 0) + ICF8101Mode = 0x21; + else if (RIG->ICF8101 && strcmp(ptr, "LSBD3") == 0) + ICF8101Mode = 0x22; + else if (RIG->ICF8101 && strcmp(ptr, "USBD3") == 0) + ICF8101Mode = 0x23; + else + continue; + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + continue; + + } + else if (strcmp(ptr, "PTT_SETS_FREQ") == 0) + { + // Send Select Soundcard as mod source with PTT commands + + RIG->PTTSetsFreq = TRUE; + ptr = strtok_s(NULL, " \t\n\r", &Context); + + continue; + + } + + else if (strcmp(ptr, "DATAPTT") == 0) + { + // Send Select Soundcard as mod source with PTT commands + + DataPTT = TRUE; + } + + else if (memcmp(ptr, "PTTONHEX=", 9) == 0) + { + // Hex String to use for PTT on + + char * ptr1 = ptr; + char * ptr2 = onString ; + int i, j, len; + + ptr1 += 9; + onLen = len = strlen(ptr1) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr1++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) + break; + } + + else if (memcmp(ptr, "PTTOFFHEX=", 10) == 0) + { + // Hex String to use for PTT off + + char * ptr2 = offString ; + int i, j, len; + + ptr += 10; + offLen = len = strlen(ptr) / 2; + + if (len < 240) + { + while ((len--) > 0) + { + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + j = i << 4; + + i = *(ptr++); + i -= '0'; + if (i > 9) + i -= 7; + + *(ptr2++) = j | i; + } + } + ptr = strtok_s(NULL, " \t\n\r", &Context); + + if (ptr == NULL) + break; + + } + + else if (memcmp(ptr, "HAMLIB=", 7) == 0) + { + // HAMLIB Emulator - param is port to listen on + + RIG->HAMLIBPORT = atoi(&ptr[7]); + } + + else if (memcmp(ptr, "TXOFFSET", 8) == 0) + { + RIG->txOffset = strtoll(&ptr[9], NULL, 10); + } + + else if (memcmp(ptr, "RXOFFSET", 8) == 0) + { + RIG->rxOffset = strtoll(&ptr[9], NULL, 10); + } + + else if (memcmp(ptr, "PTTOFFSET", 9) == 0) + { + RIG->pttOffset = strtoll(&ptr[10], NULL, 10); + } + + else if (memcmp(ptr, "RXERROR", 7) == 0) + { + RIG->rxError = atoi(&ptr[8]); + } + + else if (memcmp(ptr, "TXERROR", 7) == 0) + { + RIG->txError = atoi(&ptr[8]); + } + + else if (memcmp(ptr, "REPORTFREQS", 11) == 0) + { + RIG->reportFreqs = _strdup(&ptr[12]); + } + + else if (memcmp(ptr, "DEFAULTFREQ", 11) == 0) + { + RIG->defaultFreq = atoi(&ptr[12]); + } + + else if (atoi(ptr) || ptr[2] == ':') + break; // Not scan freq oe timeband, so see if another param + + ptr = strtok_s(NULL, " \t\n\r", &Context); + } + + if (PORT->PortType == PTT || PORT->PortType == ANT) + return RIG; + + // Set up PTT and Poll Strings + + if (PORT->PortType == ICOM) + { + char * Poll; + Poll = &RIG->PTTOn[0]; + + if (onLen && offLen) + { + memcpy(RIG->PTTOn, onString, onLen); + RIG->PTTOnLen = onLen; + memcpy(RIG->PTTOff, offString, offLen); + RIG->PTTOffLen = offLen; + } + + else if (RIG->ICF8101) + { + // Need to send ACC PTT command (1A 37 0002), not normal ICOM IC 00 + + if (PTTControlsInputMUX) + { + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1a; + *(Poll++) = 0x05; + *(Poll++) = ICF8101Mode; + *(Poll++) = 0x03; // USB Data Mode Source + *(Poll++) = 0x00; + *(Poll++) = 0x01; // Soundcard + *(Poll++) = 0xFD; + } + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1A; + *(Poll++) = 0x37; // Send/read the TX status + *(Poll++) = 0x00; + *(Poll++) = 0x02; // ACC PTT + *(Poll++) = 0xFD; + + RIG->PTTOnLen = (int)(Poll - &RIG->PTTOn[0]); + + Poll = &RIG->PTTOff[0]; + + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1A; + *(Poll++) = 0x37; // Send/read the TX status + *(Poll++) = 0x00; + *(Poll++) = 0x00; // RX + *(Poll++) = 0xFD; + + if (PTTControlsInputMUX) + { + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1a; + + *(Poll++) = 0x05; + *(Poll++) = ICF8101Mode; + *(Poll++) = 0x03; // USB Data Mode Source + *(Poll++) = 0x00; + *(Poll++) = 0x02; // ACC + *(Poll++) = 0xFD; + } + RIG->PTTOffLen = (int)(Poll - &RIG->PTTOff[0]); + } + else + { + if (PTTControlsInputMUX) + { + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1a; + + if (strcmp(RIG->RigName, "IC7100") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x91; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7200") == 0) + { + *(Poll++) = 0x03; + *(Poll++) = 0x24; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7300") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x67; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7600") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x31; // Data1 Mode Source + } + else if (strcmp(RIG->RigName, "IC7610") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x92; // Data1 Mode Source + } + else if (strcmp(RIG->RigName, "IC7410") == 0) + { + *(Poll++) = 0x03; + *(Poll++) = 0x39; // Data Mode Source + } + + *(Poll++) = 0x03; // USB Soundcard + *(Poll++) = 0xFD; + } + + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1C; // RIG STATE + *(Poll++) = 0x00; // PTT + *(Poll++) = 1; // ON + *(Poll++) = 0xFD; + + RIG->PTTOnLen = (int)(Poll - &RIG->PTTOn[0]); + + Poll = &RIG->PTTOff[0]; + + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1C; // RIG STATE + *(Poll++) = 0x00; // PTT + *(Poll++) = 0; // OFF + *(Poll++) = 0xFD; + + if (PTTControlsInputMUX) + { + *(Poll++) = 0xFE; + *(Poll++) = 0xFE; + *(Poll++) = RIG->RigAddr; + *(Poll++) = 0xE0; + *(Poll++) = 0x1a; + + if (strcmp(RIG->RigName, "IC7100") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x91; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7200") == 0) + { + *(Poll++) = 0x03; + *(Poll++) = 0x24; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7300") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x67; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7410") == 0) + { + *(Poll++) = 0x03; + *(Poll++) = 0x39; // Data Mode Source + } + else if (strcmp(RIG->RigName, "IC7600") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x31; // Data1 Mode Source + } + else if (strcmp(RIG->RigName, "IC7610") == 0) + { + *(Poll++) = 0x05; + *(Poll++) = 0x00; + *(Poll++) = 0x92; // Data1 Mode Source + } + + *(Poll++) = DataPTTOffMode; + *(Poll++) = 0xFD; + } + RIG->PTTOffLen = (int)(Poll - &RIG->PTTOff[0]); + } + } + else if (PORT->PortType == KENWOOD) + { + RIG->PollLen = 6; + strcpy(RIG->Poll, "FA;MD;"); + + if (PTTControlsInputMUX) + { + sprintf(RIG->PTTOn, "EX%03d00001;TX1;", RIG->TSMenu); // Select USB before PTT + sprintf(RIG->PTTOff, "RX;EX%03d00000;", RIG->TSMenu); // Select ACC after dropping PTT + } + else + { + strcpy(RIG->PTTOff, "RX;"); + + if (DataPTT) + strcpy(RIG->PTTOn, "TX1;"); + else + strcpy(RIG->PTTOn, "TX;"); + } + + RIG->PTTOnLen = (int)strlen(RIG->PTTOn); + RIG->PTTOffLen = (int)strlen(RIG->PTTOff); + + } + else if (PORT->PortType == SDRRADIO) + { + RIG->PollLen = sprintf(RIG->Poll, "F%c;MD;", RIG->RigAddr); + +/* if (PTTControlsInputMUX) + { + sprintf(RIG->PTTOn, "EX%03d00001;TX1;", RIG->TSMenu); // Select USB before PTT + sprintf(RIG->PTTOff, "RX;EX%03d00000;", RIG->TSMenu); // Select ACC after dropping PTT + } + else + { + strcpy(RIG->PTTOff, "RX;"); + + if (DataPTT) + strcpy(RIG->PTTOn, "TX1;"); + else + strcpy(RIG->PTTOn, "TX;"); + } +*/ + RIG->PTTOnLen = (int)strlen(RIG->PTTOn); + RIG->PTTOffLen = (int)strlen(RIG->PTTOff); + + } else if (PORT->PortType == FLEX) + { + RIG->PollLen = 10; + strcpy(RIG->Poll, "ZZFA;ZZMD;"); + + strcpy(RIG->PTTOn, "ZZTX1;"); + RIG->PTTOnLen = 6; + strcpy(RIG->PTTOff, "ZZTX0;"); + RIG->PTTOffLen = 6; + } + else if (PORT->PortType == FT2000) + { + RIG->PollLen = 6; + strcpy(RIG->Poll, "FA;MD;"); + + strcpy(RIG->PTTOn, "TX1;"); + RIG->PTTOnLen = 4; + strcpy(RIG->PTTOff, "TX0;"); + RIG->PTTOffLen = 4; + } + else if (PORT->PortType == FT991A || PORT->PortType == FTDX10) + { + RIG->PollLen = 7; + strcpy(RIG->Poll, "FA;MD0;"); + + if (PTTControlsInputMUX) + { + RIG->PTTOnLen = sprintf(RIG->PTTOn, "EX0721;TX1;"); // Select USB before PTT + RIG->PTTOffLen = sprintf(RIG->PTTOff, "TX0;EX0720;"); // Select DATA after dropping PTT + } + else + { + strcpy(RIG->PTTOn, "TX1;"); + RIG->PTTOnLen = 4; + strcpy(RIG->PTTOff, "TX0;"); + RIG->PTTOffLen = 4; + } + } + else if (PORT->PortType == NMEA) + { + int Len; + + i = sprintf(RIG->Poll, "$PICOA,90,%02x,RXF*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->Poll); + Len = i; + i = sprintf(RIG->Poll + Len, "$PICOA,90,%02x,MODE*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->Poll + Len); + RIG->PollLen = Len + i; + + i = sprintf(RIG->PTTOn, "$PICOA,90,%02x,TRX,TX*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->PTTOn); + RIG->PTTOnLen = i; + + i = sprintf(RIG->PTTOff, "$PICOA,90,%02x,TRX,RX*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->PTTOff); + RIG->PTTOffLen = i; + } + + if (ptr == NULL) return RIG; // No Scanning, just Interactive control + + if (strchr(ptr, ',') == 0 && strchr(ptr, ':') == 0) // Old Format + { + ScanFreq = atof(ptr); + +#pragma warning(push) +#pragma warning(disable : 4244) + + RIG->ScanFreq = ScanFreq * 10; + +#pragma warning(push) + + ptr = strtok_s(NULL, " \t\n\r", &Context); + } + + // Frequency List + +CheckScan: + + if (ptr) + if (ptr[0] == ';' || ptr[0] == '#') + ptr = NULL; + + if (ptr != NULL) + { + // Create Default Timeband + + struct TimeScan * Band; + + RIG->TimeBands = zalloc(sizeof(void *)); + + Band = AllocateTimeRec(RIG); + SaveBand = Band; + + Band->Start = 0; + Band->End = 84540; //23:59 + FreqPtr = Band->Scanlist = RIG->FreqPtr = malloc(1000); + memset(FreqPtr, 0, 1000); + } + + while(ptr) + { + int ModeNo; + BOOL Supress; + double Freq = 0.0; + int FreqInt = 0; + char FreqString[80]=""; + char * Modeptr = NULL; + char Split, Data, PacketMode, RPacketMode, PMinLevel, PMaxLevel, Filter; + char Mode[10] = ""; + char WinmorMode, Antenna; + char ARDOPMode[6] = ""; + char VARAMode[6] = ""; + char Appl[13]; + char * ApplCall; + int BandWidth; + int Power; + + if (ptr[0] == ';' || ptr[0] == '#') + break; + + Filter = PMinLevel = PMaxLevel = PacketMode = RPacketMode = Split = + Data = WinmorMode = Antenna = ModeNo = Supress = + MemoryBank = MemoryNumber = BandWidth = 0; + + Appl[0] = 0; + ARDOPMode[0] = 0; + VARAMode[0] = 0; + Dwell = 0.0; + + while (ptr && strchr(ptr, ':')) + { + // New TimeBand + + struct TimeScan * Band; + + Band = AllocateTimeRec(RIG); + + *FreqPtr = (struct ScanEntry *)0; // Terminate Last Band + + Band->Start = (atoi(ptr) * 3600) + (atoi(&ptr[3]) * 60); + Band->End = 84540; //23:59 + SaveBand->End = Band->Start - 60; + + SaveBand = Band; + + FreqPtr = Band->Scanlist = RIG->FreqPtr = malloc(1000); + memset(FreqPtr, 0, 1000); + + ptr = strtok_s(NULL, " \t\n\r", &Context); + } + + if (ptr == 0) + break; + + if (strchr(ptr, ',')) // New Format + { + DecodeModePtr(ptr, &Dwell, &Freq, Mode, &PMinLevel, &PMaxLevel, &PacketMode, + &RPacketMode, &Split, &Data, &WinmorMode, &Antenna, &Supress, &Filter, &Appl[0], + &MemoryBank, &MemoryNumber, ARDOPMode, VARAMode, &BandWidth, &Power); + } + else + { + Modeptr = strchr(ptr, '/'); + + if (Modeptr) + *Modeptr++ = 0; + + Freq = atof(ptr); + + if (Modeptr) + { + // Mode can include 1/2/3 for Icom Filers. W/N for Winmor/Pactor Bandwidth, and +/-/S for Repeater Shift (S = Simplex) + // First is always Mode + // First char is Mode (USB, LSB etc) + + Mode[0] = Modeptr[0]; + Filter = Modeptr[1]; + + if (strchr(&Modeptr[1], '+')) + Split = '+'; + else if (strchr(&Modeptr[1], '-')) + Split = '-'; + else if (strchr(&Modeptr[1], 'S')) + Split = 'S'; + else if (strchr(&Modeptr[1], 'D')) + Data = 1; + + if (strchr(&Modeptr[1], 'W')) + { + WinmorMode = 'W'; + PMaxLevel = '3'; + PMinLevel = '1'; + } + else if (strchr(&Modeptr[1], 'N')) + { + WinmorMode = 'N'; + PMaxLevel = '2'; + PMinLevel = '1'; + } + + if (strchr(&Modeptr[1], 'R')) // Robust Packet + RPacketMode = '2'; // R600 + else if (strchr(&Modeptr[1], 'H')) // HF Packet on Tracker + PacketMode = '1'; // 300 + + if (strchr(&Modeptr[1], 'X')) // Dont Report to WL2K + Supress = 1; + + if (strstr(&Modeptr[1], "A1")) + Antenna = '1'; + else if (strstr(&Modeptr[1], "A2")) + Antenna = '2'; + else if (strstr(&Modeptr[1], "A3")) + Antenna = '3'; + else if (strstr(&Modeptr[1], "A4")) + Antenna = '4'; + else if (strstr(&Modeptr[1], "A5")) + Antenna = '5'; + else if (strstr(&Modeptr[1], "A6")) + Antenna = '6'; + } + } + + switch(PORT->PortType) + { + case ICOM: + + for (ModeNo = 0; ModeNo < 24; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(Modes[ModeNo], Mode) == 0) + break; + } + } + break; + + case YAESU: + + for (ModeNo = 0; ModeNo < 16; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (YaesuModes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(YaesuModes[ModeNo], Mode) == 0) + break; + } + } + break; + + case KENWOOD: + + for (ModeNo = 0; ModeNo < 8; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (KenwoodModes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(KenwoodModes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FLEX: + + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FLEXModes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FLEXModes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FT2000: + + if (Modeptr) + { + if (strstr(Modeptr, "PL")) + { + ModeNo = 8; + break; + } + if (strstr(Modeptr, "PU")) + { + ModeNo = 12; + break; + } + } + for (ModeNo = 0; ModeNo < 14; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT2000Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT2000Modes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FT991A: + +/* if (Modeptr) + { + if (strstr(Modeptr, "PL")) + { + ModeNo = 8; + break; + } + if (strstr(Modeptr, "PU")) + { + ModeNo = 12; + break; + } + } +*/ + for (ModeNo = 0; ModeNo < 15; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT991AModes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT991AModes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FTDX10: + + for (ModeNo = 0; ModeNo < 16; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FTDX10Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FTDX10Modes[ModeNo], Mode) == 0) + break; + } + } + break; + + + + case FT100: + + for (ModeNo = 0; ModeNo < 8; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT100Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT100Modes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FT990: + + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT990Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT990Modes[ModeNo], Mode) == 0) + break; + } + } + break; + + case FT1000: + + for (ModeNo = 0; ModeNo < 12; ModeNo++) + { + if (strlen(Mode) == 1) + { + if (FT1000Modes[ModeNo][0] == Mode[0]) + break; + } + else + { + if (_stricmp(FT1000Modes[ModeNo], Mode) == 0) + break; + } + } + break; + } + + Freq = Freq * 1000000.0; + + sprintf(FreqString, "%09.0f", Freq); + + FreqInt = Freq; + + FreqPtr[0] = malloc(sizeof(struct ScanEntry)); + memset(FreqPtr[0], 0, sizeof(struct ScanEntry)); + +#pragma warning(push) +#pragma warning(disable : 4244) + + if (Dwell == 0.0) + FreqPtr[0]->Dwell = ScanFreq * 10; + else + FreqPtr[0]->Dwell = Dwell * 10; + +#pragma warning(pop) + + FreqPtr[0]->Freq = Freq; + FreqPtr[0]->Bandwidth = WinmorMode; + FreqPtr[0]->RPacketMode = RPacketMode; + FreqPtr[0]->HFPacketMode = PacketMode; + FreqPtr[0]->PMaxLevel = PMaxLevel; + FreqPtr[0]->PMinLevel = PMinLevel; + FreqPtr[0]->Antenna = Antenna; + strcpy(FreqPtr[0]->ARDOPMode, ARDOPMode); + FreqPtr[0]->VARAMode = VARAMode[0]; + + strcpy(FreqPtr[0]->APPL, Appl); + + ApplCall = GetApplCallFromName(Appl); + + if (strcmp(Appl, "NODE") == 0) + { + memcpy(FreqPtr[0]->APPLCALL, TNC->NodeCall, 9); + strlop(FreqPtr[0]->APPLCALL, ' '); + } + else + { + if (ApplCall && ApplCall[0] > 32) + { + memcpy(FreqPtr[0]->APPLCALL, ApplCall, 9); + strlop(FreqPtr[0]->APPLCALL, ' '); + } + } + + CmdPtr = FreqPtr[0]->Cmd1 = malloc(100); + + if (PORT->PortType == ICOM) + { + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + if (RIG->ICF8101) + { + // Set Freq is 1A 35 and set Mode 1A 36 + + + *(CmdPtr++) = 0x1A; + *(CmdPtr++) = 0x35; // Set frequency command + + // Need to convert two chars to bcd digit + + *(CmdPtr++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(CmdPtr++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(CmdPtr++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(CmdPtr++) = (FreqString[2] - 48) | ((FreqString[1] - 48) << 4); + *(CmdPtr++) = (FreqString[0] - 48); + *(CmdPtr++) = 0xFD; + FreqPtr[0]->Cmd1Len = 12; + + CmdPtr = FreqPtr[0]->Cmd2 = malloc(10); + FreqPtr[0]->Cmd2Len = 9; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1A; + *(CmdPtr++) = 0x36; // Set mode command + *(CmdPtr++) = 0; + + if (ModeNo > 19) + *(CmdPtr++) = ModeNo + 12; + else if (ModeNo > 9) + *(CmdPtr++) = ModeNo + 6; + else + *(CmdPtr++) = ModeNo; + + *(CmdPtr++) = 0xFD; + } + else + { + if (MemoryNumber) + { + // Set Memory Channel instead of Freq, Mode, etc + + char ChanString[5]; + + // Send Set Memory, then Channel + + *(CmdPtr++) = 0x08; + *(CmdPtr++) = 0xFD; + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + + sprintf(ChanString, "%04d", MemoryNumber); + + *(CmdPtr++) = 0x08; + *(CmdPtr++) = (ChanString[1] - 48) | ((ChanString[0] - 48) << 4); + *(CmdPtr++) = (ChanString[3] - 48) | ((ChanString[2] - 48) << 4); + *(CmdPtr++) = 0xFD; + + FreqPtr[0]->Cmd1Len = 14; + + if (MemoryBank) + { + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x08; + *(CmdPtr++) = 0xA0; + *(CmdPtr++) = MemoryBank - 0x40; + *(CmdPtr++) = 0xFD; + + FreqPtr[0]->Cmd1Len += 8; + } + } + else + { + *(CmdPtr++) = 0x5; // Set frequency command + + // Need to convert two chars to bcd digit + + *(CmdPtr++) = (FreqString[8] - 48) | ((FreqString[7] - 48) << 4); + *(CmdPtr++) = (FreqString[6] - 48) | ((FreqString[5] - 48) << 4); + *(CmdPtr++) = (FreqString[4] - 48) | ((FreqString[3] - 48) << 4); + *(CmdPtr++) = (FreqString[2] - 48) | ((FreqString[1] - 48) << 4); + if (RIG->IC735) + { + *(CmdPtr++) = 0xFD; + FreqPtr[0]->Cmd1Len = 10; + } + else + { + *(CmdPtr++) = (FreqString[0] - 48); + *(CmdPtr++) = 0xFD; + FreqPtr[0]->Cmd1Len = 11; + } + + // Send Set VFO in case last chan was memory + + // *(CmdPtr++) = 0xFE; + // *(CmdPtr++) = 0xFE; + // *(CmdPtr++) = RIG->RigAddr; + // *(CmdPtr++) = 0xE0; + + // *(CmdPtr++) = 0x07; + // *(CmdPtr++) = 0xFD; + + // FreqPtr[0]->Cmd1Len = 17; + + if (Filter) + { + CmdPtr = FreqPtr[0]->Cmd2 = malloc(10); + FreqPtr[0]->Cmd2Len = 8; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x6; // Set Mode + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = Filter - '0'; //Filter + *(CmdPtr++) = 0xFD; + + if (Split) + { + CmdPtr = FreqPtr[0]->Cmd3 = malloc(10); + FreqPtr[0]->Cmd3Len = 7; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0xF; // Set Mode + if (Split == 'S') + *(CmdPtr++) = 0x10; + else + if (Split == '+') + *(CmdPtr++) = 0x12; + else + if (Split == '-') + *(CmdPtr++) = 0x11; + + *(CmdPtr++) = 0xFD; + } + else + { + if (Data) + { + CmdPtr = FreqPtr[0]->Cmd3 = malloc(10); + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x1a; + + + if ((strcmp(RIG->RigName, "IC7100") == 0) || + (strcmp(RIG->RigName, "IC7410") == 0) || + (strcmp(RIG->RigName, "IC7600") == 0) || + (strcmp(RIG->RigName, "IC7610") == 0) || + (strcmp(RIG->RigName, "IC7300") == 0)) + { + FreqPtr[0]->Cmd3Len = 9; + *(CmdPtr++) = 0x6; // Send/read DATA mode with filter set + *(CmdPtr++) = 0x1; // Data On + *(CmdPtr++) = Filter - '0'; //Filter + } + else if (strcmp(RIG->RigName, "IC7200") == 0) + { + FreqPtr[0]->Cmd3Len = 9; + *(CmdPtr++) = 0x4; // Send/read DATA mode with filter set + *(CmdPtr++) = 0x1; // Data On + *(CmdPtr++) = Filter - '0'; //Filter + } + else + { + FreqPtr[0]->Cmd3Len = 8; + *(CmdPtr++) = 0x6; // Set Data + *(CmdPtr++) = 0x1; //On + } + + *(CmdPtr++) = 0xFD; + } + } + } + + if (Antenna == '5' || Antenna == '6') + { + // Antenna select for 746 and maybe others + + // Could be going in cmd2 3 or 4 + + if (FreqPtr[0]->Cmd2 == NULL) + { + CmdPtr = FreqPtr[0]->Cmd2 = malloc(10); + FreqPtr[0]->Cmd2Len = 7; + } + else if (FreqPtr[0]->Cmd3 == NULL) + { + CmdPtr = FreqPtr[0]->Cmd3 = malloc(10); + FreqPtr[0]->Cmd3Len = 7; + } + else + { + CmdPtr = FreqPtr[0]->Cmd4 = malloc(10); + FreqPtr[0]->Cmd4Len = 7; + } + + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = 0xFE; + *(CmdPtr++) = RIG->RigAddr; + *(CmdPtr++) = 0xE0; + *(CmdPtr++) = 0x12; // Set Antenna + *(CmdPtr++) = Antenna - '5'; // 0 for A5 1 for A6 + *(CmdPtr++) = 0xFD; + } + } + } + } + else if (PORT->PortType == YAESU) + { + //Send Mode first - changing mode can change freq + + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 7; + + *(CmdPtr++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(CmdPtr++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = 1; + + // FT847 Needs a Poll Here. Set up anyway, but only send if 847 + + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 3; + + + } + else if (PORT->PortType == KENWOOD) + { + if (Power == 0) + { + if (Antenna == '5' || Antenna == '6') + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA00%s;MD%d;AN%c;FA;MD;", FreqString, ModeNo, Antenna - 4); + else + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA00%s;MD%d;FA;MD;", FreqString, ModeNo); + } + else + { + if (Antenna == '5' || Antenna == '6') + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA00%s;MD%d;AN%c;PC%03d;FA;MD;PC;", FreqString, ModeNo, Antenna - 4, Power); + else + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA00%s;MD%d;PC%03d;FA;MD;PC;", FreqString, ModeNo, Power); + } + } + else if (PORT->PortType == FLEX) + { + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "ZZFA00%s;ZZMD%02d;ZZFA;ZZMD;", FreqString, ModeNo); + } + else if (PORT->PortType == FT2000) + { + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA%s;MD0%X;FA;MD;", &FreqString[1], ModeNo); + } + else if (PORT->PortType == FT991A || PORT->PortType == FTDX10) + { + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "FA%s;MD0%X;FA;MD0;", &FreqString[0], ModeNo); + } + else if (PORT->PortType == FT100 || PORT->PortType == FT990 + || PORT->PortType == FT1000) + { + // Allow Mode = "LEAVE" to suppress mode change + + //Send Mode first - changing mode can change freq + + if (strcmp(Mode, "LEAVE") == 0) + { + // we can't easily change the string length, + // so just set freq twice + + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(CmdPtr++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(CmdPtr++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(CmdPtr++) = 10; + } + else + { + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = ModeNo; + *(CmdPtr++) = 12; + } + + *(CmdPtr++) = (FreqString[7] - 48) | ((FreqString[6] - 48) << 4); + *(CmdPtr++) = (FreqString[5] - 48) | ((FreqString[4] - 48) << 4); + *(CmdPtr++) = (FreqString[3] - 48) | ((FreqString[2] - 48) << 4); + *(CmdPtr++) = (FreqString[1] - 48) | ((FreqString[0] - 48) << 4); + *(CmdPtr++) = 10; + + // Send Get Status, as these types doesn't ack commands + + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + *(CmdPtr++) = 0; + + if (PORT->PortType == FT990 || PORT->YaesuVariant == FT1000D) + *(CmdPtr++) = 3; + else + *(CmdPtr++) = 2; // F100 or FT1000MP + + *(CmdPtr++) = 16; // Get Status + } + else if (PORT->PortType == NMEA) + { + int Len; + + i = sprintf(CmdPtr, "$PICOA,90,%02x,RXF,%.6f*xx\r\n", RIG->RigAddr, Freq/1000000.); + AddNMEAChecksum(CmdPtr); + Len = i; + i = sprintf(CmdPtr + Len, "$PICOA,90,%02x,TXF,%.6f*xx\r\n", RIG->RigAddr, Freq/1000000.); + AddNMEAChecksum(CmdPtr + Len); + Len += i; + i = sprintf(CmdPtr + Len, "$PICOA,90,%02x,MODE,%s*xx\r\n", RIG->RigAddr, Mode); + AddNMEAChecksum(CmdPtr + Len); + FreqPtr[0]->Cmd1Len = Len + i; + + i = sprintf(RIG->Poll, "$PICOA,90,%02x,RXF*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->Poll); + Len = i; + i = sprintf(RIG->Poll + Len, "$PICOA,90,%02x,MODE*xx\r\n", RIG->RigAddr); + AddNMEAChecksum(RIG->Poll + Len); + RIG->PollLen = Len + i; + } + else if (PORT->PortType == HAMLIB) + { + FreqPtr[0]->Cmd1Len = sprintf(CmdPtr, "F %s\n+f\nM %s %d\n+m\n", FreqString, Mode, BandWidth); + } + + else if (PORT->PortType == FLRIG) + { + sprintf(FreqPtr[0]->Cmd1Msg, "%.0f", Freq); + sprintf(FreqPtr[0]->Cmd2Msg, "%s", Mode); + sprintf(FreqPtr[0]->Cmd3Msg, "%d", BandWidth); + } + + else if (PORT->PortType == RTLUDP) + { + int FM = 0; + int AM = 1; + int USB = 2; + int LSB = 3; + + CmdPtr[0] = 0; + memcpy(&CmdPtr[1], &FreqInt, 4); + + CmdPtr[1] = FreqInt & 0xff; + CmdPtr[2] = (FreqInt >> 8) & 0xff; + CmdPtr[3] = (FreqInt >> 16) & 0xff; + CmdPtr[4] = (FreqInt >> 24) & 0xff; + + FreqPtr[0]->Cmd1Len = 5; + + if (Mode[0]) + { + CmdPtr[5] = 1; + FreqPtr[0]->Cmd1Len = 10; + + if (strcmp(Mode, "FM") == 0) + memcpy(&CmdPtr[6], &FM, 4); + else if (strcmp(Mode, "AM") == 0) + memcpy(&CmdPtr[6], &AM, 4); + else if (strcmp(Mode, "USB") == 0) + memcpy(&CmdPtr[6], &USB, 4); + else if (strcmp(Mode, "LSB") == 0) + memcpy(&CmdPtr[6], &LSB, 4); + } + + } + + FreqPtr++; + + RIG->ScanCounter = 20; + + ptr = strtok_s(NULL, " \t\n\r", &Context); // Next Freq + } + + + + if (RIG->NumberofBands) + { + CheckTimeBands(RIG); // Set initial FreqPtr; + PORT->FreqPtr = RIG->FreqPtr[0]; + } + + return RIG; +} + +VOID SetupScanInterLockGroups(struct RIGINFO *RIG) +{ + struct PORTCONTROL * PortRecord; + struct TNCINFO * TNC; + int port; + int Interlock = RIG->Interlock; + char PortString[128] = ""; + char TxPortString[128] = ""; + + // Find TNC ports in this Rig's scan group + + for (port = 1; port < MAXBPQPORTS; port++) + { + TNC = TNCInfo[port]; + + if (TNC == NULL) + continue; + + PortRecord = &TNC->PortRecord->PORTCONTROL; + + if (TNC->RXRadio == Interlock) + { + int p = PortRecord->PORTNUMBER; + RIG->BPQPort |= ((uint64_t)1 << p); + sprintf(PortString, "%s,%d", PortString, p); + TNC->RIG = RIG; + + if (RIG->PTTMode == 0 && TNC->PTTMode) + RIG->PTTMode = TNC->PTTMode; + } + if (TNC->TXRadio == Interlock && TNC->TXRadio != TNC->RXRadio) + { + int p = PortRecord->PORTNUMBER; + RIG->BPQPort |= ((uint64_t)1 << p); + sprintf(TxPortString, "%s,%d", TxPortString, p); + TNC->TXRIG = RIG; + + if (RIG->PTTMode == 0 && TNC->PTTMode) + RIG->PTTMode = TNC->PTTMode; + } + } + + if (RIG->PTTMode == 0 && (RIG->PTTCATPort[0] || RIG->HAMLIBPORT)) // PTT Mux Implies CAT + RIG->PTTMode = PTTCI_V; + + if (PortString[0] && TxPortString[0]) // Have both + sprintf(RIG->WEB_PORTS, "Rx: %s Tx: %s", &PortString[1], &TxPortString[1]); + else if (PortString[0]) + strcpy(RIG->WEB_PORTS, &PortString[1]); + else if (TxPortString[0]) + sprintf(RIG->WEB_PORTS, "Tx: %s", &TxPortString[1]); + + SetWindowText(RIG->hPORTS, RIG->WEB_PORTS); +} + +VOID SetupPortRIGPointers() +{ + struct TNCINFO * TNC; + int port; + + for (port = 1; port < MAXBPQPORTS; port++) + { + TNC = TNCInfo[port]; + + if (TNC == NULL) + continue; + + if (TNC->RIG == NULL) + TNC->RIG = &TNC->DummyRig; // Not using Rig control, so use Dummy + } +} + +#ifdef WIN32 + +VOID PTTCATThread(struct RIGINFO *RIG) +{ + DWORD dwLength = 0; + int Length, ret, i; + UCHAR * ptr1; + UCHAR * ptr2; + UCHAR c; + UCHAR Block[4][80]; + UCHAR CurrentState[4] = {0}; +#define RTS 2 +#define DTR 4 + HANDLE Event; + HANDLE Handle[4]; + DWORD EvtMask[4]; + OVERLAPPED Overlapped[4]; + char Port[32]; + int PIndex = 0; + int HIndex = 0; + int rc; + + EndPTTCATThread = FALSE; + + while (PIndex < 4 && RIG->PTTCATPort[PIndex][0]) + { + RIG->RealMux[HIndex] = 0; + + sprintf(Port, "\\\\.\\pipe\\BPQCOM%s", RIG->PTTCATPort[PIndex]); + + Handle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + + if (Handle[HIndex] == (HANDLE) -1) + { + int Err = GetLastError(); + char errmsg[256] = ""; + + // Only report if both BPQCOM and Real COM fail + + sprintf(errmsg, "PTTMUX port BPQCOM%s Open failed code %d - trying real com port", RIG->PTTCATPort[PIndex], Err); + + // See if real com port + + sprintf(Port, "\\\\.\\\\COM%s", RIG->PTTCATPort[PIndex]); + + Handle[HIndex] = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + RIG->RealMux[HIndex] = 1; + + if (Handle[HIndex] == (HANDLE) -1) + { + int Err = GetLastError(); + if (errmsg[0]) + { + Consoleprintf("%s", errmsg); + Consoleprintf("PTTMUX port COM%s Open failed code %d", RIG->PTTCATPort[PIndex], Err); + } + } + else + { + rc = SetCommMask(Handle[HIndex], EV_CTS | EV_DSR); // Request notifications + HIndex++; + } + } + else + HIndex++; + + PIndex++; + + } + + if (PIndex == 0) + return; // No ports + + Event = CreateEvent(NULL, TRUE, FALSE, NULL); + + for (i = 0; i < HIndex; i ++) + { + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + + if (RIG->RealMux[i]) + { + // Request Interface change notifications + + rc = WaitCommEvent(Handle[i], &EvtMask[i], &Overlapped[i]); + rc = GetLastError(); + + } + else + { + + // Prime a read on each handle + + ReadFile(Handle[i], Block[i], 80, &Length, &Overlapped[i]); + } + } + + while (EndPTTCATThread == FALSE) + { + +WaitAgain: + + ret = WaitForSingleObject(Event, 1000); + + if (ret == WAIT_TIMEOUT) + { + if (EndPTTCATThread) + { + for (i = 0; i < HIndex; i ++) + { + CancelIo(Handle[i]); + CloseHandle(Handle[i]); + Handle[i] = INVALID_HANDLE_VALUE; + } + CloseHandle(Event); + return; + } + goto WaitAgain; + } + + ResetEvent(Event); + + // See which request(s) have completed + + for (i = 0; i < HIndex; i ++) + { + ret = GetOverlappedResult(Handle[i], &Overlapped[i], &Length, FALSE); + + if (ret) + { + if (RIG->RealMux[i]) + { + // Request Interface change notifications + + DWORD Mask; + + GetCommModemStatus(Handle[i], &Mask); + + if (Mask & MS_CTS_ON) + Rig_PTTEx(RIG, TRUE, RIG->PTTCATTNC[i]); + else + Rig_PTTEx(RIG, FALSE, RIG->PTTCATTNC[i]); + + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + WaitCommEvent(Handle[i], &EvtMask[i], &Overlapped[i]); + + } + else + { + + ptr1 = Block[i]; + ptr2 = Block[i]; + + while (Length > 0) + { + c = *(ptr1++); + + Length--; + + if (c == 0xff) + { + c = *(ptr1++); + Length--; + + if (c == 0xff) // ff ff means ff + { + Length--; + } + else + { + // This is connection / RTS/DTR statua from other end + // Convert to CAT Command + + if (c == CurrentState[i]) + continue; + + if (c & RTS) + Rig_PTTEx(RIG, TRUE, RIG->PTTCATTNC[i]); + else + Rig_PTTEx(RIG, FALSE, RIG->PTTCATTNC[i]); + + CurrentState[i] = c; + continue; + } + } + } + + memset(&Overlapped[i], 0, sizeof(OVERLAPPED)); + Overlapped[i].hEvent = Event; + + ReadFile(Handle[i], Block[i], 80, &Length, &Overlapped[i]); + } + } + } + } + EndPTTCATThread = FALSE; +} + +/* + memset(&Overlapped, 0, sizeof(Overlapped)); + Overlapped.hEvent = Event; + ResetEvent(Event); + + ret = ReadFile(Handle, Block, 80, &Length, &Overlapped); + + if (ret == 0) + { + ret = GetLastError(); + + if (ret != ERROR_IO_PENDING) + { + if (ret == ERROR_BROKEN_PIPE || ret == ERROR_INVALID_HANDLE) + { + CloseHandle(Handle); + RIG->PTTCATHandles[0] = INVALID_HANDLE_VALUE; + return; + } + } + } +*/ +#endif + +// HAMLIB Support Code + +VOID HAMLIBPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on HAMLIB + char cmd[80]; + int len; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + return; + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + send(PORT->remoteSock, PORT->TXBuffer, PORT->TXLen, 0); + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 0; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + return; + } + } + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && (RIG->ScanStopped == 0) && RIG->NumberofBands) + return; // no point in reading freq if we are about to change it + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + len = sprintf(cmd, "+f\n+m\n"); + + send(PORT->remoteSock, cmd, len, 0); + + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + + +void HAMLIBProcessMessage(struct RIGPORTINFO * PORT) +{ + // Called from Background thread + + int InputLen = recv(PORT->remoteSock, &PORT->RXBuffer[PORT->RXLen], 500 - PORT->RXLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + return; + } + + PORT->RXLen += InputLen; +} + +void FLRIGProcessMessage(struct RIGPORTINFO * PORT) +{ + // Called from Background thread + + int InputLen = recv(PORT->remoteSock, &PORT->RXBuffer[PORT->RXLen], 500 - PORT->RXLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + return; + } + + PORT->RXLen += InputLen; + ProcessFLRIGFrame(PORT); +} + +void ProcessHAMLIBFrame(struct RIGPORTINFO * PORT, int Length) +{ + char * msg = PORT->RXBuffer; + char * rest; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on Yaseu + msg[Length] = 0; + + PORT->Timeout = 0; + RIG->RIGOK = 1; + + // extract lines from input + + while (msg && msg[0]) + { + rest = strlop(msg, 10); + + if (memcmp(msg, "Frequency:", 10) == 0) + { + RIG->RigFreq = atof(&msg[11]) / 1000000.0; + + _gcvt(RIG->RigFreq, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + else if (memcmp(msg, "Mode:", 5) == 0) + { + if (strlen(&msg[6]) < 15) + strcpy(RIG->ModeString, &msg[6]); + } + + else if (memcmp(msg, "Passband:", 9) == 0) + { + RIG->Passband = atoi(&msg[10]); + sprintf(RIG->WEB_MODE, "%s/%d", RIG->ModeString, RIG->Passband); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + } + + msg = rest; + } +} + + +void ProcessFLRIGFrame(struct RIGPORTINFO * PORT) +{ + char * msg; + int Length; + + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one + char * ptr1, * ptr2, * val; + int Len, TotalLen; + char cmd[80]; + char ReqBuf[256]; + char SendBuff[256]; + + while (PORT->RXLen > 0) + { + int b1 = 0, b2 = 0; + + msg = PORT->RXBuffer; + Length = PORT->RXLen; + + msg[Length] = 0; + + ptr1 = strstr(msg, "Content-length:"); + + if (ptr1 == NULL) + return; + + ptr2 = strstr(ptr1, "\r\n\r\n"); + + if (ptr2 == NULL) + return; + + Len = atoi(&ptr1[15]); + + TotalLen = ptr2 + 4 + Len - msg; + + if (TotalLen > Length) // Don't have it all + return; + + val = strstr(ptr2, ""); + + if (val) + { + val += 7; + + RIG->RIGOK = 1; + PORT->RXLen -= TotalLen; + + // It is quite difficult to corrolate responses with commands, but we only poll for freq, mode and bandwidth + // and the responses can be easily identified + + if (strstr(val, "") || memcmp(val, "", 8) == 0) + { + // Reply to set command - may need to send next in set or use to send OK if interactive + + if (PORT->ScanEntry.Cmd2Msg[0]) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd2Msg); + FLRIGSendCommand(PORT, "rig.set_mode", cmd); + PORT->ScanEntry.Cmd2Msg[0] = 0; + } + + else if (PORT->ScanEntry.Cmd3Msg[0] && strcmp(PORT->ScanEntry.Cmd3Msg, "0") != 0) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd3Msg); + FLRIGSendCommand(PORT, "rig.set_bandwidth", cmd); + PORT->ScanEntry.Cmd3Msg[0] = 0; + } + + else if (!PORT->AutoPoll) + { + GetSemaphore(&Semaphore, 61); + SendResponse(RIG->Session, "Set OK"); + FreeSemaphore(&Semaphore); + PORT->AutoPoll = 1; // So we dond send another + } + } + else if(strstr(val, "")) + { + // Reply to get BW + + char * p1, * p2; + + p1 = strstr(val, ""); + + if (p1) + { + p1 += 7; + b1 = atoi(p1); + + p2 = strstr(p1, ""); + + if (p2) + { + p2 +=7; + b2 = atoi(p2); + } + } + + if (b1) + { + if (b2) + sprintf(RIG->WEB_MODE, "%s/%d:%d", RIG->ModeString, b1, b2); + else + sprintf(RIG->WEB_MODE, "%s/%d", RIG->ModeString, b1); + + MySetWindowText(RIG->hMODE, RIG->WEB_MODE); + } + } + else + { + // Either freq or mode. See if numeric + + double freq; + + strlop(val, '<'); + + freq = atof(val) / 1000000.0; + + if (freq > 0.0) + { + RIG->RigFreq = freq; + _gcvt(RIG->RigFreq, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + MySetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + // Read Mode + + Len = sprintf(ReqBuf, Req, "rig.get_mode", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(PORT->remoteSock, SendBuff, Len, 0); + + } + else + { + if (strlen(val)> 1) + { + strcpy(RIG->ModeString, val); + + if (b1) + { + if (b2) + sprintf(RIG->WEB_MODE, "%s/%d:%d", RIG->ModeString, b1, b2); + else + sprintf(RIG->WEB_MODE, "%s/%d", RIG->ModeString, b1); + } + else + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + MySetWindowText(RIG->hMODE, RIG->WEB_MODE); + + Len = sprintf(ReqBuf, Req, "rig.get_bw", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + send(PORT->remoteSock, SendBuff, Len, 0); + } + } + } + + + + // We now send all setting commands at once + + /* + + + if (memcmp(PORT->TXBuffer, "rig.set_vfo", 11) == 0) + { + // Set Freq - Send Set Mode if needed + + if (PORT->ScanEntry.Cmd2Msg[0]) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd2Msg); + FLRIGSendCommand(PORT, "rig.set_mode", cmd); + if (strcmp(PORT->ScanEntry.Cmd3Msg, "0") != 0) + { + sprintf(cmd, "%s", PORT->ScanEntry.Cmd3Msg); + FLRIGSendCommand(PORT, "rig.set_bandwidth", cmd); + } + strcpy(RIG->ModeString, PORT->ScanEntry.Cmd2Msg); + } + else + { + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Set Freq OK"); + + strcpy(PORT->TXBuffer, "rig.get_vfo"); + Len = sprintf(ReqBuf, Req, "rig.get_vfo", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + strcpy(PORT->TXBuffer, "rig.get_bw"); + Len = sprintf(ReqBuf, Req, "rig.get_bw", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + send(PORT->remoteSock, SendBuff, Len, 0); + } + continue; + } + + if (memcmp(PORT->TXBuffer, "rig.set_mode", 11) == 0) + { + strcpy(PORT->TXBuffer, "rig.get_vfo"); + Len = sprintf(ReqBuf, Req, "rig.get_vfo", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + send(PORT->remoteSock, SendBuff, Len, 0); + + if (!PORT->AutoPoll) + SendResponse(RIG->Session, "Set Freq and Mode OK"); + + continue; + } + + if (memcmp(PORT->TXBuffer, "rig.get_vfo", 11) == 0) + { + RIG->RigFreq = atof(val) / 1000000.0; + + _gcvt(RIG->RigFreq, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + MySetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + strcpy(PORT->TXBuffer, "rig.get_mode"); + Len = sprintf(ReqBuf, Req, "rig.get_mode", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(PORT->remoteSock, SendBuff, Len, 0); + + Len = sprintf(ReqBuf, Req, "rig.get_bw", ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(PORT->remoteSock, SendBuff, Len, 0); + continue; + } + + if (memcmp(PORT->TXBuffer, "rig.get_mode", 11) == 0) + { + strlop(val, '<'); + sprintf(RIG->WEB_MODE, "%s", val); + MySetWindowText(RIG->hMODE, RIG->WEB_MODE); + } + } + + */ + } + + if (PORT->RXLen > 0) + memmove(PORT->RXBuffer, &PORT->RXBuffer[TotalLen], PORT->RXLen); + else + PORT->RXLen = 0; + + PORT->Timeout = 0; + } + + /* + POST /RPC2 HTTP/1.1 + User-Agent: XMLRPC++ 0.8 + Host: 127.0.0.1:12345 + Content-type: text/xml + Content-length: 89 + + + rig.get_vfoA + + HTTP/1.1 200 OK + Server: XMLRPC++ 0.8 + Content-Type: text/xml + Content-length: 118 + + + + 14070000 + + */ + + +} + + + + + +void HLSetMode(SOCKET Sock, struct RIGINFO * RIG, unsigned char * Msg, char sep) +{ + char Resp[120]; + int Len; + char mode[80] = ""; + int filter = 0; + int n = sscanf(&Msg[1], "%s %d", mode, &filter); + + // Send to RIGCommand. Can't set Mode without Freq so need to use current + + // ?? Should be try to convert bandwidth to filter ?? + + RIG->Passband = filter; + + if (RIG->PORT->PortType == ICOM) // Needs a Filter + sprintf(Resp, "%d %s %s 1\n", 0, RIG->Valchar, mode); + else + sprintf(Resp, "%d %s %s\n", 0, RIG->Valchar, mode); + + GetSemaphore(&Semaphore, 60); + Rig_CommandEx(RIG->PORT, RIG, (TRANSPORTENTRY *) -1, Resp); + FreeSemaphore(&Semaphore); + + if (sep) + Len = sprintf(Resp, "set_mode: %s %d%cRPRT 0\n", mode, filter, sep); + else + Len = sprintf(Resp, "RPRT 0\n"); + + send(Sock, Resp, Len, 0); +} + + +void HLSetFreq(SOCKET Sock, struct RIGINFO * RIG, unsigned char * Msg, char sep) +{ + char Resp[80]; + int Len; + int freq = atoi(&Msg[1]); + + // Send to RIGCommand + + sprintf(Resp, "%d %f\n", 0, freq/1000000.0); + GetSemaphore(&Semaphore, 60); + Rig_CommandEx(RIG->PORT, RIG, (TRANSPORTENTRY *) -1, Resp); + FreeSemaphore(&Semaphore); + + if (sep) + Len = sprintf(Resp, "set_freq: %d%cRPRT 0\n", freq, sep); + else + Len = sprintf(Resp, "RPRT 0\n"); + + send(Sock, Resp, Len, 0); +} + + +void HLGetPTT(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + int ptt = 0; + + if (RIG->PTTTimer) + ptt = 1; + + if (sep) + Len = sprintf(Resp, "get_ptt:%cPTT: %d%cRPRT 0\n", sep, ptt, sep); + else + Len = sprintf(Resp, "%d\n", ptt); + + send(Sock, Resp, Len, 0); +} + +void HLSetPTT(SOCKET Sock, struct RIGINFO * RIG, unsigned char * Msg, char sep) +{ + char Resp[80]; + int Len; + int ptt = atoi(&Msg[1]); + + if (ptt) + Rig_PTTEx(RIG, 1, NULL); + else + Rig_PTTEx(RIG, 0, NULL); + + if (sep) + Len = sprintf(Resp, "set_ptt: %d%cRPRT 0\n", ptt, sep); + else + Len = sprintf(Resp, "RPRT 0\n"); + + send(Sock, Resp, Len, 0); +} + +void HLGetMode(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + + if (sep) + Len = sprintf(Resp, "get_mode:%cMode: %s%cPassband: %d%cRPRT 0\n", sep, RIG->ModeString, sep, RIG->Passband, sep); + else + Len = sprintf(Resp, "%s\n%d\n", RIG->ModeString, RIG->Passband); + + send(Sock, Resp, Len, 0); + +} + +void HLGetFreq(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + char freqval[64]; + double freq = atof(RIG->Valchar) * 1000000.0; + + sprintf(freqval, "%f", freq); + strlop(freqval, '.'); + + if (sep) + Len = sprintf(Resp, "get_freq:%cFrequency: %s%cRPRT 0\n", sep, freqval, sep); + else + Len = sprintf(Resp, "%s\n", freqval); + + send(Sock, Resp, Len, 0); +} + +void HLGetVFO(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + + if (sep) + Len = sprintf(Resp, "get_vfo:%s%cRPRT 0\n", "VFOA", sep); + else + Len = sprintf(Resp, "%s\n", "VFOA"); + + send(Sock, Resp, Len, 0); +} + +void HLGetSplit(SOCKET Sock, struct RIGINFO * RIG, char sep) +{ + char Resp[80]; + int Len; + + if (sep) + Len = sprintf(Resp, "get_vfo:%s%cRPRT 0\n", "VFOA", sep); + else + Len = sprintf(Resp, "0\n%s\n", "VFOA"); + + send(Sock, Resp, Len, 0); + +} + + + +int ProcessHAMLIBSlaveMessage(SOCKET Sock, struct RIGINFO * RIG, unsigned char * Msg, int MsgLen) +{ + // We only process a pretty small subset of rigctl messages + + // commands are generally a single character, upper case for set + // and lower case for get. If preceeded by + ; | or , response will + // be in extended form. + adds an LF between field, other values the + // supplied char is used as seperator. + + // At the moments we support freq (F) mode (m) and PTT (T) + + char sep = 0; + + if (Msg[0] == '#') + return 0; // Comment + + strlop(Msg, 13); + strlop(Msg, 10); + + // \ on front is used for long mode. Hopefully not used much + + // WSJT seems ro need \chk_vfo and \dump_state + + if (Msg[0] == '\\') + { + if (strcmp(&Msg[1], "chk_vfo") == 0) + { + char Reply[80]; + int Len = sprintf(Reply, "CHKVFO 0\n"); + send(Sock, Reply, Len, 0); + return 0; + } + + if (strcmp(&Msg[1], "dump_state") == 0) + { + char Reply[4096]; + int Len = sprintf(Reply, + "0\n" + "1\n" + "2\n" + "150000.000000 1500000000.000000 0x1ff -1 -1 0x10000003 0x3\n" + "0 0 0 0 0 0 0\n" + "0 0 0 0 0 0 0\n" + "0x1ff 1\n" + "0x1ff 0\n" + "0 0\n" + "0x1e 2400\n" + "0x2 500\n" + "0x1 8000\n" + "0x1 2400\n" + "0x20 15000\n" + "0x20 8000\n" + "0x40 230000\n" + "0 0\n" + "9990\n" + "9990\n" + "10000\n" + "0\n" + "10 \n" + "10 20 30 \n" + "0xffffffff\n" + "0xffffffff\n" + "0xf7ffffff\n" + "0x83ffffff\n" + "0xffffffff\n" + "0xffffffbf\n"); + + send(Sock, Reply, Len, 0); + return 0; + } + } + + if (Msg[0] == 'q' || Msg[0] == 'Q') + { + // close connection + + return 1; + } + + if (Msg[0] == '+') + { + sep = 10; + Msg++; + MsgLen --; + } + else if (Msg[0] == '_' || Msg[0] == '?') + { + } + else if (ispunct(Msg[0])) + { + sep = Msg[0]; + Msg++; + MsgLen --; + } + + switch (Msg[0]) + { + case 'f': // Get Frequency + + HLGetFreq(Sock, RIG, sep); + return 0; + + case 'm': // Get Mode + + HLGetMode(Sock, RIG, sep); + return 0; + + case 't': // Get PTT + + HLGetPTT(Sock, RIG, sep); + return 0; + + case 'v': // Get VFO + + HLGetVFO(Sock, RIG, sep); + return 0; + + case 's': // Get Split + + HLGetSplit(Sock, RIG, sep); + return 0; + + case 'F': + + HLSetFreq(Sock, RIG, Msg, sep); + return 0; + + case 'M': + + HLSetMode(Sock, RIG, Msg, sep); + return 0; + + case 'T': + + HLSetPTT(Sock, RIG, Msg, sep); + return 0; + } + return 0; +} + +int DecodeHAMLIBAddr(struct RIGPORTINFO * PORT, char * ptr) +{ + // Param is IPADDR:PORT. Only Allow numeric addresses + + struct sockaddr_in * destaddr = (SOCKADDR_IN *)&PORT->remoteDest; + + char * port = strlop(ptr, ':'); + + if (port == NULL) + return 0; + + destaddr->sin_family = AF_INET; + destaddr->sin_addr.s_addr = inet_addr(ptr); + destaddr->sin_port = htons(atoi(port)); + + return 1; +} + +VOID HAMLIBThread(struct RIGPORTINFO * PORT); + +VOID ConnecttoHAMLIB(struct RIGPORTINFO * PORT) +{ + if (HAMLIBMasterRunning) + _beginthread(HAMLIBThread, 0, (void *)PORT); + + return ; +} + +VOID HAMLIBThread(struct RIGPORTINFO * PORT) +{ + // Opens sockets and looks for data + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + + if (PORT->remoteSock) + { + closesocket(PORT->remoteSock); + } + + PORT->remoteSock = 0; + PORT->remoteSock = socket(AF_INET,SOCK_STREAM,0); + + if (PORT->remoteSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for HAMLIB socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + PORT->CONNECTING = FALSE; + return; + } + + setsockopt(PORT->remoteSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(PORT->remoteSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(PORT->remoteSock,(LPSOCKADDR) &PORT->remoteDest,sizeof(PORT->remoteDest)) == 0) + { + // + // Connected successful + // + + ioctl(PORT->remoteSock, FIONBIO, ¶m); + } + else + { + if (PORT->Alerted == FALSE) + { + struct sockaddr_in * destaddr = (SOCKADDR_IN * )&PORT->remoteDest; + + err = WSAGetLastError(); + + sprintf(Msg, "Connect Failed for HAMLIB socket - error code = %d Addr %s\r\n", err, PORT->IOBASE); + + WritetoConsole(Msg); + PORT->Alerted = TRUE; + } + + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTING = FALSE; + return; + } + + PORT->CONNECTED = TRUE; + PORT->hDevice = (HANDLE)1; // simplifies check code + + PORT->Alerted = TRUE; + + while (PORT->CONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(PORT->remoteSock,&readfs); + FD_SET(PORT->remoteSock,&errorfs); + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + ret = select((int)PORT->remoteSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (HAMLIBMasterRunning == 0) + return; + + if (ret == SOCKET_ERROR) + { + Debugprintf("HAMLIB Select failed %d ", WSAGetLastError()); + goto Lost; + } + + if (ret > 0) + { + // See what happened + + if (FD_ISSET(PORT->remoteSock, &readfs)) + { + HAMLIBProcessMessage(PORT); + } + + if (FD_ISSET(PORT->remoteSock, &errorfs)) + { +Lost: + sprintf(Msg, "HAMLIB Connection lost for Addr %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); + + PORT->CONNECTED = FALSE; + PORT->Alerted = FALSE; + PORT->hDevice = 0; // simplifies check code + + closesocket(PORT->remoteSock); + PORT->remoteSock = 0; + return; + } + + + continue; + } + else + { + } + } + sprintf(Msg, "HAMLIB Thread Terminated Addr %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); +} + + + +void HAMLIBSlaveThread(struct RIGINFO * RIG) +{ + // Wait for connections and messages from HAMLIB Clients + + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + int ret; + unsigned int maxsock; + + HAMLIBSlaveRunning = 1; + + Consoleprintf("HAMLIB Slave Thread %d Running", RIG->HAMLIBPORT); + + while (HAMLIBSlaveRunning) + { + struct HAMLIBSOCK * Entry = RIG->Sockets; + struct HAMLIBSOCK * Prev; + + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(RIG->ListenSocket, &readfs); + FD_SET(RIG->ListenSocket, &errorfs); + + maxsock = RIG->ListenSocket; + + while (Entry && HAMLIBSlaveRunning) + { + FD_SET(Entry->Sock, &readfs); + FD_SET(Entry->Sock, &errorfs); + + if (Entry->Sock > maxsock) + maxsock = Entry->Sock; + + Entry = Entry->Next; + } + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + ret = select(maxsock + 1, &readfs, NULL, &errorfs, &timeout); + + if (HAMLIBSlaveRunning == 0) + return; + + if (ret == -1) + { + perror("listen select"); + continue; + } + + if (ret) + { + if (FD_ISSET(RIG->ListenSocket, &readfs)) + { + // Connection. Accept it and create a socket enty + + int addrlen = sizeof(struct sockaddr_in6); + struct sockaddr_in6 sin6; + struct HAMLIBSOCK * Entry = zalloc(sizeof(struct HAMLIBSOCK)); + + Entry->Sock = accept(RIG->ListenSocket, (struct sockaddr *)&sin6, &addrlen); + + if (RIG->Sockets) + Entry->Next = RIG->Sockets; + + RIG->Sockets = Entry; + } + + // See if any Data Sockets + + Entry = RIG->Sockets; + Prev = 0; + + while (Entry) + { + unsigned char MsgBuf[256]; + unsigned char * Msg = MsgBuf; + int MsgLen; + + if (FD_ISSET(Entry->Sock, &readfs)) + { + MsgLen = recv(Entry->Sock, Msg, 256, 0); + + if (MsgLen <= 0) + { + // Closed - close socket and remove from chain + + closesocket(Entry->Sock); + + if (Prev == 0) + RIG->Sockets = Entry->Next; + else + Prev->Next = Entry->Next; + + free (Entry); + break; + } + else + { + // Could have multiple messages in packet + // Terminator can be CR LF or CRLF + + char * ptr; + int Len; + + Msg[MsgLen] = 0; +Loop: + ptr = strlop(Msg, 10); + if (ptr == NULL) + strlop(Msg, 13); + + Len = strlen(Msg); + + if (ProcessHAMLIBSlaveMessage(Entry->Sock, RIG, Msg, Len) == 1) + { + // close request + + closesocket(Entry->Sock); + + if (Prev == 0) + RIG->Sockets = Entry->Next; + else + Prev->Next = Entry->Next; + + free (Entry); + break; + } + Msg = ptr; + + if (Msg) + { + while (Msg[0] == 10 || Msg[0] == 13) + Msg++; + + if (Msg[0]) + goto Loop; + } + } + } + + if (FD_ISSET(Entry->Sock, &errorfs)) + { + // Closed - close socket and remove from chai + + closesocket(Entry->Sock); + + if (Prev == 0) + RIG->Sockets = Entry->Next; + else + Prev->Next = Entry->Next; + + free (Entry); + break; + } + + // Check Next Client + + Prev = Entry; + Entry = Entry->Next; + } + } + } + Consoleprintf("HAMLIB Slave Thread %d Exited", RIG->HAMLIBPORT); +} + + +VOID FLRIGPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; + int Len; + char ReqBuf[256]; + char SendBuff[256]; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + return; + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + char cmd[80]; + double freq; + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + // Send the Set Freq here, send set mode when we get a response + + memcpy(&PORT->ScanEntry, PORT->FreqPtr, sizeof(struct ScanEntry)); + + sprintf(cmd, "%s", PORT->FreqPtr->Cmd1Msg); + FLRIGSendCommand(PORT, "rig.set_vfo", cmd); + + // Update display as we don't get a response + + freq = atof(PORT->FreqPtr->Cmd1Msg) / 1000000.0; + + if (freq > 0.0) + { + RIG->RigFreq = freq; + _gcvt(RIG->RigFreq, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + MySetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 10; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + return; + } + } + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && (RIG->ScanStopped == 0) && RIG->NumberofBands) + return; // no point in reading freq if we are about to change it + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + strcpy(Poll, "rig.get_vfo"); + + Len = sprintf(ReqBuf, Req, Poll, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + if (PORT->CONNECTED) + { + if (send(PORT->remoteSock, SendBuff, Len, 0) != Len) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + return; + } + } + + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID FLRIGSendCommand(struct RIGPORTINFO * PORT, char * Command, char * Value) +{ + int Len; + char ReqBuf[512]; + char SendBuff[512]; + char ValueString[256] =""; + + if (!PORT->CONNECTED) + return; + + sprintf(ValueString, "%s", Value); + + strcpy(PORT->TXBuffer, Command); + Len = sprintf(ReqBuf, Req, PORT->TXBuffer, ValueString); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + if (send(PORT->remoteSock, SendBuff, Len, 0) != Len) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + } + + return; +} + + + +VOID FLRIGThread(struct RIGPORTINFO * PORT); + +VOID ConnecttoFLRIG(struct RIGPORTINFO * PORT) +{ + if (FLRIGRunning) + _beginthread(FLRIGThread, 0, (void *)PORT); + return ; +} + +VOID FLRIGThread(struct RIGPORTINFO * PORT) +{ + // Opens sockets and looks for data + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + + if (PORT->remoteSock) + { + closesocket(PORT->remoteSock); + } + + PORT->remoteSock = 0; + PORT->remoteSock = socket(AF_INET,SOCK_STREAM,0); + + if (PORT->remoteSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FLRIG socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + PORT->CONNECTING = FALSE; + return; + } + + setsockopt(PORT->remoteSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(PORT->remoteSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(PORT->remoteSock,(LPSOCKADDR) &PORT->remoteDest, sizeof(PORT->remoteDest)) == 0) + { + // + // Connected successful + // + + ioctl(PORT->remoteSock, FIONBIO, ¶m); + } + else + { + if (PORT->Alerted == FALSE) + { + struct sockaddr_in * destaddr = (SOCKADDR_IN * )&PORT->remoteDest; + + err = WSAGetLastError(); + + sprintf(Msg, "Connect Failed for FLRIG socket - error code = %d Port %d\r\n", + err, htons(destaddr->sin_port)); + + WritetoConsole(Msg); + PORT->Alerted = TRUE; + } + + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTING = FALSE; + return; + } + + PORT->CONNECTED = TRUE; + PORT->hDevice = (HANDLE)1; // simplifies check code + + PORT->Alerted = TRUE; + + while (PORT->CONNECTED && FLRIGRunning) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(PORT->remoteSock,&readfs); + FD_SET(PORT->remoteSock,&errorfs); + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + ret = select((int)PORT->remoteSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (FLRIGRunning == 0) + return; + + if (ret == SOCKET_ERROR) + { + Debugprintf("FLRIG Select failed %d ", WSAGetLastError()); + goto Lost; + } + + if (ret > 0) + { + // See what happened + + if (FD_ISSET(PORT->remoteSock, &readfs)) + { + FLRIGProcessMessage(PORT); + } + + if (FD_ISSET(PORT->remoteSock, &errorfs)) + { +Lost: + sprintf(Msg, "FLRIG Connection lost for Port %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); + + PORT->CONNECTED = FALSE; + PORT->Alerted = FALSE; + PORT->hDevice = 0; // simplifies check code + + closesocket(PORT->remoteSock); + PORT->remoteSock = 0; + return; + } + continue; + } + else + { + } + } + sprintf(Msg, "FLRIG Thread Terminated Port %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); +} + + + + +// HID Support Code + +int HID_Read_Block(struct RIGPORTINFO * PORT) +{ + int Len; + unsigned char Msg[65] = ""; + + if (PORT->RXLen > 400) + PORT->RXLen = 0; + + // Don't try to read more than 64 + +#ifdef WIN32 + Len = rawhid_recv(0, Msg, 64, 100); +#else + Len = read(PORT->hDevice, Msg, 64); +#endif + + if (Len <= 0) + return 0; + + // First byte is actual length + + Len = Msg[0]; + + if (Len > 0) + { + if (Len < 64) // Max in HID Packet + { + memcpy(&PORT->RXBuffer[PORT->RXLen], Msg + 1, Len); + return Len; + } + } + return 0; +} + +void rawhid_close(int num); + +BOOL HID_Write_Block(struct RIGPORTINFO * PORT) +{ + int n = PORT->TXLen; + UCHAR * ptr = PORT->TXBuffer; + UCHAR Msg[64] = ""; + int ret, i; + + while (n) + { + i = n; + if (i > 63) + i = 63; + + Msg[0] = i; // Length on front + memcpy(&Msg[1], ptr, i); + ptr += i; + n -= i; + // n = hid_write(PORT->hDevice, PORT->TXBuffer, PORT->TXLen); +#ifdef WIN32 + ret = rawhid_send(0, Msg, 64, 100); // Always send 64 + + if (ret < 0) + { + Debugprintf("Rigcontrol HID Write Failed %d", errno); + rawhid_close(0); + PORT->hDevice = NULL; + return FALSE; + } +#else + ret = write(PORT->hDevice, Msg, 64); + + if (ret != 64) + { + printf ("Write to %s failed, n=%d, errno=%d\n", PORT->HIDDevice, ret, errno); + close (PORT->hDevice); + PORT->hDevice = 0; + return FALSE; + } + +// printf("HID Write %d\n", i); +#endif + } + return TRUE; +} + + +BOOL OpenHIDPort(struct RIGPORTINFO * PORT, VOID * Port, int Speed) +{ +#ifdef WIN32 + + if (PORT->HIDDevice== NULL) + return FALSE; + + PORT->hDevice = rawhid_open(PORT->HIDDevice); + + if (PORT->hDevice) + Debugprintf("Rigcontrol HID Device %s opened", PORT->HIDDevice); + + /* + handle = hid_open_path(PORT->HIDDevice); + + if (handle) + hid_set_nonblocking(handle, 1); + + PORT->hDevice = handle; + */ +#else + int fd; + unsigned int param = 1; + + if (PORT->HIDDevice== NULL) + return FALSE; + + fd = open (PORT->HIDDevice, O_RDWR); + + if (fd == -1) + { + printf ("Could not open %s, errno=%d\n", PORT->HIDDevice, errno); + return FALSE; + } + + ioctl(fd, FIONBIO, ¶m); + printf("Rigcontrol HID Device %s opened", PORT->HIDDevice); + + PORT->hDevice = fd; +#endif + if (PORT->hDevice == 0) + return (FALSE); + + return TRUE; +} + + +void CM108_set_ptt(struct RIGINFO *RIG, int PTTState) +{ + char io[5]; + hid_device *handle; + int n; + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + if (RIG->CM108Device == NULL) + return; + +#ifdef WIN32 + handle = hid_open_path(RIG->CM108Device); + + if (!handle) { + printf("unable to open device\n"); + return; + } + + n = hid_write(handle, io, 5); + if (n < 0) + { + Debugprintf("Unable to write()\n"); + Debugprintf("Error: %ls\n", hid_error(RIG->PORT->hDevice)); + } + + hid_close(handle); + +#else + + int fd; + + fd = open (RIG->CM108Device, O_WRONLY); + + if (fd == -1) + { + printf ("Could not open %s for write, errno=%d\n", RIG->CM108Device, errno); + return; + } + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + n = write (fd, io, 5); + if (n != 5) + { + printf ("Write to %s failed, n=%d, errno=%d\n", RIG->CM108Device, n, errno); + } + + close (fd); +#endif + return; + +} + +/* +int CRow; + +HANDLE hComPort, hSpeed, hRigType, hButton, hAddr, hLabel, hTimes, hFreqs, hBPQPort; + +VOID CreateRigConfigLine(HWND hDlg, struct RIGPORTINFO * PORT, struct RIGINFO * RIG) +{ + char Port[10]; + + hButton = CreateWindow(WC_BUTTON , "", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | WS_TABSTOP, + 10, CRow+5, 10,10, hDlg, NULL, hInstance, NULL); + + if (PORT->PortType == ICOM) + { + char Addr[10]; + + sprintf(Addr, "%X", RIG->RigAddr); + + hAddr = CreateWindow(WC_EDIT , Addr, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER, + 305, CRow, 30,20, hDlg, NULL, hInstance, NULL); + + } + hLabel = CreateWindow(WC_EDIT , RIG->RigName, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER, + 340, CRow, 60,20, hDlg, NULL, hInstance, NULL); + + sprintf(Port, "%d", RIG->PortRecord->PORTCONTROL.PORTNUMBER); + hBPQPort = CreateWindow(WC_EDIT , Port, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL, + 405, CRow, 20, 20, hDlg, NULL, hInstance, NULL); + + hTimes = CreateWindow(WC_COMBOBOX , "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP, + 430, CRow, 100,80, hDlg, NULL, hInstance, NULL); + + hFreqs = CreateWindow(WC_EDIT , RIG->FreqText, WS_CHILD | WS_VISIBLE| WS_TABSTOP | WS_BORDER | ES_AUTOHSCROLL, + 535, CRow, 300, 20, hDlg, NULL, hInstance, NULL); + + SendMessage(hTimes, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "0000:1159"); + SendMessage(hTimes, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "1200:2359"); + SendMessage(hTimes, CB_SETCURSEL, 0, 0); + + CRow += 30; + +} + +VOID CreatePortConfigLine(HWND hDlg, struct RIGPORTINFO * PORT) +{ + char Port[20]; + int i; + + hComPort = CreateWindow(WC_COMBOBOX , "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP, + 30, CRow, 90, 160, hDlg, NULL, hInstance, NULL); + + for (i = 1; i < 256; i++) + { + sprintf(Port, "COM%d", i); + SendMessage(hComPort, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) Port); + } + + sprintf(Port, "COM%d", PORT->IOBASE); + + i = SendMessage(hComPort, CB_FINDSTRINGEXACT, 0,(LPARAM) Port); + + SendMessage(hComPort, CB_SETCURSEL, i, 0); + + + hSpeed = CreateWindow(WC_COMBOBOX , "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP, + 120, CRow, 75, 80, hDlg, NULL, hInstance, NULL); + + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "1200"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "2400"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "4800"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "9600"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "19200"); + SendMessage(hSpeed, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "38400"); + + sprintf(Port, "%d", PORT->SPEED); + + i = SendMessage(hSpeed, CB_FINDSTRINGEXACT, 0, (LPARAM)Port); + + SendMessage(hSpeed, CB_SETCURSEL, i, 0); + + hRigType = CreateWindow(WC_COMBOBOX , "", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP, + 200, CRow, 100,80, hDlg, NULL, hInstance, NULL); + + SendMessage(hRigType, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "ICOM"); + SendMessage(hRigType, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "YAESU"); + SendMessage(hRigType, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR) "KENWOOD"); + + SendMessage(hRigType, CB_SETCURSEL, PORT->PortType -1, 0); + +} + +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Cmd = LOWORD(wParam); + + switch (message) + { + case WM_INITDIALOG: + { + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + int i, p; + + CRow = 40; + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + CreatePortConfigLine(hDlg, PORT); + + for (i=0; i < PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + CreateRigConfigLine(hDlg, PORT, RIG); + } + } + + + +// CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, +// 90, Row, 40,20, hDlg, NULL, hInstance, NULL); + +// CreateWindow(WC_STATIC , "", WS_CHILD | WS_VISIBLE, +// 135, Row, 100,20, hDlg, NULL, hInstance, NULL); + +return TRUE; + } + + case WM_SIZING: + { + return TRUE; + } + + case WM_ACTIVATE: + +// SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + break; + + + case WM_COMMAND: + + if (Cmd == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} + +*/ + +#ifdef WIN32 + +/* Simple Raw HID functions for Windows - for use with Teensy RawHID example + * http://www.pjrc.com/teensy/rawhid.html + * Copyright (c) 2009 PJRC.COM, LLC + * + * rawhid_open - open 1 or more devices + * rawhid_recv - receive a packet + * rawhid_send - send a packet + * rawhid_close - close a device + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above description, website URL and copyright notice and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Version 1.0: Initial Release + */ + +#include +#include +// +//#include +#include +//#include +//#include + +typedef USHORT USAGE; + + +typedef struct _HIDD_CONFIGURATION { + PVOID cookie; + ULONG size; + ULONG RingBufferSize; +} HIDD_CONFIGURATION, *PHIDD_CONFIGURATION; + +typedef struct _HIDD_ATTRIBUTES { + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + + +typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + + +typedef struct _HIDP_PREPARSED_DATA * PHIDP_PREPARSED_DATA; + + + +// a list of all opened HID devices, so the caller can +// simply refer to them by number +typedef struct hid_struct hid_t; +static hid_t *first_hid = NULL; +static hid_t *last_hid = NULL; +struct hid_struct { + HANDLE handle; + int open; + struct hid_struct *prev; + struct hid_struct *next; +}; +static HANDLE rx_event=NULL; +static HANDLE tx_event=NULL; +static CRITICAL_SECTION rx_mutex; +static CRITICAL_SECTION tx_mutex; + + +// private functions, not intended to be used from outside this file +static void add_hid(hid_t *h); +static hid_t * get_hid(int num); +static void free_all_hid(void); +void print_win32_err(void); + + + + +// rawhid_recv - receive a packet +// Inputs: +// num = device to receive from (zero based) +// buf = buffer to receive packet +// len = buffer's size +// timeout = time to wait, in milliseconds +// Output: +// number of bytes received, or -1 on error +// +int rawhid_recv(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD r; + int n; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&rx_mutex); + ResetEvent(&rx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = rx_event; + if (!ReadFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(rx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&rx_mutex); + if (n <= 0) return -1; + n--; + if (n > len) n = len; + memcpy(buf, tmpbuf + 1, n); + return n; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&rx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&rx_mutex); + return -1; +} + +// rawhid_send - send a packet +// Inputs: +// num = device to transmit to (zero based) +// buf = buffer containing packet to send +// len = number of bytes to transmit +// timeout = time to wait, in milliseconds +// Output: +// number of bytes sent, or -1 on error +// +int rawhid_send(int num, void *buf, int len, int timeout) +{ + hid_t *hid; + unsigned char tmpbuf[516]; + OVERLAPPED ov; + DWORD n, r; + + if (sizeof(tmpbuf) < len + 1) return -1; + hid = get_hid(num); + if (!hid || !hid->open) return -1; + EnterCriticalSection(&tx_mutex); + ResetEvent(&tx_event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = tx_event; + tmpbuf[0] = 0; + memcpy(tmpbuf + 1, buf, len); + if (!WriteFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) goto return_error; + r = WaitForSingleObject(tx_event, timeout); + if (r == WAIT_TIMEOUT) goto return_timeout; + if (r != WAIT_OBJECT_0) goto return_error; + } + if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; + LeaveCriticalSection(&tx_mutex); + if (n <= 0) return -1; + return n - 1; +return_timeout: + CancelIo(hid->handle); + LeaveCriticalSection(&tx_mutex); + return 0; +return_error: + print_win32_err(); + LeaveCriticalSection(&tx_mutex); + return -1; +} + +HANDLE rawhid_open(char * Device) +{ + DWORD index=0; + HANDLE h; + hid_t *hid; + int count=0; + + if (first_hid) free_all_hid(); + + if (!rx_event) + { + rx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + tx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + InitializeCriticalSection(&rx_mutex); + InitializeCriticalSection(&tx_mutex); + } + h = CreateFile(Device, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + + if (h == INVALID_HANDLE_VALUE) + return 0; + + hid = (struct hid_struct *)malloc(sizeof(struct hid_struct)); + if (!hid) + { + CloseHandle(h); + return 0; + } + hid->handle = h; + hid->open = 1; + add_hid(hid); + + return h; +} + + +// rawhid_close - close a device +// +// Inputs: +// num = device to close (zero based) +// Output +// (nothing) +// +void rawhid_close(int num) +{ + hid_t *hid; + + hid = get_hid(num); + if (!hid || !hid->open) return; + + CloseHandle(hid->handle); + hid->handle = NULL; + hid->open = FALSE; +} + + + + +static void add_hid(hid_t *h) +{ + if (!first_hid || !last_hid) { + first_hid = last_hid = h; + h->next = h->prev = NULL; + return; + } + last_hid->next = h; + h->prev = last_hid; + h->next = NULL; + last_hid = h; +} + + +static hid_t * get_hid(int num) +{ + hid_t *p; + for (p = first_hid; p && num > 0; p = p->next, num--) ; + return p; +} + + +static void free_all_hid(void) +{ + hid_t *p, *q; + + for (p = first_hid; p; p = p->next) + { + CloseHandle(p->handle); + p->handle = NULL; + p->open = FALSE; + } + p = first_hid; + while (p) { + q = p; + p = p->next; + free(q); + } + first_hid = last_hid = NULL; +} + + + +void print_win32_err(void) +{ + char buf[256]; + DWORD err; + + err = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, + 0, buf, sizeof(buf), NULL); + Debugprintf("err %ld: %s\n", err, buf); +} + +#endif + +// RTL_SDR support code + +char RTLModes[5][6] = {"FM", "AM", "USB", "LSB", "????"}; + + +void CheckAndProcessRTLUDP(struct RIGPORTINFO * PORT) +{ + int Length; + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + int Freq; + unsigned char RXBuffer[16]; + + struct RIGINFO * RIG = &PORT->Rigs[0]; + + Length = recvfrom(PORT->remoteSock, RXBuffer, 16, 0, (struct sockaddr *)&rxaddr, &addrlen); + + if (Length == 6) + { + long long MHz, Hz; + char CharMHz[16] = ""; + char CharHz[16] = ""; + int i; + int Mode; + + PORT->Timeout = 0; + RIG->RIGOK = TRUE; + + Freq = (RXBuffer[4] << 24) + (RXBuffer[3] << 16) + (RXBuffer[2] << 8) + RXBuffer[1]; + Mode = RXBuffer[5]; + + Freq += RIG->rxOffset; + + RIG->RigFreq = Freq / 1000000.0; + + // If we convert to float to display we get rounding errors, so convert to MHz and Hz to display + + MHz = Freq / 1000000; + + Hz = Freq - MHz * 1000000; + + sprintf(CharMHz, "%lld", MHz); + sprintf(CharHz, "%06lld", Hz); + + for (i = 5; i > 2; i--) + { + if (CharHz[i] == '0') + CharHz[i] = 0; + else + break; + } + + sprintf(RIG->WEB_FREQ,"%lld.%s", MHz, CharHz); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + strcpy(RIG->Valchar, RIG->WEB_FREQ); + + sprintf(RIG->WEB_MODE,"%s", RTLModes[Mode]); + + strcpy(RIG->ModeString, Modes[Mode]); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + + } +} + +VOID RTLUDPPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + struct RIGINFO * RIG = &PORT->Rigs[0]; // Only one on HAMLIB + char cmd[80]; + int len; + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + return; + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + int n; + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + memcpy(PORT->TXBuffer, PORT->FreqPtr->Cmd1, PORT->FreqPtr->Cmd1Len); + PORT->TXLen = PORT->FreqPtr->Cmd1Len; + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + n = sendto(PORT->remoteSock, PORT->TXBuffer, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + if (PORT->TXLen == 10) + n = sendto(PORT->remoteSock, PORT->TXBuffer + 5, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + PORT->TXBuffer[0] = 'P'; // Send Poll + + n = sendto(PORT->remoteSock, PORT->TXBuffer, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 0; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + + ReleasePermission(RIG); // Release Perrmission + return; + } + } + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && (RIG->ScanStopped == 0) && RIG->NumberofBands) + return; // no point in reading freq if we are about to change it + + RIG->PollCounter = 10; // Once Per Sec + + // Read Frequency + + cmd[0] = 'P'; + + len = sendto(PORT->remoteSock, cmd, 5, 0, &PORT->remoteDest, sizeof(struct sockaddr)); + + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID ConnecttoRTLUDP(struct RIGPORTINFO * PORT) +{ + char Msg[255]; + int i; + u_long param=1; + BOOL bcopt=TRUE; + struct sockaddr_in sinx; + + if (PORT->remoteSock) + { + closesocket(PORT->remoteSock); + } + + PORT->remoteSock = 0; + PORT->remoteSock = socket(AF_INET,SOCK_DGRAM,0); + + if (PORT->remoteSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for RTLUDP socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + PORT->CONNECTING = FALSE; + return; + } + + ioctl(PORT->remoteSock, FIONBIO, ¶m); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + PORT->CONNECTED = TRUE; + PORT->hDevice = (HANDLE)1; // simplifies check code + PORT->Alerted = TRUE; +} + +char * getObjectFromArray(char * Msg); // This gets the next object from an array ({} = object, [] = array + + + +char * getArrayFromMsg(char * Msg) +{ + // This gets the next object from an array ({} = object, [] = array + // We look for the end of the object same number of { and }, teminate after } and return pointer to next object + // So we have terminated Msg, and returned next object in array + + // Only call if Msg is the next array in Msg + + + char * ptr = Msg; + char c; + + int Open = 0; + int Close = 0; + + while (c = *(ptr++)) + { + if (c == '[') Open ++; else if (c == ']') Close ++; + + if (Open == Close) + { + *(ptr++) = 0; + return ptr; + } + } + return 0; +} + + +//----- G7TAJ ----- + +void ProcessSDRANGELFrame(struct RIGPORTINFO * PORT) +{ + + int Length; + + char * msg; + + struct RIGINFO * RIG; + char * ptr, * ptr1, * ptr2, * ptr3, * pos; + char cmd[80]; + int chunklength; + int headerlen; + int i, n = 0; + char * Sets; + char * Rest; + char * Set; + int channelcount; + char * channels; + char * channel; + char * samplingDevice; + char * save; + + //Debugprintf("Process SDRANGEL Frame %d\n", PORT->RXLen); + + msg = PORT->RXBuffer; + Length = PORT->RXLen; + + msg[Length] = 0; + + ptr1 = strstr(msg, "Transfer-Encoding: chunked" ); + + if (ptr1 == NULL) + return; + + ptr2 = strstr(ptr1, "\r\n\r\n"); + + if (ptr2 == NULL) + return; + + // ptr2 +4 points to the length of the first chunk (in hex), terminated by crlf + + chunklength = (int)strtol(ptr2 + 4, &ptr3, 16); + ptr3 += 2; // pointer to first chunk data + headerlen = ptr3 - msg; + + // make sure we have first chunk + + if (chunklength + headerlen > Length) + return; + + PORT->RXLen = 0; //we have all the frame now + PORT->Timeout = 0; + + if (strstr(ptr3, "deviceSets") == 0) + { + return; + } + + // Message has info for all rigs + + // As we mess with the message, save a copy and restore for each Rig + + save = _strdup(ptr3); + + for (i = 0; i < PORT->ConfiguredRigs; i++) + { + strcpy(ptr3, save); + n = 0; + + RIG = &PORT->Rigs[i]; + RIG->RIGOK = 1; + + // we can have one or more sampling devices (eg rltsdr) each with one or + // more channels (eg ssb demod, ssb mod). each set of sampling device = channel(s) is a device set. + + // Find Device Set for this device (in RIG-> + + // Message Structure is + + //{ + // "deviceSets": [...]. + // "devicesetcount": 2, + // "devicesetfocus": 0 + //} + + // Get the device sets (JSON [..] is an array + + Sets = strchr(ptr3, '['); + + if (Sets == 0) + continue; + + Rest = getArrayFromMsg(Sets); + + // Do we need to check devicesetcount ??. Maybe use to loop through sets, or just stop at end + + // get the set for our device + + while (RIG->RigAddr >= n) + { + Set = strchr(Sets, '{'); // Position to start of first Object + + if (Set == 0) + break; + + Sets = getObjectFromArray(Set); + n++; + } + + if (Set == 0) + continue; + + + // Now get the channel. looking for key "index": + + // we could have a number of sampling devices and channels but for now get sampling device freq + // and first channel freq. Channels are in an Array + + if ((ptr = strstr(Set, "channelcount")) == 0) + continue; + + channelcount = atoi(&ptr[15]); + + if ((channels = strchr(Set, '[')) == 0) + continue; + + samplingDevice = getArrayFromMsg(channels); + + while(channelcount--) + { + channel = strchr(channels, '{'); + channels = getObjectFromArray(channel); + + if ((ptr = strstr(channel, "index"))) + { + n = atoi(&ptr[7]); + if (n == RIG->Channel) + break; + } + } + + + + if (pos = strstr(samplingDevice, "centerFrequency")) //"centerFrequency": 10489630000, + { + pos += 18; + strncpy(cmd, pos, 20); + RIG->RigFreq = atof(cmd) / 1000000.0; + } + + if (pos = strstr(channel, "deltaFrequency")) + { + pos += 17; + strncpy(cmd, pos, 20); + RIG->RigFreq += (atof(cmd) + RIG->rxOffset) / 1000000.0;; + } + + + _gcvt(RIG->RigFreq, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + + // we could get mode from Title line: + //"title": "SSB Demodulator", + + if (pos = strstr(channel, "title")) + { + pos += 9; + strncpy(cmd, pos, 20); + strlop(pos, ' '); + strncpy(RIG->ModeString, pos, 15); + sprintf(RIG->WEB_MODE, "%s", RIG->ModeString); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + } + + } + + + /* + while (msg && msg[0]) + { + rest = strlop(msg, ','); + + if ( pos = strstr(msg, "centerFrequency")) //"centerFrequency": 10489630000, + { + pos += 18; + strncpy(cmd, pos,20); + + RIG->RigFreq = atof(cmd) / 1000000.0; + + // printf("FREQ=%f\t%s\n", RIG->RigFreq, cmd); + + _gcvt(RIG->RigFreq, 9, RIG->Valchar); + + sprintf(RIG->WEB_FREQ,"%s", RIG->Valchar); + SetWindowText(RIG->hFREQ, RIG->WEB_FREQ); + } + + else if (memcmp(msg, "Mode:", 5) == 0) + { + if (strlen(&msg[6]) < 15) + strcpy(RIG->ModeString, &msg[6]); + } + + else if (memcmp(msg, "Passband:", 9) == 0) + { + RIG->Passband = atoi(&msg[10]); + sprintf(RIG->WEB_MODE, "%s/%d", RIG->ModeString, RIG->Passband); + SetWindowText(RIG->hMODE, RIG->WEB_MODE); + } + + msg = rest; + } + */ + free (save); +} + + + +VOID SDRANGELThread(struct RIGPORTINFO * PORT); + +VOID ConnecttoSDRANGEL(struct RIGPORTINFO * PORT) +{ + if (SDRANGELRunning) + _beginthread(SDRANGELThread, 0, (void *)PORT); + return ; +} + +VOID SDRANGELThread(struct RIGPORTINFO * PORT) +{ + // Opens sockets and looks for data + char Msg[512]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + + if (PORT->CONNECTING) + return; + + PORT->RXLen = 0; + + PORT->CONNECTING = 1; + + if (PORT->remoteSock) + { + closesocket(PORT->remoteSock); + } + + PORT->remoteSock = 0; + PORT->remoteSock = socket(AF_INET,SOCK_STREAM,0); + + if (PORT->remoteSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for SDRAngel socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + PORT->CONNECTING = FALSE; + return; + } + + setsockopt(PORT->remoteSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(PORT->remoteSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(PORT->remoteSock,(LPSOCKADDR) &PORT->remoteDest,sizeof(PORT->remoteDest)) == 0) + { + // + // Connected successful + // + + ioctl(PORT->remoteSock, FIONBIO, ¶m); + } + else + { + if (PORT->Alerted == FALSE) + { + struct sockaddr_in * destaddr = (SOCKADDR_IN * )&PORT->remoteDest; + + err = WSAGetLastError(); + + sprintf(Msg, "Connect Failed for SDRAngel socket - error code = %d Port %d\r\n", + err, htons(destaddr->sin_port)); + + WritetoConsole(Msg); + PORT->Alerted = TRUE; + } + + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTING = FALSE; + return; + } + + PORT->CONNECTED = TRUE; + PORT->CONNECTING = 0; + + PORT->hDevice = (HANDLE)1; // simplifies check code + + PORT->Alerted = TRUE; + + while (PORT->CONNECTED && SDRANGELRunning) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(PORT->remoteSock,&readfs); + FD_SET(PORT->remoteSock,&errorfs); + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + ret = select((int)PORT->remoteSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (SDRANGELRunning == 0) + return; + + if (ret == SOCKET_ERROR) + { + Debugprintf("SDRAngel Select failed %d ", WSAGetLastError()); + goto Lost; + } + + if (ret > 0) + { + // See what happened + + if (FD_ISSET(PORT->remoteSock, &readfs)) + { + SDRANGELProcessMessage(PORT); + } + + if (FD_ISSET(PORT->remoteSock, &errorfs)) + { +Lost: + sprintf(Msg, "SDRAngel Connection lost for Port %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); + + PORT->CONNECTED = FALSE; + PORT->Alerted = FALSE; + PORT->hDevice = 0; // simplifies check code + + closesocket(PORT->remoteSock); + PORT->remoteSock = 0; + return; + } + continue; + } + else + { + } + } + sprintf(Msg, "SDRAngel Thread Terminated Port %s\r\n", PORT->IOBASE); + WritetoConsole(Msg); +} + +/* +# 10489630000 + +CURL_DATA='{ + "deviceHwType": "RTLSDR", + "direction": 0, + "rtlSdrSettings": { + "centerFrequency": "'$1'" + } + +}'; + + +curl -X PATCH "http://127.0.0.1:8091/sdrangel/deviceset/0/device/settings" \ + -H "accept: application/json" \ + -H "Content-Type: application/json" \ + -d "$CURL_DATA" + + + + + + + + + +*/ + + +VOID SDRANGELPoll(struct RIGPORTINFO * PORT) +{ + UCHAR * Poll = PORT->TXBuffer; + + // SDRAngel can have muliple rigs but we only need to poll once to get info for all rigs so just use first entry + + struct RIGINFO * RIG = &PORT->Rigs[0]; + int Len, i; + char SendBuff[256]; + //char * SDRANGEL_GETheader = "GET /sdrangel/deviceset/%d/device/settings " + // "HTTP/1.1\nHost: %s\nConnection: keep-alive\n\r\n"; + + char * SDRANGEL_GETheader = "GET /sdrangel/devicesets " + "HTTP/1.1\nHost: %s\nConnection: keep-alive\n\r\n"; + + + if (RIG->ScanStopped == 0) + if (RIG->ScanCounter) + RIG->ScanCounter--; + + if (PORT->Timeout) + { + PORT->Timeout--; + + if (PORT->Timeout) // Still waiting + return; + + // Loop through all Rigs + + for (i = 0; i < PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + + SetWindowText(RIG->hFREQ, "------------------"); + SetWindowText(RIG->hMODE, "----------"); + strcpy(RIG->WEB_FREQ, "-----------");; + strcpy(RIG->WEB_MODE, "------"); + + RIG->RIGOK = FALSE; + } + return; + } + + // Send Data if avail, else send poll + + if (RIG->NumberofBands && RIG->RIGOK && (RIG->ScanStopped == 0)) + { + if (RIG->ScanCounter <= 0) + { + // Send Next Freq + + if (GetPermissionToChange(PORT, RIG)) + { + char cmd[80]; + + if (RIG->RIG_DEBUG) + Debugprintf("BPQ32 Change Freq to %9.4f", PORT->FreqPtr->Freq); + + _gcvt(PORT->FreqPtr->Freq / 1000000.0, 9, RIG->Valchar); // For MH + + // Send the Set Freq here, send set mode when we get a response + + memcpy(&PORT->ScanEntry, PORT->FreqPtr, sizeof(struct ScanEntry)); + +//TODO + sprintf(cmd, "%.0f", PORT->FreqPtr->Freq); + SDRANGELSendCommand(PORT, "SETFREQ", cmd); + + + PORT->CmdSent = 1; + PORT->Retries = 0; + PORT->Timeout = 10; + PORT->AutoPoll = TRUE; + + // There isn't a response to a set command, so clear Scan Lock here + ReleasePermission(RIG); // Release Perrmission + return; + } + } + } + + if (RIG->PollCounter) + { + RIG->PollCounter--; + if (RIG->PollCounter > 1) + return; + } + + if (RIG->RIGOK && (RIG->ScanStopped == 0) && RIG->NumberofBands) + return; // no point in reading freq if we are about to change it + + RIG->PollCounter = 40; + + // Read Frequency +//TODO + + +// Len = sprintf(SendBuff, SDRANGEL_GETheader, 0, &PORT->remoteDest ); // devicenum, host:port + Len = sprintf(SendBuff, SDRANGEL_GETheader, &PORT->remoteDest ); // devicenum, host:port + + if (PORT->CONNECTED) + { + if (send(PORT->remoteSock, SendBuff, Len, 0) != Len) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + return; + } + } + + PORT->Timeout = 10; + PORT->CmdSent = 0; + + PORT->AutoPoll = TRUE; + + return; +} + +VOID SDRANGELSendCommand(struct RIGPORTINFO * PORT, char * Command, char * Value) +{ + int Len, ret; + char SendBuff[512]; + char ValueString[256] =""; + char * SDRANGEL_PATCHheader = "PATCH /sdrangel/deviceset/%d/device/settings " + "HTTP/1.1\nHost: %s\n" + "accept: application/json\n" + "Content-Type: application/json\n" + "Connection: keep-alive\n" + "Content-length: %d\r\n" + "\r\n%s"; + + if (!PORT->CONNECTED) + return; + + sprintf(ValueString, SDRANGEL_FREQ_DATA, "RTLSDR", Value); + + Len = sprintf(SendBuff, SDRANGEL_PATCHheader, 0, &PORT->remoteDest, strlen(ValueString), ValueString); + + ret = send(PORT->remoteSock, SendBuff, Len, 0); + + if (ret != Len) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + } + + return; +} + + +void SDRANGELProcessMessage(struct RIGPORTINFO * PORT) +{ + // Called from Background thread + + int InputLen = recv(PORT->remoteSock, &PORT->RXBuffer[PORT->RXLen], 8192 - PORT->RXLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + if (PORT->remoteSock) + closesocket(PORT->remoteSock); + + PORT->remoteSock = 0; + + PORT->CONNECTED = FALSE; + PORT->hDevice = 0; + return; + } + + PORT->RXLen += InputLen; + ProcessSDRANGELFrame(PORT); +} + + + +// ---- G7TAJ ---- diff --git a/.svn/pristine/40/40fb803dcd812375e98c9979c2d0124116c9b402.svn-base b/.svn/pristine/40/40fb803dcd812375e98c9979c2d0124116c9b402.svn-base new file mode 100644 index 0000000..63eb53f --- /dev/null +++ b/.svn/pristine/40/40fb803dcd812375e98c9979c2d0124116c9b402.svn-base @@ -0,0 +1,147 @@ + +#define MAXSTACK 20 +#define INPUTLEN 512 + +#define MAXLINES 1000 +#define LINELEN 200 + + +#define BPQICON 2 +#define IDR_MENU1 101 +#define BPQMENU 101 +#define BPQCONNECT 102 +#define BPQDISCONNECT 103 +#define IDD_FONT 105 + +#define ID_WARNWRAP 415 +#define ID_WRAP 416 +#define ID_FLASHONBELL 417 + +#define IDC_FONTWIDTH 1008 +#define IDC_FONTNAME 1009 +#define IDC_CODEPAGE 1010 +#define IDC_CHARSET 1011 +#define IDC_FONTSIZE 1012 +#define BPQMTX 1164 +#define BPQMCOM 1165 +#define BPQCOPYMON 1166 +#define BPQCOPYOUT 1167 +#define BPQCLEARMON 1168 +#define BPQCLEAROUT 1169 +#define BPQBELLS 1170 +#define BPQCHAT 1171 +#define BPQHELP 1172 +#define BPQStripLF 1173 +#define BPQLogOutput 1174 +#define BPQLogMonitor 1175 +#define BPQSendDisconnected 1176 +#define BPQMNODES 1177 +#define MONCOLOUR 1178 +#define CHATTERM 1179 +#define IDM_CLOSEWINDOW 1180 +#define MONITORAPRS 1181 +#define MONLOCALTIME 1182 +#define MON_UI_ONLY 40006 +#define StopALLMon 40007 + +#define IDR_MAINFRAME_MENU 191 +#define TERM_MENU 192 +#define MON_MENU 193 +#define IDI_SIGMA_MAIN_ICON 104 +#define IDI_SYSTEM_INFO 106 + +#define RTFCOPY 30000 +#define ID_INFORMATION_SYSTEMINFORMATION 30001 +#define ID_HELP_ABOUT 30002 +#define ID_WINDOWS_CASCADE 30003 +#define ID_FILE_EXIT 30004 +#define ID_WINDOWS_TILE 30005 +#define ID_NEWWINDOW 30006 +#define ID_WINDOWS_RESTORE 30007 +#define ID_SETUP_FONT 30008 +#define ID_ACTION_RESETWINDOWSPLIT 30009 + +#define BPQBASE 40100 + +#define IDM_FIRSTCHILD 50000 // used in structure when creating mdi client area for the main frame + +// Port monitoring flags use BPQBASE -> BPQBASE+100 +struct ConsoleInfo +{ + struct ConsoleInfo * next; + int BPQStream; + BOOL Active; + int Incoming; + WNDPROC wpOrigInputProc; + HWND hConsole; + HWND hwndInput; + HWND hwndOutput; + HMENU hMenu; // handle of menu + RECT ConsoleRect; + RECT OutputRect; + int CharWidth; + + int Height, Width, Top, Left; + + int ClientHeight, ClientWidth; + char kbbuf[INPUTLEN]; + int kbptr; + + int readbufflen; // Current Length + char * readbuff; // Malloc'ed + char * KbdStack[MAXSTACK]; + + int StackIndex; + +// BOOL Bells; +// BOOL FlashOnBell; // Flash instead of Beep + BOOL StripLF; + +// BOOL WarnWrap; +// BOOL FlashOnConnect; +// BOOL WrapInput; +// BOOL CloseWindowOnBye; + + unsigned int WrapLen; + int WarnLen; + int maxlinelen; + + int PartLinePtr; + int PartLineIndex; // Listbox index of (last) incomplete line + + DWORD dwCharX; // average width of characters + DWORD dwCharY; // height of characters + DWORD dwClientX; // width of client area + DWORD dwClientY; // height of client area + DWORD dwLineLen; // line length + int nCaretPosX; // horizontal position of caret + int nCaretPosY; // vertical position of caret + + COLORREF FGColour; // Text Colour + COLORREF BGColour; // Background Colour + COLORREF DefaultColour; // Default Text Colour + + int CurrentLine; // Line we are writing to in circular buffer. + + int Index; + BOOL SendHeader; + BOOL Finished; + + char OutputScreen[MAXLINES][LINELEN]; + + int Colourvalue[MAXLINES]; + int LineLen[MAXLINES]; + + int CurrentColour; + int Thumb; + int FirstTime; + BOOL Scrolled; // Set if scrolled back + int RTFHeight; // Height of RTF control in pixels + + BOOL CONNECTED; + int SlowTimer; + BOOL Minimized; + BOOL NeedRefresh; + +}; + diff --git a/.svn/pristine/41/41f2e0278f986f01be589e9bc858f3b5c3192cc6.svn-base b/.svn/pristine/41/41f2e0278f986f01be589e9bc858f3b5c3192cc6.svn-base new file mode 100644 index 0000000..0b67fe7 --- /dev/null +++ b/.svn/pristine/41/41f2e0278f986f01be589e9bc858f3b5c3192cc6.svn-base @@ -0,0 +1,987 @@ +/* +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 +*/ + + + +// Monitor Code - from moncode.asm + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + +#pragma data_seg("_BPQDATA") + +#include "cheaders.h" +#include "tncinfo.h" + +// MSGFLAG contains CMD/RESPONSE BITS + +#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND +#define RESP 2 // CURRENT MSG IS RESPONSE +#define VER1 1 // CURRENT MSG IS VERSION 1 + + +#define UI 3 +#define SABM 0x2F +#define DISC 0x43 +#define DM 0x0F +#define UA 0x63 +#define FRMR 0x87 +#define RR 1 +#define RNR 5 +#define REJ 9 + +#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE + +#define NETROM_PID 0xCF +#define IP_PID 0xCC +#define ARP_PID 0xCD + +#define NODES_SIG 0xFF + +UCHAR * DisplayINP3RIF(UCHAR * ptr1, UCHAR * ptr2, unsigned int msglen); + +char * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen); +UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen); +char * DISPLAYARPDATAGRAM(UCHAR * Datagram, UCHAR * Output); + + +DllExport int APIENTRY SetTraceOptions(int mask, int mtxparam, int mcomparam) +{ + +// Sets the tracing options for DecodeFrame. Mask is a bit +// mask of ports to monitor (ie 101 binary will monitor ports +// 1 and 3). MTX enables monitoring on transmitted frames. MCOM +// enables monitoring of protocol control frames (eg SABM, UA, RR), +// as well as info frames. + +// *** For external use only, supports portnum up to 31 *** + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + + return (0); +} + +DllExport int APIENTRY SetTraceOptions64(uint64_t mask, int mtxparam, int mcomparam, int monUIOnly) +{ + +// Sets the tracing options for DecodeFrame. Mask is a bit +// mask of ports to monitor (ie 101 binary will monitor ports +// 1 and 3). MTX enables monitoring on transmitted frames. MCOM +// enables monitoring of protocol control frames (eg SABM, UA, RR), +// as well as info frames. + +// *** For external use only, supports portnum up to 63 *** + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + MUIONLY = monUIOnly; + + return (0); +} +DllExport int APIENTRY SetTraceOptionsEx(int mask, int mtxparam, int mcomparam, int monUIOnly) +{ + +// *** For external use only, supports portnum up to 31 *** + +// Sets the tracing options for DecodeFrame. Mask is a bit +// mask of ports to monitor (ie 101 binary will monitor ports +// 1 and 3). MTX enables monitoring on transmitted frames. MCOM +// enables monitoring of protocol control frames (eg SABM, UA, RR), +// as well as info frames. + + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + MUIONLY = monUIOnly; + + return 0; +} + +int IntSetTraceOptionsEx(uint64_t mask, int mtxparam, int mcomparam, int monUIOnly) +{ + +// Sets the tracing options for DecodeFrame. Mask is a bit +// mask of ports to monitor (ie 101 binary will monitor ports +// 1 and 3). MTX enables monitoring on transmitted frames. MCOM +// enables monitoring of protocol control frames (eg SABM, UA, RR), +// as well as info frames. + + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + MUIONLY = monUIOnly; + + return 0; +} + +int APRSDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, uint64_t Mask) +{ + return IntDecodeFrame(msg, buffer, Stamp, Mask, TRUE, FALSE); +} +DllExport int APIENTRY DecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp) +{ + return IntDecodeFrame(msg, buffer, Stamp, MMASK, FALSE, FALSE); +} + +int IntDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, uint64_t Mask, BOOL APRS, BOOL MINI) +{ + UCHAR * ptr; + int n; + MESSAGE * ADJBUFFER; + ptrdiff_t Work; + UCHAR CTL; + BOOL PF = 0; + char CRCHAR[3] = " "; + char PFCHAR[3] = " "; + int Port; + int MSGFLAG = 0; //CR and V1 flags + char * Output = buffer; + char TR = 'R'; + char From[10], To[10]; + BOOL Info = 0; + BOOL FRMRFLAG = 0; + BOOL XIDFLAG = 0; + BOOL TESTFLAG = 0; + + size_t MsgLen = msg->LENGTH; + + // Use gmtime so we can also display in local time + + struct tm * TM; + + if (MTX & 0x80) + TM = localtime(&Stamp); + else + TM = gmtime(&Stamp); + + // MINI mode is for Node Listen (remote monitor) Mode. Keep info to minimum +/* +KO6IZ*>K7TMG-1: +/ex +KO6IZ*>K7TMG-1: +b +KO6IZ*>K7TMG-1 (UA) +W0TX*>KC6OAR>KB9KC>ID: +W0TX/R DRC/D W0TX-2/G W0TX-1/B W0TX-7/N +KC6OAR*>ID: +*/ + // Check Port + + Port = msg->PORT; + + if (Port == 40) + Port = Port; + + if (Port & 0x80) + { + if ((MTX & 1) == 0) + return 0; // TRANSMITTED FRAME - SEE IF MTX ON + + TR = 'T'; + } + + Port &= 0x7F; + + if ((((uint64_t)1 << (Port - 1)) & Mask) == 0) // Check MMASK + { + if (msg->Padding[0] == '[') + msg->Padding[0] = 0; + + return 0; + } + + + // We now pass Text format monitoring from non-ax25 drivers through this code + // As a valid ax.25 address must have bottom bit set flag plain text messages + // with hex 01. + + // GET THE CONTROL BYTE, TO SEE IF THIS FRAME IS TO BE DISPLAYED + + if (msg->DEST[0] == 1) + { + // Just copy text (Null Terminated) to output + + // Need Timestamp and T/R + + // Add Port: unless Mail Mon (port 64) + + Output += sprintf((char *)Output, "%02d:%02d:%02d%c ", TM->tm_hour, TM->tm_min, TM->tm_sec, TR); + + strcpy(Output, &msg->DEST[1]); + Output += strlen(Output); + + if (buffer[strlen(buffer) -1] == '\r') + Output--; + + if (Port == 64) + Output += sprintf((char *)Output, " \r"); + else + Output += sprintf((char *)Output, " Port=%d\r", Port); + + return (int)strlen(buffer); + } + + n = 8; // MAX DIGIS + ptr = &msg->ORIGIN[6]; // End of Address bit + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + ptr += 7; + n--; + + if (n < 0) + return 0; // Corrupt - no end of address bit + } + + // Reached End of digis + + Work = ptr - &msg->ORIGIN[6]; // Work is length of digis + + MsgLen -= Work; + + ADJBUFFER = (MESSAGE *)((UCHAR *)msg + Work); // ADJBUFFER points to CTL, etc. allowing for digis + + CTL = ADJBUFFER->CTL; + + if (CTL & PFBIT) + PF = TRUE; + + CTL &= ~PFBIT; + + if (MUIONLY) + if (CTL != 3) + return 0; + + if ((CTL & 1) == 0 || CTL == FRMR || CTL == 3) + { + } + else + { + if (((CTL & 2) && MINI) == 0) // Want Control (but not super unless MCOM + if (MCOM == 0) + return 0; // Dont do control + } + + + // Add Port: if MINI mode and monitoring more than one port + + if (MINI == 0) + Output += sprintf((char *)Output, "%02d:%02d:%02d%c ", TM->tm_hour, TM->tm_min, TM->tm_sec, TR); + else + if (CountBits64(Mask) > 1) + Output += sprintf((char *)Output, "%d:", Port); + + From[ConvFromAX25(msg->ORIGIN, From)] = 0; + To[ConvFromAX25(msg->DEST, To)] = 0; + + Output += sprintf((char *)Output, "%s>%s", From, To); + + // Display any Digi-Peaters + + n = 8; // Max number of digi-peaters + ptr = &msg->ORIGIN[6]; // End of Address bit + + 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++) = '*'; + else + if ((ptr[7] & 0x80) == 0) // Repeased by next? + *(Output++) = '*'; // No, so need * + } + } + + if (MINI == 0) + Output += sprintf((char *)Output, " Port=%d ", Port); + + // Set up CR and PF + + CRCHAR[0] = 0; + PFCHAR[0] = 0; + + if (msg->DEST[6] & 0x80) + { + if (msg->ORIGIN[6] & 0x80) // Both set, assume V1 + MSGFLAG |= VER1; + else + { + MSGFLAG |= CMDBIT; + CRCHAR[0] = ' '; + CRCHAR[1] = 'C'; + if (PF) // If FP set + { + PFCHAR[0] = ' '; + PFCHAR[1] = 'P'; + } + } + } + else + { + if (msg->ORIGIN[6] & 0x80) // Only Origin Set + { + MSGFLAG |= RESP; + CRCHAR[0] = ' '; + CRCHAR[1] = 'R'; + if (PF) // If FP set + { + PFCHAR[0] = ' '; + PFCHAR[1] = 'F'; + } + } + else + MSGFLAG |= VER1; // Neither, assume V1 + } + + if ((CTL & 1) == 0) // I frame + { + int NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S) + int NR = (CTL >> 5) & 7; + + Info = 1; + + if (MINI == 0) + Output += sprintf((char *)Output, "", CRCHAR, PFCHAR, NS, NR); + } + else if (CTL == 3) + { + // Un-numbered Information Frame + + Output += sprintf((char *)Output, "", CRCHAR); + Info = 1; + } + else if (CTL & 2) + { + // UN Numbered + + char SUP[6] = "??"; + + switch (CTL) + { + case SABM: + + strcpy(SUP, "C"); + break; + + case SABME: + + strcpy(SUP, "SABME"); + break; + + case XID: + + strcpy(SUP, "XID"); + XIDFLAG = 1; + break; + + case TEST: + + strcpy(SUP, "TEST"); + TESTFLAG = 1; + break; + + case DISC: + + strcpy(SUP, "D"); + break; + + case DM: + + strcpy(SUP, "DM"); + break; + + case UA: + + strcpy(SUP, "UA"); + break; + + + case FRMR: + + strcpy(SUP, "FRMR"); + FRMRFLAG = 1; + break; + } + + if (MINI) + Output += sprintf((char *)Output, "<%s>", SUP); + else + Output += sprintf((char *)Output, "<%s%s%s>", SUP, CRCHAR, PFCHAR); + } + else + { + // Super + + int NR = (CTL >> 5) & 7; + char SUP[5] = "??"; + + switch (CTL & 0x0F) + { + case RR: + + strcpy(SUP, "RR"); + break; + + case RNR: + + strcpy(SUP, "RNR"); + break; + + case REJ: + + strcpy(SUP, "REJ"); + break; + case SREJ: + + strcpy(SUP, "SREJ"); + break; + } + + Output += sprintf((char *)Output, "<%s%s%s R%d>", SUP, CRCHAR, PFCHAR, NR); + + } + + if (FRMRFLAG) + Output += sprintf((char *)Output, " %02X %02X %02X", ADJBUFFER->PID, ADJBUFFER->L2DATA[0], ADJBUFFER->L2DATA[1]); + + if (XIDFLAG) + { + // Decode and display XID + + UCHAR * ptr = &ADJBUFFER->PID; + + if (*ptr++ == 0x82 && *ptr++ == 0x80) + { + int Type; + int Len; + unsigned int value; + int xidlen = *(ptr++) << 8; + xidlen += *ptr++; + + // XID is set of Type, Len, Value n-tuples + +// G8BPQ-2>G8BPQ:(XID cmd, p=1) Half-Duplex SREJ modulo-128 I-Field-Length-Rx=256 Window-Size-Rx=32 Ack-Timer=3000 Retries=10 + + + while (xidlen > 0) + { + Type = *ptr++; + Len = *ptr++; + + value = 0; + xidlen -= (Len + 2); + + while (Len--) + { + value <<=8; + value += *ptr++; + } + switch(Type) + { + case 2: //Bin fields + case 3: + + Output += sprintf((char *)Output, " %d=%x", Type, value); + break; + + case 6: //RX Size + + Output += sprintf((char *)Output, " RX Paclen=%d", value / 8); + break; + + case 8: //RX Window + + Output += sprintf((char *)Output, " RX Window=%d", value); + break; + + case 16: + + Output += sprintf((char *)Output, " Can Compress"); + break; + + case 17: + + Output += sprintf((char *)Output, " Compress ok"); + break; + } + } + } + } + + if (msg->Padding[0] == '[') + Output += sprintf((char *)Output, " %s", msg->Padding); + + msg->Padding[0] = 0; + + if (Info) + { + // We have an info frame + + switch (ADJBUFFER->PID) + { + case 0xF1: + case 0xF2: + + // Compressed L2 Data + + Output += sprintf((char *)Output, " <%d Bytes of Compressed L2 Data>", MsgLen - (19 + sizeof(void *))); + + break; + + case 0xF0: // Normal Data + { + char Infofield[257]; + char * ptr1 = Infofield; + char * ptr2 = ADJBUFFER->L2DATA; + UCHAR C; + size_t len; + + MsgLen = MsgLen - (19 + sizeof(void *)); + + if (MsgLen < 0 || MsgLen > 257) + return 0; // Duff + + while (MsgLen--) + { + C = *(ptr2++); + + if (APRS) + *(ptr1++) = C; // MIC-E needs all chars + else + { + // Convert to printable + + C &= 0x7F; + + if (C == 13 || C == 10 || C > 31) + *(ptr1++) = C; + } + } + + len = ptr1 - Infofield; + + Output[0] = ':'; + Output[1] = 13; + memcpy(&Output[2], Infofield, len); + Output += (len + 2); + + break; + } + case NETROM_PID: + + Output = DISPLAY_NETROM(ADJBUFFER, Output, (int)MsgLen); + break; + + case IP_PID: + + Output += sprintf((char *)Output, " \r"); + Output = DISPLAYIPDATAGRAM((IPMSG *)&ADJBUFFER->L2DATA[0], Output, (int)MsgLen); + break; + + case ARP_PID: + + Output = DISPLAYARPDATAGRAM(&ADJBUFFER->L2DATA[0], Output); + break; + + case 8: // Fragmented IP + + n = ADJBUFFER->L2DATA[0]; // Frag Count + + Output += sprintf((char *)Output, "\r", n); + + if (ADJBUFFER->L2DATA[0] & 0x80) // First Frag - Display Header + { + Output = DISPLAYIPDATAGRAM((IPMSG *)&ADJBUFFER->L2DATA[2], Output, (int)MsgLen - 1); + } + + break; + } + } + + if (Output[-1] != 13) + Output += sprintf((char *)Output, "\r"); + + return (int)(Output - buffer); + +} +// Display NET/ROM data + +char * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen) +{ + char Alias[7]= ""; + char Dest[10]; + char Node[10]; + UCHAR TTL, Index, ID, TXNO, RXNO, OpCode, Flags, Window; + UCHAR * ptr = &ADJBUFFER->L2DATA[0]; + + if (ADJBUFFER->L2DATA[0] == NODES_SIG) + { + // Display NODES + + + // If an INP3 RIF (type <> UI) decode as such + + if (ADJBUFFER->CTL != 3) // UI + return DisplayINP3RIF(&ADJBUFFER->L2DATA[1], Output, MsgLen - (MSGHDDRLEN + 14 + 3)); + + memcpy(Alias, ++ptr, 6); + + ptr += 6; + + Output += sprintf((char *)Output, " NODES broadcast from %s\r", Alias); + + MsgLen -= 30; //Header, mnemonic and signature length + + while(MsgLen > 20) // Entries are 21 bytes + { + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + memcpy(Alias, ptr, 6); + ptr +=6; + strlop(Alias, ' '); + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + Output += sprintf((char *)Output, " %s:%s via %s qlty=%d\r", Alias, Dest, Node, ptr[0]); + ptr++; + MsgLen -= 21; + } + return Output; + } + + // Display normal NET/ROM transmissions + + Output += sprintf((char *)Output, " NET/ROM\r "); + + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + TTL = *(ptr++); + Index = *(ptr++); + ID = *(ptr++); + TXNO = *(ptr++); + RXNO = *(ptr++); + OpCode = Flags = *(ptr++); + + OpCode &= 15; // Remove Flags + + Output += sprintf((char *)Output, "%s to %s ttl %d cct=%02X%02X ", Dest, Node, TTL, Index, ID ); + MsgLen -= 20; + + switch (OpCode) + { + case L4CREQ: + + Window = *(ptr++); + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr +=7; + Node[ConvFromAX25(ptr, Node)] = 0; + ptr +=7; + + Output += sprintf((char *)Output, " w=%d %s at %s", Window, Dest, Node); + + if (MsgLen > 38) // BPQ Extended Params + { + short Timeout = (SHORT)*ptr; + Output += sprintf((char *)Output, " t/o %d", Timeout); + } + + return Output; + + case L4CACK: + + if (Flags & L4BUSY) // BUSY RETURNED + return Output + sprintf((char *)Output, " - BUSY"); + + return Output + sprintf((char *)Output, " w=%d my cct=%02X%02X", ptr[1], TXNO, RXNO); + + case L4DREQ: + + return Output + sprintf((char *)Output, " "); + + case L4DACK: + + return Output + sprintf((char *)Output, " "); + + case L4RESET: + + return Output + sprintf((char *)Output, " "); + + case L4INFO: + { + char Infofield[257]; + char * ptr1 = Infofield; + UCHAR C; + size_t len; + + Output += sprintf((char *)Output, " ", TXNO, RXNO); + + if (Flags & L4BUSY) + *(Output++) = 'B'; + + if (Flags & L4NAK) + *(Output++) = 'N'; + + if (Flags & L4MORE) + *(Output++) = 'M'; + + if (Flags & L4COMP) + *(Output++) = 'C'; + + MsgLen = MsgLen - (19 + sizeof(void *)); + + if (MsgLen < 0 || MsgLen > 257) + return Output; // Duff + + while (MsgLen--) + { + C = *(ptr++); + + // Convert to printable + + C &= 0x7F; + + if (C == 13 || C == 10 || C > 31) + *(ptr1++) = C; + } + + len = ptr1 - Infofield; + + Output[0] = ':'; + Output[1] = 13; + memcpy(&Output[2], Infofield, len); + Output += (len + 2); + } + + return Output; + + case L4IACK: + + Output += sprintf((char *)Output, " ", RXNO); + + if (Flags & L4BUSY) + *(Output++) = 'B'; + + if (Flags & L4NAK) + *(Output++) = 'N'; + + if (Flags & L4MORE) + *(Output++) = 'M'; + + return Output; + + + case 0: + + // OPcode zero is used for several things + + if (Index == 0x0c && ID == 0x0c) // IP + { + *(Output++) = 13; + *(Output++) = ' '; + Output = DISPLAYIPDATAGRAM((IPMSG *)ptr, Output, MsgLen); + return Output; + } + + if (Index == 0 && ID == 1) // NRR + { + Output += sprintf((char *)Output, " \r"); + + MsgLen -= 23; + + while (MsgLen > 6) + { + Dest[ConvFromAX25(ptr, Dest)] = 0; + + if (ptr[7] & 0x80) + Output += sprintf((char *)Output, "%s* ", Dest); + else + Output += sprintf((char *)Output, "%s ", Dest); + + ptr +=8; + MsgLen -= 8; + } + + return Output; + } + } + + Output += sprintf((char *)Output, " "); + return Output; +} + +/* + + PUBLIC L3IP +L3IP: +; +; TCP/IP DATAGRAM +; + mov EBX,OFFSET IP_MSG + call NORMSTR +; + INC ESI ; NOW POINTING TO IP HEADER + +*/ +UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen) +{ + UCHAR * ptr; + USHORT FRAGWORD; + + ptr = (UCHAR *)&IP->IPSOURCE; + Output += sprintf((char *)Output, "%d.%d.%d.%d>", ptr[0], ptr[1], ptr[2], ptr[3]); + + ptr = (UCHAR *)&IP->IPDEST; + Output += sprintf((char *)Output, "%d.%d.%d.%d LEN:%d ", ptr[0], ptr[1], ptr[2], ptr[3], htons(IP->IPLENGTH)); + + FRAGWORD = ntohs(IP->FRAGWORD); + + if (FRAGWORD) + { + // If nonzero, check which bits are set + + //Bit 0: reserved, must be zero + //Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment. + //Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments. + //Fragment Offset: 13 bits + + if (FRAGWORD & (1 << 14)) + Output += sprintf((char *)Output, "DF "); + + if (FRAGWORD & (1 << 13)) + Output += sprintf((char *)Output, "MF "); + + FRAGWORD &= 0xfff; + + if (FRAGWORD) + { + Output += sprintf((char *)Output, "Offset %d ", FRAGWORD * 8); + return Output; // Cant display proto fields + } + } + + if (IP->IPPROTOCOL == 6) + { + PTCPMSG TCP = (PTCPMSG)&IP->Data; + + Output += sprintf((char *)Output, "TCP Src %d Dest %d ", ntohs(TCP->SOURCEPORT), ntohs(TCP->DESTPORT)); + return Output; + } + + if (IP->IPPROTOCOL == 1) + { + PICMPMSG ICMPptr = (PICMPMSG)&IP->Data; + + Output += sprintf((char *)Output, "ICMP "); + + if (ICMPptr->ICMPTYPE == 8) + Output += sprintf((char *)Output, "Echo Request "); + else + if (ICMPptr->ICMPTYPE == 0) + Output += sprintf((char *)Output, "Echo Reply "); + else + Output += sprintf((char *)Output, "Code %d", ICMPptr->ICMPTYPE); + + return Output; + } + +/* + MOV AL,IPPROTOCOL[ESI] + CMP AL,6 + JNE @F + + MOV EBX, OFFSET TCP + CALL NORMSTR + JMP ADD_CR +@@: + + CMP AL,1 + JNE @F + + MOV EBX, OFFSET ICMP + CALL NORMSTR + JMP ADD_CR +@@: + + CALL DISPLAY_BYTE_1 ; DISPLAY PROTOCOL TYPE + +; mov AL,CR +; call PUTCHAR +; +; MOV ECX,39 ; TESTING +;IPLOOP: +; lodsb +; CALL BYTE_TO_HEX +; +; LOOP IPLOOP + + JMP ADD_CR + + +*/ + return Output; +} + + + +char * DISPLAYARPDATAGRAM(UCHAR * Datagram, UCHAR * Output) +{ + UCHAR * ptr = Datagram; + char Dest[10]; + + if (ptr[7] == 1) // Request + return Output + sprintf((char *)Output, " ARP Request who has %d.%d.%d.%d? Tell %d.%d.%d.%d", + ptr[26], ptr[27], ptr[28], ptr[29], ptr[15], ptr[16], ptr[17], ptr[18]); + + // Response + + Dest[ConvFromAX25(&ptr[8], Dest)] = 0; + + return Output + sprintf((char *)Output, " ARP Reply %d.%d.%d.%d is at %s Tell %d.%d.%d.%d", + ptr[15], ptr[16], ptr[17], ptr[18], Dest, ptr[26], ptr[27], ptr[28], ptr[29]); + +} diff --git a/.svn/pristine/42/42c723543d5e4708e2726a75e32f6bc05c254e92.svn-base b/.svn/pristine/42/42c723543d5e4708e2726a75e32f6bc05c254e92.svn-base new file mode 100644 index 0000000..60ac04a --- /dev/null +++ b/.svn/pristine/42/42c723543d5e4708e2726a75e32f6bc05c254e92.svn-base @@ -0,0 +1,2809 @@ +/* +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 +*/ + + +// +// C replacement for Main.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +//#include "windows.h" +//#include "winerror.h" + +#include "time.h" +#include "stdio.h" +#include + +#include "kernelresource.h" +#include "cheaders.h" +#include "tncinfo.h" +#include "mqtt.h" + +VOID L2Routine(struct PORTCONTROL * PORT, PMESSAGE Buffer); +VOID ProcessIframe(struct _LINKTABLE * LINK, PDATAMESSAGE Buffer); +VOID FindLostBuffers(); +VOID ReadMH(); +void GetPortCTEXT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD); +int upnpInit(); +void AISTimer(); +void ADSBTimer(); +VOID SendSmartID(struct PORTCONTROL * PORT); +int CanPortDigi(int Port); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +void MQTTTimer(); +void SaveMH(); + +#include "configstructs.h" + +extern struct CONFIGTABLE xxcfg; +extern BOOL needAIS; +extern int needADSB; + +struct PORTCONFIG * PortRec; + +#define RNRSET 0x2 // RNR RECEIVED FROM OTHER END + +// STATION INFORMATION + +char DATABASESTART[14] = ""; +char xMAJORVERSION = 4; +char xMINORVERSION = 9; +char FILLER1[16] = ""; + +struct ROUTE * NEIGHBOURS = NULL; +int ROUTE_LEN = sizeof(struct ROUTE); +int MAXNEIGHBOURS = 20; + +struct DEST_LIST * DESTS = NULL; // NODE LIST +int DEST_LIST_LEN = sizeof(struct DEST_LIST); + +struct _LINKTABLE * LINKS = NULL; +int LINK_TABLE_LEN = sizeof (struct _LINKTABLE); +int MAXLINKS = 30; + + +char MYCALL[7] = ""; // DB 7 DUP (0) ; NODE CALLSIGN (BIT SHIFTED) +char MYALIASTEXT[6] = ""; // DB ' ' ; NODE ALIAS (KEEP TOGETHER) + +char MYALIASLOPPED[10]; +char MYCALLLOPPED[10]; + +UCHAR MYCALLWITHALIAS[13] = ""; + +UCHAR NETROMCALL[7] = ""; // Call used for NETROM (can be MYCALL) + +APPLCALLS APPLCALLTABLE[NumberofAppls] = {0}; + +UCHAR MYNODECALL[10] = ""; // NODE CALLSIGN (ASCII) +UCHAR MYNETROMCALL[10] = ""; // NETROM CALLSIGN (ASCII) +char NODECALLLOPPED[10]; + +VOID * FREE_Q = NULL; + +time_t TimeLoaded = 0; + +struct PORTCONTROL * PORTTABLE = NULL; +int NUMBEROFPORTS = 0; +int PORTENTRYLEN = sizeof(struct PORTCONTROL); + +struct DEST_LIST * ENDDESTLIST = NULL; // ; NODE LIST+1 +; + +VOID * BUFFERPOOL = NULL; // START OF BUFFER POOL +VOID * ENDBUFFERPOOL = NULL; + +int OBSINIT = 5; // INITIAL OBSOLESCENCE VALUE +int OBSMIN = 4; // MINIMUM TO BROADCAST +int L3INTERVAL = 60; // 'NODES' INTERVAL IN MINS +int IDINTERVAL = 20; // 'ID' BROADCAST INTERVAL +int BTINTERVAL = 20; // 'BT' BROADCAST INTERVAL +int MINQUAL = 10; // MIN QUALITY FOR AUTOUPDATES +int HIDENODES = 0; // N * COMMAND SWITCH +int BBSQUAL = 255; // QUALITY OF BBS RELATIVE TO NODE + +int NUMBEROFBUFFERS = 999; // PACKET BUFFERS + +int PACLEN = 100; //MAX PACKET SIZE + +// L2 SYSTEM TIMER RUNS AT 3 HZ + +int T3 = 3*61*3; // LINK VALIDATION TIMER (3 MINS) (+ a bit to reduce RR collisions) + +int L2KILLTIME = 16*60*3; // IDLE LINK TIMER (16 MINS) +int L3LIVES = 15; // MAX L3 HOPS +int L4N2 = 3; // LEVEL 4 RETRY COUNT +int L4LIMIT = 60*15; // IDLE SESSION LIMIT - 15 MINS +int L4DELAY = 5; // L4 DELAYED ACK TIMER + +int BBS = 1; // INCLUDE BBS SUPPORT +int NODE = 1; // INCLUDE SWITCH SUPPORT + +int FULL_CTEXT = 1; // CTEXT ON ALL CONNECTS IF NZ + +int L4Compress = 0; +int L4CompMaxframe = 3; +int L4CompPaclen = 236; + +BOOL LogL4Connects = FALSE; +BOOL LogAllConnects = FALSE; +BOOL AUTOSAVEMH = TRUE; +extern BOOL ADIFLogEnabled; +extern UCHAR LogDirectory[260]; +extern BOOL EventsEnabled; +extern BOOL SaveAPRSMsgs; +BOOL M0LTEMap = FALSE; +BOOL MQTT = FALSE; +char MQTT_HOST[80] = ""; +int MQTT_PORT = 0; +char MQTT_USER[80] = ""; +char MQTT_PASS[80] = ""; + +int MQTT_Connecting = 0; +int MQTT_Connected = 0; + + +//TNCTABLE DD 0 +//NUMBEROFSTREAMS DD 0 + +extern VOID * ENDPOOL; +extern void * APPL_Q; // Queue of frames for APRS Appl + +extern BOOL APRSActive; + +#define BPQHOSTSTREAMS 64 + +// Although externally streams are numbered 1 to 64, internally offsets are 0 - 63 + +BPQVECSTRUC XDUMMY = {0}; // Needed to force correct order of following + +BPQVECSTRUC BPQHOSTVECTOR[BPQHOSTSTREAMS + 5] = {0}; + +BPQVECSTRUC * TELNETMONVECPTR = &BPQHOSTVECTOR[BPQHOSTSTREAMS]; +BPQVECSTRUC * AGWMONVECPTR = &BPQHOSTVECTOR[BPQHOSTSTREAMS + 1]; +BPQVECSTRUC * APRSMONVECPTR = &BPQHOSTVECTOR[BPQHOSTSTREAMS + 2]; +BPQVECSTRUC * IPHOSTVECTORPTR = &BPQHOSTVECTOR[BPQHOSTSTREAMS + 3]; + +int BPQVECLENGTH = sizeof(BPQVECSTRUC); + +int NODEORDER = 0; +UCHAR LINKEDFLAG = 0; + +UCHAR UNPROTOCALL[80] = ""; + +UCHAR ExcludeList[71] = ""; // 10 ENTRIES, 7 BYTES EACH + +char * INFOMSG = NULL; + +char * CTEXTMSG = NULL; +int CTEXTLEN = 0; + +UCHAR MYALIAS[7] = ""; // ALIAS IN AX25 FORM +UCHAR BBSALIAS[7] = ""; + +UCHAR AX25CALL[7] = ""; // WORK AREA FOR AX25 <> NORMAL CALL CONVERSION +UCHAR NORMCALL[10] = ""; // CALLSIGN IN NORMAL FORMAT +int NORMLEN = 0; // LENGTH OF CALL IN NORMCALL + +int CURRENTPORT = 0; // PORT FOR CURRENT MESSAGE +VOID * CURRENTPORTPTR = NULL; // PORT CONTROL TABLE ENTRY FOR CURRENT PORT + +int SDCBYTE = 0; // CONTROL BYTE FOR CURRENT FRAME + +VOID * BUFFER = NULL; // GENERAL SAVE AREA FOR BUFFER ADDR +VOID * ADJBUFFER = NULL; // BASE ADJUSED FOR DIGIS + +UCHAR TEMPFIELD[7] = ""; // ADDRESS WORK FILED + +void * TRACE_Q = NULL; // TRANSMITTED FRAMES TO BE TRACED + +int RANDOM = 0; // 'RANDOM' NUMBER FOR PERSISTENCE CALCS + +int L2TIMERFLAG = 0; // INCREMENTED AT 18HZ BY TIMER INTERRUPT +int L3TIMERFLAG = 0; // DITTO +int L4TIMERFLAG = 0; // DITTO + +char HEADERCHAR = '}'; // CHAR FOR _NODE HEADER MSGS + +VOID * LASTPOINTER = NULL; // PERVIOUS _NODE DURING CHAINING + +int REALTIMETICKS = 0; +int BGTIMER = 0; // TO CONTROL BG SCANS + +VOID * CONFIGPTR = NULL; // Internal Config Get Offset + +int AUTOSAVE = 0; // AUTO SAVE NODES ON EXIT FLAG +int L4APPL = 1; // Application for BBSCALL/ALIAS connects +int CFLAG = 0; // C =HOST Command + +VOID * IDMSG_Q = NULL; // ID/BEACONS WAITING TO BE SENT + +int NODESINPROGRESS = 0; +VOID * CURRENTNODE = NULL; // NEXT _NODE TO SEND +VOID * DESTHEADER = NULL; // HEAD OF SORTED NODES CHAIN + +int L3TIMER = 1; // TIMER FOR 'NODES' MESSAGE +int IDTIMER = 0; // TIMER FOR ID MESSAGE +int BTTIMER = 0; // TIMER FOR BT MESSAGE + +UCHAR * NEXTFREEDATA = NULL; // ADDRESS OF NEXT FREE BYTE of shared memory + +int NEEDMH = 0; + +struct DATAMESSAGE BTHDDR = {0,0,9,240,13}; +struct _MESSAGE IDHDDR = {0,0,23,0,0,3, 240}; + +VOID * IDMSG = &IDHDDR; + +//DD 0 ; CHAIN +// DB 0 ; PORT +int BTLENGTH = 9; // DW 9 ; LENGTH +// DB 0F0H ; PID +char BTEXTFLD[256] ="\r"; + +char BridgeMap[MaxBPQPortNo + 1][MaxBPQPortNo + 1] = {0}; + +// Keep Buffers at end + +#define DATABYTES 600000 // WAS 320000 + +UCHAR DATAAREA[DATABYTES] = ""; + +void ** Bufferlist[1000] = {0}; + +extern BOOL IPRequired; +extern BOOL PMRequired; +extern int MaxHops; +extern int MAXRTT; +extern USHORT CWTABLE[]; +extern struct _TRANSPORTENTRY * L4TABLE; +extern UCHAR ROUTEQUAL; +extern UINT BPQMsg; + +extern int NUMBEROFTNCPORTS; + +extern APPLCALLS APPLCALLTABLE[]; + +// LOOPBACK PORT ROUTINES + +VOID LINKINIT(PEXTPORTDATA PORTVEC) +{ + WritetoConsoleLocal("Loopback\n"); +} + +VOID LINKTX(PEXTPORTDATA PORTVEC, PMESSAGE Buffer) +{ + // LOOP BACK TO SWITCH + struct _LINKTABLE * LINK; + + LINK = Buffer->Linkptr; + + if (LINK) + { + if (LINK->L2TIMER) + LINK->L2TIMER = LINK->L2TIME; + + Buffer->Linkptr = 0; // CLEAR FLAG FROM BUFFER + } + + C_Q_ADD(&PORTVEC->PORTCONTROL.PORTRX_Q, Buffer); +} + + +VOID LINKRX() +{ +} + + +VOID LINKTIMER() +{ +} + +VOID LINKCLOSE() +{ +} + + +VOID EXTCLOSE() +{ +} + +BOOL KISSTXCHECK() +{ + return 0; +} + +BOOL LINKTXCHECK() +{ + return 0; +} + +void * Dummy(int fn, int port, PDATAMESSAGE buff) // Dummy for missing EXT Driver +{ + return 0; +} + +VOID EXTINIT(PEXTPORTDATA PORTVEC) +{ + // LOAD DLL - NAME IS IN PORT_DLL_NAME + + void *(* Startup) (PEXTPORTDATA PORTVEC); // ADDR OF Startup ROUTINE + + PORTVEC->PORT_EXT_ADDR = Dummy; + + Startup = InitializeExtDriver(PORTVEC); + + if (Startup == 0) + { + WritetoConsoleLocal("Driver installation failed\n"); + return; + } + + +// CALL THE ROUTINE TO START IT UP + +// Startup returns address of processing routine + + PORTVEC->PORT_EXT_ADDR = (void *(__cdecl *)(int,int,PDATAMESSAGE))Startup(PORTVEC);; + + if (PORTVEC->PORT_EXT_ADDR == 0) + { + WritetoConsoleLocal("Driver Initialisation failed\n"); + return; + } + +} + +VOID EXTTX(PEXTPORTDATA PORTVEC, MESSAGE * Buffer) +{ + struct _LINKTABLE * LINK; + struct PORTCONTROL * PORT = (struct PORTCONTROL *)PORTVEC; + +// RESET TIMER, unless BAYCOM + + if (PORT->KISSFLAGS == 255) // Used for BAYCOM + { + PORTVEC->PORT_EXT_ADDR(2, PORT->PORTNUMBER, (PDATAMESSAGE)Buffer); + + return; // Baycom driver passes frames to trace once sent + } + + LINK = Buffer->Linkptr; + + if (LINK) + { + if (LINK->L2TIMER) + LINK->L2TIMER = LINK->L2TIME; + + if (PORT->TNC == 0 || PORT->TNC->Hardware != H_KISSHF) + Buffer->Linkptr = 0; // CLEAR FLAG FROM BUFFER + } + + PORTVEC->PORT_EXT_ADDR(2, PORT->PORTNUMBER, (PDATAMESSAGE)Buffer); + + if (PORT->PROTOCOL == 10 && PORT->TNC && PORT->TNC->Hardware != H_KISSHF) + { + ReleaseBuffer(Buffer); + return; + } + + C_Q_ADD(&TRACE_Q, Buffer); + + return; + +} + +VOID EXTRX(PEXTPORTDATA PORTVEC) +{ + struct _MESSAGE * Message; + size_t Len; + struct PORTCONTROL * PORT = (struct PORTCONTROL *)PORTVEC; + +Loop: + + if (QCOUNT < 10) + return; + + Message = GetBuff(); + + if (Message == NULL) + return; + + Len = (size_t)PORTVEC->PORT_EXT_ADDR(1, PORT->PORTNUMBER, (PDATAMESSAGE)Message); + + if (Len == 0) + { + ReleaseBuffer((UINT *)Message); + return; + } + + if (PORT->PROTOCOL == 10) + { + // PACTOR Style Port - Negative values used to report events - for now -1 = Disconnected + + if (Len == -1) + { + int Sessno = Message->PORT; + TRANSPORTENTRY * Session; + + ReleaseBuffer((UINT *)Message); + + // GET RID OF ANY SESSION ENTRIES + + Session = PORTVEC->ATTACHEDSESSIONS[Sessno]; + + if (Session) + { + struct TNCINFO * TNC = PORTVEC->PORTCONTROL.TNC; + + CloseSessionPartner(Session); + + // is this the place to run DisconnectScript? + + if (TNC->DisconnectScript) + { + int n = 0; + struct DATAMESSAGE * Buffer; + + TRANSPORTENTRY Session = {0}; // = TNC->PortRecord->ATTACHEDSESSIONS[Sessno]; + + while (TNC->DisconnectScript[n]) + { + Buffer = GetBuff(); + if (Buffer) + { + Session.Secure_Session = 1; + Session.CIRCUITINDEX = -1; + Buffer->LENGTH = sprintf(Buffer->L2DATA, "%s\r", TNC->DisconnectScript[n++]) + (sizeof(void *) + 4); + CommandHandler(&Session, Buffer); + }; + } + } + + PORTVEC->ATTACHEDSESSIONS[Sessno] = NULL; + } + return; + } + } + + C_Q_ADD(&PORT->PORTRX_Q, (UINT *)Message); + + goto Loop; + + return; +} + +VOID EXTTIMER(PEXTPORTDATA PORTVEC) +{ + // USED TO SEND A RE-INIT IN THE CORRECT PROCESS + + if (PORTVEC->EXTRESTART) + { + PORTVEC->EXTRESTART = 0; //CLEAR + PORTVEC->PORT_EXT_ADDR(4, PORTVEC->PORTCONTROL.PORTNUMBER, 0); + } + + PORTVEC->PORT_EXT_ADDR(7, PORTVEC->PORTCONTROL.PORTNUMBER, 0); // Timer Routine +} + +VOID EXTSLOWTIMER(PEXTPORTDATA PORTVEC) +{ + PORTVEC->PORT_EXT_ADDR(8, PORTVEC->PORTCONTROL.PORTNUMBER, 0); // Timer Routine +} + +size_t EXTTXCHECK(PEXTPORTDATA PORTVEC, int Chan) +{ + uintptr_t Temp = Chan; + + return (size_t)PORTVEC->PORT_EXT_ADDR(3, PORTVEC->PORTCONTROL.PORTNUMBER, (void *)Temp); +} + +VOID PostDataAvailable(TRANSPORTENTRY * Session) +{ +#ifndef LINBPQ + if (Session->L4CIRCUITTYPE & BPQHOST) + { + BPQVECSTRUC * HostSess = Session->L4TARGET.HOST; + + if (HostSess) + { + if (HostSess->HOSTHANDLE) + { + PostMessage(HostSess->HOSTHANDLE, BPQMsg, HostSess->HOSTSTREAM, 2); + } + } + } +#endif +} + +VOID PostStateChange(TRANSPORTENTRY * Session) +{ +#ifndef LINBPQ + if (Session->L4CIRCUITTYPE & BPQHOST) + { + BPQVECSTRUC * HostSess = Session->L4TARGET.HOST; + + if (HostSess) + { + if (HostSess->HOSTHANDLE); + { + PostMessage(HostSess->HOSTHANDLE, BPQMsg, HostSess->HOSTSTREAM, 4); + } + } + } +#endif +} + +#ifdef LINBPQ + +#define HDLCTX KHDLCTX +#define HDLCRX KHDLCRX +#define HDLCTIMER KHDLCTIMER +#define HDLCCLOSE KHDLCCLOSE +#define HDLCTXCHECK KHDLCTXCHECK + +#define PC120INIT KHDLCINIT +#define DRSIINIT KHDLCINIT +#define TOSHINIT KHDLCINIT +#define RLC100INIT KHDLCINIT +#define BAYCOMINIT KHDLCINIT +#define PA0INIT KHDLCINIT + +int KHDLCINIT(PHDLCDATA PORTVEC); +void KHDLCTX(struct KISSINFO * KISS, PMESSAGE Buffer); +int KHDLCRX(PHDLCDATA PORTVEC); +void KHDLCTIMER(PHDLCDATA PORTVEC); +void KHDLCCLOSE(PHDLCDATA PORTVEC); +BOOL KHDLCTXCHECK(); + + +#else + +extern VOID PC120INIT(), DRSIINIT(), TOSHINIT(); +extern VOID RLC100INIT(), BAYCOMINIT(), PA0INIT(); + +extern VOID HDLCTX(); +extern VOID HDLCRX(); +extern VOID HDLCTIMER(); +extern VOID HDLCCLOSE(); +extern VOID HDLCTXCHECK(); + +#endif + +extern VOID KISSINIT(), KISSTX(), KISSRX(), KISSTIMER(), KISSCLOSE(); +extern VOID EXTINIT(PEXTPORTDATA PORTVEC), EXTTX(PEXTPORTDATA PORTVEC, MESSAGE * Buffer), LINKRX(), EXTRX(PEXTPORTDATA PORTVEC); +extern VOID LINKCLOSE(), EXTCLOSE() ,LINKTIMER(), EXTTIMER(PEXTPORTDATA PORTVEC); + +// VECTORS TO HARDWARE DEPENDENT ROUTINES + +VOID * INITCODE[12] = {KISSINIT, PC120INIT, DRSIINIT, TOSHINIT, KISSINIT, +RLC100INIT, RLC100INIT, LINKINIT, EXTINIT, BAYCOMINIT, PA0INIT, KISSINIT}; + +VOID * TXCODE[12] = {KISSTX, HDLCTX, HDLCTX, HDLCTX, KISSTX, + HDLCTX, HDLCTX, LINKTX, EXTTX, HDLCTX, HDLCTX, KISSTX}; + +VOID * RXCODE[12] = {KISSRX, HDLCRX, HDLCRX, HDLCRX, KISSRX, + HDLCRX, HDLCRX, LINKRX, EXTRX, HDLCRX, HDLCRX, KISSRX}; + +VOID * TIMERCODE[12] = {KISSTIMER, HDLCTIMER, HDLCTIMER, HDLCTIMER, KISSTIMER, + HDLCTIMER, HDLCTIMER, LINKTIMER, EXTTIMER, HDLCTIMER, HDLCTIMER, KISSTIMER}; + +VOID * CLOSECODE[12] = {KISSCLOSE, HDLCCLOSE, HDLCCLOSE, HDLCCLOSE, KISSCLOSE, + HDLCCLOSE, HDLCCLOSE, LINKCLOSE, EXTCLOSE, HDLCCLOSE, HDLCCLOSE, KISSCLOSE}; + +VOID * TXCHECKCODE[12] = {KISSTXCHECK, HDLCTXCHECK, HDLCTXCHECK, HDLCTXCHECK, KISSTXCHECK, + HDLCTXCHECK, HDLCTXCHECK, LINKTXCHECK, EXTTXCHECK, HDLCTXCHECK, HDLCTXCHECK, KISSTXCHECK}; + + +extern int BACKGROUND(); +extern int L2TimerProc(); +extern int L3TimerProc(); +extern int L4TimerProc(); +extern int L3FastTimer(); +extern int StatsTimer(); +extern int COMMANDHANDLER(); +VOID SDETX(struct _LINKTABLE * LINK); +extern int L4BG(); +extern int L3BG(); +extern int TNCTimerProc(); +extern int PROCESSIFRAME(); + +int xxxxx = MAXDATA; + +BOOL Start() +{ + struct CONFIGTABLE * cfg = &xxcfg; + struct APPLCONFIG * ptr1; + struct PORTCONTROL * PORT; + struct FULLPORTDATA * FULLPORT; // Including HW Data + struct FULLPORTDATA * NEXTPORT; // Including HW Data + struct _EXTPORTDATA * EXTPORT; + APPLCALLS * APPL; + struct ROUTE * ROUTE; + struct DEST_LIST * DEST; + struct CMDX * CMD; + int PortSlot = 1; + uintptr_t int3; + + unsigned char * ptr2 = 0, * ptr3, * ptr4; + USHORT * CWPTR; + int i, n; + + struct ROUTECONFIG * Rcfg; + + NEXTFREEDATA = &DATAAREA[0]; // For Reinit + + memset(DATAAREA, 0, DATABYTES); + + // Reinit everything in case of restart + + FREE_Q = 0; + TRACE_Q = 0; + IDMSG_Q = 0; + NUMBEROFPORTS = 0; + MAXBUFFS = 0; + QCOUNT = 0; + NUMBEROFNODES = 0; + DESTHEADER = 0; + NODESINPROGRESS = 0; + CURRENTNODE = 0; + L3TIMER = 1; // SEND NODES + + if (cfg->C_NODEALIAS[0] == 0) + memset(cfg->C_NODEALIAS, ' ', 10); + + TimeLoaded = time(NULL); + + AUTOSAVE = cfg->C_AUTOSAVE; + + if (cfg->C_L4APPL) + L4APPL = cfg->C_L4APPL; + + CFLAG = cfg->C_C; + + IPRequired = cfg->C_IP; + PMRequired = cfg->C_PM; + + if (cfg->C_MAXHOPS) + MaxHops = cfg->C_MAXHOPS; + + if (cfg->C_MAXRTT) + MAXRTT = cfg->C_MAXRTT * 100; + + if (cfg->C_NODE == 0 && cfg->C_BBS) + { + // USE BBS CALL FOR NODE if Set, otherwise find first APPLCALL + // Unless BBS also = 0 + + if (cfg->C_BBSCALL[0]) + { + memcpy(MYNODECALL, cfg->C_BBSCALL, 10); + memcpy(MYALIASTEXT, cfg->C_BBSALIAS, 6); + memcpy(MYALIASLOPPED, cfg->C_BBSALIAS, 10); + } + else + { + ptr1 = &cfg->C_APPL[0]; + + for (i = 0; i < NumberofAppls; i++) + { + if (ptr1->ApplCall[0] != ' ') + { + memcpy(MYNODECALL, &ptr1->ApplCall[0], 10); + memcpy(MYALIASTEXT, &ptr1->ApplAlias, 6); + memcpy(MYALIASLOPPED, &ptr1->ApplAlias, 10); + + break; + } + ptr1++; + } + } + + } + else + { + memcpy(MYNODECALL, cfg->C_NODECALL, 10); + memcpy(MYALIASTEXT, cfg->C_NODEALIAS, 6); + memcpy(MYALIASLOPPED, cfg->C_NODEALIAS, 10); + } + + strlop(MYALIASLOPPED, ' '); + + + // IF NO BBS, SET BOTH TO _NODE CALLSIGN + + if (cfg->C_BBS == 0) + { + memcpy(APPLCALLTABLE[0].APPLCALL_TEXT, cfg->C_NODECALL, 10); + memcpy(APPLCALLTABLE[0].APPLALIAS_TEXT, cfg->C_NODEALIAS, 10); + } + else + { + memcpy(APPLCALLTABLE[0].APPLCALL_TEXT, cfg->C_BBSCALL, 10); + memcpy(APPLCALLTABLE[0].APPLALIAS_TEXT, cfg->C_BBSALIAS, 10 ); + } + + BBSQUAL = cfg->C_BBSQUAL; + + // copy MYCALL to NETROMCALL + + memcpy(MYNETROMCALL, MYNODECALL, 10); + + // if NETROMCALL Defined, use it + + if (cfg->C_NETROMCALL[0] && cfg->C_NETROMCALL[0] != ' ') + memcpy(MYNETROMCALL, cfg->C_NETROMCALL, 10); + + strlop(MYNETROMCALL, ' '); + strlop(MYNODECALL, ' '); + + memcpy(NODECALLLOPPED, MYNODECALL, 10); + strlop(NODECALLLOPPED, ' '); + + APPLCALLTABLE[0].APPLQUAL = BBSQUAL; + + if (cfg->C_WASUNPROTO == 0 && cfg->C_BTEXT) + { + char * ptr1 = &cfg->C_BTEXT[0]; + char * ptr2 = BTHDDR.L2DATA; + int len = 120; + + BTHDDR.LENGTH = 1; // PID + + while ((*ptr1) && len--) + { + *(ptr2++) = *(ptr1++); + BTHDDR.LENGTH ++; + } + + } + + OBSINIT = cfg->C_OBSINIT; + OBSMIN = cfg->C_OBSMIN; + L3INTERVAL = cfg->C_NODESINTERVAL; + IDINTERVAL = cfg->C_IDINTERVAL; + if (IDINTERVAL) + IDTIMER = 2; + + BTINTERVAL = cfg->C_BTINTERVAL; + if (BTINTERVAL) + BTTIMER = 2; + + + MINQUAL = cfg->C_MINQUAL; + FULL_CTEXT = cfg->C_FULLCTEXT; + L3LIVES = cfg->C_L3TIMETOLIVE; + L4N2 = cfg->C_L4RETRIES; + L4DEFAULTWINDOW = cfg->C_L4WINDOW; + L4T1 = cfg->C_L4TIMEOUT; + +// MOV AX,C_BUFFERS +// MOV NUMBEROFBUFFERS,AX + + PACLEN = cfg->C_PACLEN; + T3 = cfg->C_T3 * 3; + L4LIMIT = cfg->C_IDLETIME; + if (L4LIMIT && L4LIMIT < 120) + L4LIMIT = 120; // Don't allow stupidly low + L2KILLTIME = L4LIMIT * 3; + L4DELAY = cfg->C_L4DELAY; + BBS = cfg->C_BBS; + NODE = cfg->C_NODE; + LINKEDFLAG = cfg->C_LINKEDFLAG; + MAXLINKS = cfg->C_MAXLINKS; + MAXDESTS = cfg->C_MAXDESTS; + MAXNEIGHBOURS = cfg->C_MAXNEIGHBOURS; + MAXCIRCUITS = cfg->C_MAXCIRCUITS; + HIDENODES = cfg->C_HIDENODES; + + LogL4Connects = cfg->C_LogL4Connects; + LogAllConnects = cfg->C_LogAllConnects; + AUTOSAVEMH = cfg->C_SaveMH; + ADIFLogEnabled = cfg->C_ADIF; + EventsEnabled = cfg->C_EVENTS; + SaveAPRSMsgs = cfg->C_SaveAPRSMsgs; + M0LTEMap = cfg->C_M0LTEMap; + MQTT = cfg->C_MQTT; + strcpy(MQTT_HOST, cfg->C_MQTT_HOST); + MQTT_PORT = cfg->C_MQTT_PORT; + strcpy(MQTT_USER, cfg->C_MQTT_USER); + strcpy(MQTT_PASS, cfg->C_MQTT_PASS); + L4Compress = cfg->C_L4Compress; + L4CompMaxframe = cfg->C_L4CompMaxframe; + L4CompPaclen = cfg->C_L4CompPaclen; + + if (L4CompMaxframe < 1 || L4CompMaxframe > 16) + L4CompMaxframe = 3; + + if (L4CompPaclen < 64 || L4CompPaclen > 236) + L4CompPaclen = 236; + + // Get pointers to PASSWORD and APPL1 commands + +// int APPL1 = 0; +//int PASSCMD = 0; + + CMD = &COMMANDS[0]; + n = 0; + + for (n = 0; n < NUMBEROFCOMMANDS; n++) + { + if (APPL1 == 0 && CMD->String[0] == '*') // First appl + { + APPLS = (char *)CMD; + APPL1 = n; + } + + if (PASSCMD == 0 && memcmp(CMD->String, "PASSWORD", 8) == 0) + PASSCMD = n; + + CMD++; + } + + +// SET UP APPLICATION LIST + + memset(&CMDALIAS[0][0], ' ', NumberofAppls * ALIASLEN ); + + ptr1 = (struct APPLCONFIG *)&xxcfg.C_APPL[0]; + ptr3 = &CMDALIAS[0][0]; + + for (i = 0; i < NumberofAppls; i++) + { + if (ptr1->Command[0] != ' ') + { + ptr2 = (char *)&COMMANDS[APPL1 + i]; + + memcpy(ptr2, ptr1, 12); + + // See if an Alias + + if (ptr1->CommandAlias[0] != ' ') + memcpy(ptr3, ptr1->CommandAlias, ALIASLEN); + + // SET LENGTH FIELD + + *(ptr2 + 12) = 0; // LENGTH + ptr4 = ptr2; + + while (*(ptr4) > 32) + { + ptr4++; + *(ptr2 + 12) = *(ptr2 + 12) + 1; + } + } + ptr1 ++; + ptr2 += CMDXLEN; + ptr3 += ALIASLEN; + } + + // Set up Exclude List + + memcpy(ExcludeList, cfg->C_EXCLUDE, 71); + + // SET UP PORT TABLE + + PortRec = &cfg->C_PORT[0];// (struct PORTCONFIG *)ptr2; + + PORTTABLE = (VOID *)NEXTFREEDATA; + FULLPORT = (struct FULLPORTDATA *)PORTTABLE; + + while (PortRec->PORTNUM) + { + // SET UP NEXT PORT PTR + + PORT = &FULLPORT->PORTCONTROL; + NEXTPORT = FULLPORT; + NEXTPORT++; + PORT->PORTPOINTER = (struct PORTCONTROL *)NEXTPORT; + + PORT->PORTNUMBER = (UCHAR)PortRec->PORTNUM; + PORT->PortSlot = PortSlot++; + memcpy(PORT->PORTDESCRIPTION, PortRec->ID, 30); + + PORT->PORTTYPE = (char)PortRec->TYPE; + + PORT->PORTINITCODE = INITCODE[PORT->PORTTYPE / 2]; // ADDR OF INIT ROUTINE + PORT->PORTTXROUTINE = TXCODE[PORT->PORTTYPE / 2]; // ADDR OF INIT ROUTINE + PORT->PORTRXROUTINE = RXCODE[PORT->PORTTYPE / 2]; // ADDR OF INIT ROUTINE + PORT->PORTTIMERCODE = TIMERCODE[PORT->PORTTYPE / 2]; // ADDR OF INIT ROUTINE + PORT->PORTCLOSECODE = CLOSECODE[PORT->PORTTYPE / 2]; // ADDR OF INIT ROUTINE + PORT->PORTTXCHECKCODE = TXCHECKCODE[PORT->PORTTYPE / 2]; // ADDR OF INIT ROUTINE + + + PORT->PROTOCOL = (char)PortRec->PROTOCOL; + PORT->IOBASE = PortRec->IOADDR; + + if (PortRec->SerialPortName && PortRec->SerialPortName[0]) + PORT->SerialPortName = _strdup(PortRec->SerialPortName); + else + { + if (PORT->IOBASE > 0 && PORT->IOBASE < 256) + { + char Name[80]; +#ifndef WIN32 + sprintf(Name, "com%d", PORT->IOBASE); +#else + sprintf(Name, "COM%d", PORT->IOBASE); +#endif + PORT->SerialPortName = _strdup(Name); + } + else + PORT->SerialPortName = _strdup("NOPORT"); + + } + PORT->INTLEVEL = (char)PortRec->INTLEVEL; + PORT->BAUDRATE = PortRec->SPEED; + + if (PORT->BAUDRATE == 49664) + PORT->BAUDRATE = (int)115200; + + PORT->CHANNELNUM = (char)PortRec->CHANNEL; + PORT->PORTQUALITY = (UCHAR)PortRec->QUALITY; + PORT->NormalizeQuality = !PortRec->NoNormalize; + PORT->IgnoreUnlocked = PortRec->IGNOREUNLOCKED; + PORT->INP3ONLY = PortRec->INP3ONLY; + + PORT->PORTWINDOW = (UCHAR)PortRec->MAXFRAME; + + if (PortRec->PROTOCOL == 0 || PORT->PORTTYPE == 22) // KISS or I2C + PORT->PORTTXDELAY = PortRec->TXDELAY /10; + else + PORT->PORTTXDELAY = PortRec->TXDELAY; + + if (PortRec->PROTOCOL == 0 || PORT->PORTTYPE == 22) // KISS or I2C + PORT->PORTSLOTTIME = (UCHAR)PortRec->SLOTTIME / 10; + else + PORT->PORTSLOTTIME = (UCHAR)PortRec->SLOTTIME; + + PORT->PORTPERSISTANCE = (UCHAR)PortRec->PERSIST; + PORT->FULLDUPLEX = (UCHAR)PortRec->FULLDUP; + + PORT->SOFTDCDFLAG = (UCHAR)PortRec->SOFTDCD; + PORT->PORTT1 = PortRec->FRACK / 333; + PORT->PORTT2 = PortRec->RESPTIME /333; + PORT->PORTN2 = (UCHAR)PortRec->RETRIES; + + PORT->PORTPACLEN = (UCHAR)PortRec->PACLEN; + PORT->QUAL_ADJUST = (UCHAR)PortRec->QUALADJUST; + + PORT->DIGIFLAG = PortRec->DIGIFLAG; + if (PortRec->DIGIPORT && CanPortDigi(PortRec->DIGIPORT)) + PORT->DIGIPORT = PortRec->DIGIPORT; + PORT->DIGIMASK = PortRec->DIGIMASK; + PORT->USERS = (UCHAR)PortRec->USERS; + + // PORTTAILTIME - if KISS, set a default, and cnvert to ticks + + if (PORT->PORTTYPE == 0) + { + if (PortRec->TXTAIL) + PORT->PORTTAILTIME = PortRec->TXTAIL / 10; + else + PORT->PORTTAILTIME = 3; // 10ths + } + else + + //; ON HDLC, TAIL TIMER IS USED TO HOLD RTS FOR 'CONTROLLED FULL DUP' - Val in seconds + + PORT->PORTTAILTIME = (UCHAR)PortRec->TXTAIL; + + PORT->PORTBBSFLAG = (char)PortRec->ALIAS_IS_BBS; + PORT->PORTL3FLAG = (char)PortRec->L3ONLY; + PORT->KISSFLAGS = PortRec->KISSOPTIONS; + PORT->PORTINTERLOCK = (UCHAR)PortRec->INTERLOCK; + PORT->NODESPACLEN = (UCHAR)PortRec->NODESPACLEN; + PORT->TXPORT = (UCHAR)PortRec->TXPORT; + + PORT->PORTMINQUAL = PortRec->MINQUAL; + + if (PortRec->MAXDIGIS) + PORT->PORTMAXDIGIS = PortRec->MAXDIGIS; + else + PORT->PORTMAXDIGIS = 8; + + PORT->PortNoKeepAlive = PortRec->DefaultNoKeepAlives; + PORT->PortUIONLY = PortRec->UIONLY; + PORT->TXPORT = (UCHAR)PortRec->TXPORT; + + // SET UP CWID + + if (PortRec->CWIDTYPE == 'o') + PORT->CWTYPE = 'O'; + else + PORT->CWTYPE = PortRec->CWIDTYPE; + + ptr2 = &PortRec->CWID[0]; + CWPTR = &PORT->CWID[0]; + + PORT->CWIDTIMER = (29 - PORT->PORTNUMBER) * 600; // TICKSPERMINUTE + PORT->CWPOINTER = &PORT->CWID[0]; + + for (i = 0; i < 8; i++) // MAX ID LENGTH + { + char c = *(ptr2++); + if (c < 32) + break; + if (c > 'Z') + continue; + + c -= '/'; // Table stats at / + c &= 127; + + *(CWPTR) = CWTABLE[c]; + CWPTR++; + } + + // SEE IF LINK CALLSIGN/ALIAS SPECIFIED + + if (PortRec->PORTCALL[0]) + ConvToAX25(PortRec->PORTCALL, PORT->PORTCALL); + + if (PortRec->PORTALIAS[0]) + ConvToAX25(PortRec->PORTALIAS, PORT->PORTALIAS); + + if (PortRec->PORTALIAS2[0]) + ConvToAX25(PortRec->PORTALIAS2, PORT->PORTALIAS2); + + if (PortRec->DLLNAME[0]) + { + EXTPORT = (struct _EXTPORTDATA *)PORT; + memcpy(EXTPORT->PORT_DLL_NAME, PortRec->DLLNAME, 16); + } + if (PortRec->BCALL[0]) + ConvToAX25(PortRec->BCALL, PORT->PORTBCALL); + + PORT->XDIGIS = PortRec->XDIGIS; // Crossband digi aliases + + memcpy(&PORT->PORTIPADDR, &PortRec->IPADDR, 4); + PORT->ListenPort = PortRec->ListenPort; + + if (PortRec->TCPPORT) + { + PORT->KISSTCP = TRUE; + PORT->IOBASE = PortRec->TCPPORT; + if (PortRec->IPADDR == 0) + PORT->KISSSLAVE = TRUE; + } + + if (PortRec->WL2K) + memcpy(&PORT->WL2KInfo, PortRec->WL2K, sizeof(struct WL2KInfo)); + + PORT->RIGPort = PortRec->RIGPORT; + + if (PortRec->HavePermittedAppls) + PORT->PERMITTEDAPPLS = PortRec->PERMITTEDAPPLS; + else + PORT->PERMITTEDAPPLS = 0xffffffff; // Default to all + + PORT->Hide = PortRec->Hide; + + PORT->SmartIDInterval = PortRec->SmartID; + + if (PortRec->KissParams && (PORT->PORTTYPE == 0 || PORT->PORTTYPE == 22)) + { + struct KISSINFO * KISS = (struct KISSINFO *)PORT; + UCHAR KissString[128]; + int KissLen = 0; + unsigned char * Kissptr = KissString; + char * ptr; + char * Context; + + ptr = strtok_s(PortRec->KissParams, " ", &Context); + + while (ptr && ptr[0] && KissLen < 120) + { + *(Kissptr++) = atoi (ptr); + KissLen++; + ptr = strtok_s(NULL, " ", &Context); + } + + KISS->KISSCMD = malloc(256); + + KISS->KISSCMDLEN = KissEncode(KissString, KISS->KISSCMD, KissLen); + KISS->KISSCMD = realloc(KISS->KISSCMD, KISS->KISSCMDLEN); + } + + PORT->SendtoM0LTEMap = PortRec->SendtoM0LTEMap; + PORT->PortFreq = PortRec->PortFreq; + + PORT->M0LTEMapInfo = PortRec->M0LTEMapInfo; + + PORT->QtSMPort = PortRec->QtSMPort; + + if (PortRec->BBSFLAG) // Appl 1 not permitted - BBSFLAG=NOBBS + PORT->PERMITTEDAPPLS &= 0xfffffffe; // Clear bottom bit + + + // SEE IF PERMITTED LINK CALLSIGNS SPECIFIED + + ptr2 = &PortRec->VALIDCALLS[0]; + + if (*(ptr2)) + { + ptr3 = (char *)PORT->PORTPOINTER; // Permitted Calls follows Port Info + PORT->PERMITTEDCALLS = ptr3; + + while (*(ptr2) > 32) + { + ConvToAX25(ptr2, ptr3); + ptr3 += 7; + PORT->PORTPOINTER = (struct PORTCONTROL *)ptr3; + if (strchr(ptr2, ',')) + { + ptr2 = strchr(ptr2, ','); + ptr2++; + } + else + break; + } + + ptr3 ++; // Terminating NULL + + // Round to word boundary (for ARM5 etc) + + int3 = (uintptr_t)ptr3; + int3 += 7; + int3 &= 0xfffffffffffffff8; + ptr3 = (UCHAR *)int3; + + PORT->PORTPOINTER = (struct PORTCONTROL *)ptr3; + } + + // SEE IF PORT UNPROTO ADDR SPECIFIED + + ptr2 = &PortRec->UNPROTO[0]; + + if (*(ptr2)) + { + ptr3 = (char *)PORT->PORTPOINTER; // Unproto follows port info + PORT->PORTUNPROTO = ptr3; + + while (*(ptr2) > 32) + { + ConvToAX25(ptr2, ptr3); + ptr3 += 7; + PORT->PORTPOINTER = (struct PORTCONTROL *)ptr3; + if (strchr(ptr2, ',')) + { + ptr2 = strchr(ptr2, ','); + ptr2++; + } + else + break; + } + + ptr3 ++; // Terminating NULL + + // Round to word boundsaty (for ARM5 etc) + + int3 = (uintptr_t)ptr3; + int3 += 7; + int3 &= 0xfffffffffffffff8; + ptr3 = (UCHAR *)int3; + + PORT->PORTPOINTER = (struct PORTCONTROL *)ptr3; + } + + // ADD MH AREA IF NEEDED + + if (PortRec->MHEARD != 'N') + { + NEEDMH = 1; // Include MH in Command List + + ptr3 = (char *)PORT->PORTPOINTER; // Permitted Calls follows Port Info + PORT->PORTMHEARD = (PMHSTRUC)ptr3; + + ptr3 += (MHENTRIES + 1)* sizeof(MHSTRUC); + + // Round to word boundsaty (for ARM5 etc) + + int3 = (uintptr_t)ptr3; + int3 += 7; + int3 &= 0xfffffffffffffff8; + ptr3 = (UCHAR *)int3; + + PORT->PORTPOINTER = (struct PORTCONTROL *)ptr3; + } + + PortRec++; + NUMBEROFPORTS ++; + FULLPORT = (struct FULLPORTDATA *)PORT->PORTPOINTER; + } + + PORT->PORTPOINTER = NULL; // End of list + + NEXTFREEDATA = (UCHAR *)FULLPORT; + + // SET UP APPLICATION CALLS AND ALIASES + + APPL = &APPLCALLTABLE[0]; + + ptr1 = (struct APPLCONFIG *)&xxcfg.C_APPL[0]; + + i = NumberofAppls; + + if (ptr1->ApplCall[0] == ' ') + { + // APPL1CALL IS NOT SPECIFED - LEAVE VALUES SET FROM BBSCALL + + APPL++; + ptr1++; + i--; + } + + while (i) + { + memcpy(APPL->APPLCALL_TEXT, ptr1->ApplCall, 10); + ConvToAX25(APPL->APPLCALL_TEXT, APPL->APPLCALL); + memcpy(APPL->APPLALIAS_TEXT, ptr1->ApplAlias, 10); + ConvToAX25(APPL->APPLALIAS_TEXT, APPL->APPLALIAS); + ConvToAX25(ptr1->L2Alias, APPL->L2ALIAS); + memcpy(APPL->APPLCMD, ptr1->Command, 12); + + APPL->APPLQUAL = ptr1->ApplQual; + + if (ptr1->CommandAlias[0] != ' ') + { + APPL->APPLHASALIAS = 1; + memcpy(APPL->APPLALIASVAL, &ptr1->CommandAlias[0], 48); + } + + APPL++; + ptr1++; + i--; + } + + // SET UP VARIOUS CONTROL TABLES + + LINKS = (VOID *)NEXTFREEDATA; + NEXTFREEDATA += MAXLINKS * sizeof(struct _LINKTABLE); + + DESTS = (VOID *)NEXTFREEDATA; + NEXTFREEDATA += MAXDESTS * sizeof(struct DEST_LIST); + ENDDESTLIST = (VOID *)NEXTFREEDATA; + + NEIGHBOURS = (VOID *)NEXTFREEDATA; + NEXTFREEDATA += MAXNEIGHBOURS * sizeof(struct ROUTE); + + L4TABLE = (VOID *)NEXTFREEDATA; + + NEXTFREEDATA += MAXCIRCUITS * sizeof(TRANSPORTENTRY); + + // SET UP DEFAULT ROUTES LIST + + Rcfg = &cfg->C_ROUTE[0]; + + ROUTE = NEIGHBOURS; + + while (Rcfg->call[0]) + { + int FRACK; + char * VIA; + char axcall[8]; + + ConvToAX25(Rcfg->call, ROUTE->NEIGHBOUR_CALL); + + // if VIA convert digis + + VIA = strstr(Rcfg->call, "VIA"); + + if (VIA) + { + VIA += 4; + + if (ConvToAX25(VIA, axcall)) + { + memcpy(ROUTE->NEIGHBOUR_DIGI1, axcall, 7); + + VIA = strchr(VIA, ' '); + + if (VIA) + { + VIA++; + + if (ConvToAX25(VIA, axcall)) + memcpy(ROUTE->NEIGHBOUR_DIGI2, axcall, 7); + + } + } + } + + ROUTE->NEIGHBOUR_QUAL = Rcfg->quality; + ROUTE->NEIGHBOUR_PORT = Rcfg->port; + + PORT = GetPortTableEntryFromPortNum(ROUTE->NEIGHBOUR_PORT); + + if (Rcfg->pwind & 0x40) + ROUTE->NoKeepAlive = 1; + else + if (PORT != NULL) + ROUTE->NoKeepAlive = PORT->PortNoKeepAlive; + + if (Rcfg->pwind & 0x80 || (PORT && PORT->INP3ONLY)) + { + ROUTE->INP3Node = 1; + ROUTE->NoKeepAlive = 0; // Cant have INP3 and NOKEEPALIVES + } + + ROUTE->NBOUR_MAXFRAME = Rcfg->pwind & 0x3f; + + FRACK = Rcfg->pfrack; + ROUTE->NBOUR_FRACK = FRACK / 333; + ROUTE->NBOUR_PACLEN = Rcfg->ppacl; + ROUTE->OtherendsRouteQual = ROUTE->OtherendLocked = Rcfg->farQual; + + ROUTE->NEIGHBOUR_FLAG = LOCKEDBYCONFIG; // Locked + + Rcfg++; + ROUTE++; + } + + // SET UP INFO MESSAGE + + ptr2 = &cfg->C_INFOMSG[0]; + ptr3 = NEXTFREEDATA; + + INFOMSG = ptr3; + + while ((*ptr2)) + { + *(ptr3++) = *(ptr2++); + } + *ptr3++ = 0; // Null Terminate + + NEXTFREEDATA = ptr3; + + // SET UP CTEXT MESSAGE + + ptr2 = &cfg->C_CTEXT[0]; + ptr3 = NEXTFREEDATA; + + CTEXTMSG = ptr3; + + while ((*ptr2)) + { + *(ptr3++) = *(ptr2++); + } + + CTEXTLEN = (int)(ptr3 - (unsigned char *)CTEXTMSG); + + NEXTFREEDATA = ptr3; + + // SET UP ID MESSAGE + + IDHDDR.DEST[0] = 'I'+'I'; + IDHDDR.DEST[1] = 'D'+'D'; + IDHDDR.DEST[2] = 0x40; + IDHDDR.DEST[3] = 0x40; + IDHDDR.DEST[4] = 0x40; + IDHDDR.DEST[5] = 0x40; + IDHDDR.DEST[6] = 0xe0; // ; ID IN AX25 FORM + + IDHDDR.CTL = 3; + IDHDDR.PID = 0xf0; + + ptr2 = &cfg->C_IDMSG[0]; + ptr3 = &IDHDDR.L2DATA[0]; + + while ((*ptr2)) + { + *(ptr3++) = *(ptr2++); + } + + IDHDDR.LENGTH = (int)(ptr3 - (unsigned char *)&IDHDDR); + + int3 = (uintptr_t)NEXTFREEDATA; + int3 += 7; + int3 &= 0xfffffffffffffff8; + NEXTFREEDATA = (UCHAR *)int3; + ENDBUFFERPOOL = NEXTFREEDATA + DATABYTES; // So init will work, set to actual end later + + BUFFERPOOL = NEXTFREEDATA; + + Consoleprintf("PORTS %p LINKS %p DESTS %p ROUTES %p L4 %p BUFFERS %p\n", + PORTTABLE, LINKS, DESTS, NEIGHBOURS, L4TABLE, BUFFERPOOL); + + Debugprintf("PORTS %p LINKS %p DESTS %p ROUTES %p L4 %p BUFFERS %p END POOL %p", + PORTTABLE, LINKS, DESTS, NEIGHBOURS, L4TABLE, BUFFERPOOL, DATAAREA + DATABYTES); + + i = NUMBEROFBUFFERS; + + NUMBEROFBUFFERS = 0; + + while (i-- && NEXTFREEDATA < (DATAAREA + DATABYTES - (512 + 8192))) // Keep 8K free for anything that needs shared memory + { + Bufferlist[NUMBEROFBUFFERS] = (void **)NEXTFREEDATA; + + ReleaseBuffer((UINT *)NEXTFREEDATA); + NEXTFREEDATA += BUFFALLOC; // was BUFFLEN + + NUMBEROFBUFFERS++; + MAXBUFFS++; + } + + ENDBUFFERPOOL = NEXTFREEDATA; + + + // Copy Bridge Map + + memcpy(BridgeMap, &cfg->CfgBridgeMap, sizeof(BridgeMap)); + +// MOV EAX,_NEXTFREEDATA +// CALL HEXOUT + + // SET UP OUR CALLIGN(S) + + ConvToAX25(MYNETROMCALL, NETROMCALL); + + ConvToAX25(MYNODECALL, MYCALL); + memcpy(&IDHDDR.ORIGIN[0], MYCALL, 7); + IDHDDR.ORIGIN[6] |= 0x61; // SET CMD END AND RESERVED BITS + + ConvToAX25(MYALIASTEXT, MYALIAS); + + // SET UP INITIAL DEST ENTRY FOR APPLICATIONS (IF BOTH NODE AND BBS NEEDED) + // Actually wrong - we need to set up application node entries even if node = 0 + DEST = DESTS; + + // If NODECALL isn't same as NETROMCALL, Add Dest Entry for NODECALL + + if (memcmp(NETROMCALL, MYCALL, 7) != 0) + { + memcpy(DEST->DEST_CALL, MYCALL, 7); + memcpy(DEST->DEST_ALIAS, MYALIASTEXT, 6); + + DEST->DEST_STATE = 0x80; // SPECIAL ENTRY + DEST->NRROUTE[0].ROUT_QUALITY = 255; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + DEST++; + NUMBEROFNODES++; + } + + if (BBS) + { + // Add Application Entries + + APPL = &APPLCALLTABLE[0]; + i = NumberofAppls; + + while (i--) + { + if (APPL->APPLQUAL) + { + memcpy(DEST->DEST_CALL, APPL->APPLCALL, 13); + DEST->DEST_STATE = 0x80; // SPECIAL ENTRY + DEST->NRROUTE[0].ROUT_QUALITY = (UCHAR)APPL->APPLQUAL; + DEST->NRROUTE[0].ROUT_OBSCOUNT = 255; + APPL->NODEPOINTER = DEST; + + DEST++; + + NUMBEROFNODES++; + } + APPL++; + } + } + + // Read Node and MH Recovery Files + + ReadNodes(); + + if (AUTOSAVEMH) + ReadMH(); // Only if AutoSave configured + + // set up stream number in BPQHOSTVECTOR + + for (i = 0; i < 64; i++) + { + BPQHOSTVECTOR[i].HOSTSTREAM = i + 1; + } + + memcpy(MYCALLWITHALIAS, MYCALL, 7); + memcpy(&MYCALLWITHALIAS[7], MYALIASTEXT, 6); + + // Set random start value for NETROM Session ID + + NEXTID = (rand() % 254) + 1; + + GetPortCTEXT(0, 0, 0, 0); + + upnpInit(); + + lastSaveSecs = CurrentSecs = lastSlowSecs = time(NULL); + + return 0; +} + +BOOL CompareCalls(UCHAR * c1, UCHAR * c2) +{ + // COMPARE AX25 CALLSIGNS IGNORING EXTRA BITS IN SSID + + if (memcmp(c1, c2, 6)) + return FALSE; // No Match + + if ((c1[6] & 0x1e) == (c2[6] & 0x1e)) + return TRUE; + + return FALSE; +} +BOOL CompareAliases(UCHAR * c1, UCHAR * c2) +{ + // COMPARE first 6 chars of AX25 CALLSIGNS + + if (memcmp(c1, c2, 6)) + return FALSE; // No Match + + return TRUE; +} +BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE) +{ + struct ROUTE * ROUTE = NEIGHBOURS; + struct ROUTE * FIRSTSPARE = NULL; + int n = MAXNEIGHBOURS; + + while (n--) + { + if (ROUTE->NEIGHBOUR_CALL[0] == 0) // Spare + if (FIRSTSPARE == NULL) + FIRSTSPARE = ROUTE; + + if (ROUTE->NEIGHBOUR_PORT != Port) + { + ROUTE++; + continue; + } + if (CompareCalls(ROUTE->NEIGHBOUR_CALL, Call)) + { + *REQROUTE = ROUTE; + return TRUE; + } + ROUTE++; + } + + // ENTRY NOT FOUND - FIRSTSPARE HAS FIRST FREE ENTRY, OR ZERO IF TABLE FULL + + *REQROUTE = FIRSTSPARE; + return FALSE; +} + +BOOL FindDestination(UCHAR * Call, struct DEST_LIST ** REQDEST) +{ + struct DEST_LIST * DEST = DESTS; + struct DEST_LIST * FIRSTSPARE = NULL; + int n = MAXDESTS; + + while (n--) + { + if (DEST->DEST_CALL[0] == 0) // Spare + { + if (FIRSTSPARE == NULL) + FIRSTSPARE = DEST; + + DEST++; + continue; + } + if (CompareCalls(DEST->DEST_CALL, Call)) + { + *REQDEST = DEST; + return TRUE; + } + DEST++; + } + + // ENTRY NOT FOUND - FIRSTSPARE HAS FIRST FREE ENTRY, OR ZERO IF TABLE FULL + + *REQDEST = FIRSTSPARE; + return FALSE; +} + +extern UCHAR BPQDirectory[]; + +#define LINE_MAX 256 + + +// Reload saved MH file + +VOID ReadMH() +{ + char FN[260]; + FILE *fp; + char line[LINE_MAX]; + UCHAR axcall[7]; + char * ptr, *digi, *locptr, *locend; + char * Context, * Context2; + char seps[] = " \n"; + int Port; + struct PORTCONTROL * PORT = NULL; + MHSTRUC * MH; + int count = MHENTRIES; + char * Digiptr; + BOOL Digiused; + char * HasStar; + + // Set up pointer to BPQNODES file + + if (BPQDirectory[0] == 0) + { + strcpy(FN,"MHSave.txt"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"MHSave.txt"); + } + + if ((fp = fopen(FN, "r")) == NULL) + { + return; + } + + while (fgets(line, LINE_MAX, fp) != NULL) + { + if (memcmp(line, "Port:", 5) == 0) + { + Port = atoi(&line[5]); + PORT = GetPortTableEntryFromPortNum(Port); + if (PORT) + MH = PORT->PORTMHEARD; + else + MH = NULL; + + continue; + } + + // 1548777630 N9LYA-8 Jan 29 16:00:30 + + if (MH) + { + ptr = strtok_s(line, seps, &Context); + + if (ptr == NULL) + continue; + + MH->MHTIME = atoi(ptr); + + ptr = strtok_s(NULL, seps, &Context); + + if (ptr == NULL) + continue; + + MH->MHCOUNT = atoi(ptr); + + // Get LOC and Freq First before strtok messes with line + + locptr = strchr(Context, '|'); + + if (locptr == NULL) + continue; + + locend = strchr(++locptr, '|'); + + if (locend == NULL) + continue; + + if ((locend - locptr) == 6) + + memcpy(MH->MHLocator, locptr, 6); + + locend++; + + strlop(locend, '\n'); + + if (strlen(locend) < 12) + + strcpy(MH->MHFreq, locend); + + ptr = strtok_s(NULL, "|\n", &Context); + + if (ptr == NULL) + continue; + + if (ConvToAX25(ptr, axcall) == FALSE) + continue; // Duff + + memcpy(MH->MHCALL, axcall, 7); + + if (ptr[10] != ' ') + MH->MHDIGI = ptr[10]; + + // SEE IF ANY DIGIS + + digi = strstr(ptr, " via "); + + if (digi) + { + digi = digi + 5; + Digiptr = &MH->MHDIGIS[0][0]; + + if (strchr(ptr, '*')) + Digiused = 1; // At least one digi used + else + Digiused = 0; + + digi = strtok_s(digi, ",\n", &Context2); + + while(digi) + { + HasStar = strlop(digi, '*'); + + if (ConvToAX25(digi, axcall) == FALSE) + break; + + memcpy(Digiptr, axcall, 7); + + if (Digiused) + Digiptr[6] |= 0x80; // Set used + + if (HasStar) + Digiused = 0; // Only last used has * + + Digiptr += 7; + + digi = strtok_s(NULL, ",/n", &Context2); + } + + *(--Digiptr) |= 1; // Set end of address on last + + } + else + { + // No Digis + + MH->MHCALL[6] |= 1; // Set end of address + } + + MH++; + } + } + + fclose(fp); + return; +} + + +VOID ReadNodes() +{ + char FN[260]; + FILE *fp; + char line[LINE_MAX]; + UCHAR axcall[7]; + char * ptr; + char * Context; + char seps[] = " \r"; + int Port, Qual; + struct PORTCONTROL * PORT; + + // Set up pointer to BPQNODES file + + if (BPQDirectory[0] == 0) + { + strcpy(FN,"BPQNODES.dat"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"BPQNODES.dat"); + } + + if ((fp = fopen(FN, "r")) == NULL) + { + WritetoConsoleLocal( + "Route/Node recovery file BPQNODES.dat not found - Continuing without it\n"); + return; + } + + // Read the saved ROUTES/NODES file + + while (fgets(line, LINE_MAX, fp) != NULL) + { + if (memcmp(line, "ROUTE ADD", 9) == 0) + { + struct ROUTE * ROUTE = NULL; + + // FORMAT IS ROUTE ADD CALLSIGN PORT QUAL (VIA .... + + ptr = strtok_s(&line[10], seps, &Context); + + if (ConvToAX25(ptr, axcall) == FALSE) + continue; // DUff + + ptr = strtok_s(NULL, seps, &Context); + if (ptr == NULL) continue; + Port = atoi(ptr); + + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + continue; // Port has gone + + if (FindNeighbour(axcall, Port, &ROUTE)) + continue; // Already added from ROUTES: + + if (ROUTE == NULL) + continue; // Tsble Full + + memcpy(ROUTE->NEIGHBOUR_CALL, axcall, 7); + ROUTE->NEIGHBOUR_PORT = Port; + + ptr = strtok_s(NULL, seps, &Context); + if (ptr == NULL) continue; + Qual = atoi(ptr); + + ROUTE->NEIGHBOUR_QUAL = Qual; + + ptr = strtok_s(NULL, seps, &Context); // MAXFRAME + if (ptr == NULL) continue; + + // I don't thinlk we should load locked flag from save file - only from config + // Now (2025) have two locked flags, from config or by sysop. Latter is saved and restored + + if (ptr[0] == '!') + { + ROUTE->NEIGHBOUR_FLAG = LOCKEDBYSYSOP; // LOCKED ROUTE + ptr = strtok_s(NULL, seps, &Context); + if (ptr == NULL) continue; + } + + // SEE IF ANY DIGIS + + if (ptr[0] == 'V') + { + ptr = strtok_s(NULL, seps, &Context); + if (ptr == NULL) continue; + + if (ConvToAX25(ptr, axcall) == FALSE) + continue; // DUff + + memcpy(ROUTE->NEIGHBOUR_DIGI1, axcall, 7); + + ROUTE->NEIGHBOUR_FLAG = LOCKEDBYSYSOP; // LOCKED ROUTE - Digi'ed routes must be locked + + ptr = strtok_s(NULL, seps, &Context); + if (ptr == NULL) continue; + + // See if another digi or MAXFRAME + + if (strlen(ptr) > 2) + { + if (ConvToAX25(ptr, axcall) == FALSE) + continue; // DUff + + memcpy(ROUTE->NEIGHBOUR_DIGI2, axcall, 7); + + ptr = strtok_s(NULL, seps, &Context); + if (ptr == NULL) continue; + } + } + + Qual = atoi(ptr); + ROUTE->NBOUR_MAXFRAME = Qual; + + ptr = strtok_s(NULL, seps, &Context); // FRACK + if (ptr == NULL) continue; + Qual = atoi(ptr); + ROUTE->NBOUR_FRACK = Qual; + + ptr = strtok_s(NULL, seps, &Context); // PACLEN + if (ptr == NULL) continue; + Qual = atoi(ptr); + ROUTE->NBOUR_PACLEN = Qual; + + ptr = strtok_s(NULL, seps, &Context); // INP3 + if (ptr == NULL) continue; + Qual = atoi(ptr); + + // We now take Nokeepalives from the PORT, unless specifically + // Requested + + ROUTE->NoKeepAlive = PORT->PortNoKeepAlive; + + if (Qual & 4) + ROUTE->NoKeepAlive = TRUE; + + if ((Qual & 1) || PORT->INP3ONLY) + { + ROUTE->NoKeepAlive = FALSE; + ROUTE->INP3Node = TRUE; + } + + ptr = strtok_s(NULL, seps, &Context); // INP3 + if (ptr == NULL) continue; + + if (ROUTE->NEIGHBOUR_FLAG == 0 || ROUTE->OtherendLocked == 0); // Not LOCKED ROUTE + ROUTE->OtherendsRouteQual = atoi(ptr); + + ptr = strtok_s(NULL, seps, &Context); // INP3 + if (ptr == NULL) continue; + + if (ptr[0] == '!') + { + ROUTE->NEIGHBOUR_FLAG = LOCKEDBYSYSOP; // LOCKED ROUTE + ptr = strtok_s(NULL, seps, &Context); + } + continue; + } + + if (memcmp(line, "NODE ADD", 8) == 0) + { + // FORMAT IS NODE ADD ALIAS:CALL ROUTE QUAL + + dest_list * DEST = NULL; + struct ROUTE * ROUTE = NULL; + char * ALIAS; + char FULLALIAS[6] = " "; + int SavedOBSINIT = OBSINIT; + + if (line[9] == ':') + { + // No alias + + Context = &line[10]; + } + else + { + ALIAS = strtok_s(&line[9], ":", &Context); + + if (ALIAS == NULL) + continue; + + memcpy(FULLALIAS, ALIAS, (int)strlen(ALIAS)); + } + + ptr = strtok_s(NULL, seps, &Context); + if (ptr == NULL) + continue; + + if (ConvToAX25(ptr, axcall) == FALSE) + continue; // Duff + + if (CompareCalls(axcall, MYCALL)) + continue; // Shoiuldn't happen, but to be safe! + + if (FindDestination(axcall, &DEST)) + continue; + + if (DEST == NULL) + continue; // Tsble Full + + memcpy(DEST->DEST_CALL, axcall, 7); + memcpy(DEST->DEST_ALIAS, FULLALIAS, 6); + + NUMBEROFNODES++; +RouteLoop: + // GET NEIGHBOURS FOR THIS DESTINATION - CALL PORT QUAL + + ptr = strtok_s(NULL, seps, &Context); + if (ptr == NULL) continue; + + if (ConvToAX25(ptr, axcall) == FALSE) + continue; // DUff + + ptr = strtok_s(NULL, seps, &Context); + if (ptr == NULL) continue; + Port = atoi(ptr); + + ptr = strtok_s(NULL, seps, &Context); + if (ptr == NULL) continue; + Qual = atoi(ptr); + + if (Context[0] == '!') + { + OBSINIT = 255; //; SPECIAL FOR LOCKED + } + + if (FindNeighbour(axcall, Port, &ROUTE)) + { + PROCROUTES(DEST, ROUTE, Qual); + } + + OBSINIT = SavedOBSINIT; + + goto RouteLoop; + } + } + + fclose(fp); + +// loadedmsg DB cr,lf,lf,'Switch loaded and initialised OK',lf,0 + + return; +} + +int DelayBuffers = 0; + +void sendModeReport(); +void sendFreqReport(); + +VOID TIMERINTERRUPT() +{ + // Main Processing loop - CALLED EVERY 100 MS + + int i; + struct PORTCONTROL * PORT = PORTTABLE; + PMESSAGE Buffer; + struct _LINKTABLE * LINK; + struct _MESSAGE * Message; + int toPort; + + CurrentSecs = time(NULL); + + BGTIMER++; + REALTIMETICKS++; + L2TIMERFLAG++; // INCREMENT FLAG FOR BG + L3TIMERFLAG++; // INCREMENT FLAG FOR BG + L4TIMERFLAG++; // INCREMENT FLAG FOR BG + +// CALL PORT TIMER ROUTINES + + for (i = 0; i < NUMBEROFPORTS; i++) + { + PORT->PORTTIMERCODE(PORT); + + // Check Smart ID timer + + if (PORT->SmartIDNeeded && PORT->SmartIDNeeded < time(NULL)) + SendSmartID(PORT); + + PORT = PORT->PORTPOINTER; + } + + // CHECK FOR TIMER ACTIVITY + + if (L2TIMERFLAG >= 3) + { + L2TIMERFLAG -= 3; + L2TimerProc(); // 300 mS + } + + if (CurrentSecs - lastSlowSecs >= 60) // 1 PER MIN + { + lastSlowSecs = CurrentSecs; + + L3TimerProc(); + + if (needAIS) + AISTimer(); + + if (needADSB) + ADSBTimer(); + + if (APRSActive) + Debugprintf("BPQ32 Heartbeat Buffers %d APRS Queues %d %d", QCOUNT, C_Q_COUNT(&APRSMONVECPTR->HOSTTRACEQ), C_Q_COUNT(&APPL_Q)); + else + Debugprintf("BPQ32 Heartbeat Buffers %d", QCOUNT); + + StatsTimer(); + + // Call EXT Port Slow Timer + + PORT = PORTTABLE; + + for (i = 0; i < NUMBEROFPORTS; i++) + { + if (PORT->PROTOCOL == 10) + { + PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT; + EXTSLOWTIMER(PORTVEC); + } + + PORT = PORT->PORTPOINTER; + } + + // Call Mode Map support routine + + sendFreqReport(); + sendModeReport(); + + if (MQTT) + MQTTTimer(); + +/* + if (QCOUNT < 200) + { + if (DelayBuffers == 0) + { + FindLostBuffers(); + DelayBuffers = 10; + } + else + { + DelayBuffers--; + } + } +*/ + } + + // Check Autosave Nodes and MH timer + + if (CurrentSecs - lastSaveSecs >= 3600) // 1 per hour + { + lastSaveSecs = CurrentSecs; + + if (AUTOSAVE == 1) + SaveNodes(); + if (AUTOSAVEMH == 1) + SaveMH(); + } + + if (L4TIMERFLAG >= 10) // 1 PER SEC + { + L4TIMERFLAG -= 10; + + L3FastTimer(); + L4TimerProc(); + } + + // SEE IF ANY FRAMES TO TRACE + + Buffer = Q_REM(&TRACE_Q); + + while (Buffer) + { + // IF BUFFER HAS A LINK TABLE ENTRY ON END, RESET TIMEOUT + + LINK = Buffer->Linkptr; + + if (LINK) + { + if (LINK->L2TIMER) + LINK->L2TIMER = LINK->L2TIME; + + Buffer->Linkptr = 0; // CLEAR FLAG FROM BUFFER + } + + Message = (struct _MESSAGE *)Buffer; + Message->PORT |= 0x80; // Set TX Bit + + BPQTRACE(Message, FALSE); // Dont send TX'ed frames to APRS + ReleaseBuffer(Buffer); + + Buffer = Q_REM(&TRACE_Q); + } + + // CHECK FOR MESSAGES RECEIVED FROM COMMS LINKS + + PORT = PORTTABLE; + + for (i = 0; i < NUMBEROFPORTS; i++) + { + int Sent = 0; + + CURRENTPORT = PORT->PORTNUMBER; // PORT NUMBER + CURRENTPORTPTR = PORT; + + Buffer = (PMESSAGE)Q_REM((void *)&PORT->PORTRX_Q); + + while (Buffer) + { + Message = (struct _MESSAGE *) Buffer; + if (CURRENTPORT == 30) + Sent = Sent; + + if (PORT->PROTOCOL == 10) + { + int Sessno = Message->PORT; + PEXTPORTDATA PORTVEC = (PEXTPORTDATA)PORT; + TRANSPORTENTRY * Session; + TRANSPORTENTRY * Partner; + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + { + + // KISSHF - May be L2 packet or Test Response + + if (Message->DEST[0] != 240) // Should only be ax.25 address field + goto L2Packet; + } + + // PACTOR Style Message + + InOctets[PORT->PORTNUMBER] += Message->LENGTH - (MSGHDDRLEN + 1); + PORT->L2FRAMESFORUS++; + + Session = PORTVEC->ATTACHEDSESSIONS[Sessno]; + + if (Session == NULL) + { + // TNC not attached - discard + + ReleaseBuffer(Buffer); + Buffer = (PMESSAGE)Q_REM((void *)&PORT->PORTRX_Q); + continue; + } + + Session->L4KILLTIMER = 0; // Reset Idle Timeout + + Partner = Session->L4CROSSLINK; + + if (Partner == NULL) + { + // No Crosslink - pass to command handler + + CommandHandler(Session, (PDATAMESSAGE)Buffer); + break; + } + + if (Partner->L4STATE < 5) + { + // MESSAGE RECEIVED BEFORE SESSION IS UP - CANCEL SESSION + // AND PASS MESSAGE TO COMMAND HANDLER + + if (Partner->L4CIRCUITTYPE & L2LINK) // L2 SESSION? + { + // MUST CANCEL L2 SESSION + + LINK = Partner->L4TARGET.LINK; + LINK->CIRCUITPOINTER = NULL; // CLEAR REVERSE LINK + + LINK->L2STATE = 4; // DISCONNECTING + LINK->L2TIMER = 1; // USE TIMER TO KICK OFF DISC + + LINK->L2RETRIES = LINK->LINKPORT->PORTN2 - 2; //ONLY SEND DISC ONCE + } + + CLEARSESSIONENTRY(Partner); + Session->L4CROSSLINK = 0; // CLEAR CROSS LINK + CommandHandler(Session, (struct DATAMESSAGE *)Buffer); + break; + } + + C_Q_ADD(&Partner->L4TX_Q, Buffer); + PostDataAvailable(Partner); + Buffer = (PMESSAGE)Q_REM((void *)&PORT->PORTRX_Q); + continue; + } + + +L2Packet: + // TIME STAMP IT + + time(&Message->Timestamp); + + Message->PORT = CURRENTPORT; + + if (MQTT && PORT->PROTOCOL == 0) + MQTTKISSRX(Buffer); + + // Bridge if requested + + for (toPort = 1; toPort <= MaxBPQPortNo; toPort++) + { + if (BridgeMap[CURRENTPORT][toPort]) + { + MESSAGE * BBuffer = GetBuff(); + struct PORTCONTROL * BPORT; + + if (BBuffer) + { + memcpy(BBuffer, Message, Message->LENGTH); + BBuffer->PORT = toPort; + BPORT = GetPortTableEntryFromPortNum(toPort); + + if (BPORT) + { + if (BPORT->SmartIDInterval && BPORT->SmartIDNeeded == 0) + { + // Using Smart ID, but none scheduled + + BPORT->SmartIDNeeded = time(NULL) + BPORT->SmartIDInterval; + } + PUT_ON_PORT_Q(BPORT, BBuffer); + } + else + ReleaseBuffer(BBuffer); + } + } + } + + L2Routine(PORT, Buffer); + + Buffer = (PMESSAGE)Q_REM((void *)&PORT->PORTRX_Q); + continue; + } + + // End of RX_Q + + Sent = 0; + + while (PORT->PORTTX_Q && Sent < 5) + { + int ret; + void * PACTORSAVEQ; + + Buffer = PORT->PORTTX_Q; + Message = (struct _MESSAGE *) Buffer; + + ret = PORT->PORTTXCHECKCODE(PORT, Message->PORT); + + // Busy but not connected means TNC has gone - clear queue + + if (ret == 1) + { + MESSAGE * Buffer = (PMESSAGE)Q_REM((void *)&PORT->PORTTX_Q); + + if (Buffer == 0) + break; // WOT!! + +// Debugprintf("Busy but not connected - discard message %s", Buffer->L2DATA); + + ReleaseBuffer(Buffer); + break; + } + + ret = ret & 0xff; // Only check bottom byte + + if (ret == 0) // Not busy + { + MESSAGE * Buffer = (PMESSAGE)Q_REM((void *)&PORT->PORTTX_Q); + + if (Buffer == 0) + break; // WOT!! + + if (PORT->PORTDISABLED) + { + ReleaseBuffer(Buffer); + break; + } + + PORT->L2FRAMESSENT++; + OutOctets[PORT->PORTNUMBER] += Buffer->LENGTH - MSGHDDRLEN; + + PORT->PORTTXROUTINE((struct _EXTPORTDATA *)PORT, Buffer); + Sent++; + + continue; + } + + // If a Pactor Port, some channels could be busy whilst others are not. + + if (PORT->PROTOCOL != 10) + break; // BUSY + + // Try passing any other messages on the queue to the node. + + PACTORSAVEQ = 0; + +PACTORLOOP: + + Buffer = PORT->PORTTX_Q; + + if (Buffer == NULL) + goto ENDOFLIST; + + Message = (struct _MESSAGE *) Buffer; + ret = PORT->PORTTXCHECKCODE(PORT, Message->PORT); + ret = ret & 0xff; // Only check bottom byte + + if (ret) // Busy + { + // Save it + + Buffer = (PMESSAGE)Q_REM((void *)&PORT->PORTTX_Q); + C_Q_ADD(&PACTORSAVEQ, Buffer); + goto PACTORLOOP; + } + + Buffer = (PMESSAGE)Q_REM((void *)&PORT->PORTTX_Q); + + if (PORT->PORTDISABLED) + { + ReleaseBuffer(Buffer); + goto PACTORLOOP; + } + + PORT->L2FRAMESSENT++; + OutOctets[PORT->PORTNUMBER] += Message->LENGTH; + + PORT->PORTTXROUTINE((struct _EXTPORTDATA *)PORT, Buffer); + Sent++; + + if (Sent < 5) + goto PACTORLOOP; // SEE IF MORE + +ENDOFLIST: + // Move the saved frames back onto Port Q + + PORT->PORTTX_Q = PACTORSAVEQ; + break; + } + + PORT->PORTRXROUTINE((struct _EXTPORTDATA *)PORT); // SEE IF MESSAGE RECEIVED + PORT = PORT->PORTPOINTER; + } + +/* +; +; CHECK FOR INCOMING MESSAGES ON LINK CONTROL TABLE - +; BY NOW ONLY 'I' FRAMES WILL BE PRESENT - +; LEVEL 2 PROTOCOL HANDLING IS DONE IN MESSAGE RECEIVE CODE +; AND LINK HANDLING INTERRUPT ROUTINES +; +*/ + + LINK = LINKS; + i = MAXLINKS; + + while (i--) + { + if (LINK->LINKCALL[0]) + { + Buffer = Q_REM(&LINK->RX_Q); + + while (Buffer) + { + ProcessIframe(LINK, (PDATAMESSAGE)Buffer); + + Buffer =(PMESSAGE)Q_REM((void *)&LINK->RX_Q); + } + + // CHECK FOR OUTGOING MSGS + + if (LINK->L2STATE >= 5) // CANT SEND TEXT TILL CONNECTED + { + // CMP VER1FLAG[EBX],1 + // JE SHORT MAINL35 ; NEED TO RETRY WITH I FRAMES IF VER 1 + + // CMP L2RETRIES[EBX],0 + // JNE SHORT MAINL40 ; CANT SEND TEXT IF RETRYING + + if ((LINK->L2FLAGS & RNRSET) == 0) + SDETX(LINK); + } + } + LINK++; + } + + L4BG(); // DO LEVEL 4 PROCESSING + L3BG(); + TNCTimerProc(); +} + +VOID DoListenMonitor(TRANSPORTENTRY * L4, MESSAGE * Msg) +{ + uint64_t SaveMMASK = MMASK; + BOOL SaveMTX = MTX; + BOOL SaveMCOM = MCOM; + BOOL SaveMUI = MUIONLY; + PDATAMESSAGE Buffer; + char MonBuffer[1024]; + int len; + struct tm * TM; + UCHAR * monchars = (UCHAR *)Msg; + + if (CountFramesQueuedOnSession(L4) > 10) + return; + + if (monchars[21] == 3 && monchars[22] == 0xcf && monchars[23] == 0xff) // Netrom Nodes + return; + + IntSetTraceOptionsEx(L4->LISTEN, 1, 0, 0); + + TM = gmtime(&Msg->Timestamp); + sprintf(MonBuffer, "%02d:%02d:%02d ", TM->tm_hour, TM->tm_min, TM->tm_sec); + + len = IntDecodeFrame(Msg, &MonBuffer[9], Msg->Timestamp, L4->LISTEN, FALSE, TRUE); + + IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); + + if (len == 0) + return; + + len += 9; + + if (len > 256) + len = 256; + + Buffer = GetBuff(); + + if (Buffer) + { + char * ptr = &Buffer->L2DATA[0]; + Buffer->PID = 0xf0; + + memcpy(ptr, MonBuffer, len); + + Buffer->LENGTH = (USHORT)len + 4 + sizeof(void *); + + C_Q_ADD(&L4->L4TX_Q, Buffer); + + PostDataAvailable(L4); + } +} + +DllExport int APIENTRY DllBPQTRACE(MESSAGE * Msg, BOOL TOAPRS) +{ + int ret; + GetSemaphore(&Semaphore, 88); + ret = BPQTRACE(Msg, TOAPRS); + FreeSemaphore(&Semaphore); + return ret; +} + +int BPQTRACE(MESSAGE * Msg, BOOL TOAPRS) +{ + // ATTACH A COPY OF FRAME TO ANY BPQ HOST PORTS WITH MONITORING ENABLED + + TRANSPORTENTRY * L4 = L4TABLE; + + MESSAGE * Buffer; + int i = BPQHOSTSTREAMS + 2; // Include Telnet and AGW Stream + + if (TOAPRS) + i++; // Include APRS Stream + + while(i) + { + i--; + + if (QCOUNT < 100) + return FALSE; + + if (BPQHOSTVECTOR[i].HOSTAPPLFLAGS & 0x80) // Trace Enabled? + { + Buffer = GetBuff(); + if (Buffer) + { + memcpy(&Buffer->PORT, &Msg->PORT, BUFFLEN - sizeof(void *)); // Dont copy chain word + C_Q_ADD(&BPQHOSTVECTOR[i].HOSTTRACEQ, Buffer); + +#ifndef LINBPQ + if (BPQHOSTVECTOR[i].HOSTHANDLE) + PostMessage(BPQHOSTVECTOR[i].HOSTHANDLE, BPQMsg, BPQHOSTVECTOR[i].HOSTSTREAM, 1); +#endif + } + } + + } + + // Also pass to any users LISTENING on this port + + i = MAXCIRCUITS; + + if (QCOUNT < 300) + return FALSE; // Until I add by session flow control + + while (i--) + { + if (L4->LISTEN) + if ((((uint64_t)1 << ((Msg->PORT & 0x7f) - 1)) & L4->LISTEN)) + // if ((Msg->PORT & 0x7f) == L4->LISTEN) + DoListenMonitor(L4, Msg); + + L4++; + } + + return TRUE; +} +; + +VOID INITIALISEPORTS() +{ + char INITMSG[80]; + struct PORTCONTROL * PORT = PORTTABLE; + + while (PORT) + { + sprintf(INITMSG, "Initialising Port %02d ", PORT->PORTNUMBER); + WritetoConsoleLocal(INITMSG); + + PORT->PORTINITCODE(PORT); + PORT = PORT->PORTPOINTER; + } +} + +VOID FindLostBuffers() +{ + void ** Buff; + int n, i; + unsigned int rev; + + UINT CodeDump[16]; + char codeText[65] = ""; + unsigned char * codeByte = (unsigned char *) CodeDump; + + PBPQVECSTRUC HOSTSESS = BPQHOSTVECTOR; + struct _TRANSPORTENTRY * L4; // Pointer to Session + + struct DEST_LIST * DEST = DESTS; + + struct ROUTE * Routes = NEIGHBOURS; + int MaxRoutes = MAXNEIGHBOURS; + int Queued; + char Call[10]; + + n = MAXDESTS; + + Debugprintf("Looking for missing Buffers"); + + while (n--) + { + if (DEST->DEST_CALL[0] && DEST->DEST_Q) // Spare + { + Debugprintf("DEST Queue %s %d", DEST->DEST_ALIAS, C_Q_COUNT(&DEST->DEST_Q)); + } + + DEST++; + } + + n = 0; + + while (n < BPQHOSTSTREAMS + 4) + { + // Check Trace Q + + if (HOSTSESS->HOSTTRACEQ) + { + int Count = C_Q_COUNT(&HOSTSESS->HOSTTRACEQ); + + Debugprintf("Trace Buffers Stream %d Count %d", n, Count); + + L4 = HOSTSESS->HOSTSESSION; + + if (L4 && (L4->L4TX_Q || L4->L4RX_Q || L4->L4HOLD_Q || L4->L4RESEQ_Q)) + Debugprintf("Stream %d %d %d %d %d", n, C_Q_COUNT(&L4->L4TX_Q), + C_Q_COUNT(&L4->L4RX_Q), C_Q_COUNT(&L4->L4HOLD_Q), C_Q_COUNT(&L4->L4RESEQ_Q)); + + } + n++; + HOSTSESS++; + } + + n = MAXCIRCUITS; + L4 = L4TABLE; + + while (n--) + { + if (L4->L4USER[0] == 0) + { + L4++; + continue; + } + if (L4->L4TX_Q || L4->L4RX_Q || L4->L4HOLD_Q || L4->L4RESEQ_Q) + Debugprintf("L4 %d TX %d RX %d HOLD %d RESEQ %d", MAXCIRCUITS - n, C_Q_COUNT(&L4->L4TX_Q), + C_Q_COUNT(&L4->L4RX_Q), C_Q_COUNT(&L4->L4HOLD_Q), C_Q_COUNT(&L4->L4RESEQ_Q)); + L4++; + } + + // Routes + + while (MaxRoutes--) + { + if (Routes->NEIGHBOUR_CALL[0] != 0) + { + Call[ConvFromAX25(Routes->NEIGHBOUR_CALL, Call)] = 0; + if (Routes->NEIGHBOUR_LINK) + { + Queued = COUNT_AT_L2(Routes->NEIGHBOUR_LINK); // SEE HOW MANY QUEUED + if (Queued) + Debugprintf("Route %s %d", Call, Queued); + } + } + Routes++; + } + + // Build list of buffers, then mark off all on free Q + + Buff = BUFFERPOOL; + n = 0; + + for (i = 0; i < NUMBEROFBUFFERS; i++) + { + Bufferlist[n++] = Buff; + Buff += (BUFFALLOC / sizeof(void *)); + } + + Buff = FREE_Q; + + while (Buff) + { + n = NUMBEROFBUFFERS; + + while (n--) + { + if (Bufferlist[n] == Buff) + { + Bufferlist[n] = 0; + break; + } + } + Buff = *Buff; + } + n = NUMBEROFBUFFERS; + + while (n--) + { + if (Bufferlist[n]) + { + char * fileptr = (char *)Bufferlist[n]; + MESSAGE * Msg = (MESSAGE *)Bufferlist[n]; + + memcpy(CodeDump, Bufferlist[n], 64); + + for (i = 0; i < 64; i++) + { + if (codeByte[i] > 0x1f && codeByte[i] < 0x80) + codeText[i] = codeByte[i]; + else + codeText[i] = '.'; + } + + for (i = 0; i < 16; i++) + { + rev = (CodeDump[i] & 0xff) << 24; + rev |= (CodeDump[i] & 0xff00) << 8; + rev |= (CodeDump[i] & 0xff0000) >> 8; + rev |= (CodeDump[i] & 0xff000000) >> 24; + + CodeDump[i] = rev; + } + + Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ", + Bufferlist[n], CodeDump[0], CodeDump[1], CodeDump[2], CodeDump[3], CodeDump[4], CodeDump[5], CodeDump[6], CodeDump[7]); + + Debugprintf(" %08x %08x %08x %08x %08x %08x %08x %08x %d", + CodeDump[8], CodeDump[9], CodeDump[10], CodeDump[11], CodeDump[12], CodeDump[13], CodeDump[14], CodeDump[15], Msg->Process); + + Debugprintf(" %s %s", &fileptr[400], codeText); + } + } + + // rebuild list for buffer check + Buff = BUFFERPOOL; + n = 0; + + for (i = 0; i < NUMBEROFBUFFERS; i++) + { + Bufferlist[n++] = Buff; + Buff += (BUFFALLOC / sizeof(void *)); + } +} + +void WriteConnectLog(char * fromCall, char * toCall, UCHAR * Mode) +{ + UCHAR FN[MAX_PATH]; + FILE * LogHandle; + time_t T; + struct tm * tm; + char LogMsg[256]; + int MsgLen; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(FN,"%s/logs/ConnectLog_%02d%02d%02d.log", LogDirectory, tm->tm_year - 100, tm->tm_mon + 1, tm->tm_mday); + + LogHandle = fopen(FN, "ab"); + + if (LogHandle == NULL) + return; + + MsgLen = sprintf(LogMsg, "%02d:%02d:%02d Call from %s to %s Mode %s\r\n", tm->tm_hour, tm->tm_min, tm->tm_sec, fromCall, toCall, Mode); + + fwrite(LogMsg , 1, MsgLen, LogHandle); + fclose(LogHandle); +} + + + diff --git a/.svn/pristine/43/43f92e6002d31c4a18f25bcb2dc2bc8c825c37c0.svn-base b/.svn/pristine/43/43f92e6002d31c4a18f25bcb2dc2bc8c825c37c0.svn-base new file mode 100644 index 0000000..bda24f1 --- /dev/null +++ b/.svn/pristine/43/43f92e6002d31c4a18f25bcb2dc2bc8c825c37c0.svn-base @@ -0,0 +1,1801 @@ +/* +Copyright 2001-2018 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 +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// lzhuf Routines + +#include "bpqmail.h" + +BOOL isAMPRMsg(char * Addr); + +struct Country * FindCountry(char * Name); +struct UserInfo * FindAMPR(); + +/************************************************************** + lzhuf.c + written by Haruyasu Yoshizaki 1988/11/20 + some minor changes 1989/04/06 + comments translated by Haruhiko Okumura 1989/04/07 + getbit and getbyte modified 1990/03/23 by Paul Edwards + so that they would work on machines where integers are + not necessarily 16 bits (although ANSI guarantees a + minimum of 16). This program has compiled and run with + no errors under Turbo C 2.0, Power C, and SAS/C 4.5 + (running on an IBM mainframe under MVS/XA 2.2). Could + people please use YYYY/MM/DD date format so that everyone + in the world can know what format the date is in? + external storage of filesize changed 1990/04/18 by Paul Edwards to + Intel's "little endian" rather than a machine-dependant style so + that files produced on one machine with lzhuf can be decoded on + any other. "little endian" style was chosen since lzhuf + originated on PC's, and therefore they should dictate the + standard. + initialization of something predicting spaces changed 1990/04/22 by + Paul Edwards so that when the compressed file is taken somewhere + else, it will decode properly, without changing ascii spaces to + ebcdic spaces. This was done by changing the ' ' (space literal) + to 0x20 (which is the far most likely character to occur, if you + don't know what environment it will be running on. +**************************************************************/ + +int ReformatSyncMessage(CIRCUIT * conn); + +#define int __int16 + +/* crctab calculated by Mark G. Mendel, Network Systems Corporation */ + +UCHAR *infile, *outfile, * endinfile; + +short Get() +{ + if (infile == endinfile) + return -1; + else + return *(infile++); +} + +static unsigned short crctab[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + + +#define updcrc(cp, crc) ((crc << 8) ^ crctab[(cp & 0xff) ^ (crc >> 8)]) + +uint32_t textsize = 0, codesize = 0; +unsigned short crc; +int version_1; +char wterr[] = "Can't write."; + +void Error(char *message) +{ + printf("\n%s\n", message); + exit(EXIT_FAILURE); +} + +/********** LZSS compression **********/ + +#define N 2048 /* buffer size */ +#define F 60 /* lookahead buffer size */ +#define THRESHOLD 2 +#define NIL N /* leaf of tree */ + +unsigned char + text_buf[N + F - 1]; +static int match_position, match_length, + lson[N + 1], rson[N + 257], dad[N + 1]; + +static int crc_fputc(unsigned short c) +{ + crc = updcrc(c, crc); + *(outfile++) = (unsigned char)c; + return 0; +} + +short crc_fgetc() +{ + short retour = *(infile++); + + return(retour); +} + +void InitTree(void) /* initialize trees */ +{ + int i; + + for (i = N + 1; i <= N + 256; i++) + rson[i] = NIL; /* root */ + for (i = 0; i < N; i++) + dad[i] = NIL; /* node */ +} + +static void InsertNode(int r) /* insert to tree */ +{ + int i, p, cmp; + unsigned char *key; + unsigned c; + + cmp = 1; + key = &text_buf[r]; + p = N + 1 + key[0]; + rson[r] = lson[r] = NIL; + match_length = 0; + for ( ; ; ) { + if (cmp >= 0) { + if (rson[p] != NIL) + p = rson[p]; + else { + rson[p] = r; + dad[r] = p; + return; + } + } else { + if (lson[p] != NIL) + p = lson[p]; + else { + lson[p] = r; + dad[r] = p; + return; + } + } + for (i = 1; i < F; i++) + if ((cmp = key[i] - text_buf[p + i]) != 0) + break; + if (i > THRESHOLD) { + if (i > match_length) { + match_position = ((r - p) & (N - 1)) - 1; + if ((match_length = i) >= F) + break; + } + if (i == match_length) { + if ((c = ((r - p) & (N-1)) - 1) < (unsigned)match_position) { + match_position = c; + } + } + } + } + dad[r] = dad[p]; + lson[r] = lson[p]; + rson[r] = rson[p]; + dad[lson[p]] = r; + dad[rson[p]] = r; + if (rson[dad[p]] == p) + rson[dad[p]] = r; + else + lson[dad[p]] = r; + dad[p] = NIL; /* remove p */ +} + +static void DeleteNode(int p) /* remove from tree */ +{ + int q; + + if (dad[p] == NIL) + return; /* not registered */ + if (rson[p] == NIL) + q = lson[p]; + else + if (lson[p] == NIL) + q = rson[p]; + else { + q = lson[p]; + if (rson[q] != NIL) { + do { + q = rson[q]; + } while (rson[q] != NIL); + rson[dad[q]] = lson[q]; + dad[lson[q]] = dad[q]; + lson[q] = lson[p]; + dad[lson[p]] = q; + } + rson[q] = rson[p]; + dad[rson[p]] = q; + } + dad[q] = dad[p]; + if (rson[dad[p]] == p) + rson[dad[p]] = q; + else + lson[dad[p]] = q; + dad[p] = NIL; +} + +/* Huffman coding */ + +#define N_CHAR (256 - THRESHOLD + F) + /* kinds of characters (character code = 0..N_CHAR-1) */ +#define T (N_CHAR * 2 - 1) /* size of table */ +#define R (T - 1) /* position of root */ +#define MAX_FREQ 0x8000 /* updates tree when the */ + /* root frequency comes to this value. */ +typedef unsigned char uchar; + + +/* table for encoding and decoding the upper 6 bits of position */ + +/* for encoding */ +uchar p_len[64] = { + 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 +}; + +uchar p_code[64] = { + 0x00, 0x20, 0x30, 0x40, 0x50, 0x58, 0x60, 0x68, + 0x70, 0x78, 0x80, 0x88, 0x90, 0x94, 0x98, 0x9C, + 0xA0, 0xA4, 0xA8, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC, + 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, + 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE, + 0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +/* for decoding */ +uchar d_code[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, + 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, + 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, + 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, + 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, + 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, + 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B, + 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F, + 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, + 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, + 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, + 0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, +}; + +uchar d_len[256] = { + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, +}; + +unsigned int freq[T + 1]; /* frequency table */ + +int prnt[T + N_CHAR]; /* pointers to parent nodes, except for the */ + /* elements [T..T + N_CHAR - 1] which are used to get */ + /* the positions of leaves corresponding to the codes. */ + +int son[T]; /* pointers to child nodes (son[], son[] + 1) */ + +unsigned int getbuf = 0; +uchar getlen = 0; + +static int GetBit(void) /* get one bit */ +{ + unsigned int i; + + while (getlen <= 8) { + if ((i = crc_fgetc()) < 0) i = 0; + getbuf |= i << (8 - getlen); + getlen += 8; + } + i = getbuf; + getbuf <<= 1; + getlen--; + return (int)((i & 0x8000) >> 15); +} + +static int GetByte(void) /* get one byte */ +{ + unsigned int i; + + while (getlen <= 8) { + if ((i = crc_fgetc()) == 0xffff) i = 0; + getbuf |= i << (8 - getlen); + getlen += 8; + } + i = getbuf; + getbuf <<= 8; + getlen -= 8; + return (int)((i & 0xff00) >> 8); +} + +unsigned int putbuf = 0; +uchar putlen = 0; + +static void Putcode(int l, unsigned c) /* output c bits of code */ +{ + putbuf |= c >> putlen; + if ((putlen += l) >= 8) { + if (crc_fputc(putbuf >> 8) == EOF) { + Error(wterr); + } + if ((putlen -= 8) >= 8) { + if (crc_fputc(putbuf) == EOF) { + Error(wterr); + } + codesize += 2; + putlen -= 8; + putbuf = c << (l - putlen); + } else { + putbuf <<= 8; + codesize++; + } + } +} + + +/* initialization of tree */ + +int StartHuff(void) +{ + int i, j; + + for (i = 0; i < N_CHAR; i++) { + freq[i] = 1; + son[i] = i + T; + prnt[i + T] = i; + } + i = 0; j = N_CHAR; + while (j <= R) { + freq[j] = freq[i] + freq[i + 1]; + son[j] = i; + prnt[i] = prnt[i + 1] = j; + i += 2; j++; + } + freq[T] = 0xffff; + prnt[R] = 0; + + return 0; +} + +/* reconstruction of tree */ + +static void reconst(void) +{ + int i, j, k; + unsigned int f, l; + + /* collect leaf nodes in the first half of the table */ + /* and replace the freq by (freq + 1) / 2. */ + j = 0; + for (i = 0; i < T; i++) { + if (son[i] >= T) { + freq[j] = (freq[i] + 1) / 2; + son[j] = son[i]; + j++; + } + } + /* begin constructing tree by connecting sons */ + for (i = 0, j = N_CHAR; j < T; i += 2, j++) { + k = i + 1; + f = freq[j] = freq[i] + freq[k]; + for (k = j - 1; f < freq[k]; k--); + k++; + l = (j - k) * 2; + memmove(&freq[k + 1], &freq[k], l); + freq[k] = f; + memmove(&son[k + 1], &son[k], l); + son[k] = i; + } + /* connect prnt */ + for (i = 0; i < T; i++) { + if ((k = son[i]) >= T) { + prnt[k] = i; + } else { + prnt[k] = prnt[k + 1] = i; + } + } +} + + +/* increment frequency of given code by one, and update tree */ + +static void update(int c) +{ + int i, j, l; + unsigned int k; + + if (freq[R] == MAX_FREQ) { + reconst(); + } + c = prnt[c + T]; + do { + k = ++freq[c]; + + /* if the order is disturbed, exchange nodes */ + + l = c + 1; + + if ((unsigned)k > freq[l]) + { + while ((unsigned)k > freq[++l]); + l--; + freq[c] = freq[l]; + freq[l] = k; + + i = son[c]; + prnt[i] = l; + if (i < T) prnt[i + 1] = l; + + j = son[l]; + son[l] = i; + + prnt[j] = c; + if (j < T) prnt[j + 1] = c; + son[c] = j; + + c = l; + } + } while ((c = prnt[c]) != 0); /* repeat up to root */ +} + +unsigned code, len; + +static void EncodeChar(unsigned int c) +{ + unsigned int i; + int j, k; + + i = 0; + j = 0; + k = prnt[c + T]; + + /* travel from leaf to root */ + do { + i >>= 1; + + /* if node's address is odd-numbered, choose bigger brother node */ + if (k & 1) i += 0x8000; + + j++; + } while ((k = prnt[k]) != R); + Putcode(j, i); + code = i; + len = j; + update(c); +} + +static void EncodePosition(unsigned int c) +{ + unsigned int i; + + /* output upper 6 bits by table lookup */ + i = c >> 6; + Putcode(p_len[i], (unsigned)p_code[i] << 8); + + /* output lower 6 bits verbatim */ + Putcode(6, (c & 0x3f) << 10); +} + +static void EncodeEnd(void) +{ + if (putlen) { + if (crc_fputc(putbuf >> 8) == EOF) { + Error(wterr); + } + codesize++; + } +} + +int DecodeChar(void) +{ + unsigned int c; + + c = son[R]; + + /* travel from root to leaf, */ + /* choosing the smaller child node (son[]) if the read bit is 0, */ + /* the bigger (son[]+1} if 1 */ + while (c < T) { + c += GetBit(); + c = son[c]; + } + c -= T; + update(c); + return (int)c; +} + +int DecodePosition(void) +{ + unsigned int i, j, c; + + /* recover upper 6 bits from table */ + i = GetByte(); + c = (unsigned)d_code[i] << 6; + j = d_len[i]; + + /* read lower 6 bits verbatim */ + j -= 2; + while (j--) { + i = (i << 1) + GetBit(); + } + return (int)(c | (i & 0x3f)); +} + +/* compression */ + +int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress) +{ + int i, c, len, r, s, last_match_length; + unsigned char *ptr; + + putbuf = 0; + putlen = 0; + textsize = 0; + codesize = 0; + + crc = 0; + outfile = out; + + if (B1Protocol) + outfile+=2; // Space for CRC + +// infile = &conn->MailBuffer[2]; + + textsize = inlen; + + ptr = (char *)&textsize; + +#ifdef __BIG_ENDIAN__ + crc_fputc(*(ptr+3)); + crc_fputc(*(ptr+2)); + crc_fputc(*(ptr+1)); + crc_fputc(*(ptr)); +#else + crc_fputc(*(ptr++)); + crc_fputc(*(ptr++)); + crc_fputc(*(ptr++)); + crc_fputc(*(ptr++)); +#endif + + if (textsize == 0) + return 0; + + infile = in; + endinfile = infile + inlen; + textsize = 0; /* rewind and re-read */ + + // if using uncompressed just copy in to out + // may not be optimum but is simple! + + if (Compress) + { + StartHuff(); + InitTree(); + s = 0; + r = N - F; + for (i = s; i < r; i++) + text_buf[i] = 0x20; + for (len = 0; len < F && (c = Get()) != EOF; len++) + text_buf[r + len] = (unsigned char)c; + textsize = len; + for (i = 1; i <= F; i++) + InsertNode(r - i); + InsertNode(r); + + do + { + if (match_length > len) + match_length = len; + if (match_length <= THRESHOLD) + { + match_length = 1; + EncodeChar(text_buf[r]); + } + else + { + EncodeChar(255 - THRESHOLD + match_length); + EncodePosition(match_position); + } + last_match_length = match_length; + + for (i = 0; i < last_match_length && (c = Get()) != EOF; i++) + { + DeleteNode(s); + text_buf[s] = (unsigned char)c; + + if (s < F - 1) + text_buf[s + N] = (unsigned char)c; + s = (s + 1) & (N - 1); + r = (r + 1) & (N - 1); + InsertNode(r); + } + + while (i++ < last_match_length) + { + DeleteNode(s); + s = (s + 1) & (N - 1); + r = (r + 1) & (N - 1); + if (--len) InsertNode(r); + } + } + while (len > 0); + + EncodeEnd(); + } + else + { + int32_t n = inlen; + while (n--) + crc_fputc(*(infile++)); + + codesize += inlen; + } + + if (B1Protocol) + { + out[0] = crc & 0xff; + out[1]= crc >> 8; + codesize+=2; + } + + codesize += 4; + + if (Compress) + Logprintf(LOG_BBS, NULL, '|', "Compressed Message Comp Len %d Msg Len %d CRC %x", + codesize, inlen, crc); + else + Logprintf(LOG_BBS, NULL, '|', "Uncompressed Message Sent Len %d Msg Len %d CRC %x", + codesize, inlen, crc); + + return codesize; +} + +BOOL CheckifPacket(char * Via) +{ + char * ptr1, * ptr2; + + // Message addressed to a non-winlink address + // Need to see if real smtp, or a packet address + + // No . in address assume Packet - g8bpq@g8bpq + + ptr1 = strchr(Via, '.'); + + if (ptr1 == NULL) + return TRUE; // Packet + + // Find Last Element + + ptr2 = strchr(++ptr1, '.'); + + while (ptr2) + { + ptr1 = ptr2; + ptr2 = strchr(++ptr1, '.'); + } + + if (ptr1[0] == 0) + return TRUE; // Packet + + // ptr1 is last element. If a valid continent, it is a packet message + // should really accept .WW on end as it is valid + if (FindContinent(ptr1)) + return TRUE; // Packet + + if (FindCountry(ptr1)) + return TRUE; // Packet + + if ((_stricmp(ptr1, "MARS") == 0) || (_stricmp(ptr1, "USA") == 0) || (_stricmp(ptr1, "WW") == 0)) // MARS used both MARS and USA + return TRUE; // Packet + + return FALSE; +} + +void Decode(CIRCUIT * conn, int FromSync) +{ + unsigned char *ptr; + char * StartofMsg; + short i, j, k, r; + short c; + uint32_t count; + unsigned short crc_read; + int Index = 0; + struct FBBHeaderLine * FBBHeader= &conn->FBBHeaders[0]; // The Headers from an FFB forward block + BOOL NTS = FALSE; + + getbuf = 0; + getlen = 0; + textsize = 0; + codesize = 0; + + infile = &conn->MailBuffer[0]; + + crc = 0; + + if (conn->BBSFlags & FBBB1Mode) + { + short val; + uint32_t n; + + crc_read = infile[0]; + crc_read |= infile[1] << 8; + + for (n = 2; n < conn->TempMsg->length; n++) + { + val = infile[n]; + crc = updcrc(val, crc); + } + if (crc != crc_read) + { + nodeprintf(conn, "*** Message CRC Error File %x Calc %x\r", crc_read, crc); + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + + infile+=2; + } + + textsize = 0; + ptr = (char *)&textsize; + +#ifdef __BIG_ENDIAN__ + + ptr[3] = (unsigned char)crc_fgetc(); + ptr[2] = (unsigned char)crc_fgetc(); + ptr[1] = (unsigned char)crc_fgetc(); + ptr[0] = (unsigned char)crc_fgetc(); + +#else + + for (i = 0 ; i < sizeof(textsize) ; i++) + ptr[i] = (unsigned char)crc_fgetc(); + +#endif + + // Temp fix for duff MACBPQ (Message Length sent big-endian) + + if (textsize > 500000) + { + char x[4]; + char y[4]; + + memcpy(x, &textsize, 4); + y[0] = x[3]; + y[1] = x[2]; + y[2] = x[1]; + y[3] = x[0]; + + memcpy(&textsize, y, 4); + + if (textsize > 5000000) + { + nodeprintf(conn, "*** Message Size Invalid %d\r", textsize); + Debugprintf("*** Message Size Invalid %d\r", textsize); + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + conn->CloseAfterFlush = 20; // 2 Secs + return; + } + } + + Logprintf(LOG_BBS, conn, '|', "Uncompressing Message Comp Len %d Msg Len %d CRC %x", + conn->TempMsg->length, textsize, crc); + + outfile = zalloc(textsize + 10000); // Lots of space for B2 header manipulations + + if (textsize == 0) + return; + + // If compressed, decompress + + if (conn->BBSFlags & FBBCompressed) + { + StartHuff(); + + for (i = 0; i < N - F; i++) + text_buf[i] = 0x20; + + r = N - F; + + for (count = 0; count < textsize; ) + { + c = DecodeChar(); + if (c < 256) + { + *(outfile++) = (unsigned char)c; + text_buf[r++] = (unsigned char)c; + r &= (N - 1); + count++; + } + else + { + i = (r - DecodePosition() - 1) & (N - 1); + j = c - 255 + THRESHOLD; + for (k = 0; k < j; k++) + { + c = text_buf[(i + k) & (N - 1)]; + *(outfile++) = (unsigned char)c; + text_buf[r++] = (unsigned char)c; + r &= (N - 1); + count++; + } + } + } + outfile -=count; + } + else + { + count = textsize; + memcpy(outfile, infile, textsize); + } + + free(conn->MailBuffer); + conn->MailBuffer = outfile; + + conn->TempMsg->length = count; + + if (FromSync) + { + // Refomat Sync message as if from WLE + + if (ReformatSyncMessage(conn) == 0) + return; + + FBBHeader->B2Message = TRUE; + FBBHeader->MsgType = 'P'; + } + + if (!FBBHeader->B2Message) + { + // With B2 the Type is specified in the body, so can't update stats now + + if (FBBHeader->MsgType == 'P') + Index = PMSG; + else if (FBBHeader->MsgType == 'B') + Index = BMSG; + else if (FBBHeader->MsgType == 'T') + Index = TMSG; + + conn->UserPointer->Total.MsgsReceived[Index]++; + conn->UserPointer->Total.BytesForwardedIn[Index] += count; + } + + + if (FBBHeader->B2Message) + { + // Parse the Message for B2 From and To info +/* +MID: A3EDD4P00P55 +Date: 2009/07/25 10:08 +Type: Private +From: SMTP:john.wiseman@ntlworld.com +To: G8BPQ +Subject: RE: RMS Test Messaage +Mbo: SMTP +Body: 214 +File: 3556 NOLA.XLS +File: 5566 NEWBOAT.HOMEPORT.JPG + +*/ + UCHAR * ptr1, * ptr2, * ptr3; + __int32 linelen, MsgLen = 0; + struct MsgInfo * Msg = conn->TempMsg; + time_t Date; + char FullTo[100]; + char FullFrom[100]; + char ** RecpTo = NULL; // May be several Recipients + char ** HddrTo = NULL; // May be several Recipients + char ** Via = NULL; // May be several Recipients + __int32 LocalMsg[1000]; // Set if Recipient is a local wl2k address + char Type[1000]; // Message Type for each dest + __int32 B2To; // Offset to To: fields in B2 header + __int32 Recipients = 0; + __int32 RMSMsgs = 0, BBSMsgs = 0; +#ifndef LINBPQ + struct _EXCEPTION_POINTERS exinfo; + + __try { +#endif + Msg->B2Flags |= B2Msg; + + // Display the whole header for debugging + +/* + ptr1 = strstr(outfile, "\r\n\r\n"); + + if (ptr1) + { + *ptr1 = 0; + Debugprintf("B2 Header = %s", outfile); + *ptr1 = '\r'; + } +*/ + if (_stricmp(conn->Callsign, "RMS") == 0) + Msg->B2Flags |= FromCMS; + + if (conn->RadioOnlyMode == 'T') + Msg->B2Flags |= RadioOnlyMsg; + else if (conn->RadioOnlyMode == 'R') + Msg->B2Flags |= RadioOnlyFwd; + + ptr1 = outfile; + Loop: + ptr2 = strchr(ptr1, '\r'); + + linelen = (int)(ptr2 - ptr1); + + if (_memicmp(ptr1, "From:", 5) == 0) + { + memcpy(FullFrom, ptr1, linelen); + FullFrom[linelen] = 0; + + if (conn->Paclink || (conn->RMSExpress && (conn->UserPointer->flags & F_NOWINLINK) == 0)) + { + // Messages just have the call - need to add @winlink.org + + strcpy(Msg->emailfrom, "@winlink.org"); + + } + if (_memicmp(&ptr1[6], "smtp:", 5) == 0) + { + if (_stricmp(conn->Callsign, "RMS") == 0) + { + // Swap smtp: to rms: and save originator so we can reply via RMS + + strcpy(Msg->from, "RMS:"); + memcpy(Msg->emailfrom, &ptr1[11], linelen - 11); + } + else + { + strcpy(Msg->from, "SMTP:"); + memcpy(Msg->emailfrom, &ptr1[11], linelen - 11); + } + } + else + { + char SaveFrom[100]; + char * FromHA; + + // B2 From may now contain an @BBS + + strcpy(SaveFrom, FullFrom); + + FromHA = strlop(SaveFrom, '@'); + + // If it has an @ it could be an internet address - if it has a dot or longer than + // 6 chars assume it is. + // No, could also be packet + + // I don't think you can have an Internet message without a . in @field + // Why not just use checkifpacket ?? + + if (FromHA && !CheckifPacket(FromHA)) + { + // Internet address - set from empty and full email in + // emailfrom + + strcpy(Msg->from, "SMTP:"); + + if (strlen(FullFrom) > 46) + FullFrom[46] = 0; + + strcpy(Msg->emailfrom, &FullFrom[6]); + } + else + { + // Not an Internet Address so must be a callsign and therefore <= 6 chars + + if (strlen(SaveFrom) > 12) + SaveFrom[12] = 0; + + strcpy(Msg->from, &SaveFrom[6]); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + } + + // Remove any SSID + + ptr3 = strchr(Msg->from, '-'); + if (ptr3) *ptr3 = 0; + } + + // If from a CMS, and no @ in message, append @winlink.org to the B2 Header. + // so messages passed via B2 know it is from Winlink + + if ((Msg->B2Flags & FromCMS) && strchr(FullFrom, '@') == NULL) + { + // Move Message down buffer - ptr2 is the insertion point + + memmove(ptr2+12, ptr2, count); + memcpy(ptr2, "@winlink.org", 12); + count += 12; + conn->TempMsg->length += 12; + + // Also set Emailfrom, in case read on BBS (eg by outpost) + + strcat(Msg->emailfrom, "@winlink.org"); + } + + } + else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0) + { + int toLen; + + HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *)); + HddrTo[Recipients] = zalloc(100); + + memset(FullTo, 0, 99); + memcpy(FullTo, &ptr1[4], linelen-4); + memcpy(HddrTo[Recipients], ptr1, linelen+2); + LocalMsg[Recipients] = FALSE; + Type[Recipients] = Msg->type; // Default to Type from Header + + Logprintf(LOG_BBS, conn, '?', "B2 Msg To: %s", FullTo); + + conn->TempMsg->length -= (int)strlen(HddrTo[Recipients]); + + B2To = (int)(ptr1 - outfile); + + // if ending in AMPR.ORG send via ISP if we have enabled forwarding AMPR + + toLen = (int)strlen(FullTo); + + if (_memicmp(&FullTo[toLen - 8], "ampr.org", 8) == 0) + { + // if our domain keep here. + + // if not, and SendAMPRDirect set, set as ISP, + // else set as RMS + + memcpy(Msg->via, FullTo, toLen); + + ptr3 = strchr(FullTo, '@'); + + if (ptr3) + { + ptr3++; + + if (_stricmp(ptr3, AMPRDomain) == 0) + { + // Our Message + + strcpy(Msg->via, FullTo); + BBSMsgs ++; + goto BBSMsg; + } + } + + if (SendAMPRDirect && FindAMPR()) + { + strcpy(Msg->via, FullTo); + strcpy(FullTo,"AMPR"); + BBSMsgs ++; + goto BBSMsg; + } + + strcpy(FullTo,"RMS"); + RMSMsgs ++; + } + + if (conn->BPQBBS && !CheckifPacket(FullTo)) // May be an message for RMS being passed to an intermediate BBS + { + // Internet address - send via RMS + + strcpy(Msg->via, FullTo); + strcpy(FullTo,"RMS"); + RMSMsgs ++; + } + else + { + ptr3 = strchr(FullTo, '@'); + + if (ptr3) + { + *ptr3++ = 0; + strcpy(Msg->via, ptr3); + } + else + Msg->via[0] = 0; + } + + if (conn->Paclink) + { + Msg->B2Flags |= FromPaclink; + + // Message from paclink + + // Messages to WL2K just have call. + // Messages to email or BBS addresses have smtp: + + + if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) + { + // See if Packet or SMTP + + if (CheckifPacket(Msg->via)) // If no RMS, don't check for routing to it) + { + // Packet Message + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + _strupr(Msg->via); + + // Update the saved to: line (remove the smtp:) + + strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); + BBSMsgs++; + + } + else + { + // Internet address - do we send via RMS?? + + // ??? Need to see if RMS is available + + memcpy(Msg->via, &ptr1[9], linelen); + Msg->via[linelen-9] = 0; + strcpy(FullTo,"RMS"); + RMSMsgs ++; + } + } + else + { + if (conn->RadioOnlyMode == 0 && (conn->UserPointer->flags & F_NOWINLINK) == 0) // treat as Packet Address? + { + strcpy(Msg->via, "winlink.org"); // Message for WL2K - add via + RMSMsgs ++; + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + { + BBSMsgs++; + } + } + + } +// else if (conn->RMSExpress && FindRMS()) // If no RMS, don't check for routing to it + else if (conn->RMSExpress) + { + Msg->B2Flags |= FromRMSExpress; + + // Message from RMS Express + // Messages to WL2K just have call. + // Messages to email or BBS addresses don't have smtp: + + + if (Msg->via[0]) + { + // Has an @ - See if Packet or SMTP. If to our AMPR address, treat as packet + + if (CheckifPacket(Msg->via) || _stricmp(Msg->via, AMPRDomain) == 0) + { + // Packet Message + + _strupr(FullTo); + _strupr(Msg->via); + BBSMsgs++; + } + else + { + // Internet address - do we send via RMS?? + + if (_memicmp(FullTo, "smtp/", 5) == 0 && ISP_Gateway_Enabled) + { + memcpy(Msg->via, &ptr1[9], linelen - 9); + Msg->via[linelen-9] = 0; + FullTo[0] = Msg->to[0] = 0; + } + else if (FindRMS()) // have RMS + { + memcpy(Msg->via, &ptr1[4], linelen); + Msg->via[linelen-4] = 0; + strcpy(FullTo,"RMS"); + RMSMsgs ++; + } + else if (ISP_Gateway_Enabled) + { + memcpy(Msg->via, &ptr1[4], linelen); + Msg->via[linelen-4] = 0; + FullTo[0] = Msg->to[0] = 0; + } + else if (isAMPRMsg(Msg->via)) + strcpy(Msg->to, "RMS"); // Routing will redirect it + } + } + else + { + if (conn->RadioOnlyMode == 0 && (conn->UserPointer->flags & F_NOWINLINK) == 0) // Dont default to winlink.org + { + strcpy(Msg->via, "winlink.org"); // Message for WL2K - add via + RMSMsgs ++; + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + goto BBSMsg; + } + } + + else // Not Paclink or RMS Express (or no RMS) + { + if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) + { + // Airmail Sends MARS messages as SMTP + + if (CheckifPacket(Msg->via)) + { + // Packet Message + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + _strupr(Msg->via); + + // Update the saved to: line (remove the smtp:) + + strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); + BBSMsgs++; + goto BBSMsg; + } + + // If a winlink.org address we need to convert to call + + if (_stricmp(Msg->via, "winlink.org") == 0) + { + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + { + memcpy(Msg->via, &ptr1[9], linelen); + Msg->via[linelen - 9] = 0; + strcpy(FullTo,"RMS"); + } +// FullTo[0] = 0; + } + else + { + BBSMsg: + _strupr(FullTo); + _strupr(Msg->via); + } + } + + if (memcmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + } + + else if (memcmp(FullTo, "NTS:", 4) == 0) + { + // remove NTS and set type 'T' + + memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); + Type[Recipients] = 'T'; // NTS + memmove(HddrTo[Recipients] + 4, HddrTo[Recipients] + 8, 91); + + // Replace Type: Private with Type: Traffic + + } + else if ((_memicmp(FullTo, "bull/", 5) == 0) || (_memicmp(FullTo, "bull:", 5) == 0)) + { + // remove bull/ and set type 'B' + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + Type[Recipients] = 'B'; // Bulletin + memmove(HddrTo[Recipients] + 4, HddrTo[Recipients] + 9, 90); + + // Replace Type: Private with Type: Bulletin + // Have to move rest of header down to make space + + } + + if (strcmp(Msg->via, "RMS") == 0) + { + // replace RMS with @winlink.org + + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo); + } + + if (strlen(FullTo) > 6) + FullTo[6] = 0; + + strlop(FullTo, '-'); + + strcpy(Msg->to, FullTo); + + if (SendBBStoSYSOPCall) + if (_stricmp(FullTo, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0) + && (conn->Paclink || conn->RMSExpress)) + { + // No routing - check @BBS and WP + + struct UserInfo * ToUser = LookupCall(FullTo); + + Msg->via[0] = 0; // In case BPQ and not found + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + } + } + else + { + WPRecP WP = LookupWP(FullTo); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + + } + } + + // Fix To: address in B2 Header + + if (Msg->via[0]) + sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via); + else + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + + } + + RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *)); + RecpTo[Recipients] = zalloc(10); + + Via=realloc(Via, (Recipients+1) * sizeof(void *)); + Via[Recipients] = zalloc(50); + + strcpy(Via[Recipients], Msg->via); + strcpy(RecpTo[Recipients++], FullTo); + + // Remove the To: Line from the buffer + + memmove(ptr1, ptr2+2, count); + goto Loop; + + } + else if (_memicmp(ptr1, "Type:", 4) == 0) + { + if (ptr1[6] == 'N') + Msg->type = 'T'; // NTS + else + Msg->type = ptr1[6]; + } + else if (_memicmp(ptr1, "Body:", 4) == 0) + { + MsgLen = atoi(&ptr1[5]); + StartofMsg = ptr1; + } + else if (_memicmp(ptr1, "File:", 5) == 0) + { + Msg->B2Flags |= Attachments; + } + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: 2009/07/25 10:08 + + sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip crlf + goto Loop; + } + + // Processed all headers + + + // If multiple recipents, create one copy for each BBS address, and one for all others (via RMS) + + if (Recipients == 0 || HddrTo == NULL) + { + Debugprintf("B2 Message with no recipients from %s", conn->Callsign); + Logprintf(LOG_BBS, conn, '!', "B2 Message with no recipients from %s", conn->Callsign); + SetupNextFBBMessage(conn); + return; + } + else + { + __int32 i; + struct MsgInfo * SaveMsg; + char * SaveBody; + __int32 SaveMsgLen = count; + BOOL SentToRMS = FALSE; + __int32 ToLen; + char * ToString = malloc(Recipients * 100); + char * bang; + + SaveMsg = Msg; + SaveBody = conn->MailBuffer; + + // If from WL2K, create one message for each to: or cc: that is a local user + + if (Msg->B2Flags & FromCMS) + { + struct UserInfo * user; + char Call[10]; + char * ptr; + + for (i = 0; i < Recipients; i++) + { + memcpy(Call, &HddrTo[i][4], 9); + ptr = strchr(Call, 13); + if (ptr) + *ptr = 0; + + strlop(Call, '-'); + + user = LookupCall(Call); + + if (user == 0) + continue; + + if (strcmp(Call, BBSName) != 0) // always accept to bbs call + if ((user->flags & F_POLLRMS) == 0) + continue; + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + // Add our To: + + ToLen = (int)strlen(HddrTo[i]); + + if (_memicmp(HddrTo[i], "CC", 2) == 0) // Replace CC: with TO: + memcpy(HddrTo[i], "To", 2); + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], HddrTo[i], ToLen); + + conn->TempMsg->length += ToLen; + + strcpy(Msg->to, RecpTo[i]); + strcpy(Msg->via, Via[i]); + Msg->type = Type[i]; + + if (Recipients > 1) + Msg->bid[0] = 0; + + CreateMessageFromBuffer(conn); + } + } + else + { + // From a client - Create one copy with all RMS recipients, and one for each packet recipient + + // Merge all RMS To: lines + + // If don't add Winlink.org or no RMS BBS keep separate + + ToLen = 0; + ToString[0] = 0; + + for (i = 0; i < Recipients; i++) + { + if (conn->RadioOnlyMode || LocalMsg[i] || (conn->UserPointer->flags & F_NOWINLINK) || FindRMS()== NULL) + { + LocalMsg[i] = TRUE; // Make sure local copy is created + continue; // For a local RMS user + } + + // ?? Should a Bang Path override this ?? - I think so! + + if (strchr(Via[i], '!')) + continue; + + if (_stricmp(Via[i], "WINLINK.ORG") == 0 || _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + { + if (ToLen == 0) // First Addr + memcpy(HddrTo[i], "To", 2); // In Case CC + + ToLen += (int)strlen(HddrTo[i]); + strcat(ToString, HddrTo[i]); + } + } + + if (ToLen) + { + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + // Add all the To: lines back to message + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], ToString, ToLen); + + conn->TempMsg->length += ToLen; + + + if (Recipients > 1) + { + strcpy(Msg->via, "winlink.org"); + strcpy(Msg->to, "RMS"); + Msg->bid[0] = 0; // Must Change the BID + } + + // Don't change type, as we don't change the B2 Header for messages to RMS + //Msg->type = Type[0]; + + CreateMessageFromBuffer(conn); + } + + free(ToString); + + for (i = 0; i < Recipients; i++) + { + // Only Process Non - RMS Dests or local RMS Users + + // ?? Should a Bang Path override this ?? - I think so! + + if (strchr(Via[i], '!') == 0) + if (LocalMsg[i] == 0) + if (_stricmp (Via[i], "WINLINK.ORG") == 0 || + _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + continue; + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + // Add our To: + + ptr = HddrTo[i]; + + // We removed any nts: or bull: earlier on, + // and saved type. We need to set type here, as + // may be sending to more than one type + // If message contains Type: Private and not 'P', + // need to changes to Traffic or Bulletin + + // But we need to remove last bang path if any + + if (strchr(ptr, '!')) + { + bang = ptr + strlen(ptr); + + while (*(--bang) != '!'); // Find last ! + + *(bang++) = 13; // Need CR; + *(bang++) = 10; // Need LF; + *(bang) = 0; // remove it; + } + + ToLen = (int)strlen(ptr); + + // if (_memicmp(HddrTo[i], "CC", 2) == 0) // Replace CC: with TO: + memcpy(HddrTo[i], "To", 2); + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], HddrTo[i], ToLen); + + conn->TempMsg->length += ToLen; + + Msg->type = Type[i]; + + ptr = strstr(conn->MailBuffer, "\nType: "); + + if (ptr) + { + char * ptrx; + + ptr += 7; + + // This handles a message arriving with bull/ or nts/ overrides + + if (_memicmp(ptr, "Private", 7) == 0 && Msg->type != 'P') + { + if (Msg->type == 'T') + memcpy(ptr, "Traffic", 7); + else + if (Msg->type == 'B') + { + // have to make space + + memmove(ptr + 1, ptr, count); + conn->TempMsg->length++; + memcpy(ptr, "Bulletin", 8); + } + + // remove //wl2k from subject + + ptrx = strstr(conn->MailBuffer, "Subject: "); + + if (ptrx && _memicmp(ptrx + 9, "//WL2K ", 7) == 0) + { + memmove(ptrx + 9, ptrx + 16, count); + conn->TempMsg->length -= 7; + memmove(conn->TempMsg->title, &conn->TempMsg->title[7], strlen(conn->TempMsg->title) - 6); + } + } + // if we are receiving from another BBS rather + // than WLE or PAT we need to set msgtype from Type: + + Msg->type = ptr[0]; // I think it is safe to do it always + + if (Msg->type == 'N') + Msg->type = 'T'; + } + + strcpy(Msg->to, RecpTo[i]); + strcpy(Msg->via, Via[i]); + + if (i > 0 && Msg->type != 'B') // Must Change the BID + Msg->bid[0] = 0; + + // Update Stats + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else if (Msg->type == 'T') + Index = TMSG; + + conn->UserPointer->Total.MsgsReceived[Index]++; + conn->UserPointer->Total.BytesForwardedIn[Index] += MsgLen; + + CreateMessageFromBuffer(conn); + } + } // End not from RMS + + free(SaveMsg); + free(SaveBody); + conn->MailBuffer = NULL; + conn->MailBufferSize=0; + + if (FromSync) + return; + + SetupNextFBBMessage(conn); + return; + } + +#ifndef LINBPQ + } + #define EXCEPTMSG "Error Decoding B2 Message" + #include "StdExcept.c" + + BBSputs(conn, "*** Program Error Decoding B2 Message\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } +#endif + } // end if B2Msg + + CreateMessageFromBuffer(conn); + SetupNextFBBMessage(conn); +} \ No newline at end of file diff --git a/.svn/pristine/44/44facc69d12b536369a2d1328a95ffc5063f1284.svn-base b/.svn/pristine/44/44facc69d12b536369a2d1328a95ffc5063f1284.svn-base new file mode 100644 index 0000000..f27498c --- /dev/null +++ b/.svn/pristine/44/44facc69d12b536369a2d1328a95ffc5063f1284.svn-base @@ -0,0 +1,60 @@ +// FormatHTML.cpp : Defines the entry point for the console application. +// +#include + +int main () { + FILE *fp, *fp2; + char str[256]; + char newstr[256]; + char * ptr, * inptr; + + /* opening file for reading */ + fp = fopen("D:/AtomProject/test.html" , "r"); + fp2 = fopen("D:/AtomProject/test.html.c" , "w"); + + if(fp == NULL) { + perror("Error opening file"); + return(-1); + } + + while(fgets (str, 256, fp) != NULL) + { + // Replace any " with \" and add " on front and \r\n" on end + + char c; + ptr = newstr; + inptr = str; + + c = *(inptr++); + + *(ptr++) = '"'; + + while (c && c != 10) + { + if (c == '"') + *(ptr++) = '\\'; + + *(ptr++) = c; + + c = *(inptr++); + } + + + *(ptr++) = '\\'; + *(ptr++) = 'r'; + *(ptr++) = '\\'; + *(ptr++) = 'n'; + *(ptr++) = '"'; + *(ptr++) = 10; + *(ptr++) = 0; + + puts(newstr); + fputs(newstr, fp2); + + } + + fclose(fp); + fclose(fp2); + + return(0); +} \ No newline at end of file diff --git a/.svn/pristine/45/4509fedeae965003bd581c06a45005d76c51491c.svn-base b/.svn/pristine/45/4509fedeae965003bd581c06a45005d76c51491c.svn-base new file mode 100644 index 0000000..afcaa3e --- /dev/null +++ b/.svn/pristine/45/4509fedeae965003bd581c06a45005d76c51491c.svn-base @@ -0,0 +1,32 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by BPQRemotePTT.rc +// +#define IDC_BPQHOST 1000 +#define IDC_COM1 1001 +#define IDC_HAMLIBPORT1 1002 +#define IDC_COM2 1003 +#define IDC_STATE1 1004 +#define IDC_TEST1 1005 +#define IDC_HAMLIBPORT2 1006 +#define IDC_STATE2 1007 +#define IDC_TEST2 1008 +#define IDC_COM3 1009 +#define IDC_HAMLIBPORT3 1010 +#define IDC_STATE3 1011 +#define IDC_TEST3 1012 +#define IDC_COM4 1013 +#define IDC_HAMLIBPORT4 1014 +#define IDC_STATE4 1015 +#define IDC_TEST4 1016 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1006 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/.svn/pristine/47/4781e072a45ec4b6521352d22b502290a8c163d5.svn-base b/.svn/pristine/47/4781e072a45ec4b6521352d22b502290a8c163d5.svn-base new file mode 100644 index 0000000..a5dc7f2 --- /dev/null +++ b/.svn/pristine/47/4781e072a45ec4b6521352d22b502290a8c163d5.svn-base @@ -0,0 +1,1980 @@ +/******************************************************************************* + * Copyright (c) 2009, 2023 IBM Corp., Ian Craggs and others + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * https://www.eclipse.org/legal/epl-2.0/ + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Ian Craggs, Allan Stockdill-Mander - SSL updates + * Ian Craggs - multiple server connection support + * Ian Craggs - MQTT 3.1.1 support + * Ian Craggs - remove const from eyecatchers #168 + *******************************************************************************/ + +/** + * @cond MQTTClient_internal + * @mainpage MQTT Client Library Internals + * In the beginning there was one MQTT C client library, MQTTClient, as implemented in MQTTClient.c + * This library was designed to be easy to use for applications which didn't mind if some of the calls + * blocked for a while. For instance, the MQTTClient_connect call will block until a successful + * connection has completed, or a connection has failed, which could be as long as the "connection + * timeout" interval, whose default is 30 seconds. + * + * However in mobile devices and other windowing environments, blocking on the GUI thread is a bad + * thing as it causes the user interface to freeze. Hence a new API, MQTTAsync, implemented + * in MQTTAsync.c, was devised. There are no blocking calls in this library, so it is well suited + * to GUI and mobile environments, at the expense of some extra complexity. + * + * Both libraries are designed to be sparing in the use of threads. So multiple client objects are + * handled by one or two threads, with a select call in Socket_getReadySocket(), used to determine + * when a socket has incoming data. This API is thread safe: functions may be called by multiple application + * threads, with the exception of ::MQTTClient_yield and ::MQTTClient_receive, which are intended + * for single threaded environments only. + * + * @endcond + * @cond MQTTClient_main + * @mainpage MQTT Client library for C (MQTTClient) + * © Copyright 2009, 2023 IBM Corp., Ian Craggs and others + * + * @brief An MQTT client library in C. + * + * These pages describe the original more synchronous API which might be + * considered easier to use. Some of the calls will block. For the new + * totally asynchronous API where no calls block, which is especially suitable + * for use in windowed environments, see the + * MQTT C Client Asynchronous API Documentation. + * The MQTTClient API is not thread safe, whereas the MQTTAsync API is. + * + * An MQTT client application connects to MQTT-capable servers. + * A typical client is responsible for collecting information from a telemetry + * device and publishing the information to the server. It can also subscribe + * to topics, receive messages, and use this information to control the + * telemetry device. + * + * MQTT clients implement the published MQTT v3 protocol. You can write your own + * API to the MQTT protocol using the programming language and platform of your + * choice. This can be time-consuming and error-prone. + * + * To simplify writing MQTT client applications, this library encapsulates + * the MQTT v3 protocol for you. Using this library enables a fully functional + * MQTT client application to be written in a few lines of code. + * The information presented here documents the API provided + * by the MQTT Client library for C. + * + * Using the client
+ * Applications that use the client library typically use a similar structure: + *
    + *
  • Create a client object
  • + *
  • Set the options to connect to an MQTT server
  • + *
  • Set up callback functions if multi-threaded (asynchronous mode) + * operation is being used (see @ref async).
  • + *
  • Subscribe to any topics the client needs to receive
  • + *
  • Repeat until finished:
  • + *
      + *
    • Publish any messages the client needs to
    • + *
    • Handle any incoming messages
    • + *
    + *
  • Disconnect the client
  • + *
  • Free any memory being used by the client
  • + *
+ * Some simple examples are shown here: + *
    + *
  • @ref pubsync
  • + *
  • @ref pubasync
  • + *
  • @ref subasync
  • + *
+ * Additional information about important concepts is provided here: + *
    + *
  • @ref async
  • + *
  • @ref callbacks
  • + *
  • @ref wildcard
  • + *
  • @ref qos
  • + *
  • @ref tracing
  • + *
+ * @endcond + */ + +/* +/// @cond EXCLUDE +*/ +#if !defined(MQTTCLIENT_H) +#define MQTTCLIENT_H + +#if defined(__cplusplus) + extern "C" { +#endif + +#include +/* +/// @endcond +*/ + +#include "MQTTExportDeclarations.h" + +#include "MQTTProperties.h" +#include "MQTTReasonCodes.h" +#include "MQTTSubscribeOpts.h" +#if !defined(NO_PERSISTENCE) +#include "MQTTClientPersistence.h" +#endif + +/** + * Return code: No error. Indicates successful completion of an MQTT client + * operation. + */ +#define MQTTCLIENT_SUCCESS 0 +/** + * Return code: A generic error code indicating the failure of an MQTT client + * operation. + */ +#define MQTTCLIENT_FAILURE -1 + +/* error code -2 is MQTTCLIENT_PERSISTENCE_ERROR */ + +/** + * Return code: The client is disconnected. + */ +#define MQTTCLIENT_DISCONNECTED -3 +/** + * Return code: The maximum number of messages allowed to be simultaneously + * in-flight has been reached. + */ +#define MQTTCLIENT_MAX_MESSAGES_INFLIGHT -4 +/** + * Return code: An invalid UTF-8 string has been detected. + */ +#define MQTTCLIENT_BAD_UTF8_STRING -5 +/** + * Return code: A NULL parameter has been supplied when this is invalid. + */ +#define MQTTCLIENT_NULL_PARAMETER -6 +/** + * Return code: The topic has been truncated (the topic string includes + * embedded NULL characters). String functions will not access the full topic. + * Use the topic length value to access the full topic. + */ +#define MQTTCLIENT_TOPICNAME_TRUNCATED -7 +/** + * Return code: A structure parameter does not have the correct eyecatcher + * and version number. + */ +#define MQTTCLIENT_BAD_STRUCTURE -8 +/** + * Return code: A QoS value that falls outside of the acceptable range (0,1,2) + */ +#define MQTTCLIENT_BAD_QOS -9 +/** + * Return code: Attempting SSL connection using non-SSL version of library + */ +#define MQTTCLIENT_SSL_NOT_SUPPORTED -10 + /** + * Return code: unrecognized MQTT version + */ + #define MQTTCLIENT_BAD_MQTT_VERSION -11 +/** + * Return code: protocol prefix in serverURI should be: + * @li @em tcp:// or @em mqtt:// - Insecure TCP + * @li @em ssl:// or @em mqtts:// - Encrypted SSL/TLS + * @li @em ws:// - Insecure websockets + * @li @em wss:// - Secure web sockets + * The TLS enabled prefixes (ssl, mqtts, wss) are only valid if a TLS + * version of the library is linked with. + */ +#define MQTTCLIENT_BAD_PROTOCOL -14 + /** + * Return code: option not applicable to the requested version of MQTT + */ + #define MQTTCLIENT_BAD_MQTT_OPTION -15 + /** + * Return code: call not applicable to the requested version of MQTT + */ + #define MQTTCLIENT_WRONG_MQTT_VERSION -16 + /** + * Return code: 0 length will topic on connect + */ + #define MQTTCLIENT_0_LEN_WILL_TOPIC -17 + + +/** + * Default MQTT version to connect with. Use 3.1.1 then fall back to 3.1 + */ +#define MQTTVERSION_DEFAULT 0 +/** + * MQTT version to connect with: 3.1 + */ +#define MQTTVERSION_3_1 3 +/** + * MQTT version to connect with: 3.1.1 + */ +#define MQTTVERSION_3_1_1 4 + /** + * MQTT version to connect with: 5 + */ + #define MQTTVERSION_5 5 +/** + * Bad return code from subscribe, as defined in the 3.1.1 specification + */ +#define MQTT_BAD_SUBSCRIBE 0x80 + +/** + * Initialization options + */ +typedef struct +{ + /** The eyecatcher for this structure. Must be MQTG. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 */ + int struct_version; + /** 1 = we do openssl init, 0 = leave it to the application */ + int do_openssl_init; +} MQTTClient_init_options; + +#define MQTTClient_init_options_initializer { {'M', 'Q', 'T', 'G'}, 0, 0 } + +/** + * Global init of mqtt library. Call once on program start to set global behaviour. + * do_openssl_init - if mqtt library should initialize OpenSSL (1) or rely on the caller to do it before using the library (0) + */ +LIBMQTT_API void MQTTClient_global_init(MQTTClient_init_options* inits); + +/** + * A handle representing an MQTT client. A valid client handle is available + * following a successful call to MQTTClient_create(). + */ +typedef void* MQTTClient; +/** + * A value representing an MQTT message. A delivery token is returned to the + * client application when a message is published. The token can then be used to + * check that the message was successfully delivered to its destination (see + * MQTTClient_publish(), + * MQTTClient_publishMessage(), + * MQTTClient_deliveryComplete(), + * MQTTClient_waitForCompletion() and + * MQTTClient_getPendingDeliveryTokens()). + */ +typedef int MQTTClient_deliveryToken; +typedef int MQTTClient_token; + +/** + * A structure representing the payload and attributes of an MQTT message. The + * message topic is not part of this structure (see MQTTClient_publishMessage(), + * MQTTClient_publish(), MQTTClient_receive(), MQTTClient_freeMessage() + * and MQTTClient_messageArrived()). + */ +typedef struct +{ + /** The eyecatcher for this structure. must be MQTM. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 or 1 + * 0 indicates no message properties */ + int struct_version; + /** The length of the MQTT message payload in bytes. */ + int payloadlen; + /** A pointer to the payload of the MQTT message. */ + void* payload; + /** + * The quality of service (QoS) assigned to the message. + * There are three levels of QoS: + *
+ *
QoS0
+ *
Fire and forget - the message may not be delivered
+ *
QoS1
+ *
At least once - the message will be delivered, but may be + * delivered more than once in some circumstances.
+ *
QoS2
+ *
Once and one only - the message will be delivered exactly once.
+ *
+ */ + int qos; + /** + * The retained flag serves two purposes depending on whether the message + * it is associated with is being published or received. + * + * retained = true
+ * For messages being published, a true setting indicates that the MQTT + * server should retain a copy of the message. The message will then be + * transmitted to new subscribers to a topic that matches the message topic. + * For subscribers registering a new subscription, the flag being true + * indicates that the received message is not a new one, but one that has + * been retained by the MQTT server. + * + * retained = false
+ * For publishers, this indicates that this message should not be retained + * by the MQTT server. For subscribers, a false setting indicates this is + * a normal message, received as a result of it being published to the + * server. + */ + int retained; + /** + * The dup flag indicates whether or not this message is a duplicate. + * It is only meaningful when receiving QoS1 messages. When true, the + * client application should take appropriate action to deal with the + * duplicate message. + */ + int dup; + /** The message identifier is normally reserved for internal use by the + * MQTT client and server. + */ + int msgid; + /** + * The MQTT V5 properties associated with the message. + */ + MQTTProperties properties; +} MQTTClient_message; + +#define MQTTClient_message_initializer { {'M', 'Q', 'T', 'M'}, 1, 0, NULL, 0, 0, 0, 0, MQTTProperties_initializer } + +/** + * This is a callback function. The client application + * must provide an implementation of this function to enable asynchronous + * receipt of messages. The function is registered with the client library by + * passing it as an argument to MQTTClient_setCallbacks(). It is + * called by the client library when a new message that matches a client + * subscription has been received from the server. This function is executed on + * a separate thread to the one on which the client application is running. + * @param context A pointer to the context value originally passed to + * MQTTClient_setCallbacks(), which contains any application-specific context. + * @param topicName The topic associated with the received message. + * @param topicLen The length of the topic if there are one + * more NULL characters embedded in topicName, otherwise topicLen + * is 0. If topicLen is 0, the value returned by strlen(topicName) + * can be trusted. If topicLen is greater than 0, the full topic name + * can be retrieved by accessing topicName as a byte array of length + * topicLen. + * @param message The MQTTClient_message structure for the received message. + * This structure contains the message payload and attributes. + * @return This function must return 0 or 1 indicating whether or not + * the message has been safely received by the client application.
+ * Returning 1 indicates that the message has been successfully handled. + * To free the message storage, ::MQTTClient_freeMessage must be called. + * To free the topic name storage, ::MQTTClient_free must be called.
+ * Returning 0 indicates that there was a problem. In this + * case, the client library will reinvoke MQTTClient_messageArrived() to + * attempt to deliver the message to the application again. + * Do not free the message and topic storage when returning 0, otherwise + * the redelivery will fail. + */ +typedef int MQTTClient_messageArrived(void* context, char* topicName, int topicLen, MQTTClient_message* message); + +/** + * This is a callback function. The client application + * must provide an implementation of this function to enable asynchronous + * notification of delivery of messages. The function is registered with the + * client library by passing it as an argument to MQTTClient_setCallbacks(). + * It is called by the client library after the client application has + * published a message to the server. It indicates that the necessary + * handshaking and acknowledgements for the requested quality of service (see + * MQTTClient_message.qos) have been completed. This function is executed on a + * separate thread to the one on which the client application is running. + * Note:MQTTClient_deliveryComplete() is not called when messages are + * published at QoS0. + * @param context A pointer to the context value originally passed to + * MQTTClient_setCallbacks(), which contains any application-specific context. + * @param dt The ::MQTTClient_deliveryToken associated with + * the published message. Applications can check that all messages have been + * correctly published by matching the delivery tokens returned from calls to + * MQTTClient_publish() and MQTTClient_publishMessage() with the tokens passed + * to this callback. + */ +typedef void MQTTClient_deliveryComplete(void* context, MQTTClient_deliveryToken dt); + +/** + * This is a callback function. The client application + * must provide an implementation of this function to enable asynchronous + * notification of the loss of connection to the server. The function is + * registered with the client library by passing it as an argument to + * MQTTClient_setCallbacks(). It is called by the client library if the client + * loses its connection to the server. The client application must take + * appropriate action, such as trying to reconnect or reporting the problem. + * This function is executed on a separate thread to the one on which the + * client application is running. + * @param context A pointer to the context value originally passed to + * MQTTClient_setCallbacks(), which contains any application-specific context. + * @param cause The reason for the disconnection. + * Currently, cause is always set to NULL. + */ +typedef void MQTTClient_connectionLost(void* context, char* cause); + +/** + * This function sets the callback functions for a specific client. + * If your client application doesn't use a particular callback, set the + * relevant parameter to NULL. Calling MQTTClient_setCallbacks() puts the + * client into multi-threaded mode. Any necessary message acknowledgements and + * status communications are handled in the background without any intervention + * from the client application. See @ref async for more information. + * + * Note: The MQTT client must be disconnected when this function is + * called. + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param context A pointer to any application-specific context. The + * the context pointer is passed to each of the callback functions to + * provide access to the context information in the callback. + * @param cl A pointer to an MQTTClient_connectionLost() callback + * function. You can set this to NULL if your application doesn't handle + * disconnections. + * @param ma A pointer to an MQTTClient_messageArrived() callback + * function. This callback function must be set when you call + * MQTTClient_setCallbacks(), as otherwise there would be nowhere to deliver + * any incoming messages. + * @param dc A pointer to an MQTTClient_deliveryComplete() callback + * function. You can set this to NULL if your application publishes + * synchronously or if you do not want to check for successful delivery. + * @return ::MQTTCLIENT_SUCCESS if the callbacks were correctly set, + * ::MQTTCLIENT_FAILURE if an error occurred. + */ +LIBMQTT_API int MQTTClient_setCallbacks(MQTTClient handle, void* context, MQTTClient_connectionLost* cl, + MQTTClient_messageArrived* ma, MQTTClient_deliveryComplete* dc); + + +/** + * This is a callback function, which will be called when the a disconnect + * packet is received from the server. This applies to MQTT V5 and above only. + * @param context A pointer to the context value originally passed to + * ::MQTTClient_setDisconnected(), which contains any application-specific context. + * @param properties The MQTT V5 properties received with the disconnect, if any. + * @param reasonCode The MQTT V5 reason code received with the disconnect. + * Currently, cause is always set to NULL. + */ +typedef void MQTTClient_disconnected(void* context, MQTTProperties* properties, + enum MQTTReasonCodes reasonCode); + +/** + * Sets the MQTTClient_disconnected() callback function for a client. This will be called + * if a disconnect packet is received from the server. Only valid for MQTT V5 and above. + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param context A pointer to any application-specific context. The + * the context pointer is passed to each of the callback functions to + * provide access to the context information in the callback. + * @param co A pointer to an MQTTClient_disconnected() callback + * function. NULL removes the callback setting. + * @return ::MQTTCLIENT_SUCCESS if the callbacks were correctly set, + * ::MQTTCLIENT_FAILURE if an error occurred. + */ +LIBMQTT_API int MQTTClient_setDisconnected(MQTTClient handle, void* context, MQTTClient_disconnected* co); + +/** + * This is a callback function, the MQTT V5 version of MQTTClient_deliveryComplete(). + * The client application + * must provide an implementation of this function to enable asynchronous + * notification of the completed delivery of messages. + * It is called by the client library after the client application has + * published a message to the server. It indicates that the necessary + * handshaking and acknowledgements for the requested quality of service (see + * MQTTClient_message.qos) have been completed. This function is executed on a + * separate thread to the one on which the client application is running. + * Note: It is not called when messages are published at QoS0. + * @param context A pointer to the context value originally passed to + * MQTTClient_setCallbacks(), which contains any application-specific context. + * @param dt The ::MQTTClient_deliveryToken associated with + * the published message. Applications can check that all messages have been + * correctly published by matching the delivery tokens returned from calls to + * MQTTClient_publish() and MQTTClient_publishMessage() with the tokens passed + * to this callback. + * @param packet_type the last received packet type for this completion. For QoS 1 + * always PUBACK. For QoS 2 could be PUBREC or PUBCOMP. + * @param properties the MQTT V5 properties returned with the last packet from the server + * @param reasonCode the reason code returned from the server + */ +typedef void MQTTClient_published(void* context, int dt, int packet_type, MQTTProperties* properties, + enum MQTTReasonCodes reasonCode); + +LIBMQTT_API int MQTTClient_setPublished(MQTTClient handle, void* context, MQTTClient_published* co); + +/** + * This function creates an MQTT client ready for connection to the + * specified server and using the specified persistent storage (see + * MQTTClient_persistence). See also MQTTClient_destroy(). + * @param handle A pointer to an ::MQTTClient handle. The handle is + * populated with a valid client reference following a successful return from + * this function. + * @param serverURI A null-terminated string specifying the server to + * which the client will connect. It takes the form protocol://host:port. + * Currently, protocol must be: + *
+ * @em tcp:// or @em mqtt:// - Insecure TCP + *
+ * @em ssl:// or @em mqtts:// - Encrypted SSL/TLS + *
+ * @em ws:// - Insecure websockets + *
+ * @em wss:// - Secure web sockets + *
+ * The TLS enabled prefixes (ssl, mqtts, wss) are only valid if a TLS + * version of the library is linked with. + * For host, you can specify either an IP address or a host name. For + * instance, to connect to a server running on the local machines with the + * default MQTT port, specify tcp://localhost:1883. + * @param clientId The client identifier passed to the server when the + * client connects to it. It is a null-terminated UTF-8 encoded string. + * @param persistence_type The type of persistence to be used by the client: + *
+ * ::MQTTCLIENT_PERSISTENCE_NONE: Use in-memory persistence. If the device or + * system on which the client is running fails or is switched off, the current + * state of any in-flight messages is lost and some messages may not be + * delivered even at QoS1 and QoS2. + *
+ * ::MQTTCLIENT_PERSISTENCE_DEFAULT: Use the default (file system-based) + * persistence mechanism. Status about in-flight messages is held in persistent + * storage and provides some protection against message loss in the case of + * unexpected failure. + *
+ * ::MQTTCLIENT_PERSISTENCE_USER: Use an application-specific persistence + * implementation. Using this type of persistence gives control of the + * persistence mechanism to the application. The application has to implement + * the MQTTClient_persistence interface. + * @param persistence_context If the application uses + * ::MQTTCLIENT_PERSISTENCE_NONE persistence, this argument is unused and should + * be set to NULL. For ::MQTTCLIENT_PERSISTENCE_DEFAULT persistence, it + * should be set to the location of the persistence directory (if set + * to NULL, the persistence directory used is the working directory). + * Applications that use ::MQTTCLIENT_PERSISTENCE_USER persistence set this + * argument to point to a valid MQTTClient_persistence structure. + * @return ::MQTTCLIENT_SUCCESS if the client is successfully created, otherwise + * an error code is returned. + */ +LIBMQTT_API int MQTTClient_create(MQTTClient* handle, const char* serverURI, const char* clientId, + int persistence_type, void* persistence_context); + +/** Options for the ::MQTTClient_createWithOptions call */ +typedef struct +{ + /** The eyecatcher for this structure. must be MQCO. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 */ + int struct_version; + /** Whether the MQTT version is 3.1, 3.1.1, or 5. To use V5, this must be set. + * MQTT V5 has to be chosen here, because during the create call the message persistence + * is initialized, and we want to know whether the format of any persisted messages + * is appropriate for the MQTT version we are going to connect with. Selecting 3.1 or + * 3.1.1 and attempting to read 5.0 persisted messages will result in an error on create. */ + int MQTTVersion; +} MQTTClient_createOptions; + +#define MQTTClient_createOptions_initializer { {'M', 'Q', 'C', 'O'}, 0, MQTTVERSION_DEFAULT } + +/** + * A version of :MQTTClient_create() with additional options. + * This function creates an MQTT client ready for connection to the + * specified server and using the specified persistent storage (see + * MQTTClient_persistence). See also MQTTClient_destroy(). + * @param handle A pointer to an ::MQTTClient handle. The handle is + * populated with a valid client reference following a successful return from + * this function. + * @param serverURI A null-terminated string specifying the server to + * which the client will connect. It takes the form protocol://host:port. + * Currently, protocol must be tcp or ssl. + * For host, you can + * specify either an IP address or a host name. For instance, to connect to + * a server running on the local machines with the default MQTT port, specify + * tcp://localhost:1883. + * @param clientId The client identifier passed to the server when the + * client connects to it. It is a null-terminated UTF-8 encoded string. + * @param persistence_type The type of persistence to be used by the client: + *
+ * ::MQTTCLIENT_PERSISTENCE_NONE: Use in-memory persistence. If the device or + * system on which the client is running fails or is switched off, the current + * state of any in-flight messages is lost and some messages may not be + * delivered even at QoS1 and QoS2. + *
+ * ::MQTTCLIENT_PERSISTENCE_DEFAULT: Use the default (file system-based) + * persistence mechanism. Status about in-flight messages is held in persistent + * storage and provides some protection against message loss in the case of + * unexpected failure. + *
+ * ::MQTTCLIENT_PERSISTENCE_USER: Use an application-specific persistence + * implementation. Using this type of persistence gives control of the + * persistence mechanism to the application. The application has to implement + * the MQTTClient_persistence interface. + * @param persistence_context If the application uses + * ::MQTTCLIENT_PERSISTENCE_NONE persistence, this argument is unused and should + * be set to NULL. For ::MQTTCLIENT_PERSISTENCE_DEFAULT persistence, it + * should be set to the location of the persistence directory (if set + * to NULL, the persistence directory used is the working directory). + * Applications that use ::MQTTCLIENT_PERSISTENCE_USER persistence set this + * argument to point to a valid MQTTClient_persistence structure. + * @param options additional options for the create. + * @return ::MQTTCLIENT_SUCCESS if the client is successfully created, otherwise + * an error code is returned. + */ +LIBMQTT_API int MQTTClient_createWithOptions(MQTTClient* handle, const char* serverURI, const char* clientId, + int persistence_type, void* persistence_context, MQTTClient_createOptions* options); + +/** + * MQTTClient_willOptions defines the MQTT "Last Will and Testament" (LWT) settings for + * the client. In the event that a client unexpectedly loses its connection to + * the server, the server publishes the LWT message to the LWT topic on + * behalf of the client. This allows other clients (subscribed to the LWT topic) + * to be made aware that the client has disconnected. To enable the LWT + * function for a specific client, a valid pointer to an MQTTClient_willOptions + * structure is passed in the MQTTClient_connectOptions structure used in the + * MQTTClient_connect() call that connects the client to the server. The pointer + * to MQTTClient_willOptions can be set to NULL if the LWT function is not + * required. + */ +typedef struct +{ + /** The eyecatcher for this structure. must be MQTW. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 or 1 + 0 means there is no binary payload option + */ + int struct_version; + /** The LWT topic to which the LWT message will be published. */ + const char* topicName; + /** The LWT payload in string form. */ + const char* message; + /** + * The retained flag for the LWT message (see MQTTClient_message.retained). + */ + int retained; + /** + * The quality of service setting for the LWT message (see + * MQTTClient_message.qos and @ref qos). + */ + int qos; + /** The LWT payload in binary form. This is only checked and used if the message option is NULL */ + struct + { + int len; /**< binary payload length */ + const void* data; /**< binary payload data */ + } payload; +} MQTTClient_willOptions; + +#define MQTTClient_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 1, NULL, NULL, 0, 0, {0, NULL} } + +#define MQTT_SSL_VERSION_DEFAULT 0 +#define MQTT_SSL_VERSION_TLS_1_0 1 +#define MQTT_SSL_VERSION_TLS_1_1 2 +#define MQTT_SSL_VERSION_TLS_1_2 3 + +/** +* MQTTClient_sslProperties defines the settings to establish an SSL/TLS connection using the +* OpenSSL library. It covers the following scenarios: +* - Server authentication: The client needs the digital certificate of the server. It is included +* in a store containting trusted material (also known as "trust store"). +* - Mutual authentication: Both client and server are authenticated during the SSL handshake. In +* addition to the digital certificate of the server in a trust store, the client will need its own +* digital certificate and the private key used to sign its digital certificate stored in a "key store". +* - Anonymous connection: Both client and server do not get authenticated and no credentials are needed +* to establish an SSL connection. Note that this scenario is not fully secure since it is subject to +* man-in-the-middle attacks. +*/ +typedef struct +{ + /** The eyecatcher for this structure. Must be MQTS */ + char struct_id[4]; + + /** The version number of this structure. Must be 0, 1, 2, 3, 4 or 5. + * 0 means no sslVersion + * 1 means no verify, CApath + * 2 means no ssl_error_context, ssl_error_cb + * 3 means no ssl_psk_cb, ssl_psk_context, disableDefaultTrustStore + * 4 means no protos, protos_len + */ + int struct_version; + + /** The file in PEM format containing the public digital certificates trusted by the client. */ + const char* trustStore; + + /** The file in PEM format containing the public certificate chain of the client. It may also include + * the client's private key. + */ + const char* keyStore; + + /** If not included in the sslKeyStore, this setting points to the file in PEM format containing + * the client's private key. + */ + const char* privateKey; + + /** The password to load the client's privateKey if encrypted. */ + const char* privateKeyPassword; + + /** + * The list of cipher suites that the client will present to the server during the SSL handshake. For a + * full explanation of the cipher list format, please see the OpenSSL on-line documentation: + * http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT + * If this setting is ommitted, its default value will be "ALL", that is, all the cipher suites -excluding + * those offering no encryption- will be considered. + * This setting can be used to set an SSL anonymous connection ("aNULL" string value, for instance). + */ + const char* enabledCipherSuites; + + /** True/False option to enable verification of the server certificate **/ + int enableServerCertAuth; + + /** The SSL/TLS version to use. Specify one of MQTT_SSL_VERSION_DEFAULT (0), + * MQTT_SSL_VERSION_TLS_1_0 (1), MQTT_SSL_VERSION_TLS_1_1 (2) or MQTT_SSL_VERSION_TLS_1_2 (3). + * Only used if struct_version is >= 1. + */ + int sslVersion; + + /** + * Whether to carry out post-connect checks, including that a certificate + * matches the given host name. + * Exists only if struct_version >= 2 + */ + int verify; + + /** + * From the OpenSSL documentation: + * If CApath is not NULL, it points to a directory containing CA certificates in PEM format. + * Exists only if struct_version >= 2 + */ + const char* CApath; + + /** + * Callback function for OpenSSL error handler ERR_print_errors_cb + * Exists only if struct_version >= 3 + */ + int (*ssl_error_cb) (const char *str, size_t len, void *u); + + /** + * Application-specific contex for OpenSSL error handler ERR_print_errors_cb + * Exists only if struct_version >= 3 + */ + void* ssl_error_context; + + /** + * Callback function for setting TLS-PSK options. Parameters correspond to that of + * SSL_CTX_set_psk_client_callback, except for u which is the pointer ssl_psk_context. + * Exists only if struct_version >= 4 + */ + unsigned int (*ssl_psk_cb) (const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len, void *u); + + /** + * Application-specific contex for ssl_psk_cb + * Exists only if struct_version >= 4 + */ + void* ssl_psk_context; + + /** + * Don't load default SSL CA. Should be used together with PSK to make sure + * regular servers with certificate in place is not accepted. + * Exists only if struct_version >= 4 + */ + int disableDefaultTrustStore; + + /** + * The protocol-lists must be in wire-format, which is defined as a vector of non-empty, 8-bit length-prefixed, byte strings. + * The length-prefix byte is not included in the length. Each string is limited to 255 bytes. A byte-string length of 0 is invalid. + * A truncated byte-string is invalid. + * Check documentation for SSL_CTX_set_alpn_protos + * Exists only if struct_version >= 5 + */ + const unsigned char *protos; + + /** + * The length of the vector protos vector + * Exists only if struct_version >= 5 + */ + unsigned int protos_len; +} MQTTClient_SSLOptions; + +#define MQTTClient_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 5, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 } + +/** + * MQTTClient_libraryInfo is used to store details relating to the currently used + * library such as the version in use, the time it was built and relevant openSSL + * options. + * There is one static instance of this struct in MQTTClient.c + */ + +typedef struct +{ + const char* name; + const char* value; +} MQTTClient_nameValue; + +/** + * This function returns version information about the library. + * no trace information will be returned. + * @return an array of strings describing the library. The last entry is a NULL pointer. + */ +LIBMQTT_API MQTTClient_nameValue* MQTTClient_getVersionInfo(void); + +/** + * MQTTClient_connectOptions defines several settings that control the way the + * client connects to an MQTT server. + * + * Note: Default values are not defined for members of + * MQTTClient_connectOptions so it is good practice to specify all settings. + * If the MQTTClient_connectOptions structure is defined as an automatic + * variable, all members are set to random values and thus must be set by the + * client application. If the MQTTClient_connectOptions structure is defined + * as a static variable, initialization (in compliant compilers) sets all + * values to 0 (NULL for pointers). A #keepAliveInterval setting of 0 prevents + * correct operation of the client and so you must at least set a value + * for #keepAliveInterval. + * + * Suitable default values are set in the following initializers: + * - MQTTClient_connectOptions_initializer: for MQTT 3.1.1 non-WebSockets + * - MQTTClient_connectOptions_initializer5: for MQTT 5.0 non-WebSockets + * - MQTTClient_connectOptions_initializer_ws: for MQTT 3.1.1 WebSockets + * - MQTTClient_connectOptions_initializer5_ws: for MQTT 5.0 WebSockets + */ +typedef struct +{ + /** The eyecatcher for this structure. must be MQTC. */ + char struct_id[4]; + /** The version number of this structure. Must be 0, 1, 2, 3, 4, 5, 6, 7 or 8. + * 0 signifies no SSL options and no serverURIs + * 1 signifies no serverURIs + * 2 signifies no MQTTVersion + * 3 signifies no returned values + * 4 signifies no binary password option + * 5 signifies no maxInflightMessages and cleanstart + * 6 signifies no HTTP headers option + * 7 signifies no HTTP proxy and HTTPS proxy options + */ + int struct_version; + /** The "keep alive" interval, measured in seconds, defines the maximum time + * that should pass without communication between the client and the server + * The client will ensure that at least one message travels across the + * network within each keep alive period. In the absence of a data-related + * message during the time period, the client sends a very small MQTT + * "ping" message, which the server will acknowledge. The keep alive + * interval enables the client to detect when the server is no longer + * available without having to wait for the long TCP/IP timeout. + */ + int keepAliveInterval; + /** + * This is a boolean value. The cleansession setting controls the behaviour + * of both the client and the server at connection and disconnection time. + * The client and server both maintain session state information. This + * information is used to ensure "at least once" and "exactly once" + * delivery, and "exactly once" receipt of messages. Session state also + * includes subscriptions created by an MQTT client. You can choose to + * maintain or discard state information between sessions. + * + * When cleansession is true, the state information is discarded at + * connect and disconnect. Setting cleansession to false keeps the state + * information. When you connect an MQTT client application with + * MQTTClient_connect(), the client identifies the connection using the + * client identifier and the address of the server. The server checks + * whether session information for this client + * has been saved from a previous connection to the server. If a previous + * session still exists, and cleansession=true, then the previous session + * information at the client and server is cleared. If cleansession=false, + * the previous session is resumed. If no previous session exists, a new + * session is started. + */ + int cleansession; + /** + * This is a boolean value that controls how many messages can be in-flight + * simultaneously. Setting reliable to true means that a published + * message must be completed (acknowledgements received) before another + * can be sent. Attempts to publish additional messages receive an + * ::MQTTCLIENT_MAX_MESSAGES_INFLIGHT return code. Setting this flag to + * false allows up to 10 messages to be in-flight. This can increase + * overall throughput in some circumstances. + */ + int reliable; + /** + * This is a pointer to an MQTTClient_willOptions structure. If your + * application does not make use of the Last Will and Testament feature, + * set this pointer to NULL. + */ + MQTTClient_willOptions* will; + /** + * MQTT servers that support the MQTT v3.1.1 protocol provide authentication + * and authorisation by user name and password. This is the user name + * parameter. + */ + const char* username; + /** + * MQTT servers that support the MQTT v3.1.1 protocol provide authentication + * and authorisation by user name and password. This is the password + * parameter. + */ + const char* password; + /** + * The time interval in seconds to allow a connect to complete. + */ + int connectTimeout; + /** + * The time interval in seconds after which unacknowledged publish requests are + * retried during a TCP session. With MQTT 3.1.1 and later, retries are + * not required except on reconnect. 0 turns off in-session retries, and is the + * recommended setting. Adding retries to an already overloaded network only + * exacerbates the problem. + */ + int retryInterval; + /** + * This is a pointer to an MQTTClient_SSLOptions structure. If your + * application does not make use of SSL, set this pointer to NULL. + */ + MQTTClient_SSLOptions* ssl; + /** + * The number of entries in the optional serverURIs array. Defaults to 0. + */ + int serverURIcount; + /** + * An optional array of null-terminated strings specifying the servers to + * which the client will connect. Each string takes the form protocol://host:port. + * protocol must be tcp, ssl, ws or wss. + * The TLS enabled prefixes (ssl, wss) are only valid if a TLS version of the library + * is linked with. + * For host, you can + * specify either an IP address or a host name. For instance, to connect to + * a server running on the local machines with the default MQTT port, specify + * tcp://localhost:1883. + * If this list is empty (the default), the server URI specified on MQTTClient_create() + * is used. + */ + char* const* serverURIs; + /** + * Sets the version of MQTT to be used on the connect. + * MQTTVERSION_DEFAULT (0) = default: start with 3.1.1, and if that fails, fall back to 3.1 + * MQTTVERSION_3_1 (3) = only try version 3.1 + * MQTTVERSION_3_1_1 (4) = only try version 3.1.1 + * MQTTVERSION_5 (5) = only try version 5.0 + */ + int MQTTVersion; + /** + * Returned from the connect when the MQTT version used to connect is 3.1.1 + */ + struct + { + const char* serverURI; /**< the serverURI connected to */ + int MQTTVersion; /**< the MQTT version used to connect with */ + int sessionPresent; /**< if the MQTT version is 3.1.1, the value of sessionPresent returned in the connack */ + } returned; + /** + * Optional binary password. Only checked and used if the password option is NULL + */ + struct + { + int len; /**< binary password length */ + const void* data; /**< binary password data */ + } binarypwd; + /** + * The maximum number of messages in flight + */ + int maxInflightMessages; + /* + * MQTT V5 clean start flag. Only clears state at the beginning of the session. + */ + int cleanstart; + /** + * HTTP headers for websockets + */ + const MQTTClient_nameValue* httpHeaders; + /** + * HTTP proxy + */ + const char* httpProxy; + /** + * HTTPS proxy + */ + const char* httpsProxy; +} MQTTClient_connectOptions; + +/** Initializer for connect options for MQTT 3.1.1 non-WebSocket connections */ +#define MQTTClient_connectOptions_initializer { {'M', 'Q', 'T', 'C'}, 8, 60, 1, 1, NULL, NULL, NULL, 30, 0, NULL,\ +0, NULL, MQTTVERSION_DEFAULT, {NULL, 0, 0}, {0, NULL}, -1, 0, NULL, NULL, NULL} + +/** Initializer for connect options for MQTT 5.0 non-WebSocket connections */ +#define MQTTClient_connectOptions_initializer5 { {'M', 'Q', 'T', 'C'}, 8, 60, 0, 1, NULL, NULL, NULL, 30, 0, NULL,\ +0, NULL, MQTTVERSION_5, {NULL, 0, 0}, {0, NULL}, -1, 1, NULL, NULL, NULL} + +/** Initializer for connect options for MQTT 3.1.1 WebSockets connections. + * The keepalive interval is set to 45 seconds to avoid webserver 60 second inactivity timeouts. + */ +#define MQTTClient_connectOptions_initializer_ws { {'M', 'Q', 'T', 'C'}, 8, 45, 1, 1, NULL, NULL, NULL, 30, 0, NULL,\ +0, NULL, MQTTVERSION_DEFAULT, {NULL, 0, 0}, {0, NULL}, -1, 0, NULL, NULL, NULL} + +/** Initializer for connect options for MQTT 5.0 WebSockets connections. + * The keepalive interval is set to 45 seconds to avoid webserver 60 second inactivity timeouts. + */ +#define MQTTClient_connectOptions_initializer5_ws { {'M', 'Q', 'T', 'C'}, 8, 45, 0, 1, NULL, NULL, NULL, 30, 0, NULL,\ +0, NULL, MQTTVERSION_5, {NULL, 0, 0}, {0, NULL}, -1, 1, NULL, NULL, NULL} + +/** + * This function attempts to connect a previously-created client (see + * MQTTClient_create()) to an MQTT server using the specified options. If you + * want to enable asynchronous message and status notifications, you must call + * MQTTClient_setCallbacks() prior to MQTTClient_connect(). + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param options A pointer to a valid MQTTClient_connectOptions + * structure. + * @return ::MQTTCLIENT_SUCCESS if the client successfully connects to the + * server. An error code is returned if the client was unable to connect to + * the server. + * Error codes greater than 0 are returned by the MQTT protocol:

+ * 1: Connection refused: Unacceptable protocol version
+ * 2: Connection refused: Identifier rejected
+ * 3: Connection refused: Server unavailable
+ * 4: Connection refused: Bad user name or password
+ * 5: Connection refused: Not authorized
+ * 6-255: Reserved for future use
+ */ +LIBMQTT_API int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options); + +/** MQTT version 5.0 response information */ +typedef struct MQTTResponse +{ + int version; /* the version number of this structure */ + enum MQTTReasonCodes reasonCode; /* the MQTT 5.0 reason code returned */ + int reasonCodeCount; /* the number of reason codes. Used for subscribeMany5 and unsubscribeMany5 */ + enum MQTTReasonCodes* reasonCodes; /* a list of reason codes. Used for subscribeMany5 and unsubscribeMany5 */ + MQTTProperties* properties; /* optionally, the MQTT 5.0 properties returned */ +} MQTTResponse; + +#define MQTTResponse_initializer {1, MQTTREASONCODE_SUCCESS, 0, NULL, NULL} + +/** + * Frees the storage associated with the MQTT response. + * @param response the response structure to be freed + */ +LIBMQTT_API void MQTTResponse_free(MQTTResponse response); + +/** + * Attempts to connect a previously-created client (see + * MQTTClient_create()) to an MQTT server using MQTT version 5.0 and the specified options. If you + * want to enable asynchronous message and status notifications, you must call + * MQTTClient_setCallbacks() prior to MQTTClient_connect(). + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param options A pointer to a valid MQTTClient_connectOptions + * structure. + * @param connectProperties the MQTT 5.0 connect properties to use + * @param willProperties the MQTT 5.0 properties to set on the will message + * @return the MQTT 5.0 response information: error codes and properties. + */ +LIBMQTT_API MQTTResponse MQTTClient_connect5(MQTTClient handle, MQTTClient_connectOptions* options, + MQTTProperties* connectProperties, MQTTProperties* willProperties); + +/** + * This function attempts to disconnect the client from the MQTT + * server. In order to allow the client time to complete handling of messages + * that are in-flight when this function is called, a timeout period is + * specified. When the timeout period has expired, the client disconnects even + * if there are still outstanding message acknowledgements. + * The next time the client connects to the same server, any QoS 1 or 2 + * messages which have not completed will be retried depending on the + * cleansession settings for both the previous and the new connection (see + * MQTTClient_connectOptions.cleansession and MQTTClient_connect()). + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param timeout The client delays disconnection for up to this time (in + * milliseconds) in order to allow in-flight message transfers to complete. + * @return ::MQTTCLIENT_SUCCESS if the client successfully disconnects from + * the server. An error code is returned if the client was unable to disconnect + * from the server + */ +LIBMQTT_API int MQTTClient_disconnect(MQTTClient handle, int timeout); + +LIBMQTT_API int MQTTClient_disconnect5(MQTTClient handle, int timeout, enum MQTTReasonCodes reason, MQTTProperties* props); + +/** + * This function allows the client application to test whether or not a + * client is currently connected to the MQTT server. + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @return Boolean true if the client is connected, otherwise false. + */ +LIBMQTT_API int MQTTClient_isConnected(MQTTClient handle); + + +/* Subscribe is synchronous. QoS list parameter is changed on return to granted QoSs. + Returns return code, MQTTCLIENT_SUCCESS == success, non-zero some sort of error (TBD) */ + +/** + * This function attempts to subscribe a client to a single topic, which may + * contain wildcards (see @ref wildcard). This call also specifies the + * @ref qos requested for the subscription + * (see also MQTTClient_subscribeMany()). + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param topic The subscription topic, which may include wildcards. + * @param qos The requested quality of service for the subscription. + * @return ::MQTTCLIENT_SUCCESS if the subscription request is successful. + * An error code is returned if there was a problem registering the + * subscription. + */ +LIBMQTT_API int MQTTClient_subscribe(MQTTClient handle, const char* topic, int qos); + +/** + * This function attempts to subscribe an MQTT version 5.0 client to a single topic, which may + * contain wildcards (see @ref wildcard). This call also specifies the + * @ref qos requested for the subscription + * (see also MQTTClient_subscribeMany()). + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param topic The subscription topic, which may include wildcards. + * @param qos The requested quality of service for the subscription. + * @param opts the MQTT 5.0 subscribe options to be used + * @param props the MQTT 5.0 properties to be used + * @return the MQTT 5.0 response information: error codes and properties. + */ +LIBMQTT_API MQTTResponse MQTTClient_subscribe5(MQTTClient handle, const char* topic, int qos, + MQTTSubscribe_options* opts, MQTTProperties* props); + +/** + * This function attempts to subscribe a client to a list of topics, which may + * contain wildcards (see @ref wildcard). This call also specifies the + * @ref qos requested for each topic (see also MQTTClient_subscribe()). + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param count The number of topics for which the client is requesting + * subscriptions. + * @param topic An array (of length count) of pointers to + * topics, each of which may include wildcards. + * @param qos An array (of length count) of @ref qos + * values. qos[n] is the requested QoS for topic[n]. + * @return ::MQTTCLIENT_SUCCESS if the subscription request is successful. + * An error code is returned if there was a problem registering the + * subscriptions. + */ +LIBMQTT_API int MQTTClient_subscribeMany(MQTTClient handle, int count, char* const* topic, int* qos); + +/** + * This function attempts to subscribe an MQTT version 5.0 client to a list of topics, which may + * contain wildcards (see @ref wildcard). This call also specifies the + * @ref qos requested for each topic (see also MQTTClient_subscribe()). + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param count The number of topics for which the client is requesting + * subscriptions. + * @param topic An array (of length count) of pointers to + * topics, each of which may include wildcards. + * @param qos An array (of length count) of @ref qos + * values. qos[n] is the requested QoS for topic[n]. + * @param opts the MQTT 5.0 subscribe options to be used + * @param props the MQTT 5.0 properties to be used + * @return the MQTT 5.0 response information: error codes and properties. + */ +LIBMQTT_API MQTTResponse MQTTClient_subscribeMany5(MQTTClient handle, int count, char* const* topic, + int* qos, MQTTSubscribe_options* opts, MQTTProperties* props); + +/** + * This function attempts to remove an existing subscription made by the + * specified client. + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param topic The topic for the subscription to be removed, which may + * include wildcards (see @ref wildcard). + * @return ::MQTTCLIENT_SUCCESS if the subscription is removed. + * An error code is returned if there was a problem removing the + * subscription. + */ +LIBMQTT_API int MQTTClient_unsubscribe(MQTTClient handle, const char* topic); + +/** + * This function attempts to remove an existing subscription made by the + * specified client using MQTT 5.0. + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param topic The topic for the subscription to be removed, which may + * include wildcards (see @ref wildcard). + * @param props the MQTT 5.0 properties to be used + * @return the MQTT 5.0 response information: error codes and properties. + */ +LIBMQTT_API MQTTResponse MQTTClient_unsubscribe5(MQTTClient handle, const char* topic, MQTTProperties* props); + +/** + * This function attempts to remove existing subscriptions to a list of topics + * made by the specified client. + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param count The number subscriptions to be removed. + * @param topic An array (of length count) of pointers to the topics of + * the subscriptions to be removed, each of which may include wildcards. + * @return ::MQTTCLIENT_SUCCESS if the subscriptions are removed. + * An error code is returned if there was a problem removing the subscriptions. + */ +LIBMQTT_API int MQTTClient_unsubscribeMany(MQTTClient handle, int count, char* const* topic); + +/** + * This function attempts to remove existing subscriptions to a list of topics + * made by the specified client using MQTT version 5.0. + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param count The number subscriptions to be removed. + * @param topic An array (of length count) of pointers to the topics of + * the subscriptions to be removed, each of which may include wildcards. + * @param props the MQTT 5.0 properties to be used + * @return the MQTT 5.0 response information: error codes and properties. + */ +LIBMQTT_API MQTTResponse MQTTClient_unsubscribeMany5(MQTTClient handle, int count, char* const* topic, MQTTProperties* props); + +/** + * This function attempts to publish a message to a given topic (see also + * MQTTClient_publishMessage()). An ::MQTTClient_deliveryToken is issued when + * this function returns successfully. If the client application needs to + * test for succesful delivery of QoS1 and QoS2 messages, this can be done + * either asynchronously or synchronously (see @ref async, + * ::MQTTClient_waitForCompletion and MQTTClient_deliveryComplete()). + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param topicName The topic associated with this message. + * @param payloadlen The length of the payload in bytes. + * @param payload A pointer to the byte array payload of the message. + * @param qos The @ref qos of the message. + * @param retained The retained flag for the message. + * @param dt A pointer to an ::MQTTClient_deliveryToken. This is populated + * with a token representing the message when the function returns + * successfully. If your application does not use delivery tokens, set this + * argument to NULL. + * @return ::MQTTCLIENT_SUCCESS if the message is accepted for publication. + * An error code is returned if there was a problem accepting the message. + */ +LIBMQTT_API int MQTTClient_publish(MQTTClient handle, const char* topicName, int payloadlen, const void* payload, int qos, int retained, + MQTTClient_deliveryToken* dt); + +/** + * Attempts to publish a message to a given topic using MQTT version 5.0 (see also + * MQTTClient_publishMessage5()). An ::MQTTClient_deliveryToken is issued when + * this function returns successfully. If the client application needs to + * test for succesful delivery of QoS1 and QoS2 messages, this can be done + * either asynchronously or synchronously (see @ref async, + * ::MQTTClient_waitForCompletion and MQTTClient_deliveryComplete()). + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param topicName The topic associated with this message. + * @param payloadlen The length of the payload in bytes. + * @param payload A pointer to the byte array payload of the message. + * @param qos The @ref qos of the message. + * @param retained The retained flag for the message. + * @param properties the MQTT 5.0 properties to be used + * @param dt A pointer to an ::MQTTClient_deliveryToken. This is populated + * with a token representing the message when the function returns + * successfully. If your application does not use delivery tokens, set this + * argument to NULL. + * @return the MQTT 5.0 response information: error codes and properties. + */ +LIBMQTT_API MQTTResponse MQTTClient_publish5(MQTTClient handle, const char* topicName, int payloadlen, const void* payload, + int qos, int retained, MQTTProperties* properties, MQTTClient_deliveryToken* dt); +/** + * This function attempts to publish a message to a given topic (see also + * MQTTClient_publish()). An ::MQTTClient_deliveryToken is issued when + * this function returns successfully. If the client application needs to + * test for succesful delivery of QoS1 and QoS2 messages, this can be done + * either asynchronously or synchronously (see @ref async, + * ::MQTTClient_waitForCompletion and MQTTClient_deliveryComplete()). + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param topicName The topic associated with this message. + * @param msg A pointer to a valid MQTTClient_message structure containing + * the payload and attributes of the message to be published. + * @param dt A pointer to an ::MQTTClient_deliveryToken. This is populated + * with a token representing the message when the function returns + * successfully. If your application does not use delivery tokens, set this + * argument to NULL. + * @return ::MQTTCLIENT_SUCCESS if the message is accepted for publication. + * An error code is returned if there was a problem accepting the message. + */ +LIBMQTT_API int MQTTClient_publishMessage(MQTTClient handle, const char* topicName, MQTTClient_message* msg, MQTTClient_deliveryToken* dt); + + +/** + * Attempts to publish a message to the given topic using MQTT version 5.0 + * (see also + * MQTTClient_publish5()). An ::MQTTClient_deliveryToken is issued when + * this function returns successfully. If the client application needs to + * test for succesful delivery of QoS1 and QoS2 messages, this can be done + * either asynchronously or synchronously (see @ref async, + * ::MQTTClient_waitForCompletion and MQTTClient_deliveryComplete()). + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param topicName The topic associated with this message. + * @param msg A pointer to a valid MQTTClient_message structure containing + * the payload and attributes of the message to be published. + * @param dt A pointer to an ::MQTTClient_deliveryToken. This is populated + * with a token representing the message when the function returns + * successfully. If your application does not use delivery tokens, set this + * argument to NULL. + * @return the MQTT 5.0 response information: error codes and properties. + */ +LIBMQTT_API MQTTResponse MQTTClient_publishMessage5(MQTTClient handle, const char* topicName, MQTTClient_message* msg, + MQTTClient_deliveryToken* dt); + +/** + * This function is called by the client application to synchronize execution + * of the main thread with completed publication of a message. When called, + * MQTTClient_waitForCompletion() blocks execution until the message has been + * successful delivered or the specified timeout has expired. See @ref async. + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param dt The ::MQTTClient_deliveryToken that represents the message being + * tested for successful delivery. Delivery tokens are issued by the + * publishing functions MQTTClient_publish() and MQTTClient_publishMessage(). + * @param timeout The maximum time to wait in milliseconds. + * @return ::MQTTCLIENT_SUCCESS if the message was successfully delivered. + * An error code is returned if the timeout expires or there was a problem + * checking the token. + */ +LIBMQTT_API int MQTTClient_waitForCompletion(MQTTClient handle, MQTTClient_deliveryToken dt, unsigned long timeout); + + +/** + * This function sets a pointer to an array of delivery tokens for + * messages that are currently in-flight (pending completion). + * + * Important note: The memory used to hold the array of tokens is + * malloc()'d in this function. The client application is responsible for + * freeing this memory when it is no longer required. + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param tokens The address of a pointer to an ::MQTTClient_deliveryToken. + * When the function returns successfully, the pointer is set to point to an + * array of tokens representing messages pending completion. The last member of + * the array is set to -1 to indicate there are no more tokens. If no tokens + * are pending, the pointer is set to NULL. + * @return ::MQTTCLIENT_SUCCESS if the function returns successfully. + * An error code is returned if there was a problem obtaining the list of + * pending tokens. + */ +LIBMQTT_API int MQTTClient_getPendingDeliveryTokens(MQTTClient handle, MQTTClient_deliveryToken **tokens); + +/** + * When implementing a single-threaded client, call this function periodically + * to allow processing of message retries and to send MQTT keepalive pings. + * If the application is calling MQTTClient_receive() regularly, then it is + * not necessary to call this function. + */ +LIBMQTT_API void MQTTClient_yield(void); + +/** + * This function performs a synchronous receive of incoming messages. It should + * be used only when the client application has not set callback methods to + * support asynchronous receipt of messages (see @ref async and + * MQTTClient_setCallbacks()). Using this function allows a single-threaded + * client subscriber application to be written. When called, this function + * blocks until the next message arrives or the specified timeout expires + *(see also MQTTClient_yield()). + * + * Important note: The application must free() the memory allocated + * to the topic and the message when processing is complete (see + * MQTTClient_freeMessage()). + * @param handle A valid client handle from a successful call to + * MQTTClient_create(). + * @param topicName The address of a pointer to a topic. This function + * allocates the memory for the topic and returns it to the application + * by setting topicName to point to the topic. + * @param topicLen The length of the topic. If the return code from this + * function is ::MQTTCLIENT_TOPICNAME_TRUNCATED, the topic contains embedded + * NULL characters and the full topic should be retrieved by using + * topicLen. + * @param message The address of a pointer to the received message. This + * function allocates the memory for the message and returns it to the + * application by setting message to point to the received message. + * The pointer is set to NULL if the timeout expires. + * @param timeout The length of time to wait for a message in milliseconds. + * @return ::MQTTCLIENT_SUCCESS or ::MQTTCLIENT_TOPICNAME_TRUNCATED if a + * message is received. ::MQTTCLIENT_SUCCESS can also indicate that the + * timeout expired, in which case message is NULL. An error code is + * returned if there was a problem trying to receive a message. + */ +LIBMQTT_API int MQTTClient_receive(MQTTClient handle, char** topicName, int* topicLen, MQTTClient_message** message, + unsigned long timeout); + +/** + * This function frees memory allocated to an MQTT message, including the + * additional memory allocated to the message payload. The client application + * calls this function when the message has been fully processed. Important + * note: This function does not free the memory allocated to a message + * topic string. It is the responsibility of the client application to free + * this memory using the MQTTClient_free() library function. + * @param msg The address of a pointer to the ::MQTTClient_message structure + * to be freed. + */ +LIBMQTT_API void MQTTClient_freeMessage(MQTTClient_message** msg); + +/** + * This function frees memory allocated by the MQTT C client library, especially the + * topic name. This is needed on Windows when the client libary and application + * program have been compiled with different versions of the C compiler. It is + * thus good policy to always use this function when freeing any MQTT C client- + * allocated memory. + * @param ptr The pointer to the client library storage to be freed. + */ +LIBMQTT_API void MQTTClient_free(void* ptr); + +/** + * This function is used to allocate memory to be used or freed by the MQTT C client library, + * especially the data in user persistence. This is needed on Windows when the client library + * and application program have been compiled with different versions of the C compiler. + * @param size The size of the memory to be allocated. + */ +LIBMQTT_API void* MQTTClient_malloc(size_t size); + +/** + * This function frees the memory allocated to an MQTT client (see + * MQTTClient_create()). It should be called when the client is no longer + * required. + * @param handle A pointer to the handle referring to the ::MQTTClient + * structure to be freed. + */ +LIBMQTT_API void MQTTClient_destroy(MQTTClient* handle); + + +enum MQTTCLIENT_TRACE_LEVELS +{ + MQTTCLIENT_TRACE_MAXIMUM = 1, + MQTTCLIENT_TRACE_MEDIUM, + MQTTCLIENT_TRACE_MINIMUM, + MQTTCLIENT_TRACE_PROTOCOL, + MQTTCLIENT_TRACE_ERROR, + MQTTCLIENT_TRACE_SEVERE, + MQTTCLIENT_TRACE_FATAL, +}; + + +/** + * This function sets the level of trace information which will be + * returned in the trace callback. + * @param level the trace level required + */ +LIBMQTT_API void MQTTClient_setTraceLevel(enum MQTTCLIENT_TRACE_LEVELS level); + + +/** + * This is a callback function prototype which must be implemented if you want + * to receive trace information. Do not invoke any other Paho API calls in this + * callback function - unpredictable behavior may result. + * @param level the trace level of the message returned + * @param message the trace message. This is a pointer to a static buffer which + * will be overwritten on each call. You must copy the data if you want to keep + * it for later. + */ +typedef void MQTTClient_traceCallback(enum MQTTCLIENT_TRACE_LEVELS level, char* message); + +/** + * This function sets the trace callback if needed. If set to NULL, + * no trace information will be returned. The default trace level is + * MQTTASYNC_TRACE_MINIMUM. + * @param callback a pointer to the function which will handle the trace information + */ +LIBMQTT_API void MQTTClient_setTraceCallback(MQTTClient_traceCallback* callback); + +/** + * Sets the timeout value for un/subscribe commands when waiting for the un/suback response from + * the server. Values less than 5000 are not allowed. + * @param handle A valid client handle from a successful call to MQTTClient_create(). + * @param milliSeconds the maximum number of milliseconds to wait + * @return MQTTCLIENT_SUCCESS or MQTTCLIENT_FAILURE + */ +LIBMQTT_API int MQTTClient_setCommandTimeout(MQTTClient handle, unsigned long milliSeconds); + +/** + * Returns a pointer to the string representation of the error or NULL. + * + * Do not free after use. Returns NULL if the error code is unknown. + */ +LIBMQTT_API const char* MQTTClient_strerror(int code); + +#if defined(__cplusplus) + } +#endif + +#endif + +/*! + * @cond MQTTClient_main + * @page async Asynchronous vs synchronous client applications + * This client library supports two modes of operation. These are referred to + * as synchronous and asynchronous modes. If your application + * calls MQTTClient_setCallbacks(), this puts the client into asynchronous + * mode, otherwise it operates in synchronous mode. + * + * In synchronous mode, the client application runs on a single thread. + * Messages are published using the MQTTClient_publish() and + * MQTTClient_publishMessage() functions. To determine that a QoS1 or QoS2 + * (see @ref qos) message has been successfully delivered, the application + * must call the MQTTClient_waitForCompletion() function. An example showing + * synchronous publication is shown in @ref pubsync. Receiving messages in + * synchronous mode uses the MQTTClient_receive() function. Client applications + * must call either MQTTClient_receive() or MQTTClient_yield() relatively + * frequently in order to allow processing of acknowledgements and the MQTT + * "pings" that keep the network connection to the server alive. + * + * In asynchronous mode, the client application runs on several threads. The + * main program calls functions in the client library to publish and subscribe, + * just as for the synchronous mode. Processing of handshaking and maintaining + * the network connection is performed in the background, however. + * Notifications of status and message reception are provided to the client + * application using callbacks registered with the library by the call to + * MQTTClient_setCallbacks() (see MQTTClient_messageArrived(), + * MQTTClient_connectionLost() and MQTTClient_deliveryComplete()). + * This API is not thread safe however - it is not possible to call it from multiple + * threads without synchronization. You can use the MQTTAsync API for that. + * + * @page callbacks Callbacks + * You must not call a function from this API from within a callback otherwise + * a deadlock might result. The only exception to this is the ability to call + * connect within the connection lost callback, to allow a reconnect. + * + * When using MQTT 5.0, you can also call connect from within the disconnected + * callback, which is invoked when the MQTT server sends a disconnect packet. + * This server behaviour is allowed in MQTT 5.0, but not in MQTT 3.1.1, so the + * disconnected callback will never be invoked if you use MQTT 3.1.1. + * + * In particular, you must make a publish call within the message arrived callback. + * These restrictions are all lifted in the + * MQTTAsync API. + * + * If no callbacks are assigned, this will include the message arrived callback. + * This could be done if the application is a pure publisher, and does + * not subscribe to any topics. If however messages are received, and no message + * arrived callback is set, or receive not called, then those messages will accumulate + * and take up memory, as there is no place for them to be delivered. + * It is up to the application to protect against this situation. + * + * @page wildcard Subscription wildcards + * Every MQTT message includes a topic that classifies it. MQTT servers use + * topics to determine which subscribers should receive messages published to + * the server. + * + * Consider the server receiving messages from several environmental sensors. + * Each sensor publishes its measurement data as a message with an associated + * topic. Subscribing applications need to know which sensor originally + * published each received message. A unique topic is thus used to identify + * each sensor and measurement type. Topics such as SENSOR1TEMP, + * SENSOR1HUMIDITY, SENSOR2TEMP and so on achieve this but are not very + * flexible. If additional sensors are added to the system at a later date, + * subscribing applications must be modified to receive them. + * + * To provide more flexibility, MQTT supports a hierarchical topic namespace. + * This allows application designers to organize topics to simplify their + * management. Levels in the hierarchy are delimited by the '/' character, + * such as SENSOR/1/HUMIDITY. Publishers and subscribers use these + * hierarchical topics as already described. + * + * For subscriptions, two wildcard characters are supported: + *
    + *
  • A '#' character represents a complete sub-tree of the hierarchy and + * thus must be the last character in a subscription topic string, such as + * SENSOR/#. This will match any topic starting with SENSOR/, such as + * SENSOR/1/TEMP and SENSOR/2/HUMIDITY.
  • + *
  • A '+' character represents a single level of the hierarchy and is + * used between delimiters. For example, SENSOR/+/TEMP will match + * SENSOR/1/TEMP and SENSOR/2/TEMP.
  • + *
+ * Publishers are not allowed to use the wildcard characters in their topic + * names. + * + * Deciding on your topic hierarchy is an important step in your system design. + * + * @page qos Quality of service + * The MQTT protocol provides three qualities of service for delivering + * messages between clients and servers: "at most once", "at least once" and + * "exactly once". + * + * Quality of service (QoS) is an attribute of an individual message being + * published. An application sets the QoS for a specific message by setting the + * MQTTClient_message.qos field to the required value. + * + * A subscribing client can set the maximum quality of service a server uses + * to send messages that match the client subscriptions. The + * MQTTClient_subscribe() and MQTTClient_subscribeMany() functions set this + * maximum. The QoS of a message forwarded to a subscriber thus might be + * different to the QoS given to the message by the original publisher. + * The lower of the two values is used to forward a message. + * + * The three levels are: + * + * QoS0, At most once: The message is delivered at most once, or it + * may not be delivered at all. Its delivery across the network is not + * acknowledged. The message is not stored. The message could be lost if the + * client is disconnected, or if the server fails. QoS0 is the fastest mode of + * transfer. It is sometimes called "fire and forget". + * + * The MQTT protocol does not require servers to forward publications at QoS0 + * to a client. If the client is disconnected at the time the server receives + * the publication, the publication might be discarded, depending on the + * server implementation. + * + * QoS1, At least once: The message is always delivered at least once. + * It might be delivered multiple times if there is a failure before an + * acknowledgment is received by the sender. The message must be stored + * locally at the sender, until the sender receives confirmation that the + * message has been published by the receiver. The message is stored in case + * the message must be sent again. + * + * QoS2, Exactly once: The message is always delivered exactly once. + * The message must be stored locally at the sender, until the sender receives + * confirmation that the message has been published by the receiver. The + * message is stored in case the message must be sent again. QoS2 is the + * safest, but slowest mode of transfer. A more sophisticated handshaking + * and acknowledgement sequence is used than for QoS1 to ensure no duplication + * of messages occurs. + * @page pubsync Synchronous publication example +@code +#include +#include +#include +#include "MQTTClient.h" + +#define ADDRESS "tcp://mqtt.eclipseprojects.io:1883" +#define CLIENTID "ExampleClientPub" +#define TOPIC "MQTT Examples" +#define PAYLOAD "Hello World!" +#define QOS 1 +#define TIMEOUT 10000L + +int main(int argc, char* argv[]) +{ + MQTTClient client; + MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; + MQTTClient_message pubmsg = MQTTClient_message_initializer; + MQTTClient_deliveryToken token; + int rc; + + if ((rc = MQTTClient_create(&client, ADDRESS, CLIENTID, + MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to create client, return code %d\n", rc); + exit(EXIT_FAILURE); + } + + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to connect, return code %d\n", rc); + exit(EXIT_FAILURE); + } + + pubmsg.payload = PAYLOAD; + pubmsg.payloadlen = (int)strlen(PAYLOAD); + pubmsg.qos = QOS; + pubmsg.retained = 0; + if ((rc = MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to publish message, return code %d\n", rc); + exit(EXIT_FAILURE); + } + + printf("Waiting for up to %d seconds for publication of %s\n" + "on topic %s for client with ClientID: %s\n", + (int)(TIMEOUT/1000), PAYLOAD, TOPIC, CLIENTID); + rc = MQTTClient_waitForCompletion(client, token, TIMEOUT); + printf("Message with delivery token %d delivered\n", token); + + if ((rc = MQTTClient_disconnect(client, 10000)) != MQTTCLIENT_SUCCESS) + printf("Failed to disconnect, return code %d\n", rc); + MQTTClient_destroy(&client); + return rc; +} + + * @endcode + * + * @page pubasync Asynchronous publication example +@code{.c} +#include +#include +#include +#include "MQTTClient.h" + +#if !defined(_WIN32) +#include +#else +#include +#endif + +#define ADDRESS "tcp://mqtt.eclipseprojects.io:1883" +#define CLIENTID "ExampleClientPub" +#define TOPIC "MQTT Examples" +#define PAYLOAD "Hello World!" +#define QOS 1 +#define TIMEOUT 10000L + +MQTTClient_deliveryToken deliveredtoken; + +void delivered(void *context, MQTTClient_deliveryToken dt) +{ + printf("Message with token value %d delivery confirmed\n", dt); + deliveredtoken = dt; +} + +int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message) +{ + printf("Message arrived\n"); + printf(" topic: %s\n", topicName); + printf(" message: %.*s\n", message->payloadlen, (char*)message->payload); + MQTTClient_freeMessage(&message); + MQTTClient_free(topicName); + return 1; +} + +void connlost(void *context, char *cause) +{ + printf("\nConnection lost\n"); + printf(" cause: %s\n", cause); +} + +int main(int argc, char* argv[]) +{ + MQTTClient client; + MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; + MQTTClient_message pubmsg = MQTTClient_message_initializer; + MQTTClient_deliveryToken token; + int rc; + + if ((rc = MQTTClient_create(&client, ADDRESS, CLIENTID, + MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to create client, return code %d\n", rc); + rc = EXIT_FAILURE; + goto exit; + } + + if ((rc = MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to set callbacks, return code %d\n", rc); + rc = EXIT_FAILURE; + goto destroy_exit; + } + + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to connect, return code %d\n", rc); + rc = EXIT_FAILURE; + goto destroy_exit; + } + + pubmsg.payload = PAYLOAD; + pubmsg.payloadlen = (int)strlen(PAYLOAD); + pubmsg.qos = QOS; + pubmsg.retained = 0; + deliveredtoken = 0; + if ((rc = MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to publish message, return code %d\n", rc); + rc = EXIT_FAILURE; + } + else + { + printf("Waiting for publication of %s\n" + "on topic %s for client with ClientID: %s\n", + PAYLOAD, TOPIC, CLIENTID); + while (deliveredtoken != token) + { + #if defined(_WIN32) + Sleep(100); + #else + usleep(10000L); + #endif + } + } + + if ((rc = MQTTClient_disconnect(client, 10000)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to disconnect, return code %d\n", rc); + rc = EXIT_FAILURE; + } + +destroy_exit: + MQTTClient_destroy(&client); + +exit: + return rc; +} + + * @endcode + * @page subasync Asynchronous subscription example +@code +#include +#include +#include +#include "MQTTClient.h" + +#define ADDRESS "tcp://mqtt.eclipseprojects.io:1883" +#define CLIENTID "ExampleClientSub" +#define TOPIC "MQTT Examples" +#define PAYLOAD "Hello World!" +#define QOS 1 +#define TIMEOUT 10000L + +volatile MQTTClient_deliveryToken deliveredtoken; + +void delivered(void *context, MQTTClient_deliveryToken dt) +{ + printf("Message with token value %d delivery confirmed\n", dt); + deliveredtoken = dt; +} + +int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message) +{ + printf("Message arrived\n"); + printf(" topic: %s\n", topicName); + printf(" message: %.*s\n", message->payloadlen, (char*)message->payload); + MQTTClient_freeMessage(&message); + MQTTClient_free(topicName); + return 1; +} + +void connlost(void *context, char *cause) +{ + printf("\nConnection lost\n"); + printf(" cause: %s\n", cause); +} + +int main(int argc, char* argv[]) +{ + MQTTClient client; + MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; + int rc; + + if ((rc = MQTTClient_create(&client, ADDRESS, CLIENTID, + MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to create client, return code %d\n", rc); + rc = EXIT_FAILURE; + goto exit; + } + + if ((rc = MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to set callbacks, return code %d\n", rc); + rc = EXIT_FAILURE; + goto destroy_exit; + } + + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to connect, return code %d\n", rc); + rc = EXIT_FAILURE; + goto destroy_exit; + } + + printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n" + "Press Q to quit\n\n", TOPIC, CLIENTID, QOS); + if ((rc = MQTTClient_subscribe(client, TOPIC, QOS)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to subscribe, return code %d\n", rc); + rc = EXIT_FAILURE; + } + else + { + int ch; + do + { + ch = getchar(); + } while (ch!='Q' && ch != 'q'); + + if ((rc = MQTTClient_unsubscribe(client, TOPIC)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to unsubscribe, return code %d\n", rc); + rc = EXIT_FAILURE; + } + } + + if ((rc = MQTTClient_disconnect(client, 10000)) != MQTTCLIENT_SUCCESS) + { + printf("Failed to disconnect, return code %d\n", rc); + rc = EXIT_FAILURE; + } +destroy_exit: + MQTTClient_destroy(&client); +exit: + return rc; +} + + * @endcode + * @page tracing Tracing + * + * Runtime tracing is controlled by environment variables. + * + * Tracing is switched on by setting MQTT_C_CLIENT_TRACE. A value of ON, or stdout, prints to + * stdout, any other value is interpreted as a file name to use. + * + * The amount of trace detail is controlled with the MQTT_C_CLIENT_TRACE_LEVEL environment + * variable - valid values are ERROR, PROTOCOL, MINIMUM, MEDIUM and MAXIMUM + * (from least to most verbose). + * + * The variable MQTT_C_CLIENT_TRACE_MAX_LINES limits the number of lines of trace that are output + * to a file. Two files are used at most, when they are full, the last one is overwritten with the + * new trace entries. The default size is 1000 lines. + * + * ### MQTT Packet Tracing + * + * A feature that can be very useful is printing the MQTT packets that are sent and received. To + * achieve this, use the following environment variable settings: + * @code + MQTT_C_CLIENT_TRACE=ON + MQTT_C_CLIENT_TRACE_LEVEL=PROTOCOL + * @endcode + * The output you should see looks like this: + * @code + 20130528 155936.813 3 stdout-subscriber -> CONNECT cleansession: 1 (0) + 20130528 155936.813 3 stdout-subscriber <- CONNACK rc: 0 + 20130528 155936.813 3 stdout-subscriber -> SUBSCRIBE msgid: 1 (0) + 20130528 155936.813 3 stdout-subscriber <- SUBACK msgid: 1 + 20130528 155941.818 3 stdout-subscriber -> DISCONNECT (0) + * @endcode + * where the fields are: + * 1. date + * 2. time + * 3. socket number + * 4. client id + * 5. direction (-> from client to server, <- from server to client) + * 6. packet details + * + * ### Default Level Tracing + * + * This is an extract of a default level trace of a call to connect: + * @code + 19700101 010000.000 (1152206656) (0)> MQTTClient_connect:893 + 19700101 010000.000 (1152206656) (1)> MQTTClient_connectURI:716 + 20130528 160447.479 Connecting to serverURI localhost:1883 + 20130528 160447.479 (1152206656) (2)> MQTTProtocol_connect:98 + 20130528 160447.479 (1152206656) (3)> MQTTProtocol_addressPort:48 + 20130528 160447.479 (1152206656) (3)< MQTTProtocol_addressPort:73 + 20130528 160447.479 (1152206656) (3)> Socket_new:599 + 20130528 160447.479 New socket 4 for localhost, port 1883 + 20130528 160447.479 (1152206656) (4)> Socket_addSocket:163 + 20130528 160447.479 (1152206656) (5)> Socket_setnonblocking:73 + 20130528 160447.479 (1152206656) (5)< Socket_setnonblocking:78 (0) + 20130528 160447.479 (1152206656) (4)< Socket_addSocket:176 (0) + 20130528 160447.479 (1152206656) (4)> Socket_error:95 + 20130528 160447.479 (1152206656) (4)< Socket_error:104 (115) + 20130528 160447.479 Connect pending + 20130528 160447.479 (1152206656) (3)< Socket_new:683 (115) + 20130528 160447.479 (1152206656) (2)< MQTTProtocol_connect:131 (115) + * @endcode + * where the fields are: + * 1. date + * 2. time + * 3. thread id + * 4. function nesting level + * 5. function entry (>) or exit (<) + * 6. function name : line of source code file + * 7. return value (if there is one) + * + * ### Memory Allocation Tracing + * + * Setting the trace level to maximum causes memory allocations and frees to be traced along with + * the default trace entries, with messages like the following: + * @code + 20130528 161819.657 Allocating 16 bytes in heap at file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c line 177 ptr 0x179f930 + + 20130528 161819.657 Freeing 16 bytes in heap at file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c line 201, heap use now 896 bytes + * @endcode + * When the last MQTT client object is destroyed, if the trace is being recorded + * and all memory allocated by the client library has not been freed, an error message will be + * written to the trace. This can help with fixing memory leaks. The message will look like this: + * @code + 20130528 163909.208 Some memory not freed at shutdown, possible memory leak + 20130528 163909.208 Heap scan start, total 880 bytes + 20130528 163909.208 Heap element size 32, line 354, file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c, ptr 0x260cb00 + 20130528 163909.208 Content + 20130528 163909.209 Heap scan end + * @endcode + * @endcond + */ diff --git a/.svn/pristine/49/4984224f220dcdbebe6f1bce5dc07f1555aaf463.svn-base b/.svn/pristine/49/4984224f220dcdbebe6f1bce5dc07f1555aaf463.svn-base new file mode 100644 index 0000000..06c5308 --- /dev/null +++ b/.svn/pristine/49/4984224f220dcdbebe6f1bce5dc07f1555aaf463.svn-base @@ -0,0 +1,1845 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// Interface to allow G8BPQ switch to use HSMODEM TNC + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifndef WIN32 +#ifndef MACBPQ +#include +#endif +#endif + + +#include "cheaders.h" + +#pragma pack(1) + +struct BroadcastMsg +{ + unsigned char Type; + unsigned char initialVolTX; + unsigned char initialVolRX; + unsigned char AudioTimespan; + unsigned char intialVolSpeaker; + unsigned char initalVolMic; + unsigned char Retransmits; + unsigned char SendAudio; + unsigned char RTTYAutoSync; + unsigned char Speed; + char playbackDevice[100]; + char captureDevice[100]; + char Callsign[20]; + char Locator[10]; + char Name[20]; +}; + +struct FileHeader +{ + unsigned char Type; + unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only + char filename[50]; + unsigned short CRC; // of filename = transfer id + unsigned char Size[3]; // Big endian + unsigned char Data[164]; +}; + +struct FileData +{ + unsigned char Type; + unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only + unsigned char Data[219]; +}; + +#pragma pack() + +struct HSFILEINFO +{ + struct HSFILEINFO * Next; // May want to chain entries for partial files + + char fileName[50]; + unsigned short CRC; // Used as a transfer ID + int fileSize; + int Sequence; + int State; + int Type; + time_t LastRX; + unsigned char goodBlocks[1024]; + unsigned char * Data; + int dataPointer; + int lastBlock; + int lostBlocks; + unsigned char * txData; + int txSize; + int txLeft; +}; + + +struct HSMODEMINFO +{ + struct HSFILEINFO * File; + + int Mode; + char * Capture; // Capture Device Name + char * Playback; // Playback Device Name + int Seq; // To make CRC more Unique + int txFifo; + int rxFifo; + int Sync; + int DCD; +}; + + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + +#include "bpq32.h" + +#include "tncinfo.h" + +static int Socket_Data(int sock, int error, int eventcode); + +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); +BOOL HSMODEMWriteCommBlock(struct TNCINFO * TNC); +void HSMODEMCheckRX(struct TNCINFO * TNC); +int HSMODEMSendData(struct TNCINFO * TNC, UCHAR * data, int txlen); +int HSMODEMSendSingleData(struct TNCINFO * TNC, UCHAR * FN, UCHAR * data, int txlen); +int HSMODEMSendCommand(struct TNCINFO * TNC, UCHAR * data); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int HSMODEMGetLine(char * buf); +int ProcessEscape(UCHAR * TXMsg); +BOOL KAMStartPort(struct PORTCONTROL * PORT); +BOOL KAMStopPort(struct PORTCONTROL * PORT); +void SendPoll(struct TNCINFO * TNC); +void SendMode(struct TNCINFO * TNC); + +static char ClassName[]="HSMODEMSTATUS"; +static char WindowTitle[] = "HSMODEM"; +static int RigControlRow = 205; + +#ifndef LINBPQ +#include +#endif + +extern int SemHeldByAPI; + +static RECT Rect; + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC = TNCInfo[Port]; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if (ptr == NULL) return (TRUE); + + if (*ptr =='#') return (TRUE); // comment + + if (*ptr ==';') return (TRUE); // comment + + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->HSModemInfo = zalloc(sizeof(struct HSMODEMINFO)); + TNC->HSModemInfo->File = zalloc(sizeof(struct HSFILEINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->TCPPort = WINMORport; + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport + 2); // We only receive on Port + 2 + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "CAPTURE", 7) == 0) + { + TNC->HSModemInfo->Capture = _strdup(&buf[8]); + strlop(TNC->HSModemInfo->Capture, 13); + } + else if (_memicmp(buf, "PLAYBACK", 8) == 0) + { + TNC->HSModemInfo->Playback = _strdup(&buf[9]); + strlop(TNC->HSModemInfo->Playback, 13); + } + else if (_memicmp(buf, "MODE ", 5) == 0) + TNC->HSModemInfo->Mode = atoi(&buf[5]); + else if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + else + strcat (TNC->InitScript, buf); + } + + return (TRUE); +} + +static char * Config; +static char * ptr1, * ptr2; + +int HSMODEMGetLine(char * buf) +{ +loop: + + if (ptr2 == NULL) + return 0; + + memcpy(buf, ptr1, ptr2 - ptr1 + 2); + buf[ptr2 - ptr1 + 2] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + if (buf[0] < 0x20) goto loop; + if (buf[0] == '#') goto loop; + if (buf[0] == ';') goto loop; + + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + buf[strlen(buf)] = 13; + + return 1; +} + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static time_t ltime; + + + +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // HSMODEM mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +VOID HSMODEMChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + + datalen = sprintf(TXMsg, "MYCALL %s\r", Call); + HSMODEMSendCommand(TNC, TXMsg); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int bytes,txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + + if (TNC->CONNECTED) + { + TNC->CONNECTED--; + + if (TNC->CONNECTED == 0) + { + sprintf(TNC->WEB_COMMSSTATE, "Connection to HSMODEM lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + } + + TNC->PollDelay++; + + if (TNC->PollDelay > 20) + { + TNC->PollDelay = 0; + + SendPoll(TNC); + } + + return 0; + + case 1: // poll + + HSMODEMCheckRX(TNC); + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + int CallLen; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + +/* if (TNC->CONNECTED == 0 || + TNC->Streams[0].Connecting || + TNC->Streams[0].Connected) + { + // discard if TNC not connected or sesison active + + ReleaseBuffer(buffptr); + continue; + } +*/ + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + // Frame has ax.25 format header. Convert to Text + + CallLen = ConvFromAX25(Buffer + 7, Call); // Origin + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + *ptr++ = '!'; + + CallLen = ConvFromAX25(Buffer, Call); // Dest + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + Buffer += 14; // TO Digis + datalen -= 14; + + while ((Buffer[-1] & 1) == 0) + { + *ptr++ = ','; + CallLen = ConvFromAX25(Buffer, Call); + memcpy(ptr, Call, CallLen); + ptr += CallLen; + Buffer += 7; // End of addr + datalen -= 7; + } + + *ptr++ = '_'; + *ptr++ = 'U'; // UI Frame + *ptr++ = 0; // delimit calls + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + } + + HSMODEMSendSingleData(TNC, FECMsg, Buffer, datalen); + + ReleaseBuffer(buffptr); + } + + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + } + } + + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + HSMODEMSendCommand(TNC, "DISCONNECT\r"); + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("HSMODEM New Attach Stream %d", Stream); + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + + HSMODEMChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + //sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + //MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + STREAM->bytesTXed += txlen; + + bytes=HSMODEMSendData(TNC, data, txlen); + WritetoTrace(TNC, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 21; + memcpy(&buffptr->Data[0], "No Connection to TNC\r", 21); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + TXMsg = &buff->L2DATA[0]; + TXMsg[txlen] = 0; + + // for now just send, but allow sending control + // characters with \\ or ^ escape + + if (STREAM->Connected) + { + STREAM->PacketsSent++; + + bytes=HSMODEMSendData(TNC, TXMsg, txlen); + TNC->Streams[Stream].BytesOutstanding += bytes; // So flow control works - will be updated by BUFFER response + STREAM->bytesTXed += bytes; +// WritetoTrace(TNC, &buff->L2DATA[0], txlen); + + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "HSMODEM} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "MODE ", 5) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->HSModemInfo->Mode = atoi(&buff->L2DATA[5]); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "HSMODEM} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + SendMode(TNC); + + return 0; + + } + + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + txlen = sprintf(Connect, "C %s\r", &buff->L2DATA[2]); + + HSMODEMChangeMYC(TNC, TNC->Streams[0].MyCall); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + HSMODEMSendCommand(TNC, Connect); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = TRUE; + return 0; + + } + + // Normal data. Send to TNC + + + HSMODEMSendData(TNC, TXMsg, txlen); + + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // I think we should check buffer space for all comms modes + + { + int Queued; + int Outstanding = TNC->Streams[Stream].BytesOutstanding; + + if (Stream == 0) + Queued = TNC->Streams[13].FramesQueued; // ARDOP Native Mode Send Queue + else + Queued = TNC->Streams[Stream].FramesQueued; + + Outstanding = Queued = 0; + + if (Queued > 4 || Outstanding > 8500) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + + if (TNC->Streams[Stream].Attached == 0) + return (TNC->CONNECTED != 0) << 8 | 1; + + return ((TNC->CONNECTED != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + + case 4: // reinit7 + + return 0; + + case 5: // Close + + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on ARDOP"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + HSMODEMSendCommand(TNC, "CONOK OFF"); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + HSMODEMSendCommand(TNC, "CONOK ON"); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID HSMODEMReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + HSMODEMChangeMYC(TNC, TNC->NodeCall); + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID HSMODEMSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ + HSMODEMSendCommand(TNC, "CONOK OFF\r"); +} + +VOID HSMODEMReleasePort(struct TNCINFO * TNC) +{ + HSMODEMSendCommand(TNC, "CONOK ON\r"); +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "VARA Status" + "

HSMODEM Status" + "

", + TNC->Port); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +#ifndef LINBPQ + +#define BGCOLOUR RGB(236,233,216) +HBRUSH RedBrush = NULL; +HBRUSH GreenBrush; +HBRUSH BlueBrush; +static HBRUSH bgBrush = NULL; + +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + + + +static LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + PAINTSTRUCT ps; + HDC hdc; + + int i; + struct TNCINFO * TNC; + + HKEY hKey; + char Key[80]; + int retCode, disp; + + for (i=0; i<41; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->hDlg == hWnd) + break; + } + + if (TNC == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) { + + case WM_CREATE: + + break; + + case WM_PAINT: + + hdc = BeginPaint(hWnd, &ps); + + TextOut(hdc, 10, 162, "RX", 4); + TextOut(hdc, 10, 182, "TX", 4); + + if (TNC->HSModemInfo->Sync) + TextOut(hdc, 305, 162, "Sync", 4); + +// SelectObject(ps.hdc, RedBrush); + SelectObject(ps.hdc, GreenBrush); +// SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH)); + + Rectangle(ps.hdc, 40, 165, TNC->HSModemInfo->rxFifo + 42, 175); + SelectObject(ps.hdc, RedBrush); + Rectangle(ps.hdc, 40, 185, (TNC->HSModemInfo->txFifo * 10) + 42, 195); + + EndPaint(hWnd, &ps); + break; + + case WM_GETMINMAXINFO: + + if (TNC->ClientHeight) + { + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = TNC->ClientWidth; + mmi->ptMaxSize.y = TNC->ClientHeight; + mmi->ptMaxTrackSize.x = TNC->ClientWidth; + mmi->ptMaxTrackSize.y = TNC->ClientHeight; + } + + break; + + + case WM_MDIACTIVATE: + { + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + + if (TNC->hMenu) + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + +// SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + // call DrawMenuBar after the menu items are set + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)TNC->hMenu) + { + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") || strstr(TNC->ProgramPath, "VARA")) + { + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED); + + break; + } + } + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED); + } + + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case WINMOR_KILL: + + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + KillTNC(TNC); + RestartTNC(TNC); + break; + + case WINMOR_RESTARTAFTERFAILURE: + + TNC->RestartAfterFailure = !TNC->RestartAfterFailure; + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4); + RegCloseKey(hKey); + } + break; + + case ARDOP_ABORT: + + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SIZING: + case WM_SIZE: + + MoveWindows(TNC); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + + case SC_RESTORE: + + TNC->Minimized = FALSE; + break; + + case SC_MINIMIZE: + + TNC->Minimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +#endif + + + +VOID * HSMODEMExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + u_long param = 1; + int ret; + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + +#ifndef LINBPQ + + if (bgBrush == NULL) + { + bgBrush = CreateSolidBrush(BGCOLOUR); + RedBrush = CreateSolidBrush(RGB(255,0,0)); + GreenBrush = CreateSolidBrush(RGB(0,255,0)); + BlueBrush = CreateSolidBrush(RGB(0,0,255)); + } + +#endif + + Consoleprintf("HSMODEM Host %s %d", TNC->HostName, TNC->TCPPort); + + TNC->Port = port; + TNC->PortRecord = PortEntry; + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_HSMODEM; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (TNC->PacketChannels > 1) + TNC->PacketChannels = 1; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = HSMODEMSuspendPort; + TNC->ReleasePortProc = HSMODEMReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + // Set MYCALL + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,20,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + + + // Open and bind UDP socket + + TNC->TCPSock = socket(AF_INET,SOCK_DGRAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + WritetoConsole("Failed to create UDP socket for HSMODEM"); + ret = WSAGetLastError(); + } + else + ioctl (TNC->TCPSock, FIONBIO, ¶m); + + ret = bind(TNC->TCPSock, (struct sockaddr *) &TNC->destaddr, sizeof(struct sockaddr_in)); + + if (ret != 0) + { + // Bind Failed + + ret = WSAGetLastError(); + sprintf(Msg, "Bind Failed for UDP port %d - error code = %d", TNC->TCPPort + 2, ret); + WritetoConsole(Msg); + } + + +// SendInitScript(TNC); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[Stream].BytesOutstanding == 0) + HSMODEMSendCommand(TNC, "DISCONNECT\r"); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + HSMODEMSendCommand(TNC, "DISCONNECT\r"); +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + HSMODEMReleaseTNC(TNC); + } +} + +VOID HSMODEMAbort(struct TNCINFO * TNC) +{ + HSMODEMSendCommand(TNC, "ABORT\r"); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID HSMODEMDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + return; + } + + if (TNC->ReinitState == 3) + { + return; + } +} + +RECT Rect1 = {30, 160, 400, 195}; + + +VOID HSMODEMProcessTNCMessage(struct TNCINFO * TNC, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FileHeader * FH; + struct HSMODEMINFO * Modem = TNC->HSModemInfo; + struct HSFILEINFO * Info = Modem->File; + int fileLen, Seq, Offset; + + // Any message indicates Ok + + Msg[Len] = 0; + + if (TNC->CONNECTED == 0) + { + // Just come up + + sprintf(TNC->WEB_COMMSSTATE, "Connected to HSMODEM"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + SendMode(TNC); + } + + TNC->CONNECTED = 100; // time out after 10 secs + + /* + 3: responses to broadcast messages (see: GUI Interface: UDP/IP/Initialization) + 1: received payload data + 4: FFT data for a spectrum monitor + 5: IQ data for a constellation display + 6: received RTTY characters + */ + switch (Msg[0]) + { + case 1: + /* + Byte 0 ... 0x01 + Byte 1 ... frame type (which was inserted by the sender) + Byte 2 ... frame counter MSB + Byte 3 ... frame counter LSB (10 bits used) + Byte 4 ... frame information (which was inserted by the sender) + Byte 5 ... unused + Byte 6 ... measured line speed MSB + Byte 7 ... measured line speed LSB + Bytes 8-10 ... unused + Bytes 11-229 ... 219 bytes payload return; + + 1 … BER Test Pattern + 2 … Image + 3 … Ascii File + 4 … HTML File + 5 … Binary File + 6 … Voice Audio (for Codec 2 or Opus) + 7 … UserInfo + */ + + Seq = Msg[2] << 8 | Msg[3]; + + switch (Msg[1]) + { + case 1: + case 6: + case 7: + + Debugprintf("%d %d %02x %s %s %s", Msg[1], Seq, Msg[4], &Msg[11], &Msg[31], &Msg[41]); + return; + + case 2: + case 3: + case 4: + case 5: + + // File transfer types + + switch (Msg[4]) + { + case 0: + case 3: + + // File Header + + FH = (struct FileHeader *) &Msg[9]; + + if (FH->CRC == Info->CRC) + { + Debugprintf("Dup Header %X", Info->CRC); + return; + } + + Info->CRC = FH->CRC; + + fileLen = FH->Size[0] * 65536 + FH->Size[1] * 256 + FH->Size[2]; + + Info->Data = zalloc(fileLen + 512); + + if (Info->Data == NULL) + return; + + Info->fileSize = fileLen; + strcpy(Info->fileName, FH->filename); + + memset(Info->goodBlocks, 0, 1024); + Info->goodBlocks[0] = 1; + + Info->lastBlock = 0; + Info->lostBlocks = 0; + Info->LastRX = time(NULL); + + Debugprintf("%d %d %04X %02x %s %d %s", Msg[1], Seq, FH->CRC, Msg[4], + FH->filename, fileLen, FH->Data); + + memcpy(Info->Data, FH->Data, 164); + Info->dataPointer = 164; + break; + + case 1: + case 2: + + // Data Frame + + if (Seq == Info->lastBlock) + { + Debugprintf("Duplicate data frame %d", Seq); + return; + } + + Info->lastBlock++; + + if (Info->lastBlock != Seq) + Info->lostBlocks += Seq - Info->lastBlock; + + Info->goodBlocks[Seq] = 1; + + Offset = (Seq - 1) * 221 + 164; + + memcpy(&Info->Data[Offset], &Msg[11], 221); + + Debugprintf("%d %d %02x %s %d %s", Msg[1], Seq, Msg[4], &Msg[11]); + break; + + default: + + Debugprintf("%d %d %02x %s %d %s", Msg[1], Seq, Msg[4], &Msg[11]); + return; + + } + + // End of Data Frame Case + + if (Msg[4] == 2 || Msg[4] == 3) + { + // Last Frame - check file + + if (Info->lostBlocks == 0) + { + // filename is encoding of calls and frame type + + struct _MESSAGE * buffptr; + +// FILE * fp1 = fopen(Info->fileName, "wb"); +// int WriteLen; + +// if (fp1) +// { +// WriteLen = (int)fwrite(Info->Data, 1, Info->fileSize, fp1); +// fclose(fp1); +// } + + if (strchr(Info->fileName, '!')) + { + // Callsigns encoded in filename + + + + char * Origin = &Info->fileName[0]; + char * Type = strlop(Origin, '_'); + char * Dest = strlop(Origin, '!'); + unsigned char * Packet; + + + // Convert to ax.25 format + + buffptr = GetBuff(); + + // Convert to ax.25 format + + if (buffptr == 0) + return; // No buffers, so ignore + + Type = strlop(Origin, '_'); + + Packet = &buffptr->ORIGIN[0]; + + buffptr->PORT = TNC->Port; + buffptr->LENGTH = 16 + MSGHDDRLEN + Info->fileSize; + + ConvToAX25(Origin, buffptr->ORIGIN); + ConvToAX25(Dest, buffptr->DEST); + + + while (strchr(Dest, ',')) + { + Dest = strlop(Dest, ','); // Next digi + Packet += 7; + ConvToAX25(Dest, Packet); + buffptr->LENGTH += 7; + } + + Packet[6] |= 1; // Set end of address + + Packet += 7; + + *(Packet++) = 3; + *(Packet++) = 0xF0; + + memcpy(Packet, Info->Data, Info->fileSize); + time(&buffptr->Timestamp); + + BPQTRACE((MESSAGE *)buffptr, TRUE); + } + } + + return; + } + } + + return; + + case 4: // FFT data for a spectrum monitor + + Modem->txFifo = Msg[1]; + Modem->rxFifo = Msg[2]; + Modem->DCD = Msg[3]; + Modem->Sync = Msg[4]; + +#ifndef LINBPQ + InvalidateRect(TNC->hDlg, &Rect1, TRUE); +#endif + +// if (Info->Sync || Info->txFifo) +// Debugprintf("%d %d %d %d", Info->txFifo, Info->rxFifo, Info->DCD, Info->Sync); + /* + Byte 0 ... 0x04 + Byte 1 ... usage of the TX fifo (used by the transmitter to sync its data + output to the modem). This is a value between 0..255. During + an active transmission keep it above 4. + Byte 2 ... usage of RX fifo (not important, but can be displayed to the + user). A very high RX fifo usage indicates the the computer + is too slow for HSmodem. + Byte 3 ... 0 or 1. Indicates that an RF level was detected + Byte 4 ... 0 or 1. Indicates that the HSmodem receiver is synchronized + with a signal + Byte 5 ... maximum audio level (0..100%) of the audio input from the + transceiver. Can be used to detect clipping. + Byte 6 ... maximum audio level (0..100%) of the audio output to the + transceiver. Can be used to detect clipping. + Byte 7 ... in RTTY mode this is the auto-locked RTTY frequency MSB + Byte 8 ... and LSB + Byte 9 ... RTTY: 0=tx off, 1=txon + Byte 10 to the end ... FFT spectrum data, beginning at 0 Hz to 4kHz with + a resolution of 10 Hz +*/ + return; + + + case 5: // IQ data for a constellation display + return; + + case 6: //received RTTY characters + return; + } + + return; + + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", TNC->RXBuffer); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + +} + +extern char LOC[7]; + +void SendMode(struct TNCINFO * TNC) +{ + unsigned char Msg[221] = ""; + int ret; + + Msg[0] = 16; + Msg[1] = TNC->HSModemInfo->Mode; + + TNC->destaddr.sin_port = htons(TNC->TCPPort + 1); // Data Port + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + ret = sendto(TNC->TCPSock, (char *)&Msg, 221, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); + + return; +} + +void SendPoll(struct TNCINFO * TNC) +{ + struct BroadcastMsg PollMsg = {0x3c, 100, 100, 0, 50, 50, 1, 0, 0, 9}; + int ret; + + strcpy(&PollMsg.captureDevice[0], TNC->HSModemInfo->Capture); + strcpy(&PollMsg.playbackDevice[0], TNC->HSModemInfo->Playback); +// strcpy(&PollMsg.playbackDevice[0], "CABLE Input (VB-Audio Virtual Cable)"); + + strcpy(&PollMsg.Callsign[0], TNC->NodeCall); + strcpy(&PollMsg.Locator[0], LOC); + strcpy(&PollMsg.Name[0], "1234567890"); + + TNC->destaddr.sin_port = htons(TNC->TCPPort); // Command Port + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + ret = sendto(TNC->TCPSock, (char *)&PollMsg, 260, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); + + return; +} +/* + unsigned char Type; + unsigned char Info; // 0 - First, 1 - Continuation 2 Last 3 - Only + char filename[50]; + unsigned short CRC; // of filename = transfer id + unsigned char Size[3]; // Big endian + unsigned char Data[163]; +*/ + +unsigned short int compute_crc(unsigned char *buf,int len); + +int HSMODEMSendSingleData(struct TNCINFO * TNC, UCHAR * FN, UCHAR * data, int txlen) +{ + struct FileHeader Msg; + unsigned short int crc; + int ret, fragLen = txlen; + struct HSMODEMINFO * Modem = TNC->HSModemInfo; + struct HSFILEINFO * Info = Modem->File; + + char Seq[60] = ""; + + sprintf(Seq, "%04X%s", Modem->Seq++, FN); + + crc = compute_crc(Seq, 60); + + crc ^= 0xffff; + + memset(&Msg, 0, sizeof(struct FileHeader)); + + Msg.Type = 5; // Binary Data + Msg.Info = 3; // Only Fragment + + if (txlen > 163) + { + // Need to send as multiple fragments + + fragLen = 164; + Info->txData = malloc(txlen + 512); + memcpy(Info->txData, data, txlen); + Info->txSize = txlen; + Info->txLeft = txlen - 164; + Msg.Info = 0; // First Fragment + } + + strcpy(Msg.filename, FN); + memcpy(Msg.Data, data, txlen); + memcpy(&Msg.CRC, &crc, 2); + Msg.Size[0] = txlen >> 16; + Msg.Size[1] = txlen >> 8;; + Msg.Size[2] = txlen; + + TNC->destaddr.sin_port = htons(TNC->TCPPort + 1); // Data Port + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + ret = sendto(TNC->TCPSock, (char *)&Msg, 221, 0, (struct sockaddr *)&TNC->destaddr, sizeof(struct sockaddr)); + memset(&Msg, 0, sizeof(struct FileHeader)); + + return ret; +} + +int HSMODEMSendData(struct TNCINFO * TNC, UCHAR * data, int txlen) +{ + struct FileHeader Msg; + + memset(&Msg, 0, sizeof(struct FileHeader)); + + Msg.Type = 5; // Binary Data + Msg.Info = 3; // Only Fragment + + + return 0; +} + +void HSMODEMCheckRX(struct TNCINFO * TNC) +{ + int Len = 0; + unsigned char Buff[2000]; + + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + + Len = recvfrom(TNC->TCPSock, Buff, 2000, 0, (struct sockaddr *)&rxaddr, &addrlen); + + while (1) + { + if (Len == -1) + { +// Debugprintf("%d", GetLastError()); + Len = 0; + return; + } + TNC->RXLen = Len; + HSMODEMProcessTNCMessage(TNC, Buff, Len); + + Len = recvfrom(TNC->TCPSock, Buff, 2000, 0, (struct sockaddr *)&rxaddr, &addrlen); + + } + return; + + return; +} + +int HSMODEMWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + return 0; +} + + +int HSMODEMSendCommand(struct TNCINFO * TNC, UCHAR * data) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, data, (int)strlen(data)); + + return 0; +} + + \ No newline at end of file diff --git a/.svn/pristine/4c/4cf208075b33386046feb5cf48ba8a3d2913c3ca.svn-base b/.svn/pristine/4c/4cf208075b33386046feb5cf48ba8a3d2913c3ca.svn-base new file mode 100644 index 0000000..5a9b7c5 --- /dev/null +++ b/.svn/pristine/4c/4cf208075b33386046feb5cf48ba8a3d2913c3ca.svn-base @@ -0,0 +1,153 @@ +/* $Id: miniupnpc.h,v 1.56 2020/12/20 18:07:35 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project: miniupnp + * http://miniupnp.free.fr/ + * Author: Thomas Bernard + * Copyright (c) 2005-2018 Thomas Bernard + * This software is subjects to the conditions detailed + * in the LICENCE file provided within this distribution */ +#ifndef MINIUPNPC_H_INCLUDED +#define MINIUPNPC_H_INCLUDED + +#include "miniupnpc_declspec.h" +#include "igd_desc_parse.h" +#include "upnpdev.h" + +/* error codes : */ +#define UPNPDISCOVER_SUCCESS (0) +#define UPNPDISCOVER_UNKNOWN_ERROR (-1) +#define UPNPDISCOVER_SOCKET_ERROR (-101) +#define UPNPDISCOVER_MEMORY_ERROR (-102) + +/* versions : */ +#define MINIUPNPC_VERSION "2.2.2" +#define MINIUPNPC_API_VERSION 17 + +/* Source port: + Using "1" as an alias for 1900 for backwards compatibility + (presuming one would have used that for the "sameport" parameter) */ +#define UPNP_LOCAL_PORT_ANY 0 +#define UPNP_LOCAL_PORT_SAME 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structures definitions : */ +struct UPNParg { const char * elt; const char * val; }; + +char * +simpleUPnPcommand(int, const char *, const char *, + const char *, struct UPNParg *, + int *); + +/* upnpDiscover() + * discover UPnP devices on the network. + * The discovered devices are returned as a chained list. + * It is up to the caller to free the list with freeUPNPDevlist(). + * delay (in millisecond) is the maximum time for waiting any device + * response. + * If available, device list will be obtained from MiniSSDPd. + * Default path for minissdpd socket will be used if minissdpdsock argument + * is NULL. + * If multicastif is not NULL, it will be used instead of the default + * multicast interface for sending SSDP discover packets. + * If localport is set to UPNP_LOCAL_PORT_SAME(1) SSDP packets will be sent + * from the source port 1900 (same as destination port), if set to + * UPNP_LOCAL_PORT_ANY(0) system assign a source port, any other value will + * be attempted as the source port. + * "searchalltypes" parameter is useful when searching several types, + * if 0, the discovery will stop with the first type returning results. + * TTL should default to 2. */ +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscover(int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error); + +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscoverAll(int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error); + +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscoverDevice(const char * device, int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error); + +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscoverDevices(const char * const deviceTypes[], + int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error, + int searchalltypes); + +/* parserootdesc() : + * parse root XML description of a UPnP device and fill the IGDdatas + * structure. */ +MINIUPNP_LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *); + +/* structure used to get fast access to urls + * controlURL: controlURL of the WANIPConnection + * ipcondescURL: url of the description of the WANIPConnection + * controlURL_CIF: controlURL of the WANCommonInterfaceConfig + * controlURL_6FC: controlURL of the WANIPv6FirewallControl + */ +struct UPNPUrls { + char * controlURL; + char * ipcondescURL; + char * controlURL_CIF; + char * controlURL_6FC; + char * rootdescURL; +}; + +/* UPNP_GetValidIGD() : + * return values : + * 0 = NO IGD found + * 1 = A valid connected IGD has been found + * 2 = A valid IGD has been found but it reported as + * not connected + * 3 = an UPnP device has been found but was not recognized as an IGD + * + * In any non zero return case, the urls and data structures + * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to + * free allocated memory. + */ +MINIUPNP_LIBSPEC int +UPNP_GetValidIGD(struct UPNPDev * devlist, + struct UPNPUrls * urls, + struct IGDdatas * data, + char * lanaddr, int lanaddrlen); + +/* UPNP_GetIGDFromUrl() + * Used when skipping the discovery process. + * When succeding, urls, data, and lanaddr arguments are set. + * return value : + * 0 - Not ok + * 1 - OK */ +MINIUPNP_LIBSPEC int +UPNP_GetIGDFromUrl(const char * rootdescurl, + struct UPNPUrls * urls, + struct IGDdatas * data, + char * lanaddr, int lanaddrlen); + +MINIUPNP_LIBSPEC void +GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, + const char *, unsigned int); + +MINIUPNP_LIBSPEC void +FreeUPNPUrls(struct UPNPUrls *); + +/* return 0 or 1 */ +MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/.svn/pristine/4d/4dcf21db1a049395a1e7c3cec2f3096f674dd935.svn-base b/.svn/pristine/4d/4dcf21db1a049395a1e7c3cec2f3096f674dd935.svn-base new file mode 100644 index 0000000..b38b4fe --- /dev/null +++ b/.svn/pristine/4d/4dcf21db1a049395a1e7c3cec2f3096f674dd935.svn-base @@ -0,0 +1,3339 @@ +/* +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 +*/ + +// +// DLL to provide AXIP support for G8BPQ switch in a +// 32bit environment, +// +// Uses BPQ EXTERNAL interface +// + +// Version 1.1 August 2001 +// +// Send to all matching entries in map table +// (Mainly for NODES braodcasts to multiple stations) +// + +// Version 1.2 September 2001 +// +// Support UDP as well as raw IP + +// Version 1.3 October 2001 +// +// Allow host names as well as numeric IP addresses +// +// Version 1.4 November 2002 +// +// Implement keepalive for NAT routers +// + +// Version 1.5 December 2004 +// +// Implement a "MHEARD" facility +// + + +// Version 1.6 August 2005 +// +// Treat NULL string in Registry as use current directory + + +// Version 1.7 December 2005 +// +// Create a separate thread to open sockets to avoid hang on XP SP2 + + +// Version 1.8 January 2006 +// +// Get config file location from Node (will check bpq directory) + + +// Version 1.9 March 2006 +// +// Allow multiple listening UDP ports +// Kick off resolver on EXTRESTART +// Remove redundant DYNAMIC processing + +// Version 1.10 October 2006 +// +// Add "Minimize to Tray" option +// Write diagnostics to BPQ console window instead of STDOUT + +// Version 1.11 October 2007 +// +// Sort MHeard and discard last entry if full +// Add Commands to re-read config file and manually add an ARP entry + +// Version 1.12 February 2008 +// +// Check received length +// Changes for unload of bpq32.dll +// Add Close Driver function +// Dynamic Load of bpq32.dll + +// Version 1.13 October 2008 +// +// Add Linux-style config of broadcast addressess + +// Version 1.13.2 January 2009 +// +// Add Start Minimized Option + +// Version 1.13.3 February 2009 +// +// Save Window positions + +// Version 1.13.4 March 2009 +// +// Fix loop on config file error + +// Version 1.14.1 April 2009 +// +// Add option to reject messages if sender is not in ARP Table +// Add option to add received calls to ARP Table + +// Version 1.15.1 May 2009 +// +// Add IP/TCP option + +// Version 1.15.2 August 2009 +// +// Extra Debug Output in TCP Mode +// Fix problem if TCP entry was first in table +// Include TCP sessions in MHEARD +// Add T flag to Resolver window fot TCP Sessions +// Set SO_KEEPALIVE and SO_CONDITIONAL_ACCEPT socket options + +// Version 1.15.4 August 2009 + +// Recycle Listening Socket if no connect for 60 mins +// Clear data connections if no data for 60 mins +// Repaint MH Window after clear. + +// Version 1.15.5 Spetmber 2010 + +// Add option to get config from bpq32.dll +// Moved to BPQ32.dll - no separate version number onw. + +// October 2010 + +// Allow multiple axip ports. + +// June 2011 + +// Add IPv6 support + +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" +#ifndef WIN32 +#include +#include +#include +#endif +#include "bpq32.h" + +#ifndef LINBPQ +#include "kernelresource.h" +#include +#endif + +#include + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +// Cater for only systems without IPV6_V6ONLY + +#ifndef IPV6_V6ONLY +#define IPV6_V6ONLY 0x27 +#endif + +#ifndef MAXGETHOSTSTRUCT +#define MAXGETHOSTSTRUCT 1024 +#endif + + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +int ResolveDelay = 0; + + +extern BOOL StartMinimized; + +VOID * zalloc(int len); + +int ResetExtDriver(int num); +BOOL ProcessConfig(); +VOID FreeConfig(); + +extern UCHAR BPQDirectory[]; + + +extern int OffsetH, OffsetW; + +static void ResolveNames(struct AXIPPORTINFO * PORT); +void OpenSockets(struct AXIPPORTINFO * PORT); +void CloseSockets(struct AXIPPORTINFO * PORT); + + +static int CONVFROMAX25(char * incall, char * outcall); +void CreateMHWindow(struct AXIPPORTINFO * PORT); +int Update_MH_List(struct AXIPPORTINFO * PORT, UCHAR * ipad, char * call, char proto, short port, BOOL IPv6); +int Update_MH_KeepAlive(struct AXIPPORTINFO * PORT, struct in_addr ipad, char proto, short port); +unsigned short int compute_crc(unsigned char *buf,int l); +unsigned int find_arp(unsigned char * call); +BOOL add_arp_entry(struct AXIPPORTINFO * PORT, unsigned char * call, UCHAR * ip, int len, int port,unsigned char * name, +int keepalive, BOOL BCFlag, BOOL AutoAdded, int TCPMode, int SourcePort, BOOL IPv6, int noUpdate); +BOOL add_bc_entry(struct AXIPPORTINFO * PORT, unsigned char * call, int len); +BOOL convtoax25(unsigned char * callsign, unsigned char * ax25call, int * calllen); +static BOOL ReadConfigFile(int Port); +static int ProcessLine(char * buf, struct AXIPPORTINFO * PORT); +int CheckKeepalives(struct AXIPPORTINFO * PORT); +BOOL CopyScreentoBuffer(char * buff, struct AXIPPORTINFO * PORT); +int DumpFrameInHex(unsigned char * msg, int len); +VOID SendFrame(struct AXIPPORTINFO * PORT, struct arp_table_entry * arp_table, UCHAR * buff, int txlen); +BOOL CheckSourceisResolvable(struct AXIPPORTINFO * PORT, char * call, int Port, VOID * rxaddr); +int DataSocket_Read(struct arp_table_entry * sockptr, SOCKET sock); +int GetMessageFromBuffer(struct AXIPPORTINFO * PORT, char * Buffer); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +int KissDecode(UCHAR * inbuff, int len); +int Socket_Accept(int SocketId); +int Socket_Connect(int SocketId, int Error); +int Socket_Data(int sock, int error, int eventcode); +VOID TCPConnectThread(struct arp_table_entry * arp); +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Consoleprintf(const char * format, ...); +BOOL OpenListeningSocket(struct AXIPPORTINFO * PORT, struct arp_table_entry * arp); +VOID Format_Addr(unsigned char * Addr, char * Output, BOOL IPV6); +static void CreateResolverWindow(struct AXIPPORTINFO * PORT); +VOID SaveMDIWindowPos(HWND hWnd, char * RegKey, char * Value, BOOL Minimized); +VOID SaveAXIPCache(struct AXIPPORTINFO * PORT); +VOID GetAXIPCache(struct AXIPPORTINFO * PORT); + + +union +{ + struct sockaddr_in sinx; + struct sockaddr_in6 sinx6; +} sinx; +/* +union +{ + struct sockaddr_in destaddr; + struct sockaddr_in6 destaddr6; +} destaddr; +*/ + +#define IP_AXIP 93 // IP Protocol for AXIP + +#pragma pack(1) + +struct iphdr { +// unsigned int version:4; // Version of IP +// unsigned int h_len:4; // length of the header + unsigned char h_lenvers; // Version + length of the header + unsigned char tos; // Type of service + unsigned short total_len; // total length of the packet + unsigned short ident; // unique identifier + unsigned short frag_and_flags; // flags + unsigned char ttl; + unsigned char proto; // protocol (TCP, UDP etc) + unsigned short checksum; // IP checksum + + unsigned int sourceIP; + unsigned int destIP; + +}; + +#pragma pack() + + +#define TCPMaster 1 +#define TCPSlave 2 + +#define TCPListening 1 +#define TCPConnecting 2 +#define TCPConnected 4 + +#ifndef LINBPQ + +LOGFONT LFTTYFONT ; + +extern HFONT hFont ; + +RECT ResRect; +RECT MHRect; + +extern HKEY REGTREE; + +extern HWND ClientWnd, FrameWnd; +extern HMENU hMainFrameMenu, hBaseMenu, hWndMenu; +extern HBRUSH bgBrush; + +#endif + +//struct tagMSG Msg; + +//char buf[MAXGETHOSTSTRUCT]; + +int addrlen6 = sizeof(struct sockaddr_in6); +int addrlen = sizeof(struct sockaddr_in); + +extern unsigned short CRCTAB[]; +unsigned int AXIPInst = 0; + +char CantReplyList[512] = ""; // To suppress duplicate "Can't Reply" messages + +DWORD n; + +struct AXIPPORTINFO * Portlist[MaxBPQPortNo + 1]; + +int InitAXIP(int Port); + +int CurrentResEntries; + +static char ConfigClassName[]="CONFIG"; + +HANDLE hInstance; + +VOID SaveAXIPWindowPos(int port) +{ +#ifndef LINBPQ + struct AXIPPORTINFO * PORT; + char Key[80]; + + PORT = Portlist[port]; + + if (PORT == NULL) + return; + + sprintf(Key, "PACTOR\\PORT%d", port); + + SaveMDIWindowPos(PORT->hMHWnd, Key, "MHSize", PORT->MHMinimized); + SaveMDIWindowPos(PORT->hResWnd, Key, "ResSize", PORT->ResMinimized); +#endif + return; +} + + +static size_t ExtProc(int fn, int port, PMESSAGE buff) +{ + struct iphdr * iphdrptr; + int len,txlen=0,err,index,digiptr,i; + unsigned short int crc; + char rxbuff[5000]; + char axcall[7]; + char errmsg[100]; + union + { + struct sockaddr_in rxaddr; + struct sockaddr_in6 rxaddr6; + } RXaddr; + struct AXIPPORTINFO * PORT = Portlist[port]; + + switch (fn) + { + case 1: // poll + + // + // Check Keepalive timers + // + time(&PORT->ltime); + + if (PORT->ltime-PORT->lasttime >9 ) + { + PORT->lasttime=PORT->ltime; + CheckKeepalives(PORT); + } + + if (PORT->needip) + { + char call[7]; + + len = recvfrom(PORT->sock,rxbuff,500,0,(struct sockaddr *)&RXaddr.rxaddr,&addrlen); + + if (len == -1) + { + err = WSAGetLastError(); + } + else + { + iphdrptr=(struct iphdr *)&rxbuff; + + if (len == ntohs(iphdrptr->total_len)) + { + len-=20; // IP HEADER + + if (memcmp(&rxbuff[20], "Keepalive", 9) == 0 ) + { + if (PORT->MHEnabled) + Update_MH_KeepAlive(PORT, RXaddr.rxaddr.sin_addr,'I',93); + + return 0; + } + crc = compute_crc(&rxbuff[20], len); + + if (crc == 0xf0b8) // Good CRC + { + len-=2; // Remove CRC + + if (len > MAXDATA) + { + sprintf(errmsg,"BPQAXIP Invalid Msg Len=%d Source=%s",len,inet_ntoa(RXaddr.rxaddr.sin_addr)); + OutputDebugString(errmsg); + DumpFrameInHex(&rxbuff[20], len); + return 0; + } + + memcpy(&buff->DEST, &rxbuff[20],len); + len += (3 + sizeof(void *)); + + PutLengthinBuffer((PDATAMESSAGE)buff, len); // Needed for arm5 portability + + memcpy(call, &buff->ORIGIN, 7); + call[6] &= 0x7e; // Mask End of Address bit + + // + // Do MH Proccessing if enabled + // + + if (PORT->MHEnabled) + Update_MH_List(PORT, (UCHAR *)&RXaddr.rxaddr.sin_addr.s_addr, &buff->ORIGIN[0], 'I', 93, 0); + + // Check Exclude + + if (CheckExcludeList(call) == 0) + return 0; + + + if (PORT->Checkifcanreply) + { + if (CheckSourceisResolvable(PORT, call, 0, &RXaddr)) + + return 1; + + else + // Can't reply. If AutoConfig is set, add to table and accept, else reject + + if (PORT->AutoAddARP) + return add_arp_entry(PORT, call, (UCHAR *)&RXaddr.rxaddr.sin_addr.s_addr, 7, 0, inet_ntoa(RXaddr.rxaddr.sin_addr), 0, PORT->AutoAddBC, TRUE, 0, 0, FALSE, 0); + else + { + char From[11] = "|"; + From[ConvFromAX25(call, &From[1]) + 1] = 0; + if (strstr(CantReplyList, From) == 0) + { + if (strlen(CantReplyList) < 500); + strcat(CantReplyList, From); + Debugprintf("AXIP Packet from %s dropped - can't reply", &From[1]); + } + return 0; + } + } + else + return(1); + } + // + // CRC Error + // + + sprintf(errmsg,"BPQAXIP Invalid CRC=%d Source=%s",crc,inet_ntoa(RXaddr.rxaddr.sin_addr)); + OutputDebugString(errmsg); + + return (0); + } + + // + // Bad Length + // + + return (0); + } + } + + for (i=0;iNumberofUDPPorts;i++) + { + char call[7]; + + if (PORT->IPv6[i]) + len = recvfrom(PORT->udpsock[i],rxbuff,500,0,(struct sockaddr *)&RXaddr.rxaddr, &addrlen6); + else + len = recvfrom(PORT->udpsock[i],rxbuff,500,0,(struct sockaddr *)&RXaddr.rxaddr, &addrlen); + + if (len == -1) + { + err = WSAGetLastError(); + } + else + { + if (memcmp(rxbuff, "Keepalive", 9) == 0 ) + { + if (PORT->MHEnabled) + Update_MH_KeepAlive(PORT, RXaddr.rxaddr.sin_addr, 'U', PORT->udpport[i]); + + continue; + } + + crc = compute_crc(&rxbuff[0], len); + + if (crc == 0xf0b8) // Good CRC + { + len-=2; // Remove CRC + + if (len > MAXDATA) + { + sprintf(errmsg,"BPQAXIP Invalid Msg Len=%d Source=%s Port %d",len,inet_ntoa(RXaddr.rxaddr.sin_addr),PORT->udpport[i]); + OutputDebugString(errmsg); + DumpFrameInHex(&rxbuff[0], len); + return 0; + } + + memcpy(&buff->DEST, &rxbuff[0], len); + + len += (3 + sizeof(void *)); + + PutLengthinBuffer((PDATAMESSAGE)buff, len); + + memcpy(call, &buff->ORIGIN, 7); + + call[6] &= 0x7e; // Mask End of Address bit + + // + // Do MH Proccessing if enabled + // + + if (PORT->MHEnabled) + if (PORT->IPv6[i]) + Update_MH_List(PORT, (UCHAR *)&RXaddr.rxaddr6.sin6_addr, &buff->ORIGIN[0], 'U', PORT->udpport[i], TRUE); + else + Update_MH_List(PORT, (UCHAR *)&RXaddr.rxaddr.sin_addr.s_addr, &buff->ORIGIN[0], 'U', PORT->udpport[i], FALSE); + + // Check Exclude + + if (CheckExcludeList(call) == 0) + return 0; + + if (PORT->Checkifcanreply) + { + if (CheckSourceisResolvable(PORT, call, htons(RXaddr.rxaddr.sin_port), &RXaddr)) + return 1; + else + { + // Can't reply. If AutoConfig is set, add to table and accept, else reject + + if (PORT->AutoAddARP) + if (PORT->IPv6[i]) + { + char Addr[80]; + Format_Addr((UCHAR *)&RXaddr.rxaddr6.sin6_addr, Addr, TRUE); + return add_arp_entry(PORT, call, (UCHAR *)&RXaddr.rxaddr6.sin6_addr, 7, htons(RXaddr.rxaddr6.sin6_port), Addr, 0, PORT->AutoAddBC, TRUE, 0, PORT->udpport[i], TRUE, 0); + } + else + return add_arp_entry(PORT, call, (UCHAR *)&RXaddr.rxaddr.sin_addr.s_addr, 7, htons(RXaddr.rxaddr.sin_port), inet_ntoa(RXaddr.rxaddr.sin_addr), 0, PORT->AutoAddBC, TRUE, 0, PORT->udpport[i], FALSE, 0); + else + { + char From[11] = "|"; + From[ConvFromAX25(call, &From[1]) + 1] = 0; + if (strstr(CantReplyList, From) == 0) + { + if (strlen(CantReplyList) < 500); + strcat(CantReplyList, From); + Debugprintf("AXIP Packet from %s dropped - can't reply", &From[1]); + } + return 0; + } + } + } + else + return(1); + } + + // + // CRC Error + // + + sprintf(errmsg,"BPQAXIP Invalid CRC=%d Source=%s Port %d",crc,inet_ntoa(RXaddr.rxaddr.sin_addr),PORT->udpport[i]); + Debugprintf(errmsg); + rxbuff[len] = 0; + Debugprintf(rxbuff); + + + return (0); + } + } + + if (PORT->NeedTCP) + { + len = GetMessageFromBuffer(PORT, rxbuff); + + if (len) + { + len = KissDecode(rxbuff, len-1); // Len includes FEND + len -= 2; // Ignore Checksum + + if (len < MAXDATA) + { + memcpy(&buff->DEST, &rxbuff[0], len); + len += (3 + sizeof(void *)); + + PutLengthinBuffer((PDATAMESSAGE)buff, len); // fix big endian issue + return 1; + } + else + { + Debugprintf("Oversized AX/TCP frame %d", len); + return 0; + } + } + } + + return (0); + + case 2: // send + +// txlen=(buff[6]<<8) + buff[5] - 5; // Len includes buffer header (7) but we add crc + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - (MSGHDDRLEN - 2); // 2 for CRC + + crc=compute_crc(&buff->DEST[0], txlen - 2); + crc ^= 0xffff; + + buff->DEST[txlen - 2] = (crc & 0xff); + buff->DEST[txlen - 1] = (crc >> 8); + + memcpy(axcall, &buff->DEST, 7); // Set to send to dest addr + + // if digis are present, scan down list for first non-used call + + if (buff->ORIGIN[6] == 0) + { + // end of addr bit not set, so scan digis + + digiptr = 13; // start of first digi + + while (((buff->ORIGIN[digiptr] & 0x80) == 0x80) && ((buff->ORIGIN[digiptr] & 0x1) == 0)) + { + // This digi has been used, and it is not the last + + digiptr+=7; + } + + // if this has not been used, use it + + if ((buff->ORIGIN[digiptr] & 0x80) == 0) + memcpy(axcall,&buff->ORIGIN[digiptr - 6], 7); // get next call + } + + axcall[6] &= 0x7e; + +// If addresses to a broadcast address, send to all entries + + for (i=0; i< PORT->NumberofBroadcastAddreses; i++) + { + if (memcmp(axcall, PORT->BroadcastAddresses[i].callsign, 7) == 0) + { + for (index = 0; index < PORT->arp_table_len; index++) + { + if (PORT->arp_table[index].BCFlag) + SendFrame(PORT, &PORT->arp_table[index], &buff->DEST[0], txlen); + } + return 0; + } + } + +// Send to all matching calls in arp table + + index = 0; + + while (index < PORT->arp_table_len) + { + if (memcmp(PORT->arp_table[index].callsign,axcall,PORT->arp_table[index].len) == 0) + { + SendFrame(PORT, &PORT->arp_table[index],&buff->DEST[0], txlen); + } + index++; + } + return (0); + + case 3: // CHECK IF OK TO SEND + + return (0); // OK + + case 4: // reinit + + CloseSockets(PORT); + + if (ProcessConfig()) + { + FreeConfig(); + ReadConfigFile(port); + } + else + Consoleprintf("Failed to reread config file - leaving config unchanged"); + + _beginthread(OpenSockets, 0, PORT ); + + GetAXIPCache(PORT); + + ResolveDelay = 2; +#ifndef LINBPQ + InvalidateRect(PORT->hResWnd,NULL,TRUE); +#endif + break; + + case 5: // Terminate + + CloseSockets(PORT); +#ifndef LINBPQ + SendMessage(PORT->hMHWnd, WM_CLOSE, 0, 0); + SendMessage(PORT->hResWnd, WM_CLOSE, 0, 0); +#endif + break; + } + return (0); +} + +VOID SendFrame(struct AXIPPORTINFO * PORT, struct arp_table_entry * arp_table, UCHAR * buff, int txlen) +{ + int i; + SOCKET txsock, SourceSocket; + + if (arp_table->TCPMode) + { + if (arp_table->TCPState == TCPConnected) + { + char outbuff[1000]; + int newlen; + + newlen = KissEncode(buff, outbuff, txlen); + send(arp_table->TCPSock, outbuff, newlen, 0); + } + + return; + } + + // Seelcte source port by choosing right socket + + // First Set Default for Protocol + + for (i = 0; i < PORT->NumberofUDPPorts; i++) + { + if (PORT->IPv6[i] == arp_table->IPv6) + { + SourceSocket = PORT->udpsock[i]; // Use as source socket, therefore source port + break; + } + } + + for (i = 0; i < PORT->NumberofUDPPorts; i++) + { + if (PORT->udpport[i] == arp_table->SourcePort && PORT->IPv6[i] == arp_table->IPv6) + { + SourceSocket = PORT->udpsock[i]; // Use as source socket, therefore source port + break; + } + } + + if (arp_table->error == 0) + { + int sent = 0; + + if (arp_table->port == 0) txsock = PORT->sock; else txsock = SourceSocket; + + if (arp_table->IPv6) + sent = sendto(txsock, buff, txlen, 0, (struct sockaddr *)&arp_table->destaddr6, sizeof(arp_table->destaddr6)); + else + if (arp_table->destaddr.sin_addr.s_addr) + sent = sendto(txsock, buff, txlen, 0, (struct sockaddr *)&arp_table->destaddr, sizeof(arp_table->destaddr)); + + if (sent != txlen) + { + int i = GetLastError(); +// perror("Sendto"); + } + + // reset Keepalive Timer + + arp_table->keepalive=arp_table->keepaliveinit; + } +} + +unsigned short int compute_crc_ccitt(unsigned char *buf, int len); +unsigned short CCCITTChecksum(unsigned char* data, unsigned int length); + +VOID * AXIPExtInit(struct PORTCONTROL * PortEntry) +{ +// char Msg[10] = {0xD0, 01, 00, 0x11, 00, 0x0B}; +// unsigned short crc; + +// crc = CCCITTChecksum(Msg, 4); + +// crc = CalcCRC(Msg, 4); + + WritetoConsole("AXIP "); + + InitAXIP(PortEntry->PORTNUMBER); + + WritetoConsole("\n"); + + return ExtProc; +} + +int InitAXIP(int Port) +{ + struct AXIPPORTINFO * PORT; + + // + // Read config first, to get UDP info if needed + // + + if (!ReadConfigFile(Port)) + return (FALSE); + + PORT = Portlist[Port]; + + if (PORT == NULL) + return FALSE; + + PORT->Port = Port; + + GetAXIPCache(PORT); // Prime resolver from cache + + // + // Start Resolver Thread if needed + // + + if (PORT->NeedResolver) + { + CreateResolverWindow(PORT); + _beginthread(ResolveNames, 0, PORT ); + } + + time(&PORT->lasttime); // Get initial time value + + _beginthread(OpenSockets, 0, PORT ); + + // Start TCP outward connect threads + // + // Open MH window if needed + + if (PORT->MHEnabled) + CreateMHWindow(PORT); + + return (TRUE); +} + +void OpenSockets(struct AXIPPORTINFO * PORT) +{ + char Msg[255]; + int err; + u_long param=1; + BOOL bcopt=TRUE; + int i; + int index = 0; + struct arp_table_entry * arp; + + // Moved from InitAXIP, to avoid hang if started too early on XP SP2 + + // Create and bind socket + + if (PORT->needip) + { + PORT->sock=socket(AF_INET,SOCK_RAW,IP_AXIP); + + if (PORT->sock == INVALID_SOCKET) + { + err = WSAGetLastError(); + sprintf(Msg, "AXIP Failed to create RAW socket - Error %d\n", err); + WritetoConsole(Msg); + } + else + { + + ioctl (PORT->sock,FIONBIO,¶m); + + setsockopt (PORT->sock,SOL_SOCKET,SO_BROADCAST,(const char FAR *)&bcopt,4); + + sinx.sinx.sin_family = AF_INET; + sinx.sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sinx.sin_port = 0; + + if (bind(PORT->sock, (struct sockaddr *) &sinx, sizeof(sinx)) != 0 ) + { + // + // Bind Failed + // + err = WSAGetLastError(); + sprintf(Msg, "Bind Failed for RAW socket - error code = %d", err); + WritetoConsole(Msg); + return; + } + } + } + + for (i=0;iNumberofUDPPorts;i++) + { + int ret; + + if (PORT->IPv6[i]) + PORT->udpsock[i]=socket(AF_INET6,SOCK_DGRAM,0); + else + PORT->udpsock[i]=socket(AF_INET,SOCK_DGRAM,0); + + if (PORT->udpsock[i] == INVALID_SOCKET) + { + WritetoConsole("Failed to create UDP socket"); + err = WSAGetLastError(); + continue; + } + + ioctl (PORT->udpsock[i],FIONBIO,¶m); + + setsockopt (PORT->udpsock[i],SOL_SOCKET,SO_BROADCAST,(const char FAR *)&bcopt,4); + +#ifndef WIN32 + + if (PORT->IPv6[i]) + if (setsockopt(PORT->udpsock[i], IPPROTO_IPV6, IPV6_V6ONLY, ¶m, sizeof(param)) < 0) + perror("setting option IPV6_V6ONLY"); + +#endif + + if (PORT->IPv6[i]) + { + sinx.sinx.sin_family = AF_INET6; + memset (&sinx.sinx6.sin6_addr, 0, 16); + } + else + { + sinx.sinx.sin_family = AF_INET; + sinx.sinx.sin_addr.s_addr = INADDR_ANY; + } + + sinx.sinx.sin_port = htons(PORT->udpport[i]); + + if (PORT->IPv6[i]) + ret = bind(PORT->udpsock[i], (struct sockaddr *) &sinx.sinx, sizeof(sinx.sinx6)); + else + ret = bind(PORT->udpsock[i], (struct sockaddr *) &sinx.sinx, sizeof(sinx.sinx)); + + if (ret != 0) + { + // Bind Failed + + err = WSAGetLastError(); + sprintf(Msg, "Bind Failed for UDP socket %d - error code = %d", PORT->udpport[i], err); + WritetoConsole(Msg); + continue; + } + } + + // Open any TCP sockets + + while (index < PORT->arp_table_len) + { + arp = &PORT->arp_table[index++]; + + if (arp->TCPMode == TCPMaster) + { + arp->TCPBuffer=malloc(4000); + arp->TCPState = 0; + + if (arp->TCPThreadID == 0) + { + arp->TCPThreadID = _beginthread(TCPConnectThread, 0, arp); + Debugprintf("TCP Connect thread created for %s Handle %x", arp->hostname, arp->TCPThreadID); + } + continue; + } + + if (arp->TCPMode == TCPSlave) + { + OpenListeningSocket(PORT, arp); + } + } +} +int OpenListeningSocket(struct AXIPPORTINFO * PORT, struct arp_table_entry * arp) +{ + char Msg[255]; + struct sockaddr_in * psin; + BOOL bOptVal = TRUE; + struct sockaddr_in local_sin; /* Local socket - internet style */ + u_long param = 1; + arp->TCPBuffer = malloc(4000); + arp->TCPState = 0; + + arp->TCPListenSock = socket(AF_INET, SOCK_STREAM, 0); + + ioctl (arp->TCPListenSock, FIONBIO, ¶m); + + if (arp->TCPListenSock == INVALID_SOCKET) + { + sprintf(Msg, "socket() failed error %d", WSAGetLastError()); + WritetoConsole(Msg); + return FALSE; + } + +// Debugprintf("TCP Listening Socket Created - socket %d port %d ", arp->TCPListenSock, arp->port); + + setsockopt (arp->TCPListenSock, SOL_SOCKET, SO_REUSEADDR, (char *)¶m,4); + + psin=&local_sin; + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = htonl(INADDR_ANY); // Local Host Only + + psin->sin_port = htons(arp->port); /* Convert to network ordering */ + + if (bind(arp->TCPListenSock , (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) + { + sprintf(Msg, "bind(sock) failed Port %d Error %d\n", arp->port, WSAGetLastError()); + Debugprintf(Msg); + closesocket(arp->TCPListenSock); + + return FALSE; + } + + if (listen(arp->TCPListenSock, 1) < 0) + { + sprintf(Msg, "listen(sock) failed Error %d", WSAGetLastError()); + Debugprintf(Msg); + closesocket(arp->TCPListenSock); + return FALSE; + } + + arp->TCPState = TCPListening; + return TRUE; +} + +void CloseSockets(struct AXIPPORTINFO * PORT) +{ + int i; + int index = 0; + struct arp_table_entry * arp; + + if (PORT->needip) + closesocket(PORT->sock); + + for (i=0;iNumberofUDPPorts;i++) + { + closesocket(PORT->udpsock[i]); + } + + // Close any open or listening TCP sockets + + while (index < PORT->arp_table_len) + { + arp = &PORT->arp_table[index++]; + + if (arp->TCPMode == TCPMaster) + { + if (arp->TCPState) + { + closesocket(arp->TCPSock); + arp->TCPSock = 0; + } + continue; + } + + if (arp->TCPMode == TCPSlave) + { + if (arp->TCPState) + { + closesocket(arp->TCPSock); + arp->TCPSock = 0; + } + + closesocket(arp->TCPListenSock); + continue; + } + } + return ; +} + +#ifndef LINBPQ + +static LRESULT CALLBACK AXResWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + char line[100]; + char outcall[10]; + int index,displayline; + struct AXIPPORTINFO * PORT; + MINMAXINFO * mmi; + int nScrollCode,nPos; + int i, Port; + char Flags[10]; + struct arp_table_entry * arp; + + // Find our PORT Entry + + for (Port = 1; Port < 33; Port++) + { + PORT = Portlist[Port]; + if (PORT == NULL) + continue; + + if (PORT->hResWnd == hWnd) + break; + } + + if (PORT == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + i=1; + + switch (message) + { + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 600; + mmi->ptMaxSize.y = PORT->MaxResWindowlength; + mmi->ptMaxTrackSize.x = 55600; + mmi->ptMaxTrackSize.y = PORT->MaxResWindowlength; + + break; + + + case WM_MDIACTIVATE: + { + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT_PTR)PORT->hResMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM)hBaseMenu, (LPARAM)hWndMenu); + } + else + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + + DrawMenuBar(FrameWnd); + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + } + + case WM_CHAR: + + if (PORT->MHEnabled == FALSE && PORT->MHAvailable) + { + PORT->MHEnabled=TRUE; + CreateMHWindow(PORT); + ShowWindow(PORT->hMHWnd, SW_RESTORE); // In case Start Minimized set + } + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + + if (wmId == BPQREREAD) + { + CloseSockets(PORT); + + if (ProcessConfig()) + { + FreeConfig(); + ReadConfigFile(Port); + } + else + Consoleprintf("Failed to reread config file - leaving config unchanged"); + + _beginthread(OpenSockets, 0, PORT); + + ResolveDelay = 2; + InvalidateRect(hWnd,NULL,TRUE); + + return 0; + } + + if (wmId == BPQADDARP) + { + if (PORT->ConfigWnd == 0) + { + PORT->ConfigWnd=CreateDialog(hInstance, ConfigClassName, 0, NULL); + + if (!PORT->ConfigWnd) + { + return (FALSE); + } + ShowWindow(PORT->ConfigWnd, SW_SHOW); + UpdateWindow(PORT->ConfigWnd); + } + + SetForegroundWindow(PORT->ConfigWnd); + + return(0); + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_RESTORE: + + PORT->ResMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + + break; + + case SC_MINIMIZE: + + PORT->ResMinimized = TRUE; + + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + + case WM_VSCROLL: + + nScrollCode = (int) LOWORD(wParam); // scroll bar value + nPos = (short int) HIWORD(wParam); // scroll box position + + //hwndScrollBar = (HWND) lParam; // handle of scroll bar + + if (nScrollCode == SB_LINEUP || nScrollCode == SB_PAGEUP) + { + PORT->baseline--; + if (PORT->baseline <0) + PORT->baseline=0; + } + + if (nScrollCode == SB_LINEDOWN || nScrollCode == SB_PAGEDOWN) + { + PORT->baseline++; + if (PORT->baseline > PORT->arp_table_len) + PORT->baseline = PORT->arp_table_len; + } + + if (nScrollCode == SB_THUMBTRACK) + { + PORT->baseline=nPos; + } + + SetScrollPos(hWnd,SB_VERT,PORT->baseline,TRUE); + + InvalidateRect(hWnd,NULL,TRUE); + break; + + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + + hOldFont = SelectObject( hdc, hFont) ; + + index = PORT->baseline; + displayline=0; + + while (index < PORT->arp_table_len) + { + arp = &PORT->arp_table[index]; + + Flags[0] = 0; + + if (arp->BCFlag) + strcat(Flags, "B "); + + if (arp->TCPState == TCPConnected) + strcat(Flags, "C "); + + if (arp->AutoAdded) + strcat(Flags, "A"); + + if (arp->ResolveFlag && arp->error != 0) + { + // resolver error - Display Error Code + sprintf(PORT->hostaddr,"Error %d",arp->error); + } + else + { + if (arp->IPv6) + Format_Addr((unsigned char *)&arp->destaddr6.sin6_addr, PORT->hostaddr, TRUE); + else + Format_Addr((unsigned char *)&arp->destaddr.sin_addr, PORT->hostaddr, FALSE); + } + + CONVFROMAX25(arp->callsign,outcall); + + if (arp->port == arp->SourcePort) + i=sprintf(line,"%.10s = %.64s %d = %-.30s %s ", + outcall, + arp->hostname, + arp->port, + PORT->hostaddr, + Flags); + else + i=sprintf(line,"%.10s = %.64s %d<%d = %-.30s %s ", + outcall, + arp->hostname, + arp->port, + arp->SourcePort, + PORT->hostaddr, + Flags); + + TextOut(hdc, 0, (displayline++)*14+2, line, i); + + index++; + } + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + +// PostQuitMessage(0); + + break; + + + default: + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + +} + +LRESULT FAR PASCAL ConfigWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) +{ + int cmd,id,i; + HWND hwndChild; + BOOL OK1,OK2,OK3; + + char call[10], host[65]; + int Interval; + int calllen; + int port; + char axcall[7]; + BOOL UDPFlag, BCFlag; + struct AXIPPORTINFO * PORT; + + for (i=1; i <= MAXBPQPORTS; i++) + { + PORT = Portlist[i]; + if (PORT == NULL) + continue; + + if (PORT->ConfigWnd == hWnd) + break; + } + + switch (message) + { + case WM_CTLCOLORDLG: + + return (LRESULT)bgBrush; + + case WM_COMMAND: + + id = LOWORD(wParam); + hwndChild = (HWND)(UINT)lParam; + cmd = HIWORD(wParam); + + switch (id) + { + case ID_SAVE: + + OK1=GetDlgItemText(PORT->ConfigWnd,1001,(LPSTR)call,10); + OK2=GetDlgItemText(PORT->ConfigWnd,1002,(LPSTR)host,64); + OK3=1; + + for (i=0;i<7;i++) + call[i] = toupper(call[i]); + + UDPFlag=IsDlgButtonChecked(PORT->ConfigWnd,1004); + BCFlag=IsDlgButtonChecked(PORT->ConfigWnd,1005); + + if (UDPFlag) + port=GetDlgItemInt(PORT->ConfigWnd,1003,&OK3,FALSE); + else + port=0; + + Interval=0; + + if (OK1 && OK2 && OK3==1) + { + if (convtoax25(call,axcall,&calllen)) + { + add_arp_entry(PORT, axcall,0,calllen,port,host,Interval, BCFlag, FALSE, 0, port, FALSE, 0); + ResolveDelay = 2; + return(DestroyWindow(hWnd)); + } + } + + // Validation failed + + if (!OK1) SetDlgItemText(PORT->ConfigWnd,1001,"????"); + if (!OK2) SetDlgItemText(PORT->ConfigWnd,1002,"????"); + if (!OK3) SetDlgItemText(PORT->ConfigWnd,1003,"????"); + + break; + + case ID_CANCEL: + + return(DestroyWindow(hWnd)); + } + break; + +// case WM_CLOSE: + +// return(DestroyWindow(hWnd)); + + case WM_DESTROY: + + PORT->ConfigWnd=0; + + return(0); + + } + + return (DefWindowProc(hWnd, message, wParam, lParam)); + +} +#endif + +static void CreateResolverWindow(struct AXIPPORTINFO * PORT) +{ +#ifndef LINBPQ + + int WindowParam; + WNDCLASS wc; + char WindowTitle[100]; + int retCode, Type, Vallen; + HKEY hKey=0; + char Size[80]; + + HWND hResWnd; + char Key[80]; + RECT Rect = {0, 0, 300, 300}; + int Top, Left, Width, Height; + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", PORT->Port); + + retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"ResSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &PORT->ResMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + if (Rect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + + RegCloseKey(hKey); + } + + Top = Rect.top; + Left = Rect.left; + Width = Rect.right - Left; + Height = Rect.bottom - Top; + + // Register the window classes + + wc.style = CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)AXResWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = NULL ; + wc.lpszClassName = "AXAppName"; + + RegisterClass(&wc); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInstance; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.lpfnWndProc = ConfigWndProc; + wc.lpszClassName = ConfigClassName; + RegisterClass(&wc); + + WindowParam = WS_OVERLAPPEDWINDOW | WS_VSCROLL; + + sprintf(WindowTitle,"AXIP Port %d Resolver", PORT->Port); + + PORT->hResWnd = hResWnd = CreateMDIWindow("AXAppName", WindowTitle, WindowParam, + Left - (OffsetW /2), Top - OffsetH + 4, Width, Height, ClientWnd, hInstance, 1234); + + + PORT->hResMenu = CreatePopupMenu(); + AppendMenu(PORT->hResMenu, MF_STRING, BPQREREAD, "ReRead Config"); + AppendMenu(PORT->hResMenu, MF_STRING, BPQADDARP, "Add Entry"); + + SetScrollRange(hResWnd,SB_VERT, 0, PORT->arp_table_len, TRUE); + + if (PORT->ResMinimized) + ShowWindow(hResWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hResWnd, SW_RESTORE); +#endif +} +extern HWND hWndPopup; + + +static void ResolveNames(struct AXIPPORTINFO * PORT) +{ + int count = 0; + + PORT->ResolveNamesThreadId = GetCurrentThreadId(); // Detect if another started + + while(TRUE) + { + count++; // So we can trap first few loops + + ResolveDelay = 15 * 60; + + for (PORT->ResolveIndex=0; PORT->ResolveIndex < PORT->arp_table_len; PORT->ResolveIndex++) + { + struct arp_table_entry * arp = &PORT->arp_table[PORT->ResolveIndex]; + + if (arp->ResolveFlag) + { + struct addrinfo hints, *res = 0; + int n; + BOOL UseV6 = FALSE; + BOOL ForceV4 = FALSE; + + if (_memicmp(arp->hostname, "ipv6:", 5) == 0) + UseV6 = TRUE; + else if (_memicmp(arp->hostname, "ipv4:", 5) == 0) + ForceV4 = TRUE; + + memset(&hints, 0, sizeof hints); + hints.ai_socktype = SOCK_DGRAM; + + if (UseV6) + { + hints.ai_family = AF_INET6; // use IPv6 + n = getaddrinfo(&arp->hostname[5], NULL, &hints, &res); + } + else if (ForceV4) + { + hints.ai_family = AF_INET; // use IPv4 + n = getaddrinfo(&arp->hostname[5], NULL, &hints, &res); + } + else if (PORT->PortIPv6) // Can use IPv6 + { + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever + n = getaddrinfo(arp->hostname, NULL, &hints, &res); + } + else + { + hints.ai_family = AF_INET; // use IPv4 only + n = getaddrinfo(arp->hostname, NULL, &hints, &res); + } + if (res) + { + arp->error = 0; + if (res->ai_family == AF_INET) + { + memcpy(&arp->destaddr.sin_addr.s_addr, &res->ai_addr->sa_data[2], 4); + arp->IPv6 = FALSE; + arp->destaddr.sin_family = AF_INET; +// Debugprintf("AXIP %s = %d.%d.%d.%d", arp->hostname, (UCHAR)res->ai_addr->sa_data[2], +// (UCHAR)res->ai_addr->sa_data[3], (UCHAR)res->ai_addr->sa_data[4], (UCHAR)res->ai_addr->sa_data[5]); + + } + else + { + struct sockaddr_in6 * sa6 = (struct sockaddr_in6 *)res->ai_addr; + + memcpy(&arp->destaddr6.sin6_addr, &sa6->sin6_addr, 16); + arp->IPv6 = TRUE; + arp->destaddr.sin_family = AF_INET6; + } + arp->destaddr.sin_port = htons(arp->port); + freeaddrinfo(res); + } + else + { + PORT->arp_table[PORT->ResolveIndex].error = WSAGetLastError(); + + if (count < 4) + ResolveDelay = 30; // if errors try again soon + } + } + } + + SaveAXIPCache(PORT); + +#ifndef LINBPQ + InvalidateRect(PORT->hResWnd,NULL,TRUE); +#endif + while(ResolveDelay-- > 0) + { + if (pthread_equal(PORT->ResolveNamesThreadId, GetCurrentThreadId()) == FALSE) + { + Debugprintf("AXIP Resolve thread %x redundant - closing", GetCurrentThreadId()); + return; + } + Sleep(1000); + } + } + Debugprintf("AXIP Resolve thread exitied"); +} + +#ifndef LINBPQ + +LRESULT CALLBACK MHWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PAINTSTRUCT ps; + HDC hdc; + HFONT hOldFont ; + char line[100]; + char outcall[10]; + HGLOBAL hMem; + struct AXIPPORTINFO * PORT; + int index,displayline; + MINMAXINFO * mmi; + int nScrollCode,nPos; + + + int i; + + for (i=1; i <= MAXBPQPORTS; i++) + { + PORT = Portlist[i]; + if (PORT == NULL) + continue; + + if (PORT->hMHWnd == hWnd) + break; + } + + if (PORT == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) + { + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 600; + mmi->ptMaxSize.y = PORT->MaxMHWindowlength; + mmi->ptMaxTrackSize.x = 600; + mmi->ptMaxTrackSize.y = PORT->MaxMHWindowlength; + break; + + case WM_MDIACTIVATE: + { + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)PORT->hMHMenu, "Edit"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM)hBaseMenu, (LPARAM)hWndMenu); + } + else + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + } + + case WM_COMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) { + + case BPQCLEAR: + memset(PORT->MHTable, 0, sizeof(PORT->MHTable)); + InvalidateRect(hWnd,NULL,TRUE); + return 0; + + case BPQCOPY: + + hMem=GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, MaxMHEntries * 100); + + if (hMem != 0) + { + if (OpenClipboard(hWnd)) + { + CopyScreentoBuffer(GlobalLock(hMem), PORT); + GlobalUnlock(hMem); + EmptyClipboard(); + SetClipboardData(CF_TEXT,hMem); + CloseClipboard(); + } + else + { + GlobalFree(hMem); + } + } + return 0; + + default: + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); // Remember, these are... + wmEvent = HIWORD(wParam); // ...different for Win32! + + switch (wmId) + { + case SC_RESTORE: + + PORT->MHMinimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + break; + + case SC_MINIMIZE: + + PORT->MHMinimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_VSCROLL: + + nScrollCode = (int) LOWORD(wParam); // scroll bar value + nPos = (short int) HIWORD(wParam); // scroll box position + + //hwndScrollBar = (HWND) lParam; // handle of scroll bar + + if (nScrollCode == SB_LINEUP || nScrollCode == SB_PAGEUP) + { + PORT->mhbaseline--; + if (PORT->mhbaseline <0) + PORT->mhbaseline=0; + } + + if (nScrollCode == SB_LINEDOWN || nScrollCode == SB_PAGEDOWN) + { + PORT->mhbaseline++; + if (PORT->mhbaseline > PORT->CurrentMHEntries) + PORT->mhbaseline = PORT->CurrentMHEntries; + } + + if (nScrollCode == SB_THUMBTRACK) + { + PORT->mhbaseline=nPos; + } + + SetScrollPos(hWnd,SB_VERT,PORT->mhbaseline,TRUE); + + InvalidateRect(hWnd,NULL,TRUE); + break; + + + + case WM_PAINT: + + hdc = BeginPaint (hWnd, &ps); + hOldFont = SelectObject( hdc, hFont) ; + + index = PORT->mhbaseline; + displayline=0; + + PORT->CurrentMHEntries = 0; + + while (index < MaxMHEntries) + { + if (PORT->MHTable[index].proto != 0) + { + char Addr[80]; + + Format_Addr((unsigned char *)&PORT->MHTable[index].ipaddr6, Addr, PORT->MHTable[index].IPv6); + + CONVFROMAX25(PORT->MHTable[index].callsign,outcall); + + i=sprintf(line,"%-10s%-15s %c %-6d %-25s%c",outcall, + Addr, + PORT->MHTable[index].proto, + PORT->MHTable[index].port, + asctime(gmtime( &PORT->MHTable[index].LastHeard )), + (PORT->MHTable[index].Keepalive == 0) ? ' ' : 'K'); + + line[i-2]= ' '; // Clear CR returned by asctime + + TextOut(hdc,0,(displayline++)*14+2,line,i); + PORT->CurrentMHEntries ++; + } + index++; + } + + if (PORT->MaxMHWindowlength < PORT->CurrentMHEntries * 14 + 40) + PORT->MaxMHWindowlength = PORT->CurrentMHEntries * 14 + 40; + + SelectObject( hdc, hOldFont ) ; + EndPaint (hWnd, &ps); + + break; + + case WM_DESTROY: + + PORT->MHEnabled=FALSE; + + break; + + default: + return DefMDIChildProc(hWnd, message, wParam, lParam); + + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); +} + +#endif + +BOOL CopyScreentoBuffer(char * buff, struct AXIPPORTINFO * PORT) +{ + int index; + char outcall[10]; + + index = 0; + + while (index < MaxMHEntries) + { + if (PORT->MHTable[index].proto != 0) + { + CONVFROMAX25(PORT->MHTable[index].callsign,outcall); + + buff+=sprintf(buff,"%-10s%-15s %c %-6d %-26s",outcall, + inet_ntoa(PORT->MHTable[index].ipaddr), + PORT->MHTable[index].proto, + PORT->MHTable[index].port, + asctime(gmtime( &PORT->MHTable[index].LastHeard ))); + } + *(buff-2)=13; + *(buff-1)=10; + index++; + + } + *(buff)=0; + + return 0; +} + +void CreateMHWindow(struct AXIPPORTINFO * PORT) +{ +#ifndef LINBPQ + + WNDCLASS wc; + char WindowTitle[100]; + int retCode, Type, Vallen; + HKEY hKey=0; + char Size[80]; + HWND hMHWnd; + char Key[80]; + RECT Rect = {0, 0, 300, 300}; + int Top, Left, Width, Height; + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", PORT->Port); + + retCode = RegOpenKeyEx (REGTREE, Key, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen=80; + + retCode = RegQueryValueEx(hKey,"MHSize",0, + (ULONG *)&Type,(UCHAR *)&Size,(ULONG *)&Vallen); + + if (retCode == ERROR_SUCCESS) + sscanf(Size,"%d,%d,%d,%d,%d",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom, &PORT->MHMinimized); + + if (Rect.top < - 500 || Rect.left < - 500) + { + Rect.left = 0; + Rect.top = 0; + Rect.right = 600; + Rect.bottom = 400; + } + + if (Rect.top < OffsetH) // Make sure not off top of MDI frame + { + int Error = OffsetH - Rect.top; + Rect.top += Error; + Rect.bottom += Error; + } + + + RegCloseKey(hKey); + } + + Top = Rect.top; + Left = Rect.left; + Width = Rect.right - Left; + Height = Rect.bottom - Top; + + PORT->MaxMHWindowlength = Height; + + wc.style = CS_HREDRAW | CS_VREDRAW ;//| CS_NOCLOSE; + wc.lpfnWndProc = (WNDPROC)MHWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(BPQICON)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wc.lpszMenuName = NULL ; + wc.lpszClassName = "MHAppName"; + + RegisterClass(&wc); + + sprintf(WindowTitle,"AXIP Port %d MHEARD", PORT->Port); + + PORT->hMHWnd = hMHWnd = CreateMDIWindow("MHAppName", WindowTitle, + WS_OVERLAPPEDWINDOW | WS_VSCROLL, + Left - (OffsetW /2), Top - OffsetH, Width, Height, ClientWnd, hInstance, 1234); + + PORT->hMHMenu = CreatePopupMenu(); + AppendMenu(PORT->hMHMenu, MF_STRING, BPQCOPY, "Copy"); + AppendMenu(PORT->hMHMenu, MF_STRING, BPQCLEAR, "Clear"); + + if (PORT->MHMinimized) + ShowWindow(hMHWnd, SW_SHOWMINIMIZED); + else + ShowWindow(hMHWnd, SW_RESTORE); +#endif +} + +unsigned short int compute_crc(unsigned char *buf,int len) +{ + unsigned short fcs = 0xffff; + int i; + + for(i = 0; i < len; i++) + fcs = (fcs >>8 ) ^ CRCTAB[(fcs ^ buf[i]) & 0xff]; + + return fcs; +} + +/* + +static const unsigned short ccittTab[] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0}; + +unsigned short int compute_crc_ccitt(unsigned char *buf, int len) +{ + int i; + unsigned short fcs = 0; + + for(i = 0; i < len; i++) + fcs = (fcs >>8 ) ^ ccittTab[(fcs ^ buf[i]) & 0xff]; + + return fcs; +} + + + union { + unsigned short m_crc16; + unsigned char m_crc8[2U]; + } fcs; + + +unsigned short CCCITTChecksum(unsigned char* data, unsigned int length) +{ + int i; + + fcs.m_crc16 = 0; + + for (i = 0U; i < length; i++) + fcs.m_crc16 = (fcs.m_crc8[0U] << 8) ^ ccittTab[fcs.m_crc8[1U] ^ data[i]]; + + return fcs.m_crc16; +} + +*/ + +static BOOL ReadConfigFile(int Port) +{ + +/* Linux Format + +broadcast QST-0 NODES-0 +# +# ax.25 route definition, define as many as you need. +# format is route (call/wildcard) (ip host at destination) +# ssid of 0 routes all ssid's +# +# route [flags] +# +# Valid flags are: +# b - allow broadcasts to be transmitted via this route +# d - this route is the default route +# +#route vk2sut-0 44.136.8.68 b +#route vk5xxx 44.136.188.221 b +#route vk2abc 44.1.1.1 +# +*/ + +//UDP 9999 # Port we listen on +//MAP G8BPQ-7 10.2.77.1 # IP 93 for compatibility +//MAP BPQ7 10.2.77.1 UDP 2222 # UDP port to send to +//MAP BPQ8 10.2.77.2 UDP 3333 # UDP port to send to + + char buf[256],errbuf[256]; + HKEY hKey=0; + char * Config; + struct AXIPPORTINFO * PORT; + + Config = PortConfig[Port]; + + if (Portlist[Port]) // Already defined, so must be re-read + { + PORT = Portlist[Port]; + + PORT->NumberofBroadcastAddreses = 0; + PORT->needip = FALSE; + PORT->NeedTCP = FALSE; + PORT->MHAvailable = FALSE; + PORT->MHEnabled = FALSE; + PORT->NumberofUDPPorts = 0; + PORT->NeedResolver = FALSE; + PORT->arp_table_len = 0; + memset(PORT->arp_table, 0, sizeof(struct arp_table_entry) * MAX_ENTRIES); + PORT->AutoAddARP = FALSE; + PORT->AutoAddBC = FALSE; + } + else + { + Portlist[Port] = PORT = zalloc(sizeof (struct AXIPPORTINFO)); + } + + PORT->Checkifcanreply = TRUE; + + if (Config) + { + char * ptr1 = Config, * ptr2; + + // Using config from bpq32.cfg + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!ProcessLine(buf, PORT)) + { + WritetoConsole("BPQAXIP - Bad config record"); + WritetoConsole(errbuf); + WritetoConsole("\n"); + } + } + + if (PORT->NumberofUDPPorts > MAXUDPPORTS) + { + n=sprintf(buf,"BPQAXIP - Too many UDP= lines - max is %d\n", MAXUDPPORTS); + WritetoConsole(buf); + } + return TRUE; + } + + WritetoConsole("No Configuration info in bpq32.cfg"); + + return FALSE; +} + +static int ProcessLine(char * buf, struct AXIPPORTINFO * PORT) +{ + char * ptr; + char * p_call; + char * p_ipad; + char * p_UDP; + char * p_udpport; + char * p_Interval; + + int calllen; + int port, SourcePort; + int bcflag; + char axcall[7]; + int Interval; + int noUpdate=FALSE; + int TCPMode; + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if(_stricmp(ptr,"UDP") == 0) + { + if (PORT->NumberofUDPPorts > MAXUDPPORTS) PORT->NumberofUDPPorts--; + + p_udpport = strtok(NULL, " ,\t\n\r"); + + if (p_udpport == NULL) return (FALSE); + + PORT->udpport[PORT->NumberofUDPPorts] = atoi(p_udpport); + + if (PORT->udpport[PORT->NumberofUDPPorts] == 0) return (FALSE); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr && _stricmp(ptr, "ipv6") == 0) + { + PORT->PortIPv6 = TRUE; + PORT->IPv6[PORT->NumberofUDPPorts] = TRUE; + } + + PORT->NumberofUDPPorts++; + + return (TRUE); + } + + if(_stricmp(ptr,"MHEARD") == 0) + { + PORT->MHEnabled = TRUE; + PORT->MHAvailable = TRUE; + + return (TRUE); + } + + if(_stricmp(ptr,"DONTCHECKSOURCECALL") == 0) + { + PORT->Checkifcanreply = FALSE; + return (TRUE); + } + + if(_stricmp(ptr,"AUTOADDMAP") == 0) + { + PORT->AutoAddARP = TRUE; + PORT->AutoAddBC = TRUE; + return (TRUE); + } + + if(_stricmp(ptr,"AUTOADDQUIET") == 0) + { + PORT->AutoAddARP = TRUE; + PORT->AutoAddBC = FALSE; + return (TRUE); + } + + if(_stricmp(ptr,"MAP") == 0) + { + p_call = strtok(NULL, " \t\n\r"); + + if (p_call == NULL) return (FALSE); + + _strupr(p_call); + + if (_stricmp(p_call, "DUMMY") == 0) + { + Consoleprintf("MAP DUMMY is no longer needed - statement ignored"); + return TRUE; + } + + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_UDP = strtok(NULL, " \t\n\r"); + + Interval=0; + port=0; // Raw IP + bcflag=0; + TCPMode=0; + SourcePort = 0; + +// +// Look for (optional) KEEPALIVE, DYNAMIC, UDP or BROADCAST params +// + while (p_UDP != NULL) + { + if (_stricmp(p_UDP,"NOUPDATE") == 0) + { + noUpdate = TRUE; + p_UDP = strtok(NULL, " \t\n\r"); + continue; + } + + if (_stricmp(p_UDP,"KEEPALIVE") == 0) + { + p_Interval = strtok(NULL, " \t\n\r"); + + if (p_Interval == NULL) return (FALSE); + + Interval = atoi(p_Interval); + p_UDP = strtok(NULL, " \t\n\r"); + continue; + } + + if (_stricmp(p_UDP,"UDP") == 0) + { + p_udpport = strtok(NULL, " \t\n\r"); + + if (p_udpport == NULL) return (FALSE); + + port = atoi(p_udpport); + p_UDP = strtok(NULL, " \t\n\r"); + continue; + } + + if (_stricmp(p_UDP,"SOURCEPORT") == 0) + { + p_udpport = strtok(NULL, " \t\n\r"); + + if (p_udpport == NULL) return (FALSE); + + SourcePort = atoi(p_udpport); + p_UDP = strtok(NULL, " \t\n\r"); + continue; + } + + if (_stricmp(p_UDP,"TCP-Master") == 0) + { + p_udpport = strtok(NULL, " \t\n\r"); + + if (p_udpport == NULL) return (FALSE); + + port = atoi(p_udpport); + p_UDP = strtok(NULL, " \t\n\r"); + + TCPMode=TCPMaster; + + continue; + } + + if (_stricmp(p_UDP,"TCP-Slave") == 0) + { + p_udpport = strtok(NULL, " \t\n\r"); + + if (p_udpport == NULL) return (FALSE); + + port = atoi(p_udpport); + p_UDP = strtok(NULL, " \t\n\r"); + + TCPMode = TCPSlave; + continue; + + } + + + if (_stricmp(p_UDP,"B") == 0) + { + bcflag =TRUE; + p_UDP = strtok(NULL, " \t\n\r"); + continue; + } + + if ((*p_UDP == ';') || (*p_UDP == '#')) break; // Comment on end + + return FALSE; + + } + + if (convtoax25(p_call,axcall,&calllen)) + { + if (SourcePort == 0) + SourcePort = port; + + add_arp_entry(PORT, axcall, 0, calllen, port, p_ipad, Interval, bcflag, FALSE, TCPMode, SourcePort, FALSE, noUpdate); + return (TRUE); + } + } // End of Process MAP + + if(_stricmp(ptr,"BROADCAST") == 0) + { + p_call = strtok(NULL, " \t\n\r"); + + if (p_call == NULL) return (FALSE); + + if (convtoax25(p_call,axcall,&calllen)) + { + add_bc_entry(PORT, axcall,calllen); + return (TRUE); + } + + + return (FALSE); // Failed convtoax25 + } + + // + // Bad line + // + return (FALSE); +} + +int CONVFROMAX25(char * incall, char * outcall) +{ + int in,out=0; + unsigned char chr; +// +// CONVERT AX25 FORMAT CALL IN incall TO NORMAL FORMAT IN out +// RETURNS LENGTH +// + memset(outcall,0x20,9); + outcall[9]=0; + + for (in=0;in<6;in++) + { + chr=incall[in]; + if (chr == 0x40) + break; + chr >>= 1; + outcall[out++]=chr; + } + + chr=incall[6]; // ssid + chr >>= 1; + chr &= 15; + + if (chr > 0) + { + outcall[out++]='-'; + if (chr > 9) + { + chr-=10; + outcall[out++]='1'; + } + chr+=48; + outcall[out++]=chr; + } + return (out); +} + + +BOOL convtoax25(unsigned char * callsign, unsigned char * ax25call,int * calllen) +{ + int i; + + memset(ax25call,0x40,6); // in case short + ax25call[6]=0x60; // default SSID + + for (i=0;i<7;i++) + { + if (callsign[i] == '-') + { + // + // process ssid and return + // + i = atoi(&callsign[i+1]); + + if (i < 16) + { + ax25call[6] |= i<<1; + *calllen = 7; // include ssid in test + return (TRUE); + } + return (FALSE); + } + + if (callsign[i] == 0 || callsign[i] == ' ') + { + // + // End of call - no ssid + // + *calllen = 6; // wildcard ssid + return (TRUE); + } + + ax25call[i] = callsign[i] << 1; + } + + // + // Too many chars + // + + return (FALSE); +} + +BOOL add_arp_entry(struct AXIPPORTINFO * PORT, UCHAR * call, UCHAR * ip, int len, int port, + UCHAR * name, int keepalive, BOOL BCFlag, BOOL AutoAdded, int TCPFlag, int SourcePort, BOOL IPv6, int noUpdate) +{ + struct arp_table_entry * arp; + + if (PORT->arp_table_len == MAX_ENTRIES) + // + // Table full + // + return (FALSE); + + arp = &PORT->arp_table[PORT->arp_table_len]; + + if (SourcePort) + arp->SourcePort = SourcePort; + else + arp->SourcePort = port; + + arp->PORT = PORT; + + if (port == 0) PORT->needip = 1; // Enable Raw IP Mode + + arp->ResolveFlag=TRUE; + PORT->NeedResolver=TRUE; + + memcpy (&arp->callsign,call,7); + strncpy((char *)&arp->hostname,name,64); + arp->len = len; + arp->port = port; + keepalive+=9; + keepalive/=10; + + arp->keepalive = keepalive; + arp->keepaliveinit = keepalive; + arp->BCFlag = BCFlag; + arp->AutoAdded = AutoAdded; + arp->TCPMode = TCPFlag; + arp->noUpdate = noUpdate; + PORT->arp_table_len++; + + if (PORT->MaxResWindowlength < (PORT->arp_table_len * 14) + 70) + PORT->MaxResWindowlength = (PORT->arp_table_len * 14) + 70; + + PORT->NeedResolver |= TCPFlag; // Need Resolver window to handle tcp socket messages + PORT->NeedTCP |= TCPFlag; + + if (ip) + { + // Only have an IP address if dynamically added - so update destaddr + + if (IPv6) + { + memcpy(&arp->destaddr6.sin6_addr, ip, 16); + arp->IPv6 = TRUE; + arp->destaddr.sin_family = AF_INET6; + } + else + { + memcpy(&arp->destaddr.sin_addr.s_addr, ip, 4); + arp->IPv6 = FALSE; + arp->destaddr.sin_family = AF_INET; + } + arp->destaddr.sin_port = htons(arp->port); +#ifndef LINBPQ + + SetScrollRange(PORT->hResWnd,SB_VERT, 0, PORT->arp_table_len, TRUE); + InvalidateRect(PORT->hResWnd, NULL, TRUE); +#endif + } + + return (TRUE); +} + +BOOL add_bc_entry(struct AXIPPORTINFO * PORT, unsigned char * call, int len) +{ + if (PORT->NumberofBroadcastAddreses == MAX_BROADCASTS) + // + // Table full + // + return (FALSE); + + memcpy (PORT->BroadcastAddresses[PORT->NumberofBroadcastAddreses].callsign,call,7); + PORT->BroadcastAddresses[PORT->NumberofBroadcastAddreses].len = len; + PORT->NumberofBroadcastAddreses++; + + return (TRUE); +} + + +int CheckKeepalives(struct AXIPPORTINFO * PORT) +{ + int index=0; + SOCKET txsock; + struct arp_table_entry * arp; + + if (PORT->arp_table_len >= MAX_ENTRIES) + { + Debugprintf("arp_table_len corrupt - %d", PORT->arp_table_len); + PORT->arp_table_len = MAX_ENTRIES - 1; + } + + while (index < PORT->arp_table_len) + { + if (PORT->arp_table[index].keepalive != 0) + { + arp = &PORT->arp_table[index]; + arp->keepalive--; + + if (arp->keepalive == 0) + { + // + // Send Keepalive Packet + // + arp->keepalive=arp->keepaliveinit; + + if (arp->error == 0) + { + if (arp->port == 0) txsock = PORT->sock; else txsock = PORT->udpsock[0]; + + sendto(txsock,"Keepalive",9,0,(struct sockaddr *)&arp->destaddr,sizeof(arp->destaddr)); + } + } + } + + index++; + + } + + // Decrement MH Keepalive flags + + for (index = 0; index < MaxMHEntries; index++) + { + if (PORT->MHTable[index].Keepalive != 0) + PORT->MHTable[index].Keepalive--; + } + + return (0); +} + +BOOL CheckSourceisResolvable(struct AXIPPORTINFO * PORT, char * call, int Port, VOID * rxaddr) +{ + // Makes sure we can reply to call before accepting message + + int index = 0; + struct arp_table_entry * arp; + + while (index < PORT->arp_table_len) + { + arp = &PORT->arp_table[index]; + + if (memcmp(arp->callsign, call, arp->len) == 0) + { + // Call is present - if AutoAdded, refresh IP address and Port + + // Why not refreesh resolved addresses - if dynamic addr has changed + // this will give quicker response + + if (arp->noUpdate == 0) + { + if (arp->IPv6) + { + struct sockaddr_in6 * SA6 = rxaddr; + memcpy(&arp->destaddr6.sin6_addr, &SA6->sin6_addr, 16); + } + else + { + struct sockaddr_in * SA = rxaddr; + memcpy(&arp->destaddr.sin_addr.s_addr, &SA->sin_addr, 4); + } + // Dont think I should update port + + //arp->port = Port; + } + arp->LastHeard = time(NULL); + return 1; // Ok to process + } + index++; + } + + return (0); // Not in list +} + +int Update_MH_List(struct AXIPPORTINFO * PORT, UCHAR * ipad, char * call, char proto, short port, BOOL IPv6) +{ + int index; + char callsign[7]; + int SaveKeepalive=0; + struct MHTableEntry * MH; + + memcpy(callsign,call,7); + callsign[6] &= 0x3e; // Mask non-ssid bits + + for (index = 0; index < MaxMHEntries; index++) + { + MH = &PORT->MHTable[index]; + + if (MH->callsign[0] == 0) + { + // empty entry, so call not present. Move all down, and add to front + +#ifdef WIN32 + SetScrollRange(PORT->hMHWnd, SB_VERT, 0, index + 1, TRUE); +#endif + goto MoveEntries; + } + + if (memcmp(MH->callsign,callsign,7) == 0 && + memcmp(&MH->ipaddr, ipad, (MH->IPv6) ? 16 : 4) == 0 && + MH->proto == proto && + MH->port == port) + { + // Entry found, move preceeding entries down and put on front + + SaveKeepalive = MH->Keepalive; + goto MoveEntries; + } + } + + // Table full move MaxMHEntries-1 entries down, and add on front + + index=MaxMHEntries-1; + +MoveEntries: + + // + // Move all preceeding entries down one, and put on front + // + + if (index > 0) + memmove(&PORT->MHTable[1],&PORT->MHTable[0],index*sizeof(struct MHTableEntry)); + + MH = &PORT->MHTable[0]; + + memcpy(MH->callsign,callsign,7); + memcpy(&MH->ipaddr6, ipad, (IPv6) ? 16 : 4); + MH->proto = proto; + + MH->port = port; + time(&MH->LastHeard); + MH->Keepalive = SaveKeepalive; + MH->IPv6 = IPv6; +#ifndef LINBPQ + InvalidateRect(PORT->hMHWnd,NULL,TRUE); +#endif + return 0; + +} + +int Update_MH_KeepAlive(struct AXIPPORTINFO * PORT, struct in_addr ipad, char proto, short port) +{ + int index; + + for (index = 0; index < MaxMHEntries; index++) + { + if (PORT->MHTable[index].callsign[0] == 0) + + // empty entry, so call not present. + + return 0; + + if (memcmp(&PORT->MHTable[index].ipaddr,&ipad,4) == 0 && + PORT->MHTable[index].proto == proto && + PORT->MHTable[index].port == port) + { + PORT->MHTable[index].Keepalive = 30; // 5 Minutes at 10 sec ticks + return 0; + } + } + + return 0; + +} + + +int DumpFrameInHex(unsigned char * msg, int len) +{ + char errmsg[100]; + int i=0; + + for (i=0;iarp_table_len) + { + sockptr = &PORT->arp_table[index++]; + + if (sockptr->TCPMode) + { + if (sockptr->TCPState == TCPListening) + { + int addrlen; + SOCKET sock; + BOOL bOptVal = TRUE; + struct sockaddr sin; + + addrlen = sizeof(struct sockaddr); + + sock = accept(sockptr->TCPListenSock, &sin, &addrlen); + + if (sock == INVALID_SOCKET) + { + int err = WSAGetLastError(); + + if (err == 10035 || err == 11) + continue; + + if (err == 10038 || err == 9) + { + // Not a socket + + closesocket(sockptr->TCPListenSock); + OpenListeningSocket(PORT, sockptr); + + continue; + } + + + Debugprintf("AXIP accept() failed Error %d", err); + continue; + } + + Debugprintf("AXIP Connect accepted - Socket %d Port %d", sock, sockptr->port); + + if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&bOptVal, 4) != SOCKET_ERROR) + Debugprintf("Set SO_KEEPALIVE: ON"); + + sockptr->TCPSock = sock; + sockptr->TCPState = TCPConnected; + } + + if (sockptr->TCPState == TCPConnected) + { + int InputLen; + + // Poll TCP Connection for data + + // May have several messages per packet, or message split over packets + + if (sockptr->InputLen > 3000) // Shouldnt have lines longer than this in text mode + { + sockptr->InputLen = 0; + } + + ioctl(sockptr->TCPSock, FIONBIO, ¶m); + + InputLen = recv(sockptr->TCPSock, &sockptr->TCPBuffer[sockptr->InputLen], 1000, 0); + + if (InputLen == 0) + { + Debugprintf("TCP Close received for socket %d", sockptr->TCPSock); + + if (sockptr->TCPMode == TCPSlave) + sockptr->TCPState = TCPListening; + else + sockptr->TCPState = 0; + closesocket(sockptr->TCPSock); + continue; + } + + if (InputLen < 0) + { + int err = WSAGetLastError(); + + if (err == 10035 || err == 11) + InputLen = 0; + else + { + if (sockptr->TCPMode == TCPSlave) + sockptr->TCPState = TCPListening; + else + sockptr->TCPState = 0; + + closesocket(sockptr->TCPSock); + continue; + } + } + + sockptr->InputLen += InputLen; + + if (sockptr->InputLen == 0) + { + sockptr->TCPOK++; + + if (sockptr->TCPOK > 36000) // 60 MINS + { + if (sockptr->TCPSock) + { + Debugprintf("No Data for 60 Mins on Data Sock %d State %d", + sockptr->TCPListenSock, sockptr->TCPSock, sockptr->TCPState); + + sockptr->TCPState = 0; + closesocket(sockptr->TCPSock); + sockptr->TCPSock = 0; + } + + closesocket(sockptr->TCPListenSock); + OpenListeningSocket(PORT, sockptr); + + sockptr->TCPOK = 0; + } + continue; + } + } + + ptr = memchr(sockptr->TCPBuffer, FEND, sockptr->InputLen); + + if (ptr) // FEND in buffer + { + ptr2 = &sockptr->TCPBuffer[sockptr->InputLen]; + ptr++; + + if (ptr == ptr2) + { + // Usual Case - single meg in buffer + + MsgLen = sockptr->InputLen; + sockptr->InputLen = 0; + + if (MsgLen > 1) + { + memcpy(Buffer, sockptr->TCPBuffer, MsgLen); + + if (PORT->MHEnabled) + Update_MH_List(PORT, (UCHAR *)&sockptr->destaddr.sin_addr.s_addr, &Buffer[7],'T', sockptr->port, 0); + + sockptr->TCPOK = 0; + + return MsgLen; + } + } + else + { + // buffer contains more that 1 message + + MsgLen = sockptr->InputLen - (int)((ptr2-ptr)); + memcpy(Buffer, sockptr->TCPBuffer, MsgLen); + + memmove(sockptr->TCPBuffer, ptr, sockptr->InputLen-MsgLen); + + sockptr->InputLen -= MsgLen; + + if (MsgLen > 1) + { + if (PORT->MHEnabled) + Update_MH_List(PORT, (UCHAR *)&sockptr->destaddr.sin_addr.s_addr, &Buffer[7],'T', sockptr->port, 0); + + sockptr->TCPOK = 0; + + return MsgLen; + } + } + } + } + } + return 0; + +} + +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i,txptr=0; + UCHAR c; + + outbuff[0]=FEND; + txptr=1; + + for (i=0;iTCPMode == TCPMaster) + { + if (arp->TCPState == 0) + { + arp->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (arp->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for AX/TCP socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + goto wait; + } + + setsockopt (arp->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(arp->TCPSock, SOL_SOCKET, SO_KEEPALIVE, (char*)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(arp->TCPSock, (struct sockaddr *) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for AX/TCP socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + + goto wait; + } + + arp->TCPState = TCPConnecting; + + if (connect(arp->TCPSock,(struct sockaddr *) &arp->destaddr, sizeof(arp->destaddr)) == 0) + { + // + // Connected successful + // + + arp->TCPState = TCPConnected; + OutputDebugString("AXTCP Connected\r\n"); + ioctl (arp->TCPSock, FIONBIO, ¶m); + Alerted = 0; + } + else + { + err=WSAGetLastError(); + + // Connect failed + // + + if (Alerted == 0) + { + i = sprintf(Msg, "Connect Failed for AX/TCP port %d - error code = %d\n", htons(arp->destaddr.sin_port), err); + WritetoConsole(Msg); + OutputDebugString(Msg); + Alerted = 1; + } + closesocket(arp->TCPSock); + arp->TCPSock = 0; + arp->TCPState = 0; + } + } +wait: + Sleep (115000); // 2 Mins + } + + Debugprintf("AX/TCP Connect Thread %x Closing", arp->TCPThreadID); + + arp->TCPThreadID = 0; + + return; // Not Used + +} + +VOID Format_Addr(unsigned char * Addr, char * Output, BOOL IPV6) +{ + unsigned char * src; + char zeros[12] = ""; + char * ptr; + struct + { + int base, len; + } best, cur; + unsigned int words[8]; + int i; + + if (IPV6 == FALSE) + { + sprintf((char *)Output, "%d.%d.%d.%d", Addr[0], Addr[1], Addr[2], Addr[3]); + return; + } + + src = Addr; + + // See if Encapsulated IPV4 addr + + if (src[12] != 0) + { + if (memcmp(src, zeros, 12) == 0) // 12 zeros, followed by non-zero + { + sprintf((char *)Output, "::%d.%d.%d.%d", src[12], src[13], src[14], src[15]); + return; + } + } + + // COnvert 16 bytes to 8 words + + for (i = 0; i < 16; i += 2) + words[i / 2] = (src[i] << 8) | src[i + 1]; + + // Look for longest run of zeros + + best.base = -1; + cur.base = -1; + + for (i = 0; i < 8; i++) + { + if (words[i] == 0) + { + if (cur.base == -1) + cur.base = i, cur.len = 1; // New run, save start + else + cur.len++; // Continuation - increment length + } + else + { + // End of a run of zeros + + if (cur.base != -1) + { + // See if this run is longer + + if (best.base == -1 || cur.len > best.len) + best = cur; + + cur.base = -1; // Start again + } + } + } + + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + + if (best.base != -1 && best.len < 2) + best.base = -1; + + ptr = Output; + + for (i = 0; i < 8; i++) + { + /* Are we inside the best run of 0x00's? */ + + if (best.base != -1 && i >= best.base && i < (best.base + best.len)) + { + // Just output one : for whole string of zeros + + *ptr++ = ':'; + i = best.base + best.len - 1; // skip rest of zeros + continue; + } + + /* Are we following an initial run of 0x00s or any real hex? */ + + if (i != 0) + *ptr++ = ':'; + + ptr += sprintf (ptr, "%x", words[i]); + + // Was it a trailing run of 0x00's? + } + + if (best.base != -1 && (best.base + best.len) == 8) + *ptr++ = ':'; + + *ptr++ = '\0'; +} + + +#define LIBCONFIG_STATIC +#include "libconfig.h" + + + +VOID SaveAXIPCache(struct AXIPPORTINFO * PORT) +{ + config_setting_t *root, *group; + config_t cfg; + char ConfigName[256]; + int index=0; + struct arp_table_entry * arp; + unsigned char hostaddr[64]; + char Key[128]; + + if (BPQDirectory[0] == 0) + { + sprintf(ConfigName,"axipcache%d.cfg", PORT->Port); + } + else + { + sprintf(ConfigName,"%s/axipcache%d.cfg", BPQDirectory, PORT->Port); + } + + // Get rid of old config before saving + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); + + if (PORT->arp_table_len >= MAX_ENTRIES) + { + Debugprintf("arp_table_len corrupt - %d", PORT->arp_table_len); + PORT->arp_table_len = MAX_ENTRIES - 1; + } + + while (index < PORT->arp_table_len) + { + char * ptr = Key; + + arp = &PORT->arp_table[index++]; + + if (arp->IPv6) + Format_Addr((unsigned char *)&arp->destaddr6.sin6_addr, hostaddr, TRUE); + else + Format_Addr((unsigned char *)&arp->destaddr.sin_addr, hostaddr, FALSE); + + sprintf(Key, "*%s", arp->hostname); + + // libconfig keys can't contain . so replace with * + + while (*ptr) + { + if (*ptr == '.') *ptr = '*'; + ptr++; + } + + SaveStringValue(group, Key, hostaddr); + } + + if(!config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + return; + } + + config_destroy(&cfg); +} + +#ifndef LINBPQ + +static BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen) +{ + char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + { + str = (char *)config_setting_get_string (setting); + + if (strlen(str) > maxlen) + { + Debugprintf("Suspect config record %s", str); + str[maxlen] = 0; + } + strcpy(value, str); + return TRUE; + } + return FALSE; +} + +#endif + +VOID GetAXIPCache(struct AXIPPORTINFO * PORT) +{ + config_setting_t *group; + config_t cfg; + char ConfigName[256]; + int index=0; + struct arp_table_entry * arp; + unsigned char hostaddr[64]; + char Key[128]; + struct stat STAT; + + if (BPQDirectory[0] == 0) + { + sprintf(ConfigName,"axipcache%d.cfg", PORT->Port); + } + else + { + sprintf(ConfigName,"%s/axipcache%d.cfg", BPQDirectory, PORT->Port); + } + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + if (stat(ConfigName, &STAT) == -1) + return; + + if(!config_read_file(&cfg, ConfigName)) + { + fprintf(stderr, "AXIP Cache read error line %d - %s\n", config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + return; + } + + group = config_lookup(&cfg, "main"); + + if (group == NULL) + { + config_destroy(&cfg); + return; + } + + while (index < PORT->arp_table_len) + { + char * ptr = Key; + + arp = &PORT->arp_table[index++]; + + sprintf(Key, "*%s", arp->hostname); + + // libconfig keys can't contain . so replace with * + + while (*ptr) + { + if (*ptr == '.') *ptr = '*'; + ptr++; + } + + if (GetStringValue(group, Key, hostaddr, 64)) + { + arp->destaddr.sin_addr.s_addr = inet_addr(hostaddr); + } + } + + config_destroy(&cfg); +} + diff --git a/.svn/pristine/4f/4f26fde44769fbc5ab9bdfb2be60b652d310f5f2.svn-base b/.svn/pristine/4f/4f26fde44769fbc5ab9bdfb2be60b652d310f5f2.svn-base new file mode 100644 index 0000000..f2085e1 --- /dev/null +++ b/.svn/pristine/4f/4f26fde44769fbc5ab9bdfb2be60b652d310f5f2.svn-base @@ -0,0 +1,1554 @@ + +/* pngtest.c - a simple test program to test libpng + * + * libpng 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This program reads in a PNG image, writes it out again, and then + * compares the two files. If the files are identical, this shows that + * the basic chunk handling, filtering, and (de)compression code is working + * properly. It does not currently test all of the transforms, although + * it probably should. + * + * The program will report "FAIL" in certain legitimate cases: + * 1) when the compression level or filter selection method is changed. + * 2) when the maximum IDAT size (PNG_ZBUF_SIZE in pngconf.h) is not 8192. + * 3) unknown unsafe-to-copy ancillary chunks or unknown critical chunks + * exist in the input file. + * 4) others not listed here... + * In these cases, it is best to check with another tool such as "pngcheck" + * to see what the differences between the two files are. + * + * If a filename is given on the command-line, then this file is used + * for the input, rather than the default "pngtest.png". This allows + * testing a wide variety of files easily. You can also test a number + * of files at once by typing "pngtest -m file1.png file2.png ..." + */ + +#include "png.h" + +#if defined(_WIN32_WCE) +# if _WIN32_WCE < 211 + __error__ (f|w)printf functions are not supported on old WindowsCE.; +# endif +# include +# include +# define READFILE(file, data, length, check) \ + if (ReadFile(file, data, length, &check,NULL)) check = 0 +# define WRITEFILE(file, data, length, check)) \ + if (WriteFile(file, data, length, &check, NULL)) check = 0 +# define FCLOSE(file) CloseHandle(file) +#else +# include +# include +# include +# define READFILE(file, data, length, check) \ + check=(png_size_t)fread(data,(png_size_t)1,length,file) +# define WRITEFILE(file, data, length, check) \ + check=(png_size_t)fwrite(data,(png_size_t)1, length, file) +# define FCLOSE(file) fclose(file) +#endif + +#if defined(PNG_NO_STDIO) +# if defined(_WIN32_WCE) + typedef HANDLE png_FILE_p; +# else + typedef FILE * png_FILE_p; +# endif +#endif + +/* Makes pngtest verbose so we can find problems (needs to be before png.h) */ +#ifndef PNG_DEBUG +# define PNG_DEBUG 0 +#endif + +#if !PNG_DEBUG +# define SINGLE_ROWBUF_ALLOC /* makes buffer overruns easier to nail */ +#endif + +/* Turn on CPU timing +#define PNGTEST_TIMING +*/ + +#ifdef PNG_NO_FLOATING_POINT_SUPPORTED +#undef PNGTEST_TIMING +#endif + +#ifdef PNGTEST_TIMING +static float t_start, t_stop, t_decode, t_encode, t_misc; +#include +#endif + +/* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */ +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) png_ptr->jmpbuf +#endif + +#ifdef PNGTEST_TIMING +static float t_start, t_stop, t_decode, t_encode, t_misc; +#if !defined(PNG_tIME_SUPPORTED) +#include +#endif +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +static int tIME_chunk_present=0; +static char tIME_string[30] = "no tIME chunk present in file"; +#endif + +static int verbose = 0; + +int test_one_file PNGARG((PNG_CONST char *inname, PNG_CONST char *outname)); + +#ifdef __TURBOC__ +#include +#endif + +/* defined so I can write to a file on gui/windowing platforms */ +/* #define STDERR stderr */ +#define STDERR stdout /* for DOS */ + +/* example of using row callbacks to make a simple progress meter */ +static int status_pass=1; +static int status_dots_requested=0; +static int status_dots=1; + +void +#ifdef PNG_1_0_X +PNGAPI +#endif +read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) +{ + if(png_ptr == NULL || row_number > PNG_UINT_31_MAX) return; + if(status_pass != pass) + { + fprintf(stdout,"\n Pass %d: ",pass); + status_pass = pass; + status_dots = 31; + } + status_dots--; + if(status_dots == 0) + { + fprintf(stdout, "\n "); + status_dots=30; + } + fprintf(stdout, "r"); +} + +void +#ifdef PNG_1_0_X +PNGAPI +#endif +write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) +{ + if(png_ptr == NULL || row_number > PNG_UINT_31_MAX || pass > 7) return; + fprintf(stdout, "w"); +} + + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) +/* Example of using user transform callback (we don't transform anything, + but merely examine the row filters. We set this to 256 rather than + 5 in case illegal filter values are present.) */ +static png_uint_32 filters_used[256]; +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data) +{ + if(png_ptr != NULL && row_info != NULL) + ++filters_used[*(data-1)]; +} +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +/* example of using user transform callback (we don't transform anything, + but merely count the zero samples) */ + +static png_uint_32 zero_samples; + +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data) +{ + png_bytep dp = data; + if(png_ptr == NULL)return; + + /* contents of row_info: + * png_uint_32 width width of row + * png_uint_32 rowbytes number of bytes in row + * png_byte color_type color type of pixels + * png_byte bit_depth bit depth of samples + * png_byte channels number of channels (1-4) + * png_byte pixel_depth bits per pixel (depth*channels) + */ + + + /* counts the number of zero samples (or zero pixels if color_type is 3 */ + + if(row_info->color_type == 0 || row_info->color_type == 3) + { + int pos=0; + png_uint_32 n, nstop; + for (n=0, nstop=row_info->width; nbit_depth == 1) + { + if(((*dp << pos++ ) & 0x80) == 0) zero_samples++; + if(pos == 8) + { + pos = 0; + dp++; + } + } + if(row_info->bit_depth == 2) + { + if(((*dp << (pos+=2)) & 0xc0) == 0) zero_samples++; + if(pos == 8) + { + pos = 0; + dp++; + } + } + if(row_info->bit_depth == 4) + { + if(((*dp << (pos+=4)) & 0xf0) == 0) zero_samples++; + if(pos == 8) + { + pos = 0; + dp++; + } + } + if(row_info->bit_depth == 8) + if(*dp++ == 0) zero_samples++; + if(row_info->bit_depth == 16) + { + if((*dp | *(dp+1)) == 0) zero_samples++; + dp+=2; + } + } + } + else /* other color types */ + { + png_uint_32 n, nstop; + int channel; + int color_channels = row_info->channels; + if(row_info->color_type > 3)color_channels--; + + for (n=0, nstop=row_info->width; nbit_depth == 8) + if(*dp++ == 0) zero_samples++; + if(row_info->bit_depth == 16) + { + if((*dp | *(dp+1)) == 0) zero_samples++; + dp+=2; + } + } + if(row_info->color_type > 3) + { + dp++; + if(row_info->bit_depth == 16)dp++; + } + } + } +} +#endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */ + +static int wrote_question = 0; + +#if defined(PNG_NO_STDIO) +/* START of code to validate stdio-free compilation */ +/* These copies of the default read/write functions come from pngrio.c and */ +/* pngwio.c. They allow "don't include stdio" testing of the library. */ +/* This is the function that does the actual reading of data. If you are + not reading from a standard C stream, you should create a replacement + read_data function and use it at run time with png_set_read_fn(), rather + than changing the library. */ + +#ifndef USE_FAR_KEYWORD +static void +pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ + READFILE((png_FILE_p)png_ptr->io_ptr, data, length, check); + + if (check != length) + { + png_error(png_ptr, "Read Error!"); + } +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void +pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + int check; + png_byte *n_data; + png_FILE_p io_ptr; + + /* Check if data really is near. If so, use usual code. */ + n_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)n_data == data) + { + READFILE(io_ptr, n_data, length, check); + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t read, remaining, err; + check = 0; + remaining = length; + do + { + read = MIN(NEAR_BUF_SIZE, remaining); + READFILE(io_ptr, buf, 1, err); + png_memcpy(data, buf, read); /* copy far buffer to near buffer */ + if(err != read) + break; + else + check += err; + data += read; + remaining -= read; + } + while (remaining != 0); + } + if (check != length) + { + png_error(png_ptr, "read Error"); + } +} +#endif /* USE_FAR_KEYWORD */ + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +static void +pngtest_flush(png_structp png_ptr) +{ +#if !defined(_WIN32_WCE) + png_FILE_p io_ptr; + io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); + if (io_ptr != NULL) + fflush(io_ptr); +#endif +} +#endif + +/* This is the function that does the actual writing of data. If you are + not writing to a standard C stream, you should create a replacement + write_data function and use it at run time with png_set_write_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +static void +pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + + WRITEFILE((png_FILE_p)png_ptr->io_ptr, data, length, check); + if (check != length) + { + png_error(png_ptr, "Write Error"); + } +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void +pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ + png_FILE_p io_ptr; + + /* Check if data really is near. If so, use usual code. */ + near_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)near_data == data) + { + WRITEFILE(io_ptr, near_data, length, check); + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t written, remaining, err; + check = 0; + remaining = length; + do + { + written = MIN(NEAR_BUF_SIZE, remaining); + png_memcpy(buf, data, written); /* copy far buffer to near buffer */ + WRITEFILE(io_ptr, buf, written, err); + if (err != written) + break; + else + check += err; + data += written; + remaining -= written; + } + while (remaining != 0); + } + if (check != length) + { + png_error(png_ptr, "Write Error"); + } +} + +#endif /* USE_FAR_KEYWORD */ + +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void +pngtest_warning(png_structp png_ptr, png_const_charp message) +{ + PNG_CONST char *name = "UNKNOWN (ERROR!)"; + if (png_ptr != NULL && png_ptr->error_ptr != NULL) + name = png_ptr->error_ptr; + fprintf(STDERR, "%s: libpng warning: %s\n", name, message); +} + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void +pngtest_error(png_structp png_ptr, png_const_charp message) +{ + pngtest_warning(png_ptr, message); + /* We can return because png_error calls the default handler, which is + * actually OK in this case. */ +} +#endif /* PNG_NO_STDIO */ +/* END of code to validate stdio-free compilation */ + +/* START of code to validate memory allocation and deallocation */ +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + +/* Allocate memory. For reasonable files, size should never exceed + 64K. However, zlib may allocate more then 64K if you don't tell + it not to. See zconf.h and png.h for more information. zlib does + need to allocate exactly 64K, so whatever you call here must + have the ability to do that. + + This piece of code can be compiled to validate max 64K allocations + by setting MAXSEG_64K in zlib zconf.h *or* PNG_MAX_MALLOC_64K. */ +typedef struct memory_information +{ + png_uint_32 size; + png_voidp pointer; + struct memory_information FAR *next; +} memory_information; +typedef memory_information FAR *memory_infop; + +static memory_infop pinformation = NULL; +static int current_allocation = 0; +static int maximum_allocation = 0; +static int total_allocation = 0; +static int num_allocations = 0; + +png_voidp png_debug_malloc PNGARG((png_structp png_ptr, png_uint_32 size)); +void png_debug_free PNGARG((png_structp png_ptr, png_voidp ptr)); + +png_voidp +png_debug_malloc(png_structp png_ptr, png_uint_32 size) +{ + + /* png_malloc has already tested for NULL; png_create_struct calls + png_debug_malloc directly, with png_ptr == NULL which is OK */ + + if (size == 0) + return (NULL); + + /* This calls the library allocator twice, once to get the requested + buffer and once to get a new free list entry. */ + { + /* Disable malloc_fn and free_fn */ + memory_infop pinfo; + png_set_mem_fn(png_ptr, NULL, NULL, NULL); + pinfo = (memory_infop)png_malloc(png_ptr, + (png_uint_32)png_sizeof (*pinfo)); + pinfo->size = size; + current_allocation += size; + total_allocation += size; + num_allocations ++; + if (current_allocation > maximum_allocation) + maximum_allocation = current_allocation; + pinfo->pointer = (png_voidp)png_malloc(png_ptr, size); + /* Restore malloc_fn and free_fn */ + png_set_mem_fn(png_ptr, png_voidp_NULL, (png_malloc_ptr)png_debug_malloc, + (png_free_ptr)png_debug_free); + if (size != 0 && pinfo->pointer == NULL) + { + current_allocation -= size; + total_allocation -= size; + png_error(png_ptr, + "out of memory in pngtest->png_debug_malloc."); + } + pinfo->next = pinformation; + pinformation = pinfo; + /* Make sure the caller isn't assuming zeroed memory. */ + png_memset(pinfo->pointer, 0xdd, pinfo->size); + if(verbose) + printf("png_malloc %lu bytes at %x\n",size,pinfo->pointer); + assert(pinfo->size != 12345678); + return (png_voidp)(pinfo->pointer); + } +} + +/* Free a pointer. It is removed from the list at the same time. */ +void +png_debug_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL) + fprintf(STDERR, "NULL pointer to png_debug_free.\n"); + if (ptr == 0) + { +#if 0 /* This happens all the time. */ + fprintf(STDERR, "WARNING: freeing NULL pointer\n"); +#endif + return; + } + + /* Unlink the element from the list. */ + { + memory_infop FAR *ppinfo = &pinformation; + for (;;) + { + memory_infop pinfo = *ppinfo; + if (pinfo->pointer == ptr) + { + *ppinfo = pinfo->next; + current_allocation -= pinfo->size; + if (current_allocation < 0) + fprintf(STDERR, "Duplicate free of memory\n"); + /* We must free the list element too, but first kill + the memory that is to be freed. */ + png_memset(ptr, 0x55, pinfo->size); + png_free_default(png_ptr, pinfo); + pinfo=NULL; + break; + } + if (pinfo->next == NULL) + { + fprintf(STDERR, "Pointer %x not found\n", (unsigned int)ptr); + break; + } + ppinfo = &pinfo->next; + } + } + + /* Finally free the data. */ + if(verbose) + printf("Freeing %x\n",ptr); + png_free_default(png_ptr, ptr); + ptr=NULL; +} +#endif /* PNG_USER_MEM_SUPPORTED && PNG_DEBUG */ +/* END of code to test memory allocation/deallocation */ + +/* Test one file */ +int +test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) +{ + static png_FILE_p fpin; + static png_FILE_p fpout; /* "static" prevents setjmp corruption */ + png_structp read_ptr; + png_infop read_info_ptr, end_info_ptr; +#ifdef PNG_WRITE_SUPPORTED + png_structp write_ptr; + png_infop write_info_ptr; + png_infop write_end_info_ptr; +#else + png_structp write_ptr = NULL; + png_infop write_info_ptr = NULL; + png_infop write_end_info_ptr = NULL; +#endif + png_bytep row_buf; + png_uint_32 y; + png_uint_32 width, height; + int num_pass, pass; + int bit_depth, color_type; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + +#if defined(_WIN32_WCE) + TCHAR path[MAX_PATH]; +#endif + char inbuf[256], outbuf[256]; + + row_buf = NULL; + +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, inname, -1, path, MAX_PATH); + if ((fpin = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpin = fopen(inname, "rb")) == NULL) +#endif + { + fprintf(STDERR, "Could not find input file %s\n", inname); + return (1); + } + +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, outname, -1, path, MAX_PATH); + if ((fpout = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpout = fopen(outname, "wb")) == NULL) +#endif + { + fprintf(STDERR, "Could not open output file %s\n", outname); + FCLOSE(fpin); + return (1); + } + + png_debug(0, "Allocating read and write structures\n"); +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + read_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL, png_voidp_NULL, + (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); +#else + read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL); +#endif +#if defined(PNG_NO_STDIO) + png_set_error_fn(read_ptr, (png_voidp)inname, pngtest_error, + pngtest_warning); +#endif +#ifdef PNG_WRITE_SUPPORTED +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + write_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL, png_voidp_NULL, + (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); +#else + write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL); +#endif +#if defined(PNG_NO_STDIO) + png_set_error_fn(write_ptr, (png_voidp)inname, pngtest_error, + pngtest_warning); +#endif +#endif + png_debug(0, "Allocating read_info, write_info and end_info structures\n"); + read_info_ptr = png_create_info_struct(read_ptr); + end_info_ptr = png_create_info_struct(read_ptr); +#ifdef PNG_WRITE_SUPPORTED + write_info_ptr = png_create_info_struct(write_ptr); + write_end_info_ptr = png_create_info_struct(write_ptr); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_debug(0, "Setting jmpbuf for read struct\n"); +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_jmpbuf(read_ptr))) +#endif + { + fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname); + if (row_buf) + png_free(read_ptr, row_buf); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_destroy_info_struct(write_ptr, &write_end_info_ptr); + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + FCLOSE(fpin); + FCLOSE(fpout); + return (1); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(read_ptr),jmpbuf,png_sizeof(jmp_buf)); +#endif + +#ifdef PNG_WRITE_SUPPORTED + png_debug(0, "Setting jmpbuf for write struct\n"); +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_jmpbuf(write_ptr))) +#endif + { + fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); + png_destroy_info_struct(write_ptr, &write_end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + FCLOSE(fpin); + FCLOSE(fpout); + return (1); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(write_ptr),jmpbuf,png_sizeof(jmp_buf)); +#endif +#endif +#endif + + png_debug(0, "Initializing input and output streams\n"); +#if !defined(PNG_NO_STDIO) + png_init_io(read_ptr, fpin); +# ifdef PNG_WRITE_SUPPORTED + png_init_io(write_ptr, fpout); +# endif +#else + png_set_read_fn(read_ptr, (png_voidp)fpin, pngtest_read_data); +# ifdef PNG_WRITE_SUPPORTED + png_set_write_fn(write_ptr, (png_voidp)fpout, pngtest_write_data, +# if defined(PNG_WRITE_FLUSH_SUPPORTED) + pngtest_flush); +# else + NULL); +# endif +# endif +#endif + if(status_dots_requested == 1) + { +#ifdef PNG_WRITE_SUPPORTED + png_set_write_status_fn(write_ptr, write_row_callback); +#endif + png_set_read_status_fn(read_ptr, read_row_callback); + } + else + { +#ifdef PNG_WRITE_SUPPORTED + png_set_write_status_fn(write_ptr, png_write_status_ptr_NULL); +#endif + png_set_read_status_fn(read_ptr, png_read_status_ptr_NULL); + } + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + { + int i; + for(i=0; i<256; i++) + filters_used[i]=0; + png_set_read_user_transform_fn(read_ptr, count_filters); + } +#endif +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + zero_samples=0; + png_set_write_user_transform_fn(write_ptr, count_zero_samples); +#endif + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) +# ifndef PNG_HANDLE_CHUNK_ALWAYS +# define PNG_HANDLE_CHUNK_ALWAYS 3 +# endif + png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS, + png_bytep_NULL, 0); +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) +# ifndef PNG_HANDLE_CHUNK_IF_SAFE +# define PNG_HANDLE_CHUNK_IF_SAFE 2 +# endif + png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_IF_SAFE, + png_bytep_NULL, 0); +#endif + + png_debug(0, "Reading info struct\n"); + png_read_info(read_ptr, read_info_ptr); + + png_debug(0, "Transferring info struct\n"); + { + int interlace_type, compression_type, filter_type; + + if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth, + &color_type, &interlace_type, &compression_type, &filter_type)) + { + png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth, +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + color_type, interlace_type, compression_type, filter_type); +#else + color_type, PNG_INTERLACE_NONE, compression_type, filter_type); +#endif + } + } +#if defined(PNG_FIXED_POINT_SUPPORTED) +#if defined(PNG_cHRM_SUPPORTED) + { + png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y, blue_x, + blue_y; + if (png_get_cHRM_fixed(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, + &red_y, &green_x, &green_y, &blue_x, &blue_y)) + { + png_set_cHRM_fixed(write_ptr, write_info_ptr, white_x, white_y, red_x, + red_y, green_x, green_y, blue_x, blue_y); + } + } +#endif +#if defined(PNG_gAMA_SUPPORTED) + { + png_fixed_point gamma; + + if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma)) + { + png_set_gAMA_fixed(write_ptr, write_info_ptr, gamma); + } + } +#endif +#else /* Use floating point versions */ +#if defined(PNG_FLOATING_POINT_SUPPORTED) +#if defined(PNG_cHRM_SUPPORTED) + { + double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, + blue_y; + if (png_get_cHRM(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, + &red_y, &green_x, &green_y, &blue_x, &blue_y)) + { + png_set_cHRM(write_ptr, write_info_ptr, white_x, white_y, red_x, + red_y, green_x, green_y, blue_x, blue_y); + } + } +#endif +#if defined(PNG_gAMA_SUPPORTED) + { + double gamma; + + if (png_get_gAMA(read_ptr, read_info_ptr, &gamma)) + { + png_set_gAMA(write_ptr, write_info_ptr, gamma); + } + } +#endif +#endif /* floating point */ +#endif /* fixed point */ +#if defined(PNG_iCCP_SUPPORTED) + { + png_charp name; + png_charp profile; + png_uint_32 proflen; + int compression_type; + + if (png_get_iCCP(read_ptr, read_info_ptr, &name, &compression_type, + &profile, &proflen)) + { + png_set_iCCP(write_ptr, write_info_ptr, name, compression_type, + profile, proflen); + } + } +#endif +#if defined(PNG_sRGB_SUPPORTED) + { + int intent; + + if (png_get_sRGB(read_ptr, read_info_ptr, &intent)) + { + png_set_sRGB(write_ptr, write_info_ptr, intent); + } + } +#endif + { + png_colorp palette; + int num_palette; + + if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette)) + { + png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette); + } + } +#if defined(PNG_bKGD_SUPPORTED) + { + png_color_16p background; + + if (png_get_bKGD(read_ptr, read_info_ptr, &background)) + { + png_set_bKGD(write_ptr, write_info_ptr, background); + } + } +#endif +#if defined(PNG_hIST_SUPPORTED) + { + png_uint_16p hist; + + if (png_get_hIST(read_ptr, read_info_ptr, &hist)) + { + png_set_hIST(write_ptr, write_info_ptr, hist); + } + } +#endif +#if defined(PNG_oFFs_SUPPORTED) + { + png_int_32 offset_x, offset_y; + int unit_type; + + if (png_get_oFFs(read_ptr, read_info_ptr,&offset_x,&offset_y,&unit_type)) + { + png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type); + } + } +#endif +#if defined(PNG_pCAL_SUPPORTED) + { + png_charp purpose, units; + png_charpp params; + png_int_32 X0, X1; + int type, nparams; + + if (png_get_pCAL(read_ptr, read_info_ptr, &purpose, &X0, &X1, &type, + &nparams, &units, ¶ms)) + { + png_set_pCAL(write_ptr, write_info_ptr, purpose, X0, X1, type, + nparams, units, params); + } + } +#endif +#if defined(PNG_pHYs_SUPPORTED) + { + png_uint_32 res_x, res_y; + int unit_type; + + if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, &unit_type)) + { + png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type); + } + } +#endif +#if defined(PNG_sBIT_SUPPORTED) + { + png_color_8p sig_bit; + + if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit)) + { + png_set_sBIT(write_ptr, write_info_ptr, sig_bit); + } + } +#endif +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + { + int unit; + double scal_width, scal_height; + + if (png_get_sCAL(read_ptr, read_info_ptr, &unit, &scal_width, + &scal_height)) + { + png_set_sCAL(write_ptr, write_info_ptr, unit, scal_width, scal_height); + } + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + { + int unit; + png_charp scal_width, scal_height; + + if (png_get_sCAL_s(read_ptr, read_info_ptr, &unit, &scal_width, + &scal_height)) + { + png_set_sCAL_s(write_ptr, write_info_ptr, unit, scal_width, scal_height); + } + } +#endif +#endif +#endif +#if defined(PNG_TEXT_SUPPORTED) + { + png_textp text_ptr; + int num_text; + + if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text) > 0) + { + png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks\n", num_text); + png_set_text(write_ptr, write_info_ptr, text_ptr, num_text); + } + } +#endif +#if defined(PNG_tIME_SUPPORTED) + { + png_timep mod_time; + + if (png_get_tIME(read_ptr, read_info_ptr, &mod_time)) + { + png_set_tIME(write_ptr, write_info_ptr, mod_time); +#if defined(PNG_TIME_RFC1123_SUPPORTED) + /* we have to use png_strcpy instead of "=" because the string + pointed to by png_convert_to_rfc1123() gets free'ed before + we use it */ + png_strcpy(tIME_string,png_convert_to_rfc1123(read_ptr, mod_time)); + tIME_chunk_present++; +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + } + } +#endif +#if defined(PNG_tRNS_SUPPORTED) + { + png_bytep trans; + int num_trans; + png_color_16p trans_values; + + if (png_get_tRNS(read_ptr, read_info_ptr, &trans, &num_trans, + &trans_values)) + { + png_set_tRNS(write_ptr, write_info_ptr, trans, num_trans, + trans_values); + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + { + png_unknown_chunkp unknowns; + int num_unknowns = (int)png_get_unknown_chunks(read_ptr, read_info_ptr, + &unknowns); + if (num_unknowns) + { + png_size_t i; + png_set_unknown_chunks(write_ptr, write_info_ptr, unknowns, + num_unknowns); + /* copy the locations from the read_info_ptr. The automatically + generated locations in write_info_ptr are wrong because we + haven't written anything yet */ + for (i = 0; i < (png_size_t)num_unknowns; i++) + png_set_unknown_chunk_location(write_ptr, write_info_ptr, i, + unknowns[i].location); + } + } +#endif + +#ifdef PNG_WRITE_SUPPORTED + png_debug(0, "\nWriting info struct\n"); + +/* If we wanted, we could write info in two steps: + png_write_info_before_PLTE(write_ptr, write_info_ptr); + */ + png_write_info(write_ptr, write_info_ptr); +#endif + +#ifdef SINGLE_ROWBUF_ALLOC + png_debug(0, "\nAllocating row buffer..."); + row_buf = (png_bytep)png_malloc(read_ptr, + png_get_rowbytes(read_ptr, read_info_ptr)); + png_debug1(0, "0x%08lx\n\n", (unsigned long)row_buf); +#endif /* SINGLE_ROWBUF_ALLOC */ + png_debug(0, "Writing row data\n"); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) + num_pass = png_set_interlace_handling(read_ptr); +# ifdef PNG_WRITE_SUPPORTED + png_set_interlace_handling(write_ptr); +# endif +#else + num_pass=1; +#endif + +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_misc += (t_stop - t_start); + t_start = t_stop; +#endif + for (pass = 0; pass < num_pass; pass++) + { + png_debug1(0, "Writing row data for pass %d\n",pass); + for (y = 0; y < height; y++) + { +#ifndef SINGLE_ROWBUF_ALLOC + png_debug2(0, "\nAllocating row buffer (pass %d, y = %ld)...", pass,y); + row_buf = (png_bytep)png_malloc(read_ptr, + png_get_rowbytes(read_ptr, read_info_ptr)); + png_debug2(0, "0x%08lx (%ld bytes)\n", (unsigned long)row_buf, + png_get_rowbytes(read_ptr, read_info_ptr)); +#endif /* !SINGLE_ROWBUF_ALLOC */ + png_read_rows(read_ptr, (png_bytepp)&row_buf, png_bytepp_NULL, 1); + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_decode += (t_stop - t_start); + t_start = t_stop; +#endif + png_write_rows(write_ptr, (png_bytepp)&row_buf, 1); +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_encode += (t_stop - t_start); + t_start = t_stop; +#endif +#endif /* PNG_WRITE_SUPPORTED */ + +#ifndef SINGLE_ROWBUF_ALLOC + png_debug2(0, "Freeing row buffer (pass %d, y = %ld)\n\n", pass, y); + png_free(read_ptr, row_buf); +#endif /* !SINGLE_ROWBUF_ALLOC */ + } + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + png_free_data(read_ptr, read_info_ptr, PNG_FREE_UNKN, -1); +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + png_free_data(write_ptr, write_info_ptr, PNG_FREE_UNKN, -1); +#endif + + png_debug(0, "Reading and writing end_info data\n"); + + png_read_end(read_ptr, end_info_ptr); +#if defined(PNG_TEXT_SUPPORTED) + { + png_textp text_ptr; + int num_text; + + if (png_get_text(read_ptr, end_info_ptr, &text_ptr, &num_text) > 0) + { + png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks\n", num_text); + png_set_text(write_ptr, write_end_info_ptr, text_ptr, num_text); + } + } +#endif +#if defined(PNG_tIME_SUPPORTED) + { + png_timep mod_time; + + if (png_get_tIME(read_ptr, end_info_ptr, &mod_time)) + { + png_set_tIME(write_ptr, write_end_info_ptr, mod_time); +#if defined(PNG_TIME_RFC1123_SUPPORTED) + /* we have to use png_strcpy instead of "=" because the string + pointed to by png_convert_to_rfc1123() gets free'ed before + we use it */ + png_strcpy(tIME_string,png_convert_to_rfc1123(read_ptr, mod_time)); + tIME_chunk_present++; +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + { + png_unknown_chunkp unknowns; + int num_unknowns; + num_unknowns = (int)png_get_unknown_chunks(read_ptr, end_info_ptr, + &unknowns); + if (num_unknowns) + { + png_size_t i; + png_set_unknown_chunks(write_ptr, write_end_info_ptr, unknowns, + num_unknowns); + /* copy the locations from the read_info_ptr. The automatically + generated locations in write_end_info_ptr are wrong because we + haven't written the end_info yet */ + for (i = 0; i < (png_size_t)num_unknowns; i++) + png_set_unknown_chunk_location(write_ptr, write_end_info_ptr, i, + unknowns[i].location); + } + } +#endif +#ifdef PNG_WRITE_SUPPORTED + png_write_end(write_ptr, write_end_info_ptr); +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED + if(verbose) + { + png_uint_32 iwidth, iheight; + iwidth = png_get_image_width(write_ptr, write_info_ptr); + iheight = png_get_image_height(write_ptr, write_info_ptr); + fprintf(STDERR, "Image width = %lu, height = %lu\n", + iwidth, iheight); + } +#endif + + png_debug(0, "Destroying data structs\n"); +#ifdef SINGLE_ROWBUF_ALLOC + png_debug(1, "destroying row_buf for read_ptr\n"); + png_free(read_ptr, row_buf); + row_buf=NULL; +#endif /* SINGLE_ROWBUF_ALLOC */ + png_debug(1, "destroying read_ptr, read_info_ptr, end_info_ptr\n"); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_debug(1, "destroying write_end_info_ptr\n"); + png_destroy_info_struct(write_ptr, &write_end_info_ptr); + png_debug(1, "destroying write_ptr, write_info_ptr\n"); + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + png_debug(0, "Destruction complete.\n"); + + FCLOSE(fpin); + FCLOSE(fpout); + + png_debug(0, "Opening files for comparison\n"); +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, inname, -1, path, MAX_PATH); + if ((fpin = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpin = fopen(inname, "rb")) == NULL) +#endif + { + fprintf(STDERR, "Could not find file %s\n", inname); + return (1); + } + +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, outname, -1, path, MAX_PATH); + if ((fpout = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpout = fopen(outname, "rb")) == NULL) +#endif + { + fprintf(STDERR, "Could not find file %s\n", outname); + FCLOSE(fpin); + return (1); + } + + for(;;) + { + png_size_t num_in, num_out; + + READFILE(fpin, inbuf, 1, num_in); + READFILE(fpout, outbuf, 1, num_out); + + if (num_in != num_out) + { + fprintf(STDERR, "\nFiles %s and %s are of a different size\n", + inname, outname); + if(wrote_question == 0) + { + fprintf(STDERR, + " Was %s written with the same maximum IDAT chunk size (%d bytes),", + inname,PNG_ZBUF_SIZE); + fprintf(STDERR, + "\n filtering heuristic (libpng default), compression"); + fprintf(STDERR, + " level (zlib default),\n and zlib version (%s)?\n\n", + ZLIB_VERSION); + wrote_question=1; + } + FCLOSE(fpin); + FCLOSE(fpout); + return (0); + } + + if (!num_in) + break; + + if (png_memcmp(inbuf, outbuf, num_in)) + { + fprintf(STDERR, "\nFiles %s and %s are different\n", inname, outname); + if(wrote_question == 0) + { + fprintf(STDERR, + " Was %s written with the same maximum IDAT chunk size (%d bytes),", + inname,PNG_ZBUF_SIZE); + fprintf(STDERR, + "\n filtering heuristic (libpng default), compression"); + fprintf(STDERR, + " level (zlib default),\n and zlib version (%s)?\n\n", + ZLIB_VERSION); + wrote_question=1; + } + FCLOSE(fpin); + FCLOSE(fpout); + return (0); + } + } + + FCLOSE(fpin); + FCLOSE(fpout); + + return (0); +} + +/* input and output filenames */ +#ifdef RISCOS +static PNG_CONST char *inname = "pngtest/png"; +static PNG_CONST char *outname = "pngout/png"; +#else +static PNG_CONST char *inname = "pngtest.png"; +static PNG_CONST char *outname = "pngout.png"; +#endif + +int +main(int argc, char *argv[]) +{ + int multiple = 0; + int ierror = 0; + + fprintf(STDERR, "Testing libpng version %s\n", PNG_LIBPNG_VER_STRING); + fprintf(STDERR, " with zlib version %s\n", ZLIB_VERSION); + fprintf(STDERR,"%s",png_get_copyright(NULL)); + /* Show the version of libpng used in building the library */ + fprintf(STDERR," library (%lu):%s", png_access_version_number(), + png_get_header_version(NULL)); + /* Show the version of libpng used in building the application */ + fprintf(STDERR," pngtest (%lu):%s", (unsigned long)PNG_LIBPNG_VER, + PNG_HEADER_VERSION_STRING); + fprintf(STDERR," png_sizeof(png_struct)=%ld, png_sizeof(png_info)=%ld\n", + (long)png_sizeof(png_struct), (long)png_sizeof(png_info)); + + /* Do some consistency checking on the memory allocation settings, I'm + not sure this matters, but it is nice to know, the first of these + tests should be impossible because of the way the macros are set + in pngconf.h */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) + fprintf(STDERR, " NOTE: Zlib compiled for max 64k, libpng not\n"); +#endif + /* I think the following can happen. */ +#if !defined(MAXSEG_64K) && defined(PNG_MAX_MALLOC_64K) + fprintf(STDERR, " NOTE: libpng compiled for max 64k, zlib not\n"); +#endif + + if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING)) + { + fprintf(STDERR, + "Warning: versions are different between png.h and png.c\n"); + fprintf(STDERR, " png.h version: %s\n", PNG_LIBPNG_VER_STRING); + fprintf(STDERR, " png.c version: %s\n\n", png_libpng_ver); + ++ierror; + } + + if (argc > 1) + { + if (strcmp(argv[1], "-m") == 0) + { + multiple = 1; + status_dots_requested = 0; + } + else if (strcmp(argv[1], "-mv") == 0 || + strcmp(argv[1], "-vm") == 0 ) + { + multiple = 1; + verbose = 1; + status_dots_requested = 1; + } + else if (strcmp(argv[1], "-v") == 0) + { + verbose = 1; + status_dots_requested = 1; + inname = argv[2]; + } + else + { + inname = argv[1]; + status_dots_requested = 0; + } + } + + if (!multiple && argc == 3+verbose) + outname = argv[2+verbose]; + + if ((!multiple && argc > 3+verbose) || (multiple && argc < 2)) + { + fprintf(STDERR, + "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n", + argv[0], argv[0]); + fprintf(STDERR, + " reads/writes one PNG file (without -m) or multiple files (-m)\n"); + fprintf(STDERR, + " with -m %s is used as a temporary file\n", outname); + exit(1); + } + + if (multiple) + { + int i; +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + int allocation_now = current_allocation; +#endif + for (i=2; isize, + (unsigned int) pinfo->pointer); + pinfo = pinfo->next; + } + } +#endif + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + fprintf(STDERR, " Current memory allocation: %10d bytes\n", + current_allocation); + fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", + maximum_allocation); + fprintf(STDERR, " Total memory allocation: %10d bytes\n", + total_allocation); + fprintf(STDERR, " Number of allocations: %10d\n", + num_allocations); +#endif + } + else + { + int i; + for (i=0; i<3; ++i) + { + int kerror; +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + int allocation_now = current_allocation; +#endif + if (i == 1) status_dots_requested = 1; + else if(verbose == 0)status_dots_requested = 0; + if (i == 0 || verbose == 1 || ierror != 0) + fprintf(STDERR, "Testing %s:",inname); + kerror = test_one_file(inname, outname); + if(kerror == 0) + { + if(verbose == 1 || i == 2) + { +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + int k; +#endif +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + fprintf(STDERR, "\n PASS (%lu zero samples)\n",zero_samples); +#else + fprintf(STDERR, " PASS\n"); +#endif +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + for (k=0; k<256; k++) + if(filters_used[k]) + fprintf(STDERR, " Filter %d was used %lu times\n", + k,filters_used[k]); +#endif +#if defined(PNG_TIME_RFC1123_SUPPORTED) + if(tIME_chunk_present != 0) + fprintf(STDERR, " tIME = %s\n",tIME_string); +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + } + } + else + { + if(verbose == 0 && i != 2) + fprintf(STDERR, "Testing %s:",inname); + fprintf(STDERR, " FAIL\n"); + ierror += kerror; + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + if (allocation_now != current_allocation) + fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n", + current_allocation-allocation_now); + if (current_allocation != 0) + { + memory_infop pinfo = pinformation; + + fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n", + current_allocation); + while (pinfo != NULL) + { + fprintf(STDERR," %lu bytes at %x\n", + pinfo->size, (unsigned int)pinfo->pointer); + pinfo = pinfo->next; + } + } +#endif + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + fprintf(STDERR, " Current memory allocation: %10d bytes\n", + current_allocation); + fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", + maximum_allocation); + fprintf(STDERR, " Total memory allocation: %10d bytes\n", + total_allocation); + fprintf(STDERR, " Number of allocations: %10d\n", + num_allocations); +#endif + } + +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_misc += (t_stop - t_start); + t_start = t_stop; + fprintf(STDERR," CPU time used = %.3f seconds", + (t_misc+t_decode+t_encode)/(float)CLOCKS_PER_SEC); + fprintf(STDERR," (decoding %.3f,\n", + t_decode/(float)CLOCKS_PER_SEC); + fprintf(STDERR," encoding %.3f ,", + t_encode/(float)CLOCKS_PER_SEC); + fprintf(STDERR," other %.3f seconds)\n\n", + t_misc/(float)CLOCKS_PER_SEC); +#endif + + if (ierror == 0) + fprintf(STDERR, "libpng passes test\n"); + else + fprintf(STDERR, "libpng FAILS test\n"); + return (int)(ierror != 0); +} + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef version_1_2_8 your_png_h_is_not_version_1_2_8; diff --git a/.svn/pristine/4f/4fc8de1a19b271799ebffb67ffb68fa72e9e66ee.svn-base b/.svn/pristine/4f/4fc8de1a19b271799ebffb67ffb68fa72e9e66ee.svn-base new file mode 100644 index 0000000..718af82 --- /dev/null +++ b/.svn/pristine/4f/4fc8de1a19b271799ebffb67ffb68fa72e9e66ee.svn-base @@ -0,0 +1,1765 @@ +/* +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 +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use the V4 TNC as a Port Driver +// +// Uses BPQ EXTERNAL interface +// +// Uses a number of routines in WINMOR.c + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + + +#include "cheaders.h" +#include "tncinfo.h" +#include "bpq32.h" + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +static int Socket_Data(int sock, int error, int eventcode); +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + +KillTNC(struct TNCINFO * TNC); +RestartTNC(struct TNCINFO * TNC); +KillPopups(struct TNCINFO * TNC); +VOID MoveWindows(struct TNCINFO * TNC); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +static VOID ChangeMYC(struct TNCINFO * TNC, char * Call); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); + +static char ClassName[]="V4STATUS"; +static char WindowTitle[] = "V4TNC"; +static int RigControlRow = 147; + +#define V4 +#define NARROWMODE 0 +#define WIDEMODE 0 + +#include + + +extern int SemHeldByAPI; + +static RECT Rect; + +static int ProcessLine(char * buf, int Port); + +// RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if (ptr == NULL) return (TRUE); + + if (*ptr == '#') return (TRUE); // comment + + if (*ptr == ';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if ((_memicmp(buf, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) + {} // Ignore + else + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + {} // Ignore + else + + strcat (TNC->InitScript, buf); + } + + return (TRUE); +} + + + +void ConnecttoWINMORThread(int port); +VOID V4ProcessDataSocketData(int port); +int ConnecttoWINMOR(); +int ProcessReceivedData(struct TNCINFO * TNC); +VOID ReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static time_t ltime; + +#pragma pack() + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + +static VOID ChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + +// send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + + datalen = sprintf(TXMsg, "MYCALL %s\r\n", Call); + send(TNC->TCPSock,TXMsg, datalen, 0); + +// send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); +// TNC->StartSent = TRUE; + +// send(TNC->TCPSock, "MYCALL\r\n", 8, 0); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; + int i,winerr; + char txbuff[500]; + char Status[80]; + unsigned int bytes,txlen=0; + char ErrMsg[255]; + int Param; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + if (TNC == NULL) + return 0; // Port not defined + + switch (fn) + { + case 1: // poll + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + if (TNC->BusyDelay) + { + // Still Busy? + + if ((TNC->Busy & CDBusy) == 0) + { + // No, so send + + send(TNC->TCPSock, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd), 0); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[11], strlen(TNC->ConnectCmd)-13); + + sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + free(TNC->ConnectCmd); + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "Sorry, Can't Connect - Channel is busy\r"); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + } + } + } + + if (TNC->HeartBeat++ > 600 || (TNC->Streams[0].Connected && TNC->HeartBeat > 50)) // Every Minute unless connected + { + TNC->HeartBeat = 0; + + if (TNC->CONNECTED) + + // Probe link + + send(TNC->TCPSock, "BUFFER\r\n", 8, 0); + } + + if (TNC->FECMode) + { + if (TNC->FECIDTimer++ > 6000) // ID every 10 Mins + { + if (!TNC->Busy) + { + TNC->FECIDTimer = 0; + send(TNC->TCPSock, "SENDID 0\r\n", 10, 0); + } + } + if (TNC->FECPending) // Check if FEC Send needed + { + if (!TNC->Busy) + { + TNC->FECPending = 0; + + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + } + } + } + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + send(TNC->TCPSock,"ARQEND\r\n", 8, 0); + TNC->Streams[0].ARQENDSent = TRUE; + } + } + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + + if (TNC->PID) + { + KillTNC(TNC); + RestartTNC(TNC); + } + } + } +/* + if (TNC->UpdateWL2K) + { + TNC->UpdateWL2KTimer--; + + if (TNC->UpdateWL2KTimer == 0) + { + TNC->UpdateWL2KTimer = 32910/2; // Every Hour + if (CheckAppl(TNC, "RMS ")) // Is RMS Available? + SendReporttoWL2K(TNC); + } + } +*/ + if (TNC->RIG) + { + if (TNC->RIG->RigFreq != TNC->LastFreq) + { + char FREQMsg[80]; + int Len; + + TNC->LastFreq = TNC->RIG->RigFreq; + Len = sprintf(FREQMsg, "DISPLAY CF:%1.4f\r\n", TNC->LastFreq + .0015); + send(TNC->TCPSock,FREQMsg, Len, 0); + } + } + + if (TNC->TimeSinceLast++ > 700) // Allow 10 secs for Keepalive + { + // Restart TNC + + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, "V4 TNC")) + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); + + KillTNC(TNC); + RestartTNC(TNC); + + TNC->TimeSinceLast = 0; + } + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + TNC->Streams[0].Attached = TRUE; + TNC->Streams[0].ARQENDSent = FALSE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + // Stop Listening, and set MYCALL to user's call + +// send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + ChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(Status, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + + } + + if (TNC->Streams[0].Attached) + CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); + + if (TNC->Streams[0].ReportDISC) + { + TNC->Streams[0].ReportDISC = FALSE; + buff->PORT = 0; + return -1; + } + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime >9 ) + { + TNC->LastFreq = 0; // so display will be updated + ConnecttoWINMOR(port); + TNC->lasttime = ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); + + FD_ZERO(&writefs); + + if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + FD_ZERO(&errorfs); + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + if (select((int)TNC->TCPSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (readfs.fd_count == 1) + V4ProcessDataSocketData(port); + + if (writefs.fd_count == 1) + { + // Write block has cleared. Send rest of packet + + buffptr=Q_REM(&TNC->BPQtoWINMOR_Q); + txlen = (unsigned int)buffptr->Len; + + memcpy(txbuff,buffptr->Data, txlen); + bytes=send(TNC->TCPSock,(const char FAR *)&txbuff,txlen,0); + ReleaseBuffer(buffptr); + } + + if (errorfs.fd_count == 1) + { + i=sprintf(ErrMsg, "V4 Data Connection lost for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + } + } + + // See if any frames for this port + + if (TNC->WINMORtoBPQ_Q != 0) + { + buffptr = Q_REM(&TNC->WINMORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA[0], buffptr->Data, datalen); + + datalen += sizeof(void *) + 4; + PutLengthinBuffer(buff, (int)datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + return (0); + + case 2: // send + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "No Connection to V4 TNC\r"); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + if (TNC->BPQtoWINMOR_Q) + return 0; // Socket is blocked - just drop packets till it clears + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + + if (TNC->Streams[0].Connected) + bytes=send(TNC->TCPDataSock, buff->L2DATA, txlen,0); + else + { + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + if (TNC->FECMode) + { + char Buffer[300]; + int len; + + // Send FEC Data + + buff->L2DATA[txlen] = 0; + len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, buff->L2DATA); + + send(TNC->TCPDataSock, Buffer, len, 0); + +/* if (TNC->Busy) + { + TNC->FECPending = 1; + } + else + { + if (TNC->FEC1600) + send(TNC->TCPSock,"FECSEND 1600\r\n", 14, 0); + else + send(TNC->TCPSock,"FECSEND 500\r\n", 13, 0); + } +*/ return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(buff->L2DATA, "RADIO ", 6) == 0) + { + char cmd[56]; + + strcpy(cmd, &buff->L2DATA[6]); + sprintf(buff->L2DATA, "%d %s", TNC->Port, cmd); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, buff->L2DATA)) + { + } + else + { + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, buff->L2DATA); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(buff->L2DATA, "CODEC TRUE", 9) == 0) + TNC->StartSent = TRUE; + + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + if (_memicmp(&buff[8], "FEC\r", 4) == 0 || _memicmp(&buff[8], "FEC ", 4) == 0) + { + TNC->FECMode = TRUE; + TNC->FECIDTimer = 0; + send(TNC->TCPSock,"MODE FEC\r\n", 10, 0); + strcpy(TNC->WEB_MODE, "FEC"); + MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + + return 0; + } + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80] = "ARQCONNECT "; + + memcpy(&Connect[11], &buff->L2DATA[2], txlen); + txlen += 9; + Connect[txlen++] = 0x0a; + Connect[txlen] = 0; + + _strupr(Connect); + + // See if Busy + + if (TNC->Busy & CDBusy) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = 100; // 10 secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + bytes=send(TNC->TCPSock, Connect, txlen, 0); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &Connect[11], txlen-13); + + sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + } + else + { + buff->L2DATA[txlen++] = 0x0a; + bytes=send(TNC->TCPSock, buff->L2DATA, txlen, 0); + } + } + if (bytes != txlen) + { + + // WINMOR doesn't seem to recover from a blocked write. For now just reset + + winerr = WSAGetLastError(); + + sprintf(ErrMsg, "V4 Write Failed for port %d - error code = %d\n", port, winerr); + WritetoConsole(ErrMsg); + closesocket(TNC->TCPSock); + TNC->CONNECTED = FALSE; + + return (0); + } + return (0); + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + if (TNC->Streams[0].Attached == 0) + return TNC->CONNECTED << 8 | 1; + + return (TNC->CONNECTED << 8 | TNC->Streams[0].Disconnecting << 15); // OK + + break; + + case 4: // reinit + + return (0); + + case 5: // Close + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + Sleep(100); + shutdown(TNC->TCPDataSock, SD_BOTH); + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPDataSock); + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return (0); + + case 6: // Scan Stop Interface + + Param = (int)(size_t)buff; + + if (Param == 1) // Request Permission + { + if (!TNC->ConnectPending) + return 0; // OK to Change + +// send(TNC->TCPSock, "LISTEN FALSE\r\n", 14, 0); + + return TRUE; + } + + if (Param == 2) // Check Permission + { + if (TNC->ConnectPending) + return -1; // Skip Interval + + return 1; // OK to change + } + + if (Param == 3) // Release Permission + { +// send(TNC->TCPSock, "LISTEN TRUE\r\n", 13, 0); + return 0; + } + + if (Param == 4) // Set Wide Mode + { + send(TNC->TCPSock, "BW 1600\r\n", 9, 0); + return 0; + } + + if (Param == 5) // Set Narrow Mode + { + send(TNC->TCPSock, "BW 500\r\n", 8, 0); + return 0; + } + + return 0; + } + return 0; +} + +VOID V4ProcessDataSocketData(int port) +{ + // Info on Data Socket - just packetize and send on + + struct TNCINFO * TNC = TNCInfo[port]; + int InputLen, PacLen = 236, i; + PMSGWITHLEN buffptr; + char * msg; + + TNC->TimeSinceLast = 0; + +loop: + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + InputLen=recv(TNC->TCPDataSock, (char *)&buffptr[2], PacLen, 0); + + if (InputLen == -1) + { + ReleaseBuffer(buffptr); + return; + } + + + //Debugprintf("Winmor: RXD %d bytes", InputLen); + + if (InputLen == 0) + { + // Does this mean closed? + + strcpy(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + ReleaseBuffer(buffptr); + return; + } + + + msg = buffptr->Data; + + // Message should always be received in 17 char chunks. 17th is a status byte + // In ARQ, 6 = "Echo as sent" ack + + if (InputLen != 17) + { + Debugprintf("V4 TNC incorrect RX Len = %d", InputLen); + goto loop; + } + + if (msg[16] == 0x06) + goto loop; + + InputLen = 16; + + for (i = 0; i < 16; i++) + { + if (msg[i] == 0) + break; + + if (msg[i] == 10) + continue; + + if (msg[i] < 0x20 || msg[i] > 0x7e) + msg[i] = '?'; + } + + + msg[InputLen] = 0; + + WritetoTrace(TNC, msg, InputLen); + + // V4 Sends null padded blocks + + InputLen = (int)strlen(buffptr->Data); + + if (msg[InputLen - 1] == 10) // LF + { + // Replace with CRLF + + msg[InputLen-1] = 13; // Add CR + msg[InputLen++] = 10; + } + + buffptr->Len = InputLen; + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + goto loop; +} + + + + +static VOID ReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call + + ChangeMYC(TNC, TNC->NodeCall); + +// send(TNC->TCPSock, "LISTEN TRUE\r\nMAXCONREQ 4\r\n", 26, 0); + + MySetWindowText(TNC->xIDC_TNCSTATE, "Free"); + strcpy(TNC->WEB_TNCSTATE, "Free"); + + // Start Scanner + + ReleaseOtherPorts(TNC); + +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "V4 Status" + "

V4 Status

"); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + + + +void * V4ExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->PortRecord = PortEntry; + + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_V4; + + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->MAXHOSTMODESESSIONS = 1; +// PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = malloc(1000); + + strcpy(TempScript, "DebugLog True\r\n"); + strcat(TempScript, "AUTOID FALSE\r\n"); + strcat(TempScript, "CODEC FALSE\r\n"); + strcat(TempScript, "TIMEOUT 90\r\n"); + strcat(TempScript, "MODE ARQ\r\n"); + strcat(TempScript, "TUNING 100\r\n"); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Set MYCALL + + +// strcat(TNC->InitScript,"FECRCV True\r\n"); + + sprintf(Msg, "MYCALL %s\r\nCODEC TRUE\r\nMYCALL\r\n", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcat(TNC->InitScript,"PROCESSID\r\n"); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->HostName=malloc(10); + + if (TNC->HostName != NULL) + strcpy(TNC->HostName,"127.0.0.1"); + + } + + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 450, 500, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,116,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,116,40,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,116,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,116,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,138,250,300, TNC->hDlg, NULL, hInstance, NULL); + + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill V4 TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart V4 TNC"); +// AppendMenu(TNC->hPopMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after each Connection"); +// CheckMenuItem(TNC->hPopMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + + MoveWindows(TNC); +#endif + i=sprintf(Msg,"V4 Host %s %d\n", TNC->HostName, htons(TNC->destaddr.sin_port)); + WritetoConsole(Msg); + + strcpy(TNC->WEB_MODE, "ARQ"); + MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + ConnecttoWINMOR(port); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + +#ifndef LINBPQ + +static BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[100]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowText(hwnd,wtext,99); + + if (memcmp(wtext,"Registration", 12) == 0) + { + SendMessage(hwnd, WM_CLOSE, 0, 0); + return TRUE; + } + if (memcmp(wtext,"V4 Sound Card TNC", 17) == 0) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + if (TNC->PID == ProcessId) + { + // Our Process + + sprintf (wtext, "V4 Sound Card TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + MySetWindowText(hwnd, wtext); + // return FALSE; + } + } + + return (TRUE); +} +#endif + +static VOID ProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + // Response on WINMOR control channel. Could be a reply to a command, or + // an Async Response + + PMSGWITHLEN buffptr; + char Status[80]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + if (_memicmp(Buffer, "FAULT failure to Restart Sound card", 20) == 0) + { + // Force a restart + + send(TNC->TCPSock, "CODEC FALSE\r\n", 13, 0); + send(TNC->TCPSock, "CODEC TRUE\r\n", 12, 0); + } + else + { + TNC->TimeSinceLast = 0; + } + + Buffer[MsgLen - 2] = 0; // Remove CRLF + + if (_memicmp(Buffer, "PTT T", 5) == 0) + { + TNC->Busy |= PTTBusy; + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + return; + } + if (_memicmp(Buffer, "PTT F", 5) == 0) + { + TNC->Busy &= ~PTTBusy; + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + return; + } + + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->Busy |= CDBusy; + MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + return; + } + + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->Busy &= ~CDBusy; + MySetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + return; + } + + if (_memicmp(Buffer, "OFFSET", 6) == 0) + { +// WritetoTrace(TNC, Buffer, MsgLen - 2); +// memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "CONNECTED", 9) == 0) + { + char Call[11]; + char * ptr; + char * ApplPtr = APPLS; + APPLCALLS * APPL; + int App; + char Appl[10]; + + WritetoTrace(TNC, Buffer, MsgLen - 2); + + STREAM->ConnectTime = time(NULL); + + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + // Incomming Connect + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + ProcessIncommingConnect(TNC, Call, 0, TRUE); + TNC->Streams[0].ARQENDSent = FALSE; + + if (TNC->RIG) + sprintf(Status, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + else + sprintf(Status, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->CurrentMYC, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(Buffer, "%s\r", AppName); + + GetSemaphore(&Semaphore, 50); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + FreeSemaphore(&Semaphore); + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(buffptr+2, Buffer, MsgLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + FreeSemaphore(&Semaphore); + + TNC->SwallowSignon = TRUE; + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + send(TNC->TCPDataSock, Msg, (int)strlen(Msg), 0); + STREAM->NeedDisc = 100; // 10 secs + } + } + + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + ReplyLen = sprintf(Reply, "*** Connected to %s\r", &Buffer[10]); + + buffptr->Len = ReplyLen; + memcpy(buffptr+2, Reply, ReplyLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + if (TNC->RIG) + sprintf(Status, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(Status, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + UpdateMH(TNC, Call, '+', 'O'); + + return; + } + } + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0) + { + if (TNC->FECMode) + return; + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "V4} Failure with %s\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + sprintf(Status, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, Status); + strcpy(TNC->WEB_TNCSTATE, Status); + + return; + } + + WritetoTrace(TNC, Buffer, MsgLen - 2); + + // Release Session + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + if (TNC->Streams[0].Disconnecting) // + ReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + + return; + } + + if (_memicmp(Buffer, "CMD", 3) == 0) + { + return; + } + + if (_memicmp(Buffer, "PENDING", 6) == 0) + return; + +/* + + if (_memicmp(Buffer, "FAULT Not connected!", 20) == 0) + { + // If in response to ARQEND, assume Disconnected was missed + + if (TNC->Streams[0].Disconnecting) + { + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + ReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + } + } +*/ + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + if (_memicmp(Buffer, "BUFFER", 6) == 0) + { + sscanf(&Buffer[7], "%d", &TNC->Streams[0].BytesOutstanding); + + if (TNC->Streams[0].BytesOutstanding == 0) + { + // all sent + + if (TNC->Streams[0].Disconnecting) // Disconnect when all sent + { + if (TNC->Streams[0].ARQENDSent == FALSE) + { + send(TNC->TCPSock,"ARQEND\r\n", 8, 0); + TNC->Streams[0].ARQENDSent = TRUE; + } + } + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + MySetWindowText(TNC->xIDC_TRAFFIC, &Buffer[7]); + strcpy(TNC->WEB_TRAFFIC, &Buffer[7]); + + return; + } + + if (_memicmp(Buffer, "PROCESSID", 9) == 0) + { + HANDLE hProc; + char ExeName[256] = ""; + + TNC->PID = atoi(&Buffer[10]); + + // Get the File Name in case we want to restart it. + + if (GetModuleFileNameExPtr) + { + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + if (TNC->ProgramPath) + free(TNC->ProgramPath); + + TNC->ProgramPath = _strdup(ExeName); + } + } + + // Set Window Title to reflect BPQ Port Description + +#ifndef LINBPQ + EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); +#endif + } + + if (_memicmp(Buffer, "PLAYBACKDEVICES", 15) == 0) + { + TNC->PlaybackDevices = _strdup(&Buffer[16]); + } + // Others should be responses to commands + + if (_memicmp(Buffer, "BLOCKED", 6) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 2); + return; + } + + if (_memicmp(Buffer, "CONREQ", 6) == 0) + { + // if to one of our APPLCALLS, change TNC MYCALL + + APPLCALLS * APPL; + char Appl[11]; + char Target[20]; + char * ptr; + int i; + + memcpy(Target, &Buffer[7], 12); + ptr = memchr(Target, ' ', 12); + if (ptr) + *ptr = 0; + + if (strcmp(Target, TNC->NodeCall) == 0) + ChangeMYC(TNC, Target); + else + { + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (strcmp(Appl, Target) == 0) + { + ChangeMYC(TNC, Target); + break; + } + } + } + } + WritetoTrace(TNC, Buffer, MsgLen - 2); + + // Update MH + + ptr = strstr(Buffer, " de "); + if (ptr) + UpdateMH(TNC, ptr + 4, '!', 'O'); + } + + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "V4} %s\r", Buffer); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + +} + +int V4ProcessReceivedData(struct TNCINFO * TNC) +{ + char ErrMsg[255]; + + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[2000]; + + // May have several messages per packet, or message split over packets + + if (TNC->InputLen > 1000) // Shouldnt have lines longer than this on command connection + TNC->InputLen=0; + + InputLen=recv(TNC->TCPSock, &TNC->TCPBuffer[TNC->InputLen], 1000 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + if (!TNC->CONNECTING) + { + sprintf(ErrMsg, "V4TNC Connection lost for BPQ Port %d\r\n", TNC->Port); + WritetoConsole(ErrMsg); + } + TNC->CONNECTING = FALSE; + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + return 0; + } + + TNC->InputLen += InputLen; + +loop: + + ptr = memchr(TNC->TCPBuffer, '\n', TNC->InputLen); + + if (ptr) // CR in buffer + { + ptr2 = &TNC->TCPBuffer[TNC->InputLen]; + ptr++; // Assume LF Follows CR + + if (ptr == ptr2) + { + // Usual Case - single meg in buffer + + ProcessResponse(TNC, TNC->TCPBuffer, TNC->InputLen); + TNC->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = TNC->InputLen - (int)(ptr2 - ptr); + + memcpy(Buffer, TNC->TCPBuffer, MsgLen); + + ProcessResponse(TNC, Buffer, MsgLen); + + memmove(TNC->TCPBuffer, ptr, TNC->InputLen-MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + } + return 0; +} + +/* +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Cmd = LOWORD(wParam); + + switch (message) + { + case WM_INITDIALOG: + { + struct TNCINFO * TNC = (struct TNCINFO * )lParam; + char * ptr1, *ptr2; + int ptr3 = 0; + char Line[1000]; + int len; + + ptr1 = TNC->CaptureDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_CAPTURE, Line); + + ptr3 = 0; + + ptr1 = TNC->PlaybackDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_PLAYBACK, Line); + + SendDlgItemMessage(hDlg, IDC_PLAYBACK, EM_SETSEL, -1, 0); + +// KillTNC(TNC); + + return TRUE; + } + + case WM_SIZING: + { + return TRUE; + } + + case WM_ACTIVATE: + +// SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + break; + + + case WM_COMMAND: + + + if (Cmd == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} +*/ +static VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[0].BytesOutstanding == 0) + { + send(TNC->TCPSock,"ARQEND\r\n", 8, 0); + TNC->Streams[0].ARQENDSent = TRUE; + } +} + +static VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + send(TNC->TCPSock,"ABORT\r\n", 7, 0); +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + ReleaseTNC(TNC); + + ChangeMYC(TNC, TNC->NodeCall); // In case changed to an applcall + + if (TNC->FECMode) + { + TNC->FECMode = FALSE; + send(TNC->TCPSock,"MODE ARQ\r\n", 10, 0); + strcpy(TNC->WEB_MODE, "ARQ"); + MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + } +} diff --git a/.svn/pristine/50/503fcb8116d4883496ff1d52b8178d600ee5c5f5.svn-base b/.svn/pristine/50/503fcb8116d4883496ff1d52b8178d600ee5c5f5.svn-base new file mode 100644 index 0000000..19c0877 --- /dev/null +++ b/.svn/pristine/50/503fcb8116d4883496ff1d52b8178d600ee5c5f5.svn-base @@ -0,0 +1,29 @@ + +char VersionString[50]=""; +char VersionStringWithBuild[50]=""; +int Ver[4] = {Vers}; +char TextVerstring[50] = ""; + +void GetVersionInfo(char * File) +{ +#ifndef LINBPQ + + char isDebug[40]=""; + +#ifdef SPECIALVERSION + strcat(isDebug, " "); + strcat(isDebug, SPECIALVERSION); +#endif +#ifdef _DEBUG + strcat(isDebug, " Debug Build"); +#endif + + sprintf(VersionString,"%d.%d.%d.%d%s", Ver[0], Ver[1], Ver[2], Ver[3], isDebug); + + sprintf(TextVerstring,"V%d.%d.%d.%d", Ver[0], Ver[1], Ver[2], Ver[3]); + + sprintf(VersionStringWithBuild,"%d.%d.%d Build %d %s", Ver[0], Ver[1], Ver[2], Ver[3], isDebug); + + return; +#endif +} diff --git a/.svn/pristine/50/504eb3e5a1205b43fafb65e9b8f007c3afa7e657.svn-base b/.svn/pristine/50/504eb3e5a1205b43fafb65e9b8f007c3afa7e657.svn-base new file mode 100644 index 0000000..eb6b2d7 --- /dev/null +++ b/.svn/pristine/50/504eb3e5a1205b43fafb65e9b8f007c3afa7e657.svn-base @@ -0,0 +1,2281 @@ +/* LzmaEnc.c -- LZMA Encoder + 2009-02-02 : Igor Pavlov : Public domain */ + +#include + +/* #define SHOW_STAT */ +/* #define SHOW_STAT2 */ + +#if defined(SHOW_STAT) || defined(SHOW_STAT2) +#include +#endif + +#include "LzmaEnc.h" + +#include "LzFind.h" +#ifdef COMPRESS_MF_MT +#include "LzFindMt.h" +#endif + +#ifdef SHOW_STAT +static int ttt = 0; +#endif + +#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) + +#define kBlockSize (9 << 10) +#define kUnpackBlockSize (1 << 18) +#define kMatchArraySize (1 << 21) +#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) + +#define kNumMaxDirectBits (31) + +#define kNumTopBits 24 +#define kTopValue ((LZ_UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 +#define kProbInitValue (kBitModelTotal >> 1) + +#define kNumMoveReducingBits 4 +#define kNumBitPriceShiftBits 4 +#define kBitPrice (1 << kNumBitPriceShiftBits) + +void LzmaEncProps_Init(CLzmaEncProps *p) +{ + p->level = 5; + p->dictSize = p->mc = 0; + p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; + p->writeEndMark = 0; +} + +void LzmaEncProps_Normalize(CLzmaEncProps *p) +{ + int level = p->level; + if (level < 0) level = 5; + p->level = level; + if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); + if (p->lc < 0) p->lc = 3; + if (p->lp < 0) p->lp = 0; + if (p->pb < 0) p->pb = 2; + if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); + if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); + if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); + if (p->numHashBytes < 0) p->numHashBytes = 4; + if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); + if (p->numThreads < 0) + p->numThreads = +#ifdef COMPRESS_MF_MT + ((p->btMode && p->algo) ? 2 : 1); +#else + 1; +#endif +} + +LZ_UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) +{ + CLzmaEncProps props = *props2; + LzmaEncProps_Normalize(&props); + return props.dictSize; +} + +/* #define LZMA_LOG_BSR */ +/* Define it for Intel's CPU */ + + +#ifdef LZMA_LOG_BSR + +#define kDicLogSizeMaxCompress 30 + +#define BSR2_RET(pos, res) { uint32_t i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } + +LZ_UInt32 GetPosSlot1(LZ_UInt32 pos) +{ + LZ_UInt32 res; + BSR2_RET(pos, res); + return res; +} +#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } +#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } + +#else + +#define kNumLogBits (9 + (int)sizeof(size_t) / 2) +#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) + +void LzmaEnc_FastPosInit(Byte *g_FastPos) +{ + int c = 2, slotFast; + g_FastPos[0] = 0; + g_FastPos[1] = 1; + + for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) + { + LZ_UInt32 k = (1 << ((slotFast >> 1) - 1)); + LZ_UInt32 j; + for (j = 0; j < k; j++, c++) + g_FastPos[c] = (Byte)slotFast; + } +} + +#define BSR2_RET(pos, res) { LZ_UInt32 i = 6 + ((kNumLogBits - 1) & \ +(0 - (((((LZ_UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ +res = p->g_FastPos[pos >> i] + (i * 2); } +/* + #define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ + p->g_FastPos[pos >> 6] + 12 : \ + p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } + */ + +#define GetPosSlot1(pos) p->g_FastPos[pos] +#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } +#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } + +#endif + + +#define LZMA_NUM_REPS 4 + +typedef unsigned CState; + +typedef struct _COptimal +{ + LZ_UInt32 price; + + CState state; + int prev1IsChar; + int prev2; + + LZ_UInt32 posPrev2; + LZ_UInt32 backPrev2; + + LZ_UInt32 posPrev; + LZ_UInt32 backPrev; + LZ_UInt32 backs[LZMA_NUM_REPS]; +} COptimal; + +#define kNumOpts (1 << 12) + +#define kNumLenToPosStates 4 +#define kNumPosSlotBits 6 +#define kDicLogSizeMin 0 +#define kDicLogSizeMax 32 +#define kDistTableSizeMax (kDicLogSizeMax * 2) + + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) +#define kAlignMask (kAlignTableSize - 1) + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) + +#define kNumFullDistances (1 << (kEndPosModelIndex / 2)) + +#ifdef _LZMA_PROB32 +#define CLzmaProb LZ_UInt32 +#else +#define CLzmaProb UInt16 +#endif + +#define LZMA_PB_MAX 4 +#define LZMA_LC_MAX 8 +#define LZMA_LP_MAX 4 + +#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) + + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define LZMA_MATCH_LEN_MIN 2 +#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) + +#define kNumStates 12 + +typedef struct +{ + CLzmaProb choice; + CLzmaProb choice2; + CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; + CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; + CLzmaProb high[kLenNumHighSymbols]; +} CLenEnc; + +typedef struct +{ + CLenEnc p; + LZ_UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; + LZ_UInt32 tableSize; + LZ_UInt32 counters[LZMA_NUM_PB_STATES_MAX]; +} CLenPriceEnc; + +typedef struct _CRangeEnc +{ + LZ_UInt32 range; + Byte cache; + LZ_UInt64 low; + LZ_UInt64 cacheSize; + Byte *buf; + Byte *bufLim; + Byte *bufBase; + ISeqOutStream *outStream; + LZ_UInt64 processed; + SRes res; +} CRangeEnc; + +typedef struct _CSeqInStreamBuf +{ + ISeqInStream funcTable; + const Byte *data; + SizeT rem; +} CSeqInStreamBuf; + +static SRes MyRead(void *pp, void *data, size_t *size) +{ + size_t curSize = *size; + CSeqInStreamBuf *p = (CSeqInStreamBuf *)pp; + if (p->rem < curSize) + curSize = p->rem; + memcpy(data, p->data, curSize); + p->rem -= curSize; + p->data += curSize; + *size = curSize; + return SZ_OK; +} + +typedef struct +{ + CLzmaProb *litProbs; + + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb isRep[kNumStates]; + CLzmaProb isRepG0[kNumStates]; + CLzmaProb isRepG1[kNumStates]; + CLzmaProb isRepG2[kNumStates]; + CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + + CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; + CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + + CLenPriceEnc lenEnc; + CLenPriceEnc repLenEnc; + + LZ_UInt32 reps[LZMA_NUM_REPS]; + LZ_UInt32 state; +} CSaveState; + +typedef struct _CLzmaEnc +{ + IMatchFinder matchFinder; + void *matchFinderObj; + +#ifdef COMPRESS_MF_MT + int mtMode; + CMatchFinderMt matchFinderMt; +#endif + + CMatchFinder matchFinderBase; + +#ifdef COMPRESS_MF_MT + Byte pad[128]; +#endif + + LZ_UInt32 optimumEndIndex; + LZ_UInt32 optimumCurrentIndex; + + LZ_UInt32 longestMatchLength; + LZ_UInt32 numPairs; + LZ_UInt32 numAvail; + COptimal opt[kNumOpts]; + +#ifndef LZMA_LOG_BSR + Byte g_FastPos[1 << kNumLogBits]; +#endif + + LZ_UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; + LZ_UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; + LZ_UInt32 numFastBytes; + LZ_UInt32 additionalOffset; + LZ_UInt32 reps[LZMA_NUM_REPS]; + LZ_UInt32 state; + + LZ_UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; + LZ_UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; + LZ_UInt32 alignPrices[kAlignTableSize]; + LZ_UInt32 alignPriceCount; + + LZ_UInt32 distTableSize; + + unsigned lc, lp, pb; + unsigned lpMask, pbMask; + + CLzmaProb *litProbs; + + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb isRep[kNumStates]; + CLzmaProb isRepG0[kNumStates]; + CLzmaProb isRepG1[kNumStates]; + CLzmaProb isRepG2[kNumStates]; + CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + + CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; + CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + + CLenPriceEnc lenEnc; + CLenPriceEnc repLenEnc; + + unsigned lclp; + + int fastMode; + + CRangeEnc rc; + + int writeEndMark; + LZ_UInt64 nowPos64; + LZ_UInt32 matchPriceCount; + int finished; + int multiThread; + + SRes result; + LZ_UInt32 dictSize; + LZ_UInt32 matchFinderCycles; + + ISeqInStream *inStream; + CSeqInStreamBuf seqBufInStream; + + CSaveState saveState; +} CLzmaEnc; + +void LzmaEnc_SaveState(CLzmaEncHandle pp) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + CSaveState *dest = &p->saveState; + int i; + dest->lenEnc = p->lenEnc; + dest->repLenEnc = p->repLenEnc; + dest->state = p->state; + + for (i = 0; i < kNumStates; i++) + { + memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); + memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); + } + for (i = 0; i < kNumLenToPosStates; i++) + memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); + memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); + memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); + memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); + memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); + memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); + memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); + memcpy(dest->reps, p->reps, sizeof(p->reps)); + memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); +} + +void LzmaEnc_RestoreState(CLzmaEncHandle pp) +{ + CLzmaEnc *dest = (CLzmaEnc *)pp; + const CSaveState *p = &dest->saveState; + int i; + dest->lenEnc = p->lenEnc; + dest->repLenEnc = p->repLenEnc; + dest->state = p->state; + + for (i = 0; i < kNumStates; i++) + { + memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); + memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); + } + for (i = 0; i < kNumLenToPosStates; i++) + memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); + memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); + memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); + memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); + memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); + memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); + memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); + memcpy(dest->reps, p->reps, sizeof(p->reps)); + memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); +} + +SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + CLzmaEncProps props = *props2; + LzmaEncProps_Normalize(&props); + + if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || + props.dictSize > (1 << kDicLogSizeMaxCompress) || props.dictSize > (1 << 30)) + return SZ_ERROR_PARAM; + p->dictSize = props.dictSize; + p->matchFinderCycles = props.mc; + { + unsigned fb = props.fb; + if (fb < 5) + fb = 5; + if (fb > LZMA_MATCH_LEN_MAX) + fb = LZMA_MATCH_LEN_MAX; + p->numFastBytes = fb; + } + p->lc = props.lc; + p->lp = props.lp; + p->pb = props.pb; + p->fastMode = (props.algo == 0); + p->matchFinderBase.btMode = props.btMode; + { + LZ_UInt32 numHashBytes = 4; + if (props.btMode) + { + if (props.numHashBytes < 2) + numHashBytes = 2; + else if (props.numHashBytes < 4) + numHashBytes = props.numHashBytes; + } + p->matchFinderBase.numHashBytes = numHashBytes; + } + + p->matchFinderBase.cutValue = props.mc; + + p->writeEndMark = props.writeEndMark; + +#ifdef COMPRESS_MF_MT + /* + if (newMultiThread != _multiThread) + { + ReleaseMatchFinder(); + _multiThread = newMultiThread; + } + */ + p->multiThread = (props.numThreads > 1); +#endif + + return SZ_OK; +} + +static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; +static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; +static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; +static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + +#define IsCharState(s) ((s) < 7) + +#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) + +#define kInfinityPrice (1 << 30) + +static void RangeEnc_Construct(CRangeEnc *p) +{ + p->outStream = 0; + p->bufBase = 0; +} + +#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) + +#define RC_BUF_SIZE (1 << 16) +static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) +{ + if (p->bufBase == 0) + { + p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); + if (p->bufBase == 0) + return 0; + p->bufLim = p->bufBase + RC_BUF_SIZE; + } + return 1; +} + +static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->bufBase); + p->bufBase = 0; +} + +static void RangeEnc_Init(CRangeEnc *p) +{ + /* Stream.Init(); */ + p->low = 0; + p->range = 0xFFFFFFFF; + p->cacheSize = 1; + p->cache = 0; + + p->buf = p->bufBase; + + p->processed = 0; + p->res = SZ_OK; +} + +static void RangeEnc_FlushStream(CRangeEnc *p) +{ + size_t num; + if (p->res != SZ_OK) + return; + num = p->buf - p->bufBase; + if (num != p->outStream->Write(p->outStream, p->bufBase, num)) + p->res = SZ_ERROR_WRITE; + p->processed += num; + p->buf = p->bufBase; +} + +static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) +{ + if ((LZ_UInt32)p->low < (LZ_UInt32)0xFF000000 || (int)(p->low >> 32) != 0) + { + Byte temp = p->cache; + do + { + Byte *buf = p->buf; + *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); + p->buf = buf; + if (buf == p->bufLim) + RangeEnc_FlushStream(p); + temp = 0xFF; + } + while (--p->cacheSize != 0); + p->cache = (Byte)((LZ_UInt32)p->low >> 24); + } + p->cacheSize++; + p->low = (LZ_UInt32)p->low << 8; +} + +static void RangeEnc_FlushData(CRangeEnc *p) +{ + int i; + for (i = 0; i < 5; i++) + RangeEnc_ShiftLow(p); +} + +static void RangeEnc_EncodeDirectBits(CRangeEnc *p, LZ_UInt32 value, int numBits) +{ + do + { + p->range >>= 1; + p->low += p->range & (0 - ((value >> --numBits) & 1)); + if (p->range < kTopValue) + { + p->range <<= 8; + RangeEnc_ShiftLow(p); + } + } + while (numBits != 0); +} + +static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, LZ_UInt32 symbol) +{ + LZ_UInt32 ttt = *prob; + LZ_UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; + if (symbol == 0) + { + p->range = newBound; + ttt += (kBitModelTotal - ttt) >> kNumMoveBits; + } + else + { + p->low += newBound; + p->range -= newBound; + ttt -= ttt >> kNumMoveBits; + } + *prob = (CLzmaProb)ttt; + if (p->range < kTopValue) + { + p->range <<= 8; + RangeEnc_ShiftLow(p); + } +} + +static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, LZ_UInt32 symbol) +{ + symbol |= 0x100; + do + { + RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); + symbol <<= 1; + } + while (symbol < 0x10000); +} + +static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, LZ_UInt32 symbol, LZ_UInt32 matchByte) +{ + LZ_UInt32 offs = 0x100; + symbol |= 0x100; + do + { + matchByte <<= 1; + RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); + symbol <<= 1; + offs &= ~(matchByte ^ symbol); + } + while (symbol < 0x10000); +} + +void LzmaEnc_InitPriceTables(LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 i; + for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) + { + const int kCyclesBits = kNumBitPriceShiftBits; + LZ_UInt32 w = i; + LZ_UInt32 bitCount = 0; + int j; + for (j = 0; j < kCyclesBits; j++) + { + w = w * w; + bitCount <<= 1; + while (w >= ((LZ_UInt32)1 << 16)) + { + w >>= 1; + bitCount++; + } + } + ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); + } +} + + +#define GET_PRICE(prob, symbol) \ +p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICEa(prob, symbol) \ +ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +static LZ_UInt32 LitEnc_GetPrice(const CLzmaProb *probs, LZ_UInt32 symbol, LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 price = 0; + symbol |= 0x100; + do + { + price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); + symbol <<= 1; + } + while (symbol < 0x10000); + return price; +} + +static LZ_UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, LZ_UInt32 symbol, LZ_UInt32 matchByte, LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 price = 0; + LZ_UInt32 offs = 0x100; + symbol |= 0x100; + do + { + matchByte <<= 1; + price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); + symbol <<= 1; + offs &= ~(matchByte ^ symbol); + } + while (symbol < 0x10000); + return price; +} + + +static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, LZ_UInt32 symbol) +{ + LZ_UInt32 m = 1; + int i; + for (i = numBitLevels; i != 0;) + { + LZ_UInt32 bit; + i--; + bit = (symbol >> i) & 1; + RangeEnc_EncodeBit(rc, probs + m, bit); + m = (m << 1) | bit; + } +} + +static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, LZ_UInt32 symbol) +{ + LZ_UInt32 m = 1; + int i; + for (i = 0; i < numBitLevels; i++) + { + LZ_UInt32 bit = symbol & 1; + RangeEnc_EncodeBit(rc, probs + m, bit); + m = (m << 1) | bit; + symbol >>= 1; + } +} + +static LZ_UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, LZ_UInt32 symbol, LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 price = 0; + symbol |= (1 << numBitLevels); + while (symbol != 1) + { + price += GET_PRICEa(probs[symbol >> 1], symbol & 1); + symbol >>= 1; + } + return price; +} + +static LZ_UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, LZ_UInt32 symbol, LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 price = 0; + LZ_UInt32 m = 1; + int i; + for (i = numBitLevels; i != 0; i--) + { + LZ_UInt32 bit = symbol & 1; + symbol >>= 1; + price += GET_PRICEa(probs[m], bit); + m = (m << 1) | bit; + } + return price; +} + + +static void LenEnc_Init(CLenEnc *p) +{ + unsigned i; + p->choice = p->choice2 = kProbInitValue; + for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) + p->low[i] = kProbInitValue; + for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) + p->mid[i] = kProbInitValue; + for (i = 0; i < kLenNumHighSymbols; i++) + p->high[i] = kProbInitValue; +} + +static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, LZ_UInt32 symbol, LZ_UInt32 posState) +{ + if (symbol < kLenNumLowSymbols) + { + RangeEnc_EncodeBit(rc, &p->choice, 0); + RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); + } + else + { + RangeEnc_EncodeBit(rc, &p->choice, 1); + if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) + { + RangeEnc_EncodeBit(rc, &p->choice2, 0); + RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); + } + else + { + RangeEnc_EncodeBit(rc, &p->choice2, 1); + RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); + } + } +} + +static void LenEnc_SetPrices(CLenEnc *p, LZ_UInt32 posState, LZ_UInt32 numSymbols, LZ_UInt32 *prices, LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 a0 = GET_PRICE_0a(p->choice); + LZ_UInt32 a1 = GET_PRICE_1a(p->choice); + LZ_UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); + LZ_UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); + LZ_UInt32 i = 0; + for (i = 0; i < kLenNumLowSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); + } + for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); + } + for (; i < numSymbols; i++) + prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); +} + +static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, LZ_UInt32 posState, LZ_UInt32 *ProbPrices) +{ + LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); + p->counters[posState] = p->tableSize; +} + +static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, LZ_UInt32 numPosStates, LZ_UInt32 *ProbPrices) +{ + LZ_UInt32 posState; + for (posState = 0; posState < numPosStates; posState++) + LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + +static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, LZ_UInt32 symbol, LZ_UInt32 posState, int updatePrice, LZ_UInt32 *ProbPrices) +{ + LenEnc_Encode(&p->p, rc, symbol, posState); + if (updatePrice) + if (--p->counters[posState] == 0) + LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + + + + +static void MovePos(CLzmaEnc *p, LZ_UInt32 num) +{ +#ifdef SHOW_STAT + ttt += num; + printf("\n MovePos %d", num); +#endif + if (num != 0) + { + p->additionalOffset += num; + p->matchFinder.Skip(p->matchFinderObj, num); + } +} + +static LZ_UInt32 ReadMatchDistances(CLzmaEnc *p, LZ_UInt32 *numDistancePairsRes) +{ + LZ_UInt32 lenRes = 0, numPairs; + p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); + numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); +#ifdef SHOW_STAT + printf("\n i = %d numPairs = %d ", ttt, numPairs / 2); + ttt++; + { + LZ_UInt32 i; + for (i = 0; i < numPairs; i += 2) + printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); + } +#endif + if (numPairs > 0) + { + lenRes = p->matches[numPairs - 2]; + if (lenRes == p->numFastBytes) + { + const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + LZ_UInt32 distance = p->matches[numPairs - 1] + 1; + LZ_UInt32 numAvail = p->numAvail; + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + { + const Byte *pby2 = pby - distance; + for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); + } + } + } + p->additionalOffset++; + *numDistancePairsRes = numPairs; + return lenRes; +} + + +#define MakeAsChar(p) (p)->backPrev = (LZ_UInt32)(-1); (p)->prev1IsChar = False; +#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; +#define IsShortRep(p) ((p)->backPrev == 0) + +static LZ_UInt32 GetRepLen1Price(CLzmaEnc *p, LZ_UInt32 state, LZ_UInt32 posState) +{ + return + GET_PRICE_0(p->isRepG0[state]) + + GET_PRICE_0(p->isRep0Long[state][posState]); +} + +static LZ_UInt32 GetPureRepPrice(CLzmaEnc *p, LZ_UInt32 repIndex, LZ_UInt32 state, LZ_UInt32 posState) +{ + LZ_UInt32 price; + if (repIndex == 0) + { + price = GET_PRICE_0(p->isRepG0[state]); + price += GET_PRICE_1(p->isRep0Long[state][posState]); + } + else + { + price = GET_PRICE_1(p->isRepG0[state]); + if (repIndex == 1) + price += GET_PRICE_0(p->isRepG1[state]); + else + { + price += GET_PRICE_1(p->isRepG1[state]); + price += GET_PRICE(p->isRepG2[state], repIndex - 2); + } + } + return price; +} + +static LZ_UInt32 GetRepPrice(CLzmaEnc *p, LZ_UInt32 repIndex, LZ_UInt32 len, LZ_UInt32 state, LZ_UInt32 posState) +{ + return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + + GetPureRepPrice(p, repIndex, state, posState); +} + +static LZ_UInt32 Backward(CLzmaEnc *p, LZ_UInt32 *backRes, LZ_UInt32 cur) +{ + LZ_UInt32 posMem = p->opt[cur].posPrev; + LZ_UInt32 backMem = p->opt[cur].backPrev; + p->optimumEndIndex = cur; + do + { + if (p->opt[cur].prev1IsChar) + { + MakeAsChar(&p->opt[posMem]) + p->opt[posMem].posPrev = posMem - 1; + if (p->opt[cur].prev2) + { + p->opt[posMem - 1].prev1IsChar = False; + p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; + p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; + } + } + { + LZ_UInt32 posPrev = posMem; + LZ_UInt32 backCur = backMem; + + backMem = p->opt[posPrev].backPrev; + posMem = p->opt[posPrev].posPrev; + + p->opt[posPrev].backPrev = backCur; + p->opt[posPrev].posPrev = cur; + cur = posPrev; + } + } + while (cur != 0); + *backRes = p->opt[0].backPrev; + p->optimumCurrentIndex = p->opt[0].posPrev; + return p->optimumCurrentIndex; +} + +#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) + +static LZ_UInt32 GetOptimum(CLzmaEnc *p, LZ_UInt32 position, LZ_UInt32 *backRes) +{ + LZ_UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; + LZ_UInt32 matchPrice, repMatchPrice, normalMatchPrice; + LZ_UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; + LZ_UInt32 *matches; + const Byte *data; + Byte curByte, matchByte; + if (p->optimumEndIndex != p->optimumCurrentIndex) + { + const COptimal *opt = &p->opt[p->optimumCurrentIndex]; + LZ_UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; + *backRes = opt->backPrev; + p->optimumCurrentIndex = opt->posPrev; + return lenRes; + } + p->optimumCurrentIndex = p->optimumEndIndex = 0; + + if (p->additionalOffset == 0) + mainLen = ReadMatchDistances(p, &numPairs); + else + { + mainLen = p->longestMatchLength; + numPairs = p->numPairs; + } + + numAvail = p->numAvail; + if (numAvail < 2) + { + *backRes = (LZ_UInt32)(-1); + return 1; + } + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + repMaxIndex = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + LZ_UInt32 lenTest; + const Byte *data2; + reps[i] = p->reps[i]; + data2 = data - (reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + { + repLens[i] = 0; + continue; + } + for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); + repLens[i] = lenTest; + if (lenTest > repLens[repMaxIndex]) + repMaxIndex = i; + } + if (repLens[repMaxIndex] >= p->numFastBytes) + { + LZ_UInt32 lenRes; + *backRes = repMaxIndex; + lenRes = repLens[repMaxIndex]; + MovePos(p, lenRes - 1); + return lenRes; + } + + matches = p->matches; + if (mainLen >= p->numFastBytes) + { + *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; + MovePos(p, mainLen - 1); + return mainLen; + } + curByte = *data; + matchByte = *(data - (reps[0] + 1)); + + if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) + { + *backRes = (LZ_UInt32)-1; + return 1; + } + + p->opt[0].state = (CState)p->state; + + posState = (position & p->pbMask); + + { + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + + (!IsCharState(p->state) ? + LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : + LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + } + + MakeAsChar(&p->opt[1]); + + matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); + repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); + + if (matchByte == curByte) + { + LZ_UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); + if (shortRepPrice < p->opt[1].price) + { + p->opt[1].price = shortRepPrice; + MakeAsShortRep(&p->opt[1]); + } + } + lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); + + if (lenEnd < 2) + { + *backRes = p->opt[1].backPrev; + return 1; + } + + p->opt[1].posPrev = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + p->opt[0].backs[i] = reps[i]; + + len = lenEnd; + do + p->opt[len--].price = kInfinityPrice; + while (len >= 2); + + for (i = 0; i < LZMA_NUM_REPS; i++) + { + LZ_UInt32 repLen = repLens[i]; + LZ_UInt32 price; + if (repLen < 2) + continue; + price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); + do + { + LZ_UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; + COptimal *opt = &p->opt[repLen]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = 0; + opt->backPrev = i; + opt->prev1IsChar = False; + } + } + while (--repLen >= 2); + } + + normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); + + len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); + if (len <= mainLen) + { + LZ_UInt32 offs = 0; + while (len > matches[offs]) + offs += 2; + for (; ; len++) + { + COptimal *opt; + LZ_UInt32 distance = matches[offs + 1]; + + LZ_UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; + LZ_UInt32 lenToPosState = GetLenToPosState(len); + if (distance < kNumFullDistances) + curAndLenPrice += p->distancesPrices[lenToPosState][distance]; + else + { + LZ_UInt32 slot; + GetPosSlot2(distance, slot); + curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; + } + opt = &p->opt[len]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = 0; + opt->backPrev = distance + LZMA_NUM_REPS; + opt->prev1IsChar = False; + } + if (len == matches[offs]) + { + offs += 2; + if (offs == numPairs) + break; + } + } + } + + cur = 0; + +#ifdef SHOW_STAT2 + if (position >= 0) + { + unsigned i; + printf("\n pos = %4X", position); + for (i = cur; i <= lenEnd; i++) + printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); + } +#endif + + for (;;) + { + LZ_UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; + LZ_UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; + int nextIsChar; + Byte curByte, matchByte; + const Byte *data; + COptimal *curOpt; + COptimal *nextOpt; + + cur++; + if (cur == lenEnd) + return Backward(p, backRes, cur); + + newLen = ReadMatchDistances(p, &numPairs); + if (newLen >= p->numFastBytes) + { + p->numPairs = numPairs; + p->longestMatchLength = newLen; + return Backward(p, backRes, cur); + } + position++; + curOpt = &p->opt[cur]; + posPrev = curOpt->posPrev; + if (curOpt->prev1IsChar) + { + posPrev--; + if (curOpt->prev2) + { + state = p->opt[curOpt->posPrev2].state; + if (curOpt->backPrev2 < LZMA_NUM_REPS) + state = kRepNextStates[state]; + else + state = kMatchNextStates[state]; + } + else + state = p->opt[posPrev].state; + state = kLiteralNextStates[state]; + } + else + state = p->opt[posPrev].state; + if (posPrev == cur - 1) + { + if (IsShortRep(curOpt)) + state = kShortRepNextStates[state]; + else + state = kLiteralNextStates[state]; + } + else + { + LZ_UInt32 pos; + const COptimal *prevOpt; + if (curOpt->prev1IsChar && curOpt->prev2) + { + posPrev = curOpt->posPrev2; + pos = curOpt->backPrev2; + state = kRepNextStates[state]; + } + else + { + pos = curOpt->backPrev; + if (pos < LZMA_NUM_REPS) + state = kRepNextStates[state]; + else + state = kMatchNextStates[state]; + } + prevOpt = &p->opt[posPrev]; + if (pos < LZMA_NUM_REPS) + { + LZ_UInt32 i; + reps[0] = prevOpt->backs[pos]; + for (i = 1; i <= pos; i++) + reps[i] = prevOpt->backs[i - 1]; + for (; i < LZMA_NUM_REPS; i++) + reps[i] = prevOpt->backs[i]; + } + else + { + LZ_UInt32 i; + reps[0] = (pos - LZMA_NUM_REPS); + for (i = 1; i < LZMA_NUM_REPS; i++) + reps[i] = prevOpt->backs[i - 1]; + } + } + curOpt->state = (CState)state; + + curOpt->backs[0] = reps[0]; + curOpt->backs[1] = reps[1]; + curOpt->backs[2] = reps[2]; + curOpt->backs[3] = reps[3]; + + curPrice = curOpt->price; + nextIsChar = False; + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + curByte = *data; + matchByte = *(data - (reps[0] + 1)); + + posState = (position & p->pbMask); + + curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); + { + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + curAnd1Price += + (!IsCharState(state) ? + LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : + LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + } + + nextOpt = &p->opt[cur + 1]; + + if (curAnd1Price < nextOpt->price) + { + nextOpt->price = curAnd1Price; + nextOpt->posPrev = cur; + MakeAsChar(nextOpt); + nextIsChar = True; + } + + matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); + repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); + + if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) + { + LZ_UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); + if (shortRepPrice <= nextOpt->price) + { + nextOpt->price = shortRepPrice; + nextOpt->posPrev = cur; + MakeAsShortRep(nextOpt); + nextIsChar = True; + } + } + numAvailFull = p->numAvail; + { + LZ_UInt32 temp = kNumOpts - 1 - cur; + if (temp < numAvailFull) + numAvailFull = temp; + } + + if (numAvailFull < 2) + continue; + numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); + + if (!nextIsChar && matchByte != curByte) /* speed optimization */ + { + /* try Literal + rep0 */ + LZ_UInt32 temp; + LZ_UInt32 lenTest2; + const Byte *data2 = data - (reps[0] + 1); + LZ_UInt32 limit = p->numFastBytes + 1; + if (limit > numAvailFull) + limit = numAvailFull; + + for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); + lenTest2 = temp - 1; + if (lenTest2 >= 2) + { + LZ_UInt32 state2 = kLiteralNextStates[state]; + LZ_UInt32 posStateNext = (position + 1) & p->pbMask; + LZ_UInt32 nextRepMatchPrice = curAnd1Price + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + /* for (; lenTest2 >= 2; lenTest2--) */ + { + LZ_UInt32 curAndLenPrice; + COptimal *opt; + LZ_UInt32 offset = cur + 1 + lenTest2; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = False; + } + } + } + } + + startLen = 2; /* speed optimization */ + { + LZ_UInt32 repIndex; + for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) + { + LZ_UInt32 lenTest; + LZ_UInt32 lenTestTemp; + LZ_UInt32 price; + const Byte *data2 = data - (reps[repIndex] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); + while (lenEnd < cur + lenTest) + p->opt[++lenEnd].price = kInfinityPrice; + lenTestTemp = lenTest; + price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); + do + { + LZ_UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; + COptimal *opt = &p->opt[cur + lenTest]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur; + opt->backPrev = repIndex; + opt->prev1IsChar = False; + } + } + while (--lenTest >= 2); + lenTest = lenTestTemp; + + if (repIndex == 0) + startLen = lenTest + 1; + + /* if (_maxMode) */ + { + LZ_UInt32 lenTest2 = lenTest + 1; + LZ_UInt32 limit = lenTest2 + p->numFastBytes; + LZ_UInt32 nextRepMatchPrice; + if (limit > numAvailFull) + limit = numAvailFull; + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + LZ_UInt32 state2 = kRepNextStates[state]; + LZ_UInt32 posStateNext = (position + lenTest) & p->pbMask; + LZ_UInt32 curAndLenCharPrice = + price + p->repLenEnc.prices[posState][lenTest - 2] + + GET_PRICE_0(p->isMatch[state2][posStateNext]) + + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), + data[lenTest], data2[lenTest], p->ProbPrices); + state2 = kLiteralNextStates[state2]; + posStateNext = (position + lenTest + 1) & p->pbMask; + nextRepMatchPrice = curAndLenCharPrice + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + + /* for (; lenTest2 >= 2; lenTest2--) */ + { + LZ_UInt32 curAndLenPrice; + COptimal *opt; + LZ_UInt32 offset = cur + lenTest + 1 + lenTest2; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + lenTest + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = True; + opt->posPrev2 = cur; + opt->backPrev2 = repIndex; + } + } + } + } + } + } + /* for (LZ_UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ + if (newLen > numAvail) + { + newLen = numAvail; + for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); + matches[numPairs] = newLen; + numPairs += 2; + } + if (newLen >= startLen) + { + LZ_UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); + LZ_UInt32 offs, curBack, posSlot; + LZ_UInt32 lenTest; + while (lenEnd < cur + newLen) + p->opt[++lenEnd].price = kInfinityPrice; + + offs = 0; + while (startLen > matches[offs]) + offs += 2; + curBack = matches[offs + 1]; + GetPosSlot2(curBack, posSlot); + for (lenTest = /*2*/ startLen; ; lenTest++) + { + LZ_UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; + LZ_UInt32 lenToPosState = GetLenToPosState(lenTest); + COptimal *opt; + if (curBack < kNumFullDistances) + curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; + else + curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; + + opt = &p->opt[cur + lenTest]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur; + opt->backPrev = curBack + LZMA_NUM_REPS; + opt->prev1IsChar = False; + } + + if (/*_maxMode && */lenTest == matches[offs]) + { + /* Try Match + Literal + Rep0 */ + const Byte *data2 = data - (curBack + 1); + LZ_UInt32 lenTest2 = lenTest + 1; + LZ_UInt32 limit = lenTest2 + p->numFastBytes; + LZ_UInt32 nextRepMatchPrice; + if (limit > numAvailFull) + limit = numAvailFull; + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + LZ_UInt32 state2 = kMatchNextStates[state]; + LZ_UInt32 posStateNext = (position + lenTest) & p->pbMask; + LZ_UInt32 curAndLenCharPrice = curAndLenPrice + + GET_PRICE_0(p->isMatch[state2][posStateNext]) + + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), + data[lenTest], data2[lenTest], p->ProbPrices); + state2 = kLiteralNextStates[state2]; + posStateNext = (posStateNext + 1) & p->pbMask; + nextRepMatchPrice = curAndLenCharPrice + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + + /* for (; lenTest2 >= 2; lenTest2--) */ + { + LZ_UInt32 offset = cur + lenTest + 1 + lenTest2; + LZ_UInt32 curAndLenPrice; + COptimal *opt; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + lenTest + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = True; + opt->posPrev2 = cur; + opt->backPrev2 = curBack + LZMA_NUM_REPS; + } + } + } + offs += 2; + if (offs == numPairs) + break; + curBack = matches[offs + 1]; + if (curBack >= kNumFullDistances) + GetPosSlot2(curBack, posSlot); + } + } + } + } +} + +#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) + +static LZ_UInt32 GetOptimumFast(CLzmaEnc *p, LZ_UInt32 *backRes) +{ + LZ_UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; + const Byte *data; + const LZ_UInt32 *matches; + + if (p->additionalOffset == 0) + mainLen = ReadMatchDistances(p, &numPairs); + else + { + mainLen = p->longestMatchLength; + numPairs = p->numPairs; + } + + numAvail = p->numAvail; + *backRes = (LZ_UInt32)-1; + if (numAvail < 2) + return 1; + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + + repLen = repIndex = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + LZ_UInt32 len; + const Byte *data2 = data - (p->reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + for (len = 2; len < numAvail && data[len] == data2[len]; len++); + if (len >= p->numFastBytes) + { + *backRes = i; + MovePos(p, len - 1); + return len; + } + if (len > repLen) + { + repIndex = i; + repLen = len; + } + } + + matches = p->matches; + if (mainLen >= p->numFastBytes) + { + *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; + MovePos(p, mainLen - 1); + return mainLen; + } + + mainDist = 0; /* for GCC */ + if (mainLen >= 2) + { + mainDist = matches[numPairs - 1]; + while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) + { + if (!ChangePair(matches[numPairs - 3], mainDist)) + break; + numPairs -= 2; + mainLen = matches[numPairs - 2]; + mainDist = matches[numPairs - 1]; + } + if (mainLen == 2 && mainDist >= 0x80) + mainLen = 1; + } + + if (repLen >= 2 && ( + (repLen + 1 >= mainLen) || + (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || + (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) + { + *backRes = repIndex; + MovePos(p, repLen - 1); + return repLen; + } + + if (mainLen < 2 || numAvail <= 2) + return 1; + + p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); + if (p->longestMatchLength >= 2) + { + LZ_UInt32 newDistance = matches[p->numPairs - 1]; + if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || + (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || + (p->longestMatchLength > mainLen + 1) || + (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) + return 1; + } + + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + LZ_UInt32 len, limit; + const Byte *data2 = data - (p->reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + limit = mainLen - 1; + for (len = 2; len < limit && data[len] == data2[len]; len++); + if (len >= limit) + return 1; + } + *backRes = mainDist + LZMA_NUM_REPS; + MovePos(p, mainLen - 2); + return mainLen; +} + +static void WriteEndMarker(CLzmaEnc *p, LZ_UInt32 posState) +{ + LZ_UInt32 len; + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + p->state = kMatchNextStates[p->state]; + len = LZMA_MATCH_LEN_MIN; + LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); + RangeEnc_EncodeDirectBits(&p->rc, (((LZ_UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); + RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); +} + +static SRes CheckErrors(CLzmaEnc *p) +{ + if (p->result != SZ_OK) + return p->result; + if (p->rc.res != SZ_OK) + p->result = SZ_ERROR_WRITE; + if (p->matchFinderBase.result != SZ_OK) + p->result = SZ_ERROR_READ; + if (p->result != SZ_OK) + p->finished = True; + return p->result; +} + +static SRes Flush(CLzmaEnc *p, LZ_UInt32 nowPos) +{ + /* ReleaseMFStream(); */ + p->finished = True; + if (p->writeEndMark) + WriteEndMarker(p, nowPos & p->pbMask); + RangeEnc_FlushData(&p->rc); + RangeEnc_FlushStream(&p->rc); + return CheckErrors(p); +} + +static void FillAlignPrices(CLzmaEnc *p) +{ + LZ_UInt32 i; + for (i = 0; i < kAlignTableSize; i++) + p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); + p->alignPriceCount = 0; +} + +static void FillDistancesPrices(CLzmaEnc *p) +{ + LZ_UInt32 tempPrices[kNumFullDistances]; + LZ_UInt32 i, lenToPosState; + for (i = kStartPosModelIndex; i < kNumFullDistances; i++) + { + LZ_UInt32 posSlot = GetPosSlot1(i); + LZ_UInt32 footerBits = ((posSlot >> 1) - 1); + LZ_UInt32 base = ((2 | (posSlot & 1)) << footerBits); + tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); + } + + for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) + { + LZ_UInt32 posSlot; + const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; + LZ_UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; + for (posSlot = 0; posSlot < p->distTableSize; posSlot++) + posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); + for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) + posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); + + { + LZ_UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; + LZ_UInt32 i; + for (i = 0; i < kStartPosModelIndex; i++) + distancesPrices[i] = posSlotPrices[i]; + for (; i < kNumFullDistances; i++) + distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; + } + } + p->matchPriceCount = 0; +} + +void LzmaEnc_Construct(CLzmaEnc *p) +{ + RangeEnc_Construct(&p->rc); + MatchFinder_Construct(&p->matchFinderBase); +#ifdef COMPRESS_MF_MT + MatchFinderMt_Construct(&p->matchFinderMt); + p->matchFinderMt.MatchFinder = &p->matchFinderBase; +#endif + + { + CLzmaEncProps props; + LzmaEncProps_Init(&props); + LzmaEnc_SetProps(p, &props); + } + +#ifndef LZMA_LOG_BSR + LzmaEnc_FastPosInit(p->g_FastPos); +#endif + + LzmaEnc_InitPriceTables(p->ProbPrices); + p->litProbs = 0; + p->saveState.litProbs = 0; +} + +CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) +{ + void *p; + p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); + if (p != 0) + LzmaEnc_Construct((CLzmaEnc *)p); + return p; +} + +void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->litProbs); + alloc->Free(alloc, p->saveState.litProbs); + p->litProbs = 0; + p->saveState.litProbs = 0; +} + +void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) +{ +#ifdef COMPRESS_MF_MT + MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); +#endif + MatchFinder_Free(&p->matchFinderBase, allocBig); + LzmaEnc_FreeLits(p, alloc); + RangeEnc_Free(&p->rc, alloc); +} + +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); + alloc->Free(alloc, p); +} + +static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, int useLimits, LZ_UInt32 maxPackSize, LZ_UInt32 maxUnpackSize) +{ + LZ_UInt32 nowPos32, startPos32; + if (p->inStream != 0) + { + p->matchFinderBase.stream = p->inStream; + p->matchFinder.Init(p->matchFinderObj); + p->inStream = 0; + } + + if (p->finished) + return p->result; + RINOK(CheckErrors(p)); + + nowPos32 = (LZ_UInt32)p->nowPos64; + startPos32 = nowPos32; + + if (p->nowPos64 == 0) + { + LZ_UInt32 numPairs; + Byte curByte; + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) + return Flush(p, nowPos32); + ReadMatchDistances(p, &numPairs); + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); + p->state = kLiteralNextStates[p->state]; + curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); + LitEnc_Encode(&p->rc, p->litProbs, curByte); + p->additionalOffset--; + nowPos32++; + } + + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) + for (;;) + { + LZ_UInt32 pos, len, posState; + + if (p->fastMode) + len = GetOptimumFast(p, &pos); + else + len = GetOptimum(p, nowPos32, &pos); + +#ifdef SHOW_STAT2 + printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); +#endif + + posState = nowPos32 & p->pbMask; + if (len == 1 && pos == (LZ_UInt32)-1) + { + Byte curByte; + CLzmaProb *probs; + const Byte *data; + + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; + curByte = *data; + probs = LIT_PROBS(nowPos32, *(data - 1)); + if (IsCharState(p->state)) + LitEnc_Encode(&p->rc, probs, curByte); + else + LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); + p->state = kLiteralNextStates[p->state]; + } + else + { + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); + if (pos < LZMA_NUM_REPS) + { + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); + if (pos == 0) + { + RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); + RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); + } + else + { + LZ_UInt32 distance = p->reps[pos]; + RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); + if (pos == 1) + RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); + else + { + RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); + RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); + if (pos == 3) + p->reps[3] = p->reps[2]; + p->reps[2] = p->reps[1]; + } + p->reps[1] = p->reps[0]; + p->reps[0] = distance; + } + if (len == 1) + p->state = kShortRepNextStates[p->state]; + else + { + LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + p->state = kRepNextStates[p->state]; + } + } + else + { + LZ_UInt32 posSlot; + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + p->state = kMatchNextStates[p->state]; + LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + pos -= LZMA_NUM_REPS; + GetPosSlot(pos, posSlot); + RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); + + if (posSlot >= kStartPosModelIndex) + { + LZ_UInt32 footerBits = ((posSlot >> 1) - 1); + LZ_UInt32 base = ((2 | (posSlot & 1)) << footerBits); + LZ_UInt32 posReduced = pos - base; + + if (posSlot < kEndPosModelIndex) + RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); + else + { + RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); + RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); + p->alignPriceCount++; + } + } + p->reps[3] = p->reps[2]; + p->reps[2] = p->reps[1]; + p->reps[1] = p->reps[0]; + p->reps[0] = pos; + p->matchPriceCount++; + } + } + p->additionalOffset -= len; + nowPos32 += len; + if (p->additionalOffset == 0) + { + LZ_UInt32 processed; + if (!p->fastMode) + { + if (p->matchPriceCount >= (1 << 7)) + FillDistancesPrices(p); + if (p->alignPriceCount >= kAlignTableSize) + FillAlignPrices(p); + } + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) + break; + processed = nowPos32 - startPos32; + if (useLimits) + { + if (processed + kNumOpts + 300 >= maxUnpackSize || + RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) + break; + } + else if (processed >= (1 << 15)) + { + p->nowPos64 += nowPos32 - startPos32; + return CheckErrors(p); + } + } + } + p->nowPos64 += nowPos32 - startPos32; + return Flush(p, nowPos32); +} + +#define kBigHashDicLimit ((LZ_UInt32)1 << 24) + +static SRes LzmaEnc_Alloc(CLzmaEnc *p, LZ_UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + LZ_UInt32 beforeSize = kNumOpts; + //int btMode; + if (!RangeEnc_Alloc(&p->rc, alloc)) + return SZ_ERROR_MEM; + //btMode = (p->matchFinderBase.btMode != 0); +#ifdef COMPRESS_MF_MT + p->mtMode = (p->multiThread && !p->fastMode && btMode); +#endif + + { + unsigned lclp = p->lc + p->lp; + if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) + { + LzmaEnc_FreeLits(p, alloc); + p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); + p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); + if (p->litProbs == 0 || p->saveState.litProbs == 0) + { + LzmaEnc_FreeLits(p, alloc); + return SZ_ERROR_MEM; + } + p->lclp = lclp; + } + } + + p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); + + if (beforeSize + p->dictSize < keepWindowSize) + beforeSize = keepWindowSize - p->dictSize; + +#ifdef COMPRESS_MF_MT + if (p->mtMode) + { + RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); + p->matchFinderObj = &p->matchFinderMt; + MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); + } + else +#endif + { + if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) + return SZ_ERROR_MEM; + p->matchFinderObj = &p->matchFinderBase; + MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); + } + return SZ_OK; +} + +void LzmaEnc_Init(CLzmaEnc *p) +{ + LZ_UInt32 i; + p->state = 0; + for (i = 0 ; i < LZMA_NUM_REPS; i++) + p->reps[i] = 0; + + RangeEnc_Init(&p->rc); + + + for (i = 0; i < kNumStates; i++) + { + LZ_UInt32 j; + for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) + { + p->isMatch[i][j] = kProbInitValue; + p->isRep0Long[i][j] = kProbInitValue; + } + p->isRep[i] = kProbInitValue; + p->isRepG0[i] = kProbInitValue; + p->isRepG1[i] = kProbInitValue; + p->isRepG2[i] = kProbInitValue; + } + + { + LZ_UInt32 num = 0x300 << (p->lp + p->lc); + for (i = 0; i < num; i++) + p->litProbs[i] = kProbInitValue; + } + + { + for (i = 0; i < kNumLenToPosStates; i++) + { + CLzmaProb *probs = p->posSlotEncoder[i]; + LZ_UInt32 j; + for (j = 0; j < (1 << kNumPosSlotBits); j++) + probs[j] = kProbInitValue; + } + } + { + for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) + p->posEncoders[i] = kProbInitValue; + } + + LenEnc_Init(&p->lenEnc.p); + LenEnc_Init(&p->repLenEnc.p); + + for (i = 0; i < (1 << kNumAlignBits); i++) + p->posAlignEncoder[i] = kProbInitValue; + + p->optimumEndIndex = 0; + p->optimumCurrentIndex = 0; + p->additionalOffset = 0; + + p->pbMask = (1 << p->pb) - 1; + p->lpMask = (1 << p->lp) - 1; +} + +void LzmaEnc_InitPrices(CLzmaEnc *p) +{ + if (!p->fastMode) + { + FillDistancesPrices(p); + FillAlignPrices(p); + } + + p->lenEnc.tableSize = + p->repLenEnc.tableSize = + p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; + LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); + LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); +} + +static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, LZ_UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + LZ_UInt32 i; + for (i = 0; i < (LZ_UInt32)kDicLogSizeMaxCompress; i++) + if (p->dictSize <= ((LZ_UInt32)1 << i)) + break; + p->distTableSize = i * 2; + + p->finished = False; + p->result = SZ_OK; + RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); + LzmaEnc_Init(p); + LzmaEnc_InitPrices(p); + p->nowPos64 = 0; + return SZ_OK; +} + +static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqInStream *inStream, ISeqOutStream *outStream, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + p->inStream = inStream; + p->rc.outStream = outStream; + return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); +} + +SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, + ISeqInStream *inStream, LZ_UInt32 keepWindowSize, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + p->inStream = inStream; + return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); +} + +static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) +{ + p->seqBufInStream.funcTable.Read = MyRead; + p->seqBufInStream.data = src; + p->seqBufInStream.rem = srcLen; +} + +SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, + LZ_UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + LzmaEnc_SetInputBuf(p, src, srcLen); + p->inStream = &p->seqBufInStream.funcTable; + return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); +} + +void LzmaEnc_Finish(CLzmaEncHandle pp) +{ +#ifdef COMPRESS_MF_MT + CLzmaEnc *p = (CLzmaEnc *)pp; + if (p->mtMode) + MatchFinderMt_ReleaseStream(&p->matchFinderMt); +#else + return; //pp = pp; +#endif +} + +typedef struct _CSeqOutStreamBuf +{ + ISeqOutStream funcTable; + Byte *data; + SizeT rem; + int overflow; +} CSeqOutStreamBuf; + +static size_t MyWrite(void *pp, const void *data, size_t size) +{ + CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; + if (p->rem < size) + { + size = p->rem; + p->overflow = True; + } + memcpy(p->data, data, size); + p->rem -= size; + p->data += size; + return size; +} + + +LZ_UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) +{ + const CLzmaEnc *p = (CLzmaEnc *)pp; + return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); +} + +const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) +{ + const CLzmaEnc *p = (CLzmaEnc *)pp; + return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; +} + +SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, int reInit, + Byte *dest, size_t *destLen, LZ_UInt32 desiredPackSize, LZ_UInt32 *unpackSize) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + LZ_UInt64 nowPos64; + SRes res; + CSeqOutStreamBuf outStream; + + outStream.funcTable.Write = MyWrite; + outStream.data = dest; + outStream.rem = *destLen; + outStream.overflow = False; + + p->writeEndMark = False; + p->finished = False; + p->result = SZ_OK; + + if (reInit) + LzmaEnc_Init(p); + LzmaEnc_InitPrices(p); + nowPos64 = p->nowPos64; + RangeEnc_Init(&p->rc); + p->rc.outStream = &outStream.funcTable; + + res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); + + *unpackSize = (LZ_UInt32)(p->nowPos64 - nowPos64); + *destLen -= outStream.rem; + if (outStream.overflow) + return SZ_ERROR_OUTPUT_EOF; + + return res; +} + +SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, + ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + SRes res = SZ_OK; + +#ifdef COMPRESS_MF_MT + Byte allocaDummy[0x300]; + int i = 0; + for (i = 0; i < 16; i++) + allocaDummy[i] = (Byte)i; +#endif + + RINOK(LzmaEnc_Prepare(pp, inStream, outStream, alloc, allocBig)); + + for (;;) + { + res = LzmaEnc_CodeOneBlock(p, False, 0, 0); + if (res != SZ_OK || p->finished != 0) + break; + if (progress != 0) + { + res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); + if (res != SZ_OK) + { + res = SZ_ERROR_PROGRESS; + break; + } + } + } + LzmaEnc_Finish(pp); + return res; +} + +SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + int i; + LZ_UInt32 dictSize = p->dictSize; + if (*size < LZMA_PROPS_SIZE) + return SZ_ERROR_PARAM; + *size = LZMA_PROPS_SIZE; + props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); + + for (i = 11; i <= 30; i++) + { + if (dictSize <= ((LZ_UInt32)2 << i)) + { + dictSize = (2 << i); + break; + } + if (dictSize <= ((LZ_UInt32)3 << i)) + { + dictSize = (3 << i); + break; + } + } + + for (i = 0; i < 4; i++) + props[1 + i] = (Byte)(dictSize >> (8 * i)); + return SZ_OK; +} + +SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + SRes res; + CLzmaEnc *p = (CLzmaEnc *)pp; + + CSeqOutStreamBuf outStream; + + LzmaEnc_SetInputBuf(p, src, srcLen); + + outStream.funcTable.Write = MyWrite; + outStream.data = dest; + outStream.rem = *destLen; + outStream.overflow = False; + + p->writeEndMark = writeEndMark; + res = LzmaEnc_Encode(pp, &outStream.funcTable, &p->seqBufInStream.funcTable, + progress, alloc, allocBig); + + *destLen -= outStream.rem; + if (outStream.overflow) + return SZ_ERROR_OUTPUT_EOF; + return res; +} + +SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) +{ + CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); + SRes res; + if (p == 0) + return SZ_ERROR_MEM; + + res = LzmaEnc_SetProps(p, props); + if (res == SZ_OK) + { + res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); + if (res == SZ_OK) + res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, + writeEndMark, progress, alloc, allocBig); + } + + LzmaEnc_Destroy(p, alloc, allocBig); + return res; +} diff --git a/.svn/pristine/50/5084f290d9c555171d7b3b6720f74d2f90cf4afc.svn-base b/.svn/pristine/50/5084f290d9c555171d7b3b6720f74d2f90cf4afc.svn-base new file mode 100644 index 0000000..ed11c1a --- /dev/null +++ b/.svn/pristine/50/5084f290d9c555171d7b3b6720f74d2f90cf4afc.svn-base @@ -0,0 +1,3455 @@ +/* png.h - header file for PNG reference library + * + * libpng version 1.2.8 - December 3, 2004 + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.2.8 - December 3, 2004: Glenn + * See also "Contributing Authors", below. + * + * Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + * 1.0.8rc1 1 10008 2.1.0.8rc1 + * 1.0.8 1 10008 2.1.0.8 + * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + * 1.0.9rc1 1 10009 2.1.0.9rc1 + * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + * 1.0.9rc2 1 10009 2.1.0.9rc2 + * 1.0.9 1 10009 2.1.0.9 + * 1.0.10beta1 1 10010 2.1.0.10beta1 + * 1.0.10rc1 1 10010 2.1.0.10rc1 + * 1.0.10 1 10010 2.1.0.10 + * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + * 1.0.11rc1 1 10011 2.1.0.11rc1 + * 1.0.11 1 10011 2.1.0.11 + * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + * 1.0.12rc1 2 10012 2.1.0.12rc1 + * 1.0.12 2 10012 2.1.0.12 + * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) + * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + * 1.2.0rc1 3 10200 3.1.2.0rc1 + * 1.2.0 3 10200 3.1.2.0 + * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 + * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + * 1.2.1 3 10201 3.1.2.1 + * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + * 1.0.13 10 10013 10.so.0.1.0.13 + * 1.2.2 12 10202 12.so.0.1.2.2 + * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + * 1.2.3 12 10203 12.so.0.1.2.3 + * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 + * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + * 1.0.14 10 10014 10.so.0.1.0.14 + * 1.2.4 13 10204 12.so.0.1.2.4 + * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 + * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 + * 1.0.15 10 10015 10.so.0.1.0.15 + * 1.2.5 13 10205 12.so.0.1.2.5 + * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + * 1.0.16 10 10016 10.so.0.1.0.16 + * 1.2.6 13 10206 12.so.0.1.2.6 + * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + * 1.0.17rc1 10 10017 12.so.0.1.0.17rc1 + * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + * 1.0.17 10 10017 12.so.0.1.0.17 + * 1.2.7 13 10207 12.so.0.1.2.7 + * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + * 1.0.18rc1-5 10 10018 12.so.0.1.0.18rc1-5 + * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + * 1.0.18 10 10018 12.so.0.1.0.18 + * 1.2.8 13 10208 12.so.0.1.2.8 + * + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO Specification, + * +#endif +#include "zlib.h" +#endif + +/* include all user configurable info, including optional assembler routines */ +#include "pngconf.h" + + + +/* Memory block identification */ +#define _FREE_BLOCK 0 +#define _NORMAL_BLOCK 1 +#define _CRT_BLOCK 2 +#define _IGNORE_BLOCK 3 +#define _CLIENT_BLOCK 4 +#define _MAX_BLOCKS 5 + + +#ifdef ZZZ_DEBUG + +void * _malloc_dbg_trace(int len, int type, char * file, int line); + +#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define realloc(p, s) _realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define _recalloc(p, c, s) _recalloc_dbg(p, c, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define _expand(p, s) _expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__) +#define free(p) _free_dbg(p, _NORMAL_BLOCK) +#define _strdup(s) _strdup_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) + + +#define zalloc(s) _zalloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) +#else +//#define zalloc(s) _zalloc(s) +#endif + + +/* + * Added at libpng-1.2.8 */ +/* Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#if defined(PNG_USER_PRIVATEBUILD) +# define PNG_LIBPNG_BUILD_TYPE PNG_LIBPNG_BUILD_BASE_TYPE | \ + PNG_LIBPNG_BUILD_PRIVATE +#else +# if defined(PNG_LIBPNG_SPECIALBUILD) +# define PNG_LIBPNG_BUILD_TYPE PNG_LIBPNG_BUILD_BASE_TYPE | \ + PNG_LIBPNG_BUILD_SPECIAL +# else +# define PNG_LIBPNG_BUILD_TYPE PNG_LIBPNG_BUILD_BASE_TYPE +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* This file is arranged in several sections. The first section contains + * structure and type definitions. The second section contains the external + * library functions, while the third has the internal library functions, + * which applications aren't expected to use directly. + */ + +#ifndef PNG_NO_TYPECAST_NULL +#define int_p_NULL (int *)NULL +#define png_bytep_NULL (png_bytep)NULL +#define png_bytepp_NULL (png_bytepp)NULL +#define png_doublep_NULL (png_doublep)NULL +#define png_error_ptr_NULL (png_error_ptr)NULL +#define png_flush_ptr_NULL (png_flush_ptr)NULL +#define png_free_ptr_NULL (png_free_ptr)NULL +#define png_infopp_NULL (png_infopp)NULL +#define png_malloc_ptr_NULL (png_malloc_ptr)NULL +#define png_read_status_ptr_NULL (png_read_status_ptr)NULL +#define png_rw_ptr_NULL (png_rw_ptr)NULL +#define png_structp_NULL (png_structp)NULL +#define png_uint_16p_NULL (png_uint_16p)NULL +#define png_voidp_NULL (png_voidp)NULL +#define png_write_status_ptr_NULL (png_write_status_ptr)NULL +#else +#define int_p_NULL NULL +#define png_bytep_NULL NULL +#define png_bytepp_NULL NULL +#define png_doublep_NULL NULL +#define png_error_ptr_NULL NULL +#define png_flush_ptr_NULL NULL +#define png_free_ptr_NULL NULL +#define png_infopp_NULL NULL +#define png_malloc_ptr_NULL NULL +#define png_read_status_ptr_NULL NULL +#define png_rw_ptr_NULL NULL +#define png_structp_NULL NULL +#define png_uint_16p_NULL NULL +#define png_voidp_NULL NULL +#define png_write_status_ptr_NULL NULL +#endif + +/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */ +#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#ifdef PNG_USE_GLOBAL_ARRAYS +PNG_EXPORT_VAR (const char) png_libpng_ver[18]; + /* need room for 99.99.99beta99z */ +#else +#define png_libpng_ver png_get_header_ver(NULL) +#endif + +#ifdef PNG_USE_GLOBAL_ARRAYS +/* This was removed in version 1.0.5c */ +/* Structures to facilitate easy interlacing. See png.c for more details */ +PNG_EXPORT_VAR (const int FARDATA) png_pass_start[7]; +PNG_EXPORT_VAR (const int FARDATA) png_pass_inc[7]; +PNG_EXPORT_VAR (const int FARDATA) png_pass_ystart[7]; +PNG_EXPORT_VAR (const int FARDATA) png_pass_yinc[7]; +PNG_EXPORT_VAR (const int FARDATA) png_pass_mask[7]; +PNG_EXPORT_VAR (const int FARDATA) png_pass_dsp_mask[7]; +#ifdef PNG_HAVE_ASSEMBLER_COMBINE_ROW +PNG_EXPORT_VAR (const int FARDATA) png_pass_width[7]; +#endif +/* This isn't currently used. If you need it, see png.c for more details. +PNG_EXPORT_VAR (const int FARDATA) png_pass_height[7]; +*/ +#endif + +#endif /* PNG_NO_EXTERN */ + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color FAR * png_colorp; +typedef png_color FAR * FAR * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 FAR * png_color_16p; +typedef png_color_16 FAR * FAR * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 FAR * png_color_8p; +typedef png_color_8 FAR * FAR * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry FAR * png_sPLT_entryp; +typedef png_sPLT_entry FAR * FAR * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t FAR * png_sPLT_tp; +typedef png_sPLT_t FAR * FAR * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text", "lang", and + * "lang_key" fields can be regular C strings, empty strings, or NULL pointers. + * However, the * structure returned by png_get_text() will always contain + * regular zero-terminated C strings (possibly empty), never NULL pointers, + * so they can be safely used in printf() and other string-handling functions. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + png_size_t text_length; /* length of the text string */ +#ifdef PNG_iTXt_SUPPORTED + png_size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +#endif +} png_text; +typedef png_text FAR * png_textp; +typedef png_text FAR * FAR * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time FAR * png_timep; +typedef png_time FAR * FAR * png_timepp; + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; + png_byte *data; + png_size_t size; + + /* libpng-using applications should NOT directly modify this byte. */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; +typedef png_unknown_chunk FAR * png_unknown_chunkp; +typedef png_unknown_chunk FAR * FAR * png_unknown_chunkpp; +#endif + +/* png_info is a structure that holds the information in a PNG file so + * that the application can find out the characteristics of the image. + * If you are reading the file, this structure will tell you what is + * in the PNG file. If you are writing the file, fill in the information + * you want to put into the PNG file, then call png_write_info(). + * The names chosen should be very close to the PNG specification, so + * consult that document for information about the meaning of each field. + * + * With libpng < 0.95, it was only possible to directly set and read the + * the values in the png_info_struct, which meant that the contents and + * order of the values had to remain fixed. With libpng 0.95 and later, + * however, there are now functions that abstract the contents of + * png_info_struct from the application, so this makes it easier to use + * libpng with dynamic libraries, and even makes it possible to use + * libraries that don't have all of the libpng ancillary chunk-handing + * functionality. + * + * In any case, the order of the parameters in png_info_struct should NOT + * be changed for as long as possible to keep compatibility with applications + * that use the old direct-access method with png_info_struct. + * + * The following members may have allocated storage attached that should be + * cleaned up before the structure is discarded: palette, trans, text, + * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile, + * splt_palettes, scal_unit, row_pointers, and unknowns. By default, these + * are automatically freed when the info structure is deallocated, if they were + * allocated internally by libpng. This behavior can be changed by means + * of the png_data_freer() function. + * + * More allocation details: all the chunk-reading functions that + * change these members go through the corresponding png_set_* + * functions. A function to clear these members is available: see + * png_free_data(). The png_set_* functions do not depend on being + * able to point info structure members to any of the storage they are + * passed (they make their own copies), EXCEPT that the png_set_text + * functions use the same storage passed to them in the text_ptr or + * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns + * functions do not make their own copies. + */ +typedef struct png_info_struct +{ + /* the following are necessary for every PNG file */ + png_uint_32 width; /* width of image in pixels (from IHDR) */ + png_uint_32 height; /* height of image in pixels (from IHDR) */ + png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */ + png_uint_32 rowbytes; /* bytes needed to hold an untransformed row */ + png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */ + png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */ + png_uint_16 num_trans; /* number of transparent palette color (tRNS) */ + png_byte bit_depth; /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */ + png_byte color_type; /* see PNG_COLOR_TYPE_ below (from IHDR) */ + /* The following three should have been named *_method not *_type */ + png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */ + png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */ + png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + + /* The following is informational only on read, and not used on writes. */ + png_byte channels; /* number of data channels per pixel (1, 2, 3, 4) */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte spare_byte; /* to align the data, and for future use */ + png_byte signature[8]; /* magic bytes read by libpng from start of file */ + + /* The rest of the data is optional. If you are reading, check the + * valid field to see if the information in these are valid. If you + * are writing, set the valid field to those chunks you want written, + * and initialize the appropriate fields below. + */ + +#if defined(PNG_gAMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + /* The gAMA chunk describes the gamma characteristics of the system + * on which the image was created, normally in the range [1.0, 2.5]. + * Data is valid if (valid & PNG_INFO_gAMA) is non-zero. + */ + float gamma; /* gamma value of image, if (valid & PNG_INFO_gAMA) */ +#endif + +#if defined(PNG_sRGB_SUPPORTED) + /* GR-P, 0.96a */ + /* Data valid if (valid & PNG_INFO_sRGB) non-zero. */ + png_byte srgb_intent; /* sRGB rendering intent [0, 1, 2, or 3] */ +#endif + +#if defined(PNG_TEXT_SUPPORTED) + /* The tEXt, and zTXt chunks contain human-readable textual data in + * uncompressed, compressed, and optionally compressed forms, respectively. + * The data in "text" is an array of pointers to uncompressed, + * null-terminated C strings. Each chunk has a keyword that describes the + * textual data contained in that chunk. Keywords are not required to be + * unique, and the text string may be empty. Any number of text chunks may + * be in an image. + */ + int num_text; /* number of comments read/to write */ + int max_text; /* current size of text array */ + png_textp text; /* array of comments read/to write */ +#endif /* PNG_TEXT_SUPPORTED */ + +#if defined(PNG_tIME_SUPPORTED) + /* The tIME chunk holds the last time the displayed image data was + * modified. See the png_time struct for the contents of this struct. + */ + png_time mod_time; +#endif + +#if defined(PNG_sBIT_SUPPORTED) + /* The sBIT chunk specifies the number of significant high-order bits + * in the pixel data. Values are in the range [1, bit_depth], and are + * only specified for the channels in the pixel data. The contents of + * the low-order bits is not specified. Data is valid if + * (valid & PNG_INFO_sBIT) is non-zero. + */ + png_color_8 sig_bit; /* significant bits in color channels */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \ +defined(PNG_READ_BACKGROUND_SUPPORTED) + /* The tRNS chunk supplies transparency data for paletted images and + * other image types that don't need a full alpha channel. There are + * "num_trans" transparency values for a paletted image, stored in the + * same order as the palette colors, starting from index 0. Values + * for the data are in the range [0, 255], ranging from fully transparent + * to fully opaque, respectively. For non-paletted images, there is a + * single color specified that should be treated as fully transparent. + * Data is valid if (valid & PNG_INFO_tRNS) is non-zero. + */ + png_bytep trans; /* transparent values for paletted image */ + png_color_16 trans_values; /* transparent color for non-palette image */ +#endif + +#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + /* The bKGD chunk gives the suggested image background color if the + * display program does not have its own background color and the image + * is needs to composited onto a background before display. The colors + * in "background" are normally in the same color space/depth as the + * pixel data. Data is valid if (valid & PNG_INFO_bKGD) is non-zero. + */ + png_color_16 background; +#endif + +#if defined(PNG_oFFs_SUPPORTED) + /* The oFFs chunk gives the offset in "offset_unit_type" units rightwards + * and downwards from the top-left corner of the display, page, or other + * application-specific co-ordinate space. See the PNG_OFFSET_ defines + * below for the unit types. Valid if (valid & PNG_INFO_oFFs) non-zero. + */ + png_int_32 x_offset; /* x offset on page */ + png_int_32 y_offset; /* y offset on page */ + png_byte offset_unit_type; /* offset units type */ +#endif + +#if defined(PNG_pHYs_SUPPORTED) + /* The pHYs chunk gives the physical pixel density of the image for + * display or printing in "phys_unit_type" units (see PNG_RESOLUTION_ + * defines below). Data is valid if (valid & PNG_INFO_pHYs) is non-zero. + */ + png_uint_32 x_pixels_per_unit; /* horizontal pixel density */ + png_uint_32 y_pixels_per_unit; /* vertical pixel density */ + png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */ +#endif + +#if defined(PNG_hIST_SUPPORTED) + /* The hIST chunk contains the relative frequency or importance of the + * various palette entries, so that a viewer can intelligently select a + * reduced-color palette, if required. Data is an array of "num_palette" + * values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST) + * is non-zero. + */ + png_uint_16p hist; +#endif + +#ifdef PNG_cHRM_SUPPORTED + /* The cHRM chunk describes the CIE color characteristics of the monitor + * on which the PNG was created. This data allows the viewer to do gamut + * mapping of the input image to ensure that the viewer sees the same + * colors in the image as the creator. Values are in the range + * [0.0, 0.8]. Data valid if (valid & PNG_INFO_cHRM) non-zero. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float x_white; + float y_white; + float x_red; + float y_red; + float x_green; + float y_green; + float x_blue; + float y_blue; +#endif +#endif + +#if defined(PNG_pCAL_SUPPORTED) + /* The pCAL chunk describes a transformation between the stored pixel + * values and original physical data values used to create the image. + * The integer range [0, 2^bit_depth - 1] maps to the floating-point + * range given by [pcal_X0, pcal_X1], and are further transformed by a + * (possibly non-linear) transformation function given by "pcal_type" + * and "pcal_params" into "pcal_units". Please see the PNG_EQUATION_ + * defines below, and the PNG-Group's PNG extensions document for a + * complete description of the transformations and how they should be + * implemented, and for a description of the ASCII parameter strings. + * Data values are valid if (valid & PNG_INFO_pCAL) non-zero. + */ + png_charp pcal_purpose; /* pCAL chunk description string */ + png_int_32 pcal_X0; /* minimum value */ + png_int_32 pcal_X1; /* maximum value */ + png_charp pcal_units; /* Latin-1 string giving physical units */ + png_charpp pcal_params; /* ASCII strings containing parameter values */ + png_byte pcal_type; /* equation type (see PNG_EQUATION_ below) */ + png_byte pcal_nparams; /* number of parameters given in pcal_params */ +#endif + +/* New members added in libpng-1.0.6 */ +#ifdef PNG_FREE_ME_SUPPORTED + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + /* storage for unknown chunks that the library doesn't recognize. */ + png_unknown_chunkp unknown_chunks; + png_size_t unknown_chunks_num; +#endif + +#if defined(PNG_iCCP_SUPPORTED) + /* iCCP chunk data. */ + png_charp iccp_name; /* profile name */ + png_charp iccp_profile; /* International Color Consortium profile data */ + /* Note to maintainer: should be png_bytep */ + png_uint_32 iccp_proflen; /* ICC profile data length */ + png_byte iccp_compression; /* Always zero */ +#endif + +#if defined(PNG_sPLT_SUPPORTED) + /* data on sPLT chunks (there may be more than one). */ + png_sPLT_tp splt_palettes; + png_uint_32 splt_palettes_num; +#endif + +#if defined(PNG_sCAL_SUPPORTED) + /* The sCAL chunk describes the actual physical dimensions of the + * subject matter of the graphic. The chunk contains a unit specification + * a byte value, and two ASCII strings representing floating-point + * values. The values are width and height corresponsing to one pixel + * in the image. This external representation is converted to double + * here. Data values are valid if (valid & PNG_INFO_sCAL) is non-zero. + */ + png_byte scal_unit; /* unit of physical scale */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + double scal_pixel_width; /* width of one pixel */ + double scal_pixel_height; /* height of one pixel */ +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_charp scal_s_width; /* string containing height */ + png_charp scal_s_height; /* string containing width */ +#endif +#endif + +#if defined(PNG_INFO_IMAGE_SUPPORTED) + /* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS) non-zero */ + /* Data valid if (valid & PNG_INFO_IDAT) non-zero */ + png_bytepp row_pointers; /* the image bits */ +#endif + +#if defined(PNG_FIXED_POINT_SUPPORTED) && defined(PNG_gAMA_SUPPORTED) + png_fixed_point int_gamma; /* gamma of image, if (valid & PNG_INFO_gAMA) */ +#endif + +#if defined(PNG_cHRM_SUPPORTED) && defined(PNG_FIXED_POINT_SUPPORTED) + png_fixed_point int_x_white; + png_fixed_point int_y_white; + png_fixed_point int_x_red; + png_fixed_point int_y_red; + png_fixed_point int_x_green; + png_fixed_point int_y_green; + png_fixed_point int_x_blue; + png_fixed_point int_y_blue; +#endif + +} png_info; + +typedef png_info FAR * png_infop; +typedef png_info FAR * FAR * png_infopp; + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((png_size_t)(-1)) +/* PNG_MAX_UINT is deprecated; use PNG_UINT_31_MAX instead. */ +#define PNG_MAX_UINT PNG_UINT_31_MAX + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001 +#define PNG_INFO_sBIT 0x0002 +#define PNG_INFO_cHRM 0x0004 +#define PNG_INFO_PLTE 0x0008 +#define PNG_INFO_tRNS 0x0010 +#define PNG_INFO_bKGD 0x0020 +#define PNG_INFO_hIST 0x0040 +#define PNG_INFO_pHYs 0x0080 +#define PNG_INFO_oFFs 0x0100 +#define PNG_INFO_tIME 0x0200 +#define PNG_INFO_pCAL 0x0400 +#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + png_uint_32 rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info FAR * png_row_infop; +typedef png_row_info FAR * FAR * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. + */ +typedef struct png_struct_def png_struct; +typedef png_struct FAR * png_structp; + +typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); +typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); +typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); +typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, + int)); +typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp, + png_row_infop, png_bytep)); +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) +typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp)); +#endif +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* WRITE only */ + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_size_t)); +typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp)); + +/* The structure that holds the information to read and write PNG files. + * The only people who need to care about what is inside of this are the + * people who will be modifying the library for their own special needs. + * It should NOT be accessed directly by an application, except to store + * the jmp_buf. + */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmpbuf; /* used in png_error */ +#endif + png_error_ptr error_fn; /* function for printing errors and aborting */ + png_error_ptr warning_fn; /* function for printing warnings */ + png_voidp error_ptr; /* user supplied struct for error functions */ + png_rw_ptr write_data_fn; /* function for writing output data */ + png_rw_ptr read_data_fn; /* function for reading input data */ + png_voidp io_ptr; /* ptr to application struct for I/O functions */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr read_user_transform_fn; /* user read transform */ +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr write_user_transform_fn; /* user write transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr; /* user supplied struct for user transform */ + png_byte user_transform_depth; /* bit depth of user transformed pixels */ + png_byte user_transform_channels; /* channels in user transformed pixels */ +#endif +#endif + + png_uint_32 mode; /* tells us where we are in the PNG file */ + png_uint_32 flags; /* flags indicating various things to libpng */ + png_uint_32 transformations; /* which transformations to perform */ + + z_stream zstream; /* pointer to decompression structure (below) */ + png_bytep zbuf; /* buffer for zlib */ + png_size_t zbuf_size; /* size of zbuf */ + int zlib_level; /* holds zlib compression level */ + int zlib_method; /* holds zlib compression method */ + int zlib_window_bits; /* holds zlib compression window bits */ + int zlib_mem_level; /* holds zlib compression memory level */ + int zlib_strategy; /* holds zlib compression strategy */ + + png_uint_32 width; /* width of image in pixels */ + png_uint_32 height; /* height of image in pixels */ + png_uint_32 num_rows; /* number of rows in current pass */ + png_uint_32 usr_width; /* width of row at start of write */ + png_uint_32 rowbytes; /* size of row in bytes */ + png_uint_32 irowbytes; /* size of current interlaced row in bytes */ + png_uint_32 iwidth; /* width of current interlaced row in pixels */ + png_uint_32 row_number; /* current row in interlace pass */ + png_bytep prev_row; /* buffer to save previous (unfiltered) row */ + png_bytep row_buf; /* buffer to save current (unfiltered) row */ + png_bytep sub_row; /* buffer to save "sub" row when filtering */ + png_bytep up_row; /* buffer to save "up" row when filtering */ + png_bytep avg_row; /* buffer to save "avg" row when filtering */ + png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ + png_row_info row_info; /* used for transformation routines */ + + png_uint_32 idat_size; /* current IDAT size for read */ + png_uint_32 crc; /* current chunk CRC value */ + png_colorp palette; /* palette from the input file */ + png_uint_16 num_palette; /* number of color entries in palette */ + png_uint_16 num_trans; /* number of transparency values */ + png_byte chunk_name[5]; /* null-terminated name of current chunk */ + png_byte compression; /* file compression type (always 0) */ + png_byte filter; /* file filter type (always 0) */ + png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + png_byte pass; /* current interlace pass (0 - 6) */ + png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ + png_byte color_type; /* color type of file */ + png_byte bit_depth; /* bit depth of file */ + png_byte usr_bit_depth; /* bit depth of users row */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte channels; /* number of channels in file */ + png_byte usr_channels; /* channels at start of write */ + png_byte sig_bytes; /* magic bytes read/written from start of file */ + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +#ifdef PNG_LEGACY_SUPPORTED + png_byte filler; /* filler byte for pixel expansion */ +#else + png_uint_16 filler; /* filler bytes for pixel expansion */ +#endif +#endif + +#if defined(PNG_bKGD_SUPPORTED) + png_byte background_gamma_type; +# ifdef PNG_FLOATING_POINT_SUPPORTED + float background_gamma; +# endif + png_color_16 background; /* background color in screen gamma space */ +#if defined(PNG_READ_GAMMA_SUPPORTED) + png_color_16 background_1; /* background normalized to gamma 1.0 */ +#endif +#endif /* PNG_bKGD_SUPPORTED */ + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_flush_ptr output_flush_fn;/* Function for flushing output */ + png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows; /* number of rows written since last flush */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + int gamma_shift; /* number of "insignificant" bits 16-bit gamma */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float gamma; /* file gamma value */ + float screen_gamma; /* screen gamma value (display_exponent) */ +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep gamma_table; /* gamma table for 8-bit depth files */ + png_bytep gamma_from_1; /* converts from 1.0 to screen */ + png_bytep gamma_to_1; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ + png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ + png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit; /* significant bits in each available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift; /* shift for significant bit tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans; /* transparency values for paletted files */ + png_color_16 trans_values; /* transparency values for non-paletted files */ +#endif + + png_read_status_ptr read_row_fn; /* called after each row is decoded */ + png_write_status_ptr write_row_fn; /* called after each row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn; /* called after header data fully read */ + png_progressive_row_ptr row_fn; /* called after each prog. row is decoded */ + png_progressive_end_ptr end_fn; /* called after image is complete */ + png_bytep save_buffer_ptr; /* current location in save_buffer */ + png_bytep save_buffer; /* buffer for previously read data */ + png_bytep current_buffer_ptr; /* current location in current_buffer */ + png_bytep current_buffer; /* buffer for recently used data */ + png_uint_32 push_length; /* size of current input chunk */ + png_uint_32 skip_length; /* bytes to skip in input data */ + png_size_t save_buffer_size; /* amount of data now in save_buffer */ + png_size_t save_buffer_max; /* total size of save_buffer */ + png_size_t buffer_size; /* total amount of available input data */ + png_size_t current_buffer_size; /* amount of data now in current_buffer */ + int process_mode; /* what push library is currently doing */ + int cur_palette; /* current push library palette index */ + +# if defined(PNG_TEXT_SUPPORTED) + png_size_t current_text_size; /* current size of text input data */ + png_size_t current_text_left; /* how much text left to read in input */ + png_charp current_text; /* current text chunk buffer */ + png_charp current_text_ptr; /* current location in current_text */ +# endif /* PNG_PROGRESSIVE_READ_SUPPORTED && PNG_TEXT_SUPPORTED */ + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* for the Borland special 64K segment handler */ + png_bytepp offset_table_ptr; + png_bytep offset_table; + png_uint_16 offset_table_number; + png_uint_16 offset_table_count; + png_uint_16 offset_table_count_free; +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + png_bytep palette_lookup; /* lookup table for dithering */ + png_bytep dither_index; /* index translation for palette files */ +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_hIST_SUPPORTED) + png_uint_16p hist; /* histogram */ +#endif + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_byte heuristic_method; /* heuristic for row filter selection */ + png_byte num_prev_filters; /* number of weights for previous rows */ + png_bytep prev_filters; /* filter type(s) of previous row(s) */ + png_uint_16p filter_weights; /* weight(s) for previous line(s) */ + png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ + png_uint_16p filter_costs; /* relative filter calculation cost */ + png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_charp time_buffer; /* String to hold RFC 1123 time text */ +#endif + +/* New members added in libpng-1.0.6 */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) + png_voidp user_chunk_ptr; + png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + int num_chunk_list; + png_bytep chunk_list; +#endif + +/* New members added in libpng-1.0.3 */ +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + png_byte rgb_to_gray_status; + /* These were changed from png_byte in libpng-1.0.6 */ + png_uint_16 rgb_to_gray_red_coeff; + png_uint_16 rgb_to_gray_green_coeff; + png_uint_16 rgb_to_gray_blue_coeff; +#endif + +/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) || \ + defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* changed from png_byte to png_uint_32 at version 1.2.0 */ +#ifdef PNG_1_0_X + png_byte mng_features_permitted; +#else + png_uint_32 mng_features_permitted; +#endif /* PNG_1_0_X */ +#endif + +/* New member added in libpng-1.0.7 */ +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_fixed_point int_gamma; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_byte filter_type; +#endif + +#if defined(PNG_1_0_X) || (defined(PNG_DEBUG) && defined(PNG_USE_PNGGCCRD)) +/* New member added in libpng-1.0.10, ifdef'ed out in 1.2.0 */ + png_uint_32 row_buf_size; +#endif + +/* New members added in libpng-1.2.0 */ +#if !defined(PNG_1_0_X) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) + png_byte mmx_bitdepth_threshold; + png_uint_32 mmx_rowbytes_threshold; + png_uint_32 asm_flags; +#endif + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr; /* user supplied struct for mem functions */ + png_malloc_ptr malloc_fn; /* function for allocating memory */ + png_free_ptr free_fn; /* function for freeing memory */ +#endif + +/* New member added in libpng-1.0.13 and 1.2.0 */ + png_bytep big_row_buf; /* buffer to save current (unfiltered) row */ + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep dither_sort; /* working sort array */ + png_bytep index_to_palette; /* where the original index currently is */ + /* in the palette */ + png_bytep palette_to_index; /* which original index points to this */ + /* palette color */ +#endif + +/* New members added in libpng-1.0.16 and 1.2.6 */ + png_byte compression_type; + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max; + png_uint_32 user_height_max; +#endif + +}; + + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef png_structp version_1_2_8; + +typedef png_struct FAR * FAR * png_structpp; + +/* Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + */ + +/* Returns the version number of the library */ +extern PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +extern PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr, + int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +extern PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start, + png_size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num)); + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +extern PNG_EXPORT(png_structp,png_create_read_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +extern PNG_EXPORT(png_structp,png_create_write_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_compression_buffer_size) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(void,png_set_compression_buffer_size) + PNGARG((png_structp png_ptr, png_uint_32 size)); +#endif + +/* Reset the compression stream */ +extern PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_structp,png_create_read_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +extern PNG_EXPORT(png_structp,png_create_write_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +#endif + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +extern PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_bytep data, png_size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +extern PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +extern PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +extern PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr)); + +/* Allocate and initialize the info structure */ +extern PNG_EXPORT(png_infop,png_create_info_struct) + PNGARG((png_structp png_ptr)); + +/* Initialize the info structure (old interface - DEPRECATED) */ +extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr)); +#undef png_info_init +#define png_info_init(info_ptr) png_info_init_3(&info_ptr,\ + png_sizeof(png_info)); +extern PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, + png_size_t png_info_struct_size)); + +/* Writes all the PNG information before the image. */ +extern PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +extern PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the information before the actual image data. */ +extern PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +extern PNG_EXPORT(png_charp,png_convert_to_rfc1123) + PNGARG((png_structp png_ptr, png_timep ptime)); +#endif + +#if !defined(_WIN32_WCE) +/* "time.h" functions are not supported on WindowsCE */ +#if defined(PNG_WRITE_tIME_SUPPORTED) +/* convert from a struct tm to png_time */ +extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, + struct tm FAR * ttime)); + +/* convert from time_t to png_time. Uses gmtime() */ +extern PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime, + time_t ttime)); +#endif /* PNG_WRITE_tIME_SUPPORTED */ +#endif /* _WIN32_WCE */ + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +extern PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +/* Expand the grayscale to 24-bit RGB if necessary. */ +extern PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* Reduce RGB to grayscale. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, + int error_action, double red, double green )); +#endif +extern PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green )); +extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp + png_ptr)); +#endif + +extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, + png_colorp palette)); + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ +extern PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +#define PNG_FILLER_BEFORE 0 +#define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ +#if !defined(PNG_1_0_X) +extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +#endif +#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +extern PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +extern PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +extern PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr, + png_color_8p true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. */ +extern PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +extern PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +/* Handle alpha and tRNS by replacing with a background color. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)); +#endif +#define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +#define PNG_BACKGROUND_GAMMA_SCREEN 1 +#define PNG_BACKGROUND_GAMMA_FILE 2 +#define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* strip the second byte of information from a 16-bit depth file. */ +extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* Turn on dithering, and reduce the palette to the number of colors available. */ +extern PNG_EXPORT(void,png_set_dither) PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_uint_16p histogram, int full_dither)); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +/* Handle gamma correction. Screen_gamma=(display_exponent) */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, + double screen_gamma, double default_file_gamma)); +#endif +#endif + +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* Permit or disallow empty PLTE (0: not permitted, 1: permitted) */ +/* Deprecated and will be removed. Use png_permit_mng_features() instead. */ +extern PNG_EXPORT(void,png_permit_empty_plte) PNGARG((png_structp png_ptr, + int empty_plte_permitted)); +#endif + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +/* Set how many lines between output flushes - 0 for no flushing */ +extern PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); +#endif + +/* optional update palette with requested transformations */ +extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); + +/* optional call to update the users info structure */ +extern PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read one or more rows of image data. */ +extern PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read a row of data. */ +extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, + png_bytep row, + png_bytep display_row)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the whole image into memory at once. */ +extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, + png_bytepp image)); +#endif + +/* write a row of image data */ +extern PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr, + png_bytep row)); + +/* write a few rows of image data */ +extern PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_uint_32 num_rows)); + +/* write the image data */ +extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr, + png_bytepp image)); + +/* writes the end of the PNG file. */ +extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the end of the PNG file. */ +extern PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +/* free any memory associated with the png_info_struct */ +extern PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr, + png_infopp info_ptr_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp + png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* free all memory used by the read (old method - NOT DLL EXPORTED) */ +extern void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr, + png_infop end_info_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_write_struct) + PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); + +/* free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */ +extern void png_write_destroy PNGARG((png_structp png_ptr)); + +/* set the libpng method of handling chunk CRC errors */ +extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, + int crit_action, int ancil_action)); + +/* Values for png_set_crc_action() to say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, + int filters)); + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */ +/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ + * defines, either the default (minimum-sum-of-absolute-differences), or + * the experimental method (weighted-minimum-sum-of-absolute-differences). + * + * Weights are factors >= 1.0, indicating how important it is to keep the + * filter type consistent between rows. Larger numbers mean the current + * filter is that many times as likely to be the same as the "num_weights" + * previous filters. This is cumulative for each previous row with a weight. + * There needs to be "num_weights" values in "filter_weights", or it can be + * NULL if the weights aren't being specified. Weights have no influence on + * the selection of the first row filter. Well chosen weights can (in theory) + * improve the compression for a given image. + * + * Costs are factors >= 1.0 indicating the relative decoding costs of a + * filter type. Higher costs indicate more decoding expense, and are + * therefore less likely to be selected over a filter with lower computational + * costs. There needs to be a value in "filter_costs" for each valid filter + * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't + * setting the costs. Costs try to improve the speed of decompression without + * unduly increasing the compressed image size. + * + * A negative weight or cost indicates the default value is to be used, and + * values in the range [0.0, 1.0) indicate the value is to remain unchanged. + * The default values for both weights and costs are currently 1.0, but may + * change if good general weighting/cost heuristics can be found. If both + * the weights and costs are set to 1.0, this degenerates the WEIGHTED method + * to the UNWEIGHTED method, but with added encoding time/computation. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, + int heuristic_method, int num_weights, png_doublep filter_weights, + png_doublep filter_costs)); +#endif +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +/* Heuristic used for row filter selection. These defines should NOT be + * changed. + */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +extern PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr, + int level)); + +extern PNG_EXPORT(void,png_set_compression_mem_level) + PNGARG((png_structp png_ptr, int mem_level)); + +extern PNG_EXPORT(void,png_set_compression_strategy) + PNGARG((png_structp png_ptr, int strategy)); + +extern PNG_EXPORT(void,png_set_compression_window_bits) + PNGARG((png_structp png_ptr, int window_bits)); + +extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, + int method)); + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng.txt for + * more information. + */ + +#if !defined(PNG_NO_STDIO) +/* Initialize the input/output for the PNG file to the default functions. */ +extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +extern PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +extern PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + */ +extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); + +extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, + png_read_status_ptr read_row_fn)); + +extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +extern PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +extern PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr read_user_transform_fn)); +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr write_user_transform_fn)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp + png_ptr, png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +extern PNG_EXPORT(png_voidp,png_get_user_transform_ptr) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +extern PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp + png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, + png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn)); + +/* returns the user pointer associated with the push read functions */ +extern PNG_EXPORT(png_voidp,png_get_progressive_ptr) + PNGARG((png_structp png_ptr)); + +/* function to be called when data becomes available */ +extern PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep buffer, png_size_t buffer_size)); + +/* function that combines rows. Not very much different than the + * png_combine_row() call. Is this even used????? + */ +extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, + png_bytep old_row, png_bytep new_row)); +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, + png_uint_32 size)); + +#if defined(PNG_1_0_X) +# define png_malloc_warn png_malloc +#else +/* Added at libpng version 1.2.4 */ +extern PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr, + png_uint_32 size)); +#endif + +/* frees a pointer allocated by png_malloc() */ +extern PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr)); + +#if defined(PNG_1_0_X) +/* Function to allocate memory for zlib. */ +extern PNG_EXPORT(voidpf,png_zalloc) PNGARG((voidpf png_ptr, uInt items, + uInt size)); + +/* Function to free memory for zlib */ +extern PNG_EXPORT(void,png_zfree) PNGARG((voidpf png_ptr, voidpf ptr)); +#endif + +/* Free data that was allocated internally */ +extern PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 free_me, int num)); +#ifdef PNG_FREE_ME_SUPPORTED +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application */ +extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, + png_infop info_ptr, int freer, png_uint_32 mask)); +#endif +/* assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008 +#define PNG_FREE_ICCP 0x0010 +#define PNG_FREE_SPLT 0x0020 +#define PNG_FREE_ROWS 0x0040 +#define PNG_FREE_PCAL 0x0080 +#define PNG_FREE_SCAL 0x0100 +#define PNG_FREE_UNKN 0x0200 +#define PNG_FREE_LIST 0x0400 +#define PNG_FREE_PLTE 0x1000 +#define PNG_FREE_TRNS 0x2000 +#define PNG_FREE_TEXT 0x4000 +#define PNG_FREE_ALL 0x7fff +#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, + png_uint_32 size)); +extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, + png_voidp ptr)); +#endif + +extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr, + png_voidp s1, png_voidp s2, png_uint_32 size)); + +extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr, + png_voidp s1, int value, png_uint_32 size)); + +#if defined(USE_FAR_KEYWORD) /* memory model conversion function */ +extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, + int check)); +#endif /* USE_FAR_KEYWORD */ + +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); + +/* The same, but the chunk name is prepended to the error string. */ +extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); + +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +extern PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +extern PNG_EXPORT(png_uint_32,png_get_rowbytes) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* Returns row_pointers, which is an array of pointers to scanlines that was +returned from png_read_png(). */ +extern PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr, +png_infop info_ptr)); +/* Set row_pointers, which is an array of pointers to scanlines for use +by png_write_png(). */ +extern PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +extern PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image height in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image bit_depth. */ +extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image color_type. */ +extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image filter_type. */ +extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image interlace_type. */ +extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image compression_type. */ +extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +#endif + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +extern PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +/* Returns pointer to signature string read from PNG header */ +extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p *background)); +#endif + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p background)); +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point + *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point + *int_blue_x, png_fixed_point *int_blue_y)); +#endif +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double white_x, double white_y, double red_x, + double red_y, double green_x, double green_y, double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *file_gamma)); +#endif +extern PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_file_gamma)); +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double file_gamma)); +#endif +extern PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_file_gamma)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p *hist)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p hist)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, + int *type, int *nparams, png_charp *units, png_charpp *params)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_charp units, png_charpp params)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp *palette, int *num_palette)); + +extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp palette, int num_palette)); + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p *sig_bit)); +#endif + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p sig_bit)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *intent)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen)); + /* Note to maintainer: profile should be png_bytepp */ +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tpp entries)); +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) +/* png_get_text also returns the number of text chunks in *num_text */ +extern PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* + * Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#if defined(PNG_TEXT_SUPPORTED) +extern PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep *mod_time)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep mod_time)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep *trans, int *num_trans, + png_color_16p *trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep trans, int num_trans, + png_color_16p trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +#endif + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, double *width, double *height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED */ + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, double width, double height)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); +#endif +#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +/* provide a list of chunks and how they are to be handled, if the built-in + handling or default unknown chunk handling is not desired. Any chunks not + listed will be handled in the default manner. The IHDR and IEND chunks + must not be listed. + keep = 0: follow default behavour + = 1: do not keep + = 2: keep only if safe-to-copy + = 3: keep even if unsafe-to-copy +*/ +extern PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp + png_ptr, int keep, png_bytep chunk_list, int num_chunks)); +extern PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); +extern PNG_EXPORT(void, png_set_unknown_chunk_location) + PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); +extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp + png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); +#endif +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep + chunk_name)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + If you need to turn it off for a chunk that your application has freed, + you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); */ +extern PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, + png_infop info_ptr, int mask)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* The "params" pointer is currently not used and is for future expansion. */ +extern PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +#endif + +/* Define PNG_DEBUG at compile time for debugging information. Higher + * numbers for PNG_DEBUG mean more debugging information. This has + * only been added since version 0.95 so it is not implemented throughout + * libpng yet, but more support will be added as needed. + */ +#ifdef PNG_DEBUG +#if (PNG_DEBUG > 0) +#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) +#include +#if (PNG_DEBUG > 1) +#define png_debug(l,m) _RPT0(_CRT_WARN,m) +#define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m,p1) +#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m,p1,p2) +#endif +#else /* PNG_DEBUG_FILE || !_MSC_VER */ +#ifndef PNG_DEBUG_FILE +#define PNG_DEBUG_FILE stderr +#endif /* PNG_DEBUG_FILE */ +#if (PNG_DEBUG > 1) +#define png_debug(l,m) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ +} +#define png_debug1(l,m,p1) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ +} +#define png_debug2(l,m,p1,p2) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ +} +#endif /* (PNG_DEBUG > 1) */ +#endif /* _MSC_VER */ +#endif /* (PNG_DEBUG > 0) */ +#endif /* PNG_DEBUG */ +#ifndef png_debug +#define png_debug(l, m) +#endif +#ifndef png_debug1 +#define png_debug1(l, m, p1) +#endif +#ifndef png_debug2 +#define png_debug2(l, m, p1, p2) +#endif + +extern PNG_EXPORT(png_bytep,png_sig_bytes) PNGARG((void)); + +extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp + png_ptr, png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 + +/* Added to version 1.2.0 */ +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#define PNG_ASM_FLAG_MMX_SUPPORT_COMPILED 0x01 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU 0x02 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_READ_COMBINE_ROW 0x04 +#define PNG_ASM_FLAG_MMX_READ_INTERLACE 0x08 +#define PNG_ASM_FLAG_MMX_READ_FILTER_SUB 0x10 +#define PNG_ASM_FLAG_MMX_READ_FILTER_UP 0x20 +#define PNG_ASM_FLAG_MMX_READ_FILTER_AVG 0x40 +#define PNG_ASM_FLAG_MMX_READ_FILTER_PAETH 0x80 +#define PNG_ASM_FLAGS_INITIALIZED 0x80000000 /* not user-settable */ + +#define PNG_MMX_READ_FLAGS ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_INTERLACE \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ) +#define PNG_MMX_WRITE_FLAGS ( 0 ) + +#define PNG_MMX_FLAGS ( PNG_ASM_FLAG_MMX_SUPPORT_COMPILED \ + | PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU \ + | PNG_MMX_READ_FLAGS \ + | PNG_MMX_WRITE_FLAGS ) + +#define PNG_SELECT_READ 1 +#define PNG_SELECT_WRITE 2 + +#if !defined(PNG_1_0_X) +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_flagmask) + PNGARG((int flag_select, int *compilerID)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flagmask) + PNGARG((int flag_select)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flags) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_byte,png_get_mmx_bitdepth_threshold) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_rowbytes_threshold) + PNGARG((png_structp png_ptr)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_asm_flags) + PNGARG((png_structp png_ptr, png_uint_32 asm_flags)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_mmx_thresholds) + PNGARG((png_structp png_ptr, png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold)); + +#endif /* PNG_1_0_X */ +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + +#if !defined(PNG_1_0_X) +/* png.c, pnggccrd.c, or pngvcrd.c */ +extern PNG_EXPORT(int,png_mmx_support) PNGARG((void)); + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +extern PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp + png_ptr, png_uint_32 strip_mode)); +#endif + +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +extern PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp + png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); +extern PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp + png_ptr)); +extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp + png_ptr)); +#endif + +/* Maintainer: Put new public prototypes here ^, in libpng.3, and project defs */ + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 - \ + (png_uint_16)(alpha)) + (png_uint_16)128); \ + (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } + +# define png_composite_16(composite, fg, alpha, bg) \ + { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(png_uint_32)(65535L - \ + (png_uint_32)(alpha)) + (png_uint_32)32768L); \ + (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } + +#else /* standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + (png_uint_16)127) / 255) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ + (png_uint_32)32767) / (png_uint_32)65535L) + +#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ + +/* These next functions are used internally in the code. They generally + * shouldn't be used unless you are writing code to add or replace some + * functionality in libpng. More information about most functions can + * be found in the files where the functions are located. + */ + +#if defined(PNG_INTERNAL) + +/* Various modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. + */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_HAVE_IDAT 0x04 +#define PNG_AFTER_IDAT 0x08 +#define PNG_HAVE_IEND 0x10 +#define PNG_HAVE_gAMA 0x20 +#define PNG_HAVE_cHRM 0x40 +#define PNG_HAVE_sRGB 0x80 +#define PNG_HAVE_CHUNK_HEADER 0x100 +#define PNG_WROTE_tIME 0x200 +#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 +#define PNG_BACKGROUND_IS_GRAY 0x800 +#define PNG_HAVE_PNG_SIGNATURE 0x1000 + +/* flags for the transformations the PNG library does on the image data */ +#define PNG_BGR 0x0001 +#define PNG_INTERLACE 0x0002 +#define PNG_PACK 0x0004 +#define PNG_SHIFT 0x0008 +#define PNG_SWAP_BYTES 0x0010 +#define PNG_INVERT_MONO 0x0020 +#define PNG_DITHER 0x0040 +#define PNG_BACKGROUND 0x0080 +#define PNG_BACKGROUND_EXPAND 0x0100 + /* 0x0200 unused */ +#define PNG_16_TO_8 0x0400 +#define PNG_RGBA 0x0800 +#define PNG_EXPAND 0x1000 +#define PNG_GAMMA 0x2000 +#define PNG_GRAY_TO_RGB 0x4000 +#define PNG_FILLER 0x8000L +#define PNG_PACKSWAP 0x10000L +#define PNG_SWAP_ALPHA 0x20000L +#define PNG_STRIP_ALPHA 0x40000L +#define PNG_INVERT_ALPHA 0x80000L +#define PNG_USER_TRANSFORM 0x100000L +#define PNG_RGB_TO_GRAY_ERR 0x200000L +#define PNG_RGB_TO_GRAY_WARN 0x400000L +#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ + /* 0x800000L Unused */ +#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ + /* 0x2000000L unused */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +/* flags for png_create_struct */ +#define PNG_STRUCT_PNG 0x0001 +#define PNG_STRUCT_INFO 0x0002 + +/* Scaling factor for filter heuristic weighting calculations */ +#define PNG_WEIGHT_SHIFT 8 +#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) +#define PNG_COST_SHIFT 3 +#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) + +/* flags for the png_ptr->flags rather than declaring a byte for each one */ +#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 +#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 +#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 +#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 +#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 +#define PNG_FLAG_ZLIB_FINISHED 0x0020 +#define PNG_FLAG_ROW_INIT 0x0040 +#define PNG_FLAG_FILLER_AFTER 0x0080 +#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 +#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 +#define PNG_FLAG_FREE_PLTE 0x1000 +#define PNG_FLAG_FREE_TRNS 0x2000 +#define PNG_FLAG_FREE_HIST 0x4000 +#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L +#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L +#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L +#define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ +#define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ + /* 0x800000L unused */ + /* 0x1000000L unused */ + /* 0x2000000L unused */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ + PNG_FLAG_CRC_ANCILLARY_NOWARN) + +#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ + PNG_FLAG_CRC_CRITICAL_IGNORE) + +#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ + PNG_FLAG_CRC_CRITICAL_MASK) + +/* save typing and make code easier to understand */ + +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +/* Added to libpng-1.2.6 JB */ +#define PNG_ROWBYTES(pixel_bits, width) \ + ((pixel_bits) >= 8 ? \ + ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \ + (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) ) + +/* PNG_OUT_OF_RANGE returns true if value is outside the range + ideal-delta..ideal+delta. Each argument is evaluated twice. + "ideal" and "delta" should be constants, normally simple + integers, "value" a variable. Added to libpng-1.2.6 JB */ +#define PNG_OUT_OF_RANGE(value, ideal, delta) \ + ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) + +/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */ +#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) +/* place to hold the signature string for a PNG file. */ +#ifdef PNG_USE_GLOBAL_ARRAYS + PNG_EXPORT_VAR (const png_byte FARDATA) png_sig[8]; +#else +#define png_sig png_sig_bytes(NULL) +#endif +#endif /* PNG_NO_EXTERN */ + +/* Constant strings for known chunk types. If you need to add a chunk, + * define the name here, and add an invocation of the macro in png.c and + * wherever it's needed. + */ +#define PNG_IHDR const png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} +#define PNG_IDAT const png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} +#define PNG_IEND const png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} +#define PNG_PLTE const png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'} +#define PNG_bKGD const png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'} +#define PNG_cHRM const png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'} +#define PNG_gAMA const png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'} +#define PNG_hIST const png_byte png_hIST[5] = {104, 73, 83, 84, '\0'} +#define PNG_iCCP const png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} +#define PNG_iTXt const png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'} +#define PNG_oFFs const png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'} +#define PNG_pCAL const png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'} +#define PNG_sCAL const png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'} +#define PNG_pHYs const png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'} +#define PNG_sBIT const png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'} +#define PNG_sPLT const png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'} +#define PNG_sRGB const png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'} +#define PNG_tEXt const png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'} +#define PNG_tIME const png_byte png_tIME[5] = {116, 73, 77, 69, '\0'} +#define PNG_tRNS const png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'} +#define PNG_zTXt const png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'} + +#ifdef PNG_USE_GLOBAL_ARRAYS +PNG_EXPORT_VAR (const png_byte FARDATA) png_IHDR[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_IDAT[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_IEND[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_PLTE[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_bKGD[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_cHRM[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_gAMA[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_hIST[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_iCCP[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_iTXt[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_oFFs[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_pCAL[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_sCAL[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_pHYs[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_sBIT[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_sPLT[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_sRGB[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_tEXt[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_tIME[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_tRNS[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_zTXt[5]; +#endif /* PNG_USE_GLOBAL_ARRAYS */ + + +/* Inline macros to do direct reads of bytes from the input buffer. These + * require that you are using an architecture that uses PNG byte ordering + * (MSB first) and supports unaligned data storage. I think that PowerPC + * in big-endian mode and 680x0 are the only ones that will support this. + * The x86 line of processors definitely do not. The png_get_int_32() + * routine also assumes we are using two's complement format for negative + * values, which is almost certainly true. + */ +#if defined(PNG_READ_BIG_ENDIAN_SUPPORTED) +# if defined(PNG_pCAL_SUPPORTED) || defined(PNG_oFFs_SUPPORTED) +# define png_get_int_32(buf) ( *((png_int_32p) (buf))) +# endif +# define png_get_uint_32(buf) ( *((png_uint_32p) (buf))) +# define png_get_uint_16(buf) ( *((png_uint_16p) (buf))) +#else +# if defined(PNG_pCAL_SUPPORTED) || defined(PNG_oFFs_SUPPORTED) +PNG_EXTERN png_int_32 png_get_int_32 PNGARG((png_bytep buf)); +# endif +PNG_EXTERN png_uint_32 png_get_uint_32 PNGARG((png_bytep buf)); +PNG_EXTERN png_uint_16 png_get_uint_16 PNGARG((png_bytep buf)); +#endif /* !PNG_READ_BIG_ENDIAN_SUPPORTED */ +PNG_EXTERN png_uint_32 png_get_uint_31 PNGARG((png_structp png_ptr, + png_bytep buf)); + +/* Initialize png_ptr struct for reading, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_read_struct instead). + */ +extern PNG_EXPORT(void,png_read_init) PNGARG((png_structp png_ptr)); +#undef png_read_init +#define png_read_init(png_ptr) png_read_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +extern PNG_EXPORT(void,png_read_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); + +/* Initialize png_ptr struct for writing, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_write_struct instead). + */ +extern PNG_EXPORT(void,png_write_init) PNGARG((png_structp png_ptr)); +#undef png_write_init +#define png_write_init(png_ptr) png_write_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +extern PNG_EXPORT(void,png_write_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +extern PNG_EXPORT(void,png_write_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); + +/* Allocate memory for an internal libpng struct */ +PNG_EXTERN png_voidp png_create_struct PNGARG((int type)); + +/* Free memory from internal libpng struct */ +PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)); + +PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr + malloc_fn, png_voidp mem_ptr)); +PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, + png_free_ptr free_fn, png_voidp mem_ptr)); + +/* Free any memory that info_ptr points to and reset struct. */ +PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_1_0_X +/* Function to allocate memory for zlib. */ +PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size)); + +/* Function to free memory for zlib */ +PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr)); + +#ifdef PNG_SIZE_T +/* Function to convert a sizeof an item to png_sizeof item */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +#endif + +/* Next four functions are used internally as callbacks. PNGAPI is required + * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3. */ + +PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif + +PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#if !defined(PNG_NO_STDIO) +PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr)); +#endif +#endif +#else /* PNG_1_0_X */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif +#endif /* PNG_1_0_X */ + +/* Reset the CRC variable */ +PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr)); + +/* Write the "data" buffer to whatever output you are using. */ +PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read data from whatever input you are using into the "data" buffer */ +PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read bytes into buf, and update png_ptr->crc */ +PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, + png_size_t length)); + +/* Decompress data in a chunk that uses compression */ +#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \ + defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) +PNG_EXTERN png_charp png_decompress_chunk PNGARG((png_structp png_ptr, + int comp_type, png_charp chunkdata, png_size_t chunklength, + png_size_t prefix_length, png_size_t *data_length)); +#endif + +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip)); + +/* Read the CRC from the file and compare it to the libpng calculated CRC */ +PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)); + +/* Calculate the CRC over a section of data. Note that we are only + * passing a maximum of 64K on systems that have this as a memory limit, + * since this is the maximum buffer size we can specify. + */ +PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, + png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +PNG_EXTERN void png_flush PNGARG((png_structp png_ptr)); +#endif + + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). + * The only currently known PNG chunks that use signed numbers are + * the ancillary extension chunks, oFFs and pCAL. + */ +PNG_EXTERN void png_save_uint_32 PNGARG((png_bytep buf, png_uint_32 i)); + +#if defined(PNG_WRITE_pCAL_SUPPORTED) || defined(PNG_WRITE_oFFs_SUPPORTED) +PNG_EXTERN void png_save_int_32 PNGARG((png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +PNG_EXTERN void png_save_uint_16 PNGARG((png_bytep buf, unsigned int i)); + +/* simple function to write the signature */ +PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr)); + +/* write various chunks */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. + */ +PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, + png_uint_32 height, + int bit_depth, int color_type, int compression_method, int filter_method, + int interlace_method)); + +PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette, + png_uint_32 num_pal)); + +PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr)); + +#if defined(PNG_WRITE_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, png_fixed_point + file_gamma)); +#endif +#endif + +#if defined(PNG_WRITE_sBIT_SUPPORTED) +PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit, + int color_type)); +#endif + +#if defined(PNG_WRITE_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr, + double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_WRITE_sRGB_SUPPORTED) +PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, + int intent)); +#endif + +#if defined(PNG_WRITE_iCCP_SUPPORTED) +PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr, + png_charp name, int compression_type, + png_charp profile, int proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr, + png_sPLT_tp palette)); +#endif + +#if defined(PNG_WRITE_tRNS_SUPPORTED) +PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans, + png_color_16p values, int number, int color_type)); +#endif + +#if defined(PNG_WRITE_bKGD_SUPPORTED) +PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr, + png_color_16p values, int color_type)); +#endif + +#if defined(PNG_WRITE_hIST_SUPPORTED) +PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist, + int num_hist)); +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, + png_charp key, png_charpp new_key)); +#endif + +#if defined(PNG_WRITE_tEXt_SUPPORTED) +PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len)); +#endif + +#if defined(PNG_WRITE_zTXt_SUPPORTED) +PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len, int compression)); +#endif + +#if defined(PNG_WRITE_iTXt_SUPPORTED) +PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr, + int compression, png_charp key, png_charp lang, png_charp lang_key, + png_charp text)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) /* Added at version 1.0.14 and 1.2.4 */ +PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_WRITE_oFFs_SUPPORTED) +PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr, + png_int_32 x_offset, png_int_32 y_offset, int unit_type)); +#endif + +#if defined(PNG_WRITE_pCAL_SUPPORTED) +PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose, + png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params)); +#endif + +#if defined(PNG_WRITE_pHYs_SUPPORTED) +PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr, + png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, + int unit_type)); +#endif + +#if defined(PNG_WRITE_tIME_SUPPORTED) +PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr, + png_timep mod_time)); +#endif + +#if defined(PNG_WRITE_sCAL_SUPPORTED) +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr, + int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, + int unit, png_charp width, png_charp height)); +#endif +#endif +#endif + +/* Called when finished processing a row of data */ +PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)); + +/* Internal use only. Called before first row of data */ +PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr)); + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr)); +#endif + +/* combine a row of data, dealing with alpha, etc. if requested */ +PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row, + int mask)); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) +/* expand an interlaced row */ +/* OLD pre-1.0.9 interface: +PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass, png_uint_32 transformations)); + */ +PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr)); +#endif + +/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* grab pixels out of a row for an interlaced pass */ +PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass)); +#endif + +/* unfilter a row */ +PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr, + png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter)); + +/* Choose the best filter to use and filter the row data */ +PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr, + png_row_infop row_info)); + +/* Write out the filtered row. */ +PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, + png_bytep filtered_row)); +/* finish a row while reading, dealing with interlacing passes, etc. */ +PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); + +/* initialize the row buffers, etc. */ +PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)); +/* optional call to update the users info structure */ +PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +/* these are the functions that do the transformations */ +#if defined(PNG_READ_FILLER_SUPPORTED) +PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 filler, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop + row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) +PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p sig_bits)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info, + png_bytep row, png_bytep palette_lookup, png_bytep dither_lookup)); + +# if defined(PNG_CORRECT_PALETTE_SUPPORTED) +PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette)); +# endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) +PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 bit_depth)); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p bit_depth)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background, + png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift)); +#else +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background)); +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift)); +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info, + png_bytep row, png_colorp palette, png_bytep trans, int num_trans)); +PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info, + png_bytep row, png_color_16p trans_value)); +#endif + +/* The following decodes the appropriate chunks, and does error correction, + * then calls the appropriate callback for the chunk if it is valid. + */ + +/* decode the IHDR chunk */ +PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); + +#if defined(PNG_READ_bKGD_SUPPORTED) +PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_cHRM_SUPPORTED) +PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_gAMA_SUPPORTED) +PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_hIST_SUPPORTED) +PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_iCCP_SUPPORTED) +extern void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_oFFs_SUPPORTED) +PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pCAL_SUPPORTED) +PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pHYs_SUPPORTED) +PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sBIT_SUPPORTED) +PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) +PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sPLT_SUPPORTED) +extern void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#if defined(PNG_READ_sRGB_SUPPORTED) +PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tIME_SUPPORTED) +PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tRNS_SUPPORTED) +PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); + +PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr, + png_bytep chunk_name)); + +/* handle the transformations for reading and writing */ +PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr)); + +PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr, + png_uint_32 length)); +PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row)); +PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr)); +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +/* png.c */ /* PRIVATE */ +PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr)); +#endif +/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ + +#endif /* PNG_INTERNAL */ + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* do not put anything past this line */ +#endif /* PNG_H */ diff --git a/.svn/pristine/51/51c5c6438b142c4a6d93af6a6d0b68e17b3e68a6.svn-base b/.svn/pristine/51/51c5c6438b142c4a6d93af6a6d0b68e17b3e68a6.svn-base new file mode 100644 index 0000000..718ab50 --- /dev/null +++ b/.svn/pristine/51/51c5c6438b142c4a6d93af6a6d0b68e17b3e68a6.svn-base @@ -0,0 +1,1557 @@ +/* +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 +*/ + +// +// DLL to provide interface to allow G8BPQ switch to use MultoPSK ALE400 Mode +// +// Uses BPQ EXTERNAL interface +// + + +#define _CRT_SECURE_NO_DEPRECATE + +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" +#include +#include + +#include "tncinfo.h" + +#include "bpq32.h" + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#define TIMESTAMP 352 + +#define CONTIMEOUT 1200 + + + +#define AGWHDDRLEN sizeof(struct AGWHEADER) + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); + +//int ResetExtDriver(int num); + +static void ConnecttoMPSKThread(void * portptr); + +void CreateMHWindow(); +int Update_MH_List(struct in_addr ipad, char * call, char proto); + +static int ConnecttoMPSK(int port); +static int ProcessReceivedData(int bpqport); +static int ProcessLine(char * buf, int Port); +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +VOID ProcessMPSKPacket(struct TNCINFO * TNC, char * Message, int Len); +struct TNCINFO * GetSessionKey(char * key, struct TNCINFO * TNC); +static VOID SendData(struct TNCINFO * TNC, char * Msg, int MsgLen); +static VOID DoMonitorHddr(struct TNCINFO * TNC, struct AGWHEADER * RXHeader, UCHAR * Msg); +VOID SendRPBeacon(struct TNCINFO * TNC); + +extern UCHAR BPQDirectory[]; + +#define MAXMPSKPORTS 16 + +//LOGFONT LFTTYFONT ; + +//HFONT hFont ; + +static int MPSKChannel[MAXBPQPORTS+1]; // BPQ Port to MPSK Port +static int BPQPort[MAXMPSKPORTS][MAXBPQPORTS+1]; // MPSK Port and Connection to BPQ Port + +extern int MasterPort[MAXBPQPORTS+1]; // Pointer to first BPQ port for a specific MPSK host + +// Each port may be on a different machine. We only open one connection to each MPSK instance + +static char * MPSKSignon[MAXBPQPORTS+1]; // Pointer to message for secure signin + +static unsigned int MPSKInst = 0; +static int AttachedProcesses=0; + +static HWND hResWnd,hMHWnd; +static BOOL GotMsg; + +static HANDLE STDOUT=0; + +//SOCKET sock; + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; +static SOCKADDR_IN destaddr[MAXBPQPORTS+1]; + +static int addrlen=sizeof(sinx); + +//static short MPSKPort=0; + +static time_t ltime,lasttime[MAXBPQPORTS+1]; + +static BOOL CONNECTING[MAXBPQPORTS+1]; +static BOOL CONNECTED[MAXBPQPORTS+1]; + +//HANDLE hInstance; + + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + +#ifndef LINBPQ + +static BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[200]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + char FN[MAX_PATH] = ""; + + if (TNC->ProgramPath == NULL) + return FALSE; + + GetWindowText(hwnd, wtext, 199); + + if (strstr(wtext,"* MULTIPSK")) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + TNC->PID = ProcessId; + return FALSE; + } + + return (TRUE); +} + +#endif + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + PMSGWITHLEN buffptr; + unsigned int txlen=0; + struct TNCINFO * TNC = TNCInfo[port]; + int Stream = 0; + struct STREAMINFO * STREAM; + int TNCOK; + + if (TNC == NULL) + return 0; // Port not defined + + // Look for attach on any call + + for (Stream = 0; Stream <= TNC->MPSKInfo->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + char Cmd[80]; + int len; + + // New Attach + + int calllen; + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + STREAM->FramesOutstanding = 0; + + // Stop Scanning + + sprintf(Cmd, "%d SCANSTOP", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Cmd); + + len = sprintf(Cmd, "%cSTOP_BEACON_ARQ_FAE\x1b", '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting + else + send(TNC->TCPSock, Cmd, len, 0); + + } + } + + switch (fn) + { + case 1: // poll + + if (MasterPort[port] == port) + { + // Only on first port using a host + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time( <ime ); + if (ltime-lasttime[port] >9 ) + { + ConnecttoMPSK(port); + lasttime[port]=ltime; + } + } + + FD_ZERO(&readfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPSock,&readfs); + + + FD_ZERO(&writefs); + + if (TNC->CONNECTING) FD_SET(TNC->TCPSock,&writefs); // Need notification of Connect + + if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPSock,&writefs); // Need notification of busy clearing + + + + FD_ZERO(&errorfs); + + if (TNC->CONNECTING ||TNC->CONNECTED) FD_SET(TNC->TCPSock,&errorfs); + + if (select((int)TNC->TCPSock+ 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock,&readfs)) + { + // data available + + ProcessReceivedData(port); + } + + if (FD_ISSET(TNC->TCPSock,&writefs)) + { + // Connect success + + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + // If required, send signon + + send(TNC->TCPSock,"\x1a", 1, 0); + send(TNC->TCPSock,"DIGITAL MODE ?", 14, 0); + send(TNC->TCPSock,"\x1b", 1, 0); + +// EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); + } + + if (FD_ISSET(TNC->TCPSock,&errorfs)) + { + + // if connecting, then failed, if connected then has just disconnected + +// if (CONNECTED[port]) +// if (!CONNECTING[port]) +// { +// i=sprintf(ErrMsg, "MPSK Connection lost for BPQ Port %d\r\n", port); +// WritetoConsole(ErrMsg); +// } + + CONNECTING[port]=FALSE; + CONNECTED[port]=FALSE; + + } + + } + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= TNC->MPSKInfo->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + // Have to time out connects, as TNC doesn't report failure + + if (STREAM->Connecting) + { + STREAM->Connecting--; + + if (STREAM->Connecting == 0) + { + // Report Connect Failed, and drop back to command mode + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "MPSK} Failure with %s\r", STREAM->RemoteCall); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->DiscWhenAllSent = 10; + + // Send Disc to TNC + + TidyClose(TNC, Stream); + } + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + + // if Busy, send buffer status poll + + if (STREAM->PACTORtoBPQ_Q == 0) + { + if (STREAM->DiscWhenAllSent) + { + STREAM->DiscWhenAllSent--; + if (STREAM->DiscWhenAllSent == 0) + STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion + } + } + else + { + int datalen; + + buffptr = Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + ReleaseBuffer(buffptr); + + return (1); + } + } + + if (TNC->PortRecord->UI_Q) + { + struct _MESSAGE * buffptr; + + SOCKET Sock; + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + Sock = TNCInfo[MasterPort[port]]->TCPSock; + + ReleaseBuffer((UINT *)buffptr); + } + + + return (0); + + + + case 2: // send + + + if (!TNCInfo[MasterPort[port]]->CONNECTED) return 0; // Don't try if not connected to TNC + + Stream = buff->PORT; + + STREAM = &TNC->Streams[Stream]; + +// txlen=(buff[6]<<8) + buff[5] - 8; + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - 8; + + if (STREAM->Connected) + { + SendData(TNC, buff->L2DATA, txlen); + } + else + { + char Command[80]; + int len; + + buff->L2DATA[txlen] = 0; + + _strupr(buff->L2DATA); + + if (_memicmp(buff->L2DATA, "D\r", 2) == 0) + { + TidyClose(TNC, buff->PORT); + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + // See if Local command (eg RADIO) + + if (_memicmp(buff->L2DATA, "RADIO ", 6) == 0) + { + char cmd[56]; + + strcpy(cmd, &buff->L2DATA[6]); + sprintf(buff->L2DATA, "%d %s", TNC->Port, cmd); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, buff->L2DATA)) + { + } + else + { + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "%s", buff->L2DATA); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (STREAM->Connecting && _memicmp(buff->L2DATA, "ABORT", 5) == 0) + { + len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + return (0); + } + + if (_memicmp(buff->L2DATA, "MODE", 4) == 0) + { + buff->L2DATA[txlen - 1] = 0; // Remove CR + + len = sprintf(Command,"%cDIGITAL MODE %s\x1b", '\x1a', &buff->L2DATA[5]); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + return (0); + } + + + if (_memicmp(buff->L2DATA, "INUSE?", 6) == 0) + { + // Return Error if in use, OK if not + + PMSGWITHLEN buffptr = GetBuff(); + int s = 0; + + while(s <= TNC->MPSKInfo->MaxSessions) + { + if (s != Stream) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[s]) + { + buffptr->Len = sprintf(buffptr->Data, "MPSK} Error - In use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return 1; // Busy + } + } + s++; + } + buffptr->Len = sprintf(buffptr->Data, "MPSK} Ok - Not in use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char * ptr; + char * context; + + buff->L2DATA[txlen] = 0; + _strupr(buff->L2DATA); + + memset(STREAM->RemoteCall, 0, 10); + + ptr = strtok_s(&buff->L2DATA[2], " ,\r", &context); + + if (ptr == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], + "MPSK} Error - Call missing from C command\r", STREAM->MyCall, STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->DiscWhenAllSent = 10; + return 0; + } + + strcpy(STREAM->RemoteCall, ptr); + + len = sprintf(Command,"%cCALLSIGN_TO_CALL_ARQ_FAE %s%c%cSELECTIVE_CALL_ARQ_FAE\x1b", + '\x1a', STREAM->RemoteCall, '\x1b', '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + STREAM->Connecting = TNC->MPSKInfo->ConnTimeOut; // It doesn't report failure + +// sprintf(Status, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); +// SetDlgItemText(TNC->hDlg, IDC_TNCSTATE, Status); + + return 0; + } + + // Send any other command to Multipsk + + buff->L2DATA[txlen - 1] = 0; + _strupr(buff->L2DATA); + + len = sprintf(Command,"%c%s\x1b", '\x1a', buff->L2DATA); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting + else + send(TNC->TCPSock, Command, len, 0); + + TNC->InternalCmd = TRUE; + + } + + return (0); + + case 3: + + Stream = (int)(size_t)buff; + + TNCOK = TNCInfo[MasterPort[port]]->CONNECTED; + + STREAM = &TNC->Streams[Stream]; + + if (STREAM->FramesOutstanding > 8) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); + + return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + break; + + case 4: // reinit + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + TNC->CONNECTED = FALSE; + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + + return (0); + + case 5: // Close + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return 0; + } + + return 0; +} + +#ifndef LINBPQ + +static KillTNC(struct TNCINFO * TNC) +{ + HANDLE hProc; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->PID == 0) return 0; + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + TNC->PID = 0; // So we don't try again + + return 0; +} + +static RestartTNC(struct TNCINFO * TNC) +{ + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char HomeDir[MAX_PATH]; + int i, ret; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (TNC->ProgramPath && TNC->DontRestart == 0) + { + strcpy(HomeDir, TNC->ProgramPath); + i = strlen(HomeDir); + + while(--i) + { + if (HomeDir[i] == '/' || HomeDir[i] == '\\') + { + HomeDir[i] = 0; + break; + } + } + ret = CreateProcess(TNC->ProgramPath, "MultiPSK TCP_IP_ON", NULL, NULL, FALSE,0 ,NULL ,HomeDir, &SInfo, &PInfo); + + if (ret) + TNC->PID = PInfo.dwProcessId; + + return ret; + } + return 0; +} +#endif + +void * MPSKExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + struct TNCINFO * TNC; + char * ptr; + + // + // Will be called once for each MPSK port to be mapped to a BPQ Port + // The MPSK port number is in CHANNEL - A=0, B=1 etc + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // Scan Control - None + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 64; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_MPSK; + + MPSKChannel[port] = PortEntry->PORTCONTROL.CHANNELNUM-65; + + PortEntry->MAXHOSTMODESESSIONS = 1; + + i=sprintf(Msg,"MPSK Host %s Port %d \n", + TNC->HostName, TNC->TCPPort); + + WritetoConsole(Msg); + + // See if we already have a port for this host + + MasterPort[port] = port; + + for (i = 1; i < port; i++) + { + if (i == port) continue; + + if (TNCInfo[i] && TNCInfo[i]->TCPPort == TNC->TCPPort && + _stricmp(TNCInfo[i]->HostName, TNC->HostName) == 0) + { + MasterPort[port] = i; + break; + } + } + + BPQPort[PortEntry->PORTCONTROL.CHANNELNUM-65][MasterPort[port]] = port; + +#ifndef LINBPQ + if (MasterPort[port] == port) + { + if (EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC)) + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + ConnecttoMPSK(port); + } +#endif + time(&lasttime[port]); // Get initial time value + +// SendMessage(0x40eaa, WM_COMMAND, 0x03000eaa, 0x40eaa); + + return ExtProc; +} + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + struct MPSKINFO * AGW; + + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + AGW = TNC->MPSKInfo = zalloc(sizeof(struct MPSKINFO)); // AGW Sream Mode Specific Data + + AGW->MaxSessions = 10; + AGW->ConnTimeOut = CONTIMEOUT; + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->TCPPort = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(TNC->TCPPort); + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "CONTIMEOUT", 10) == 0) + AGW->ConnTimeOut = atoi(&buf[11]) * 10; + else + if (_memicmp(buf, "UPDATEMAP", 9) == 0) + TNC->PktUpdateMap = TRUE; + else + if (_memicmp(buf, "ALEBEACON", 9) == 0) // Send Beacon after each session + TNC->MPSKInfo->Beacon = TRUE; + else + if (_memicmp(buf, "DEFAULTMODE", 11) == 0) // Send Beacon after each session + strcpy(TNC->MPSKInfo->DefaultMode, &buf[12]); + else + + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + +static int ConnecttoMPSK(int port) +{ + _beginthread(ConnecttoMPSKThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID ConnecttoMPSKThread(void * portptr) +{ + + int port = (int)(size_t)portptr; + char Msg[255]; + int err,i; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + + Sleep(5000); // Allow init to complete + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) return; // Resolve failed + + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + } + + if (TNC->TCPSock) + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for MPSK socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + + return; + } + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + TNC->CONNECTING = TRUE; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + + TNC->CONNECTED=TRUE; + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for MPSK socket - error code = %d\n", err); + WritetoConsole(Msg); + MySetWindowText(TNC->xIDC_COMMSSTATE, "Connection to TNC failed"); + + TNC->Alerted = TRUE; + } + + TNC->CONNECTING = FALSE; + return; + } + + TNC->LastFreq = 0; // so V4 display will be updated + + MySetWindowText(TNC->xIDC_COMMSSTATE, "Connected to MPSK TNC"); + + return; + +} + +static int ProcessReceivedData(int port) +{ + unsigned int bytes; + int i; + char ErrMsg[255]; + char Message[500]; + struct TNCINFO * TNC = TNCInfo[port]; + + // Need to extract messages from byte stream + + bytes = recv(TNC->TCPSock,(char *)&Message, 500, 0); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for MPSK socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "MPSK Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + // Have some data + + ProcessMPSKPacket(TNC, Message, bytes); // Data may be for another port + + return (0); + +} + +VOID ProcessMSPKCmd(struct TNCINFO * TNC); +VOID ProcessMSPKComment(struct TNCINFO * TNC); +VOID ProcessMSPKData(struct TNCINFO * TNC); + +VOID ProcessMPSKPacket(struct TNCINFO * TNC, char * Message, int Len) +{ + char * MPTR = Message; + +/* +3) each text character transmitted by the client to the server (for the Multipsk TX text editor) must be preceded by the character CHR(25) or CHR(22) in the case of a special link (KISS in Packet or Pax, for example). + +4) each command string transmitted by the client to the server must be preceded by the character CHR(26) and finished by CHR(27), + +5) each character effectively transmitted by Multipsk to the transceiver and transmitted to the client is preceded by the character CHR(28), + +6) each character received by Multipsk and transmitted to the client is preceded by the character CHR(29), + +7) each command string transmitted by the server to the client must be preceded by the character CHR(30) and finished by CHR(31), + +8) all commands (written in readable text ) will have an answer (see further for details), + +9) each server comment (Call ID or RS ID reception, switch to RX or to TX) string transmitted by the server to the client must be preceded by a string: "CHR(23)RX CALL ID=", "CHR(23)RX RS ID=", "CHR(23)SWITCH=RX", "CHR(23) SWITCH=TX", and finished by CHR(24). + +10) each server command, for the transceiver control, transmitted by the server to the client must be preceded by the string "CHR(23) XCVR=" and finished by CHR(24). + +Data + +End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] call "THIS I[End of TX] end of link to GM8BPQ[End of TX] sounding "THIS WAS"[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQ[End of TX] ARQ FAE CQFAE BEACON OH5RM Kouvola KP30JR +[End of TX] ARQ FAE selective callGM8BPQ DE OH5RM + +[Connection made with OH5RM] + + +18103 but I have to go out to change antenna + +[End of connection with OH5RM]FAE BEACON OH5RM Kouvola KP30JR +S" to GM8BPQ + +10:23:55 AM Comment: SWITCH=RX +10:24:00 AM Comment: RX RS ID=10:24:00 UTC ALE400 1609 Hz 0 MHz +10:24:19 AM Comment: RX RS ID=10:24:19 UTC ALE400 1604 Hz 0 MHz +10:25:04 AM Comment: SWITCH=TX +10:25:07 AM Comment: SWITCH=RX +10:25:15 AM Comment: SWITCH=TX +:30:22 AM Comment: SWITCH=RX +10:30:25 AM Comment: SWITCH=TX +10:30:27 AM Comment: SWITCH=RX +10:30:35 AM Comment: RX RS ID=10:30:35 UTC ALE400 1598 Hz 0 MHz + + +*/ + + // Reuse the HAL CMD and Data Buffers to build messages from TCP stream + + // See if sequence split over a packet boundary + + if (TNC->CmdEsc == 23) + { + TNC->CmdEsc = 0; + goto CommentEsc; + } + + if (TNC->CmdEsc == 29) + { + TNC->CmdEsc = 0; + goto DataEsc; + } + + if (TNC->CmdEsc == 30) + { + TNC->CmdEsc = 0; + goto CmdEsc; + } + + // No Split + + while(Len) + { + switch (*(MPTR++)) + { + case 29: // Data Char + + Len--; + DataEsc: + if (Len) + { + TNC->DataBuffer[TNC->DataLen++] = *MPTR; + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdEsc = 29; + + if (TNC->DataLen) + ProcessMSPKData(TNC); + + + return; // Nothing left + + case 30: + + Len --; + CmdEsc: + while (Len) + { + if (*MPTR == 31) // End of String + { + ProcessMSPKCmd(TNC); + TNC->CmdLen = 0; + + // Process any data left in buffer + + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdBuffer[TNC->CmdLen++] = *MPTR; + MPTR++; + Len--; + } + + TNC->CmdEsc = 30; + return; // Nothing left + + case 23: // Server Comment + + Len --; + CommentEsc: + while (Len) + { + if (*MPTR == 24) // End of String + { + // Process Comment + + ProcessMSPKCmd(TNC); + TNC->CmdLen = 0; + + // Process any data left in buffer + + MPTR++; + Len--; + goto OuterLoop; + } + + TNC->CmdBuffer[TNC->CmdLen++] = *MPTR; + MPTR++; + Len--; + } + + TNC->CmdEsc = 23; + return; // Nothing left + + default: + + Len--; + + } +OuterLoop:; + } + + if (TNC->DataLen) + ProcessMSPKData(TNC); +} + +VOID ProcessMSPKCmd(struct TNCINFO * TNC) +{ + TNC->CmdBuffer[TNC->CmdLen] = 0; + + if (strcmp(TNC->CmdBuffer, "SWITCH=TX") == 0) + TNC->MPSKInfo->TX = TRUE; + else + { + if (strcmp(TNC->CmdBuffer, "SWITCH=RX") == 0) + { + TNC->MPSKInfo->TX = FALSE; + + // See if a command was queued while busy + + if (TNC->CmdSet) + { + send(TNC->TCPSock, TNC->CmdSet, strlen(TNC->CmdSet), 0); + free (TNC->CmdSet); + TNC->CmdSet = NULL; + } + } + else + { + Debugprintf("MPSK CMD %s", TNC->CmdBuffer); + + if (TNC->InternalCmd) + { + PMSGWITHLEN buffptr = GetBuff(); + char * ptr = strstr(TNC->CmdBuffer, "OK"); + + if (ptr) + *(ptr+2) = 0; // Convert OKn to OK for BBS Connect Script + + TNC->InternalCmd = FALSE; + + if (buffptr) + { + buffptr->Len= sprintf(buffptr->Data, "MPSK} %s\r", TNC->CmdBuffer); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + + if (strstr(TNC->CmdBuffer, "STOP_SELECTIVE_CALL_ARQ_FAE OK")) + TNC->Streams[0].Connecting = FALSE; + + } + } + } +} + +VOID ProcessMSPKComment(struct TNCINFO * TNC) +{ + TNC->CmdBuffer[TNC->CmdLen] = 0; + Debugprintf("MPSK Comment %s", TNC->CmdBuffer); +} + +static int UnStuff(UCHAR * inbuff, int len) +{ + int i,txptr=0; + UCHAR c; + UCHAR * outbuff = inbuff; + + for (i = 0; i < len; i++) + { + c = inbuff[i]; + + if (c == 0xc0) + c = inbuff[++i] - 0x20; + + outbuff[txptr++]=c; + } + + return txptr; +} + +VOID ProcessMSPKData(struct TNCINFO * TNC) +{ + PMSGWITHLEN buffptr; + + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char * ptr; + int Len = TNC->DataLen; + + TNC->DataBuffer[TNC->DataLen] = 0; + + // Process Data + + if (STREAM->Connected) + { + ptr = strstr(TNC->DataBuffer, "[End of connection"); + + if (ptr) + { + // Disconnect + + TNC->DataLen = 0; + + if (STREAM->DiscWhenAllSent) + return; // Already notified + + if (STREAM->Connecting) + { + // Report Connect Failed, and drop back to command mode + + STREAM->Connecting = FALSE; + buffptr = GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "MPSK} Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->DiscWhenAllSent = 10; + + return; + } + + // Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + STREAM->DiscWhenAllSent = 10; + STREAM->FramesOutstanding = 0; + + return; + } + + // Pass to Application. Remove any transparency (hex 0xc0 used as an escape) + + buffptr = GetBuff(); + + if (TNC->DataBuffer[TNC->DataLen - 1] == 0xc0) + return; // Last char is an escape, so wait for the escaped char to arrive + + if (buffptr) + { + if (memchr(TNC->DataBuffer, 0xc0, TNC->DataLen)) + TNC->DataLen = UnStuff(TNC->DataBuffer, TNC->DataLen); + + buffptr->Len = TNC->DataLen; + memcpy(buffptr->Data, TNC->DataBuffer, TNC->DataLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->bytesRXed += TNC->DataLen; + } + + TNC->DataLen = 0; + return; + } + + // Not Connected. We get various status messages, including Connection made, + // but they may be split across packets, or have more that one to a packet. + // I think they are all CR/LF terminated . No they aren't! + + // Look for [] this seems to be what is important + +DataLoop: + + if (memcmp(TNC->DataBuffer, "[End of TX] ARQ FAE CQ", 22) == 0) + { + // Remove string from buffer + + if (Len == 22) // Most Likely + { + TNC->DataLen = 0; + return; + } + + TNC->DataLen -= 22; + memmove(TNC->DataBuffer, &TNC->DataBuffer[22], Len - 21); //Copy Null + Len -= 22; + goto DataLoop; + + } + + ptr = strchr(TNC->DataBuffer, '['); + + if (ptr) + { + // Start of a significant Message + + char * eptr = strchr(TNC->DataBuffer, ']'); + char CallFrom[20]; + char * cptr ; + + if (eptr == 0) + return; // wait for matching [] + + cptr = strstr(TNC->DataBuffer, "[Connection made with "); + + // TNC->DataLen -= LineLen; + // memmove(TNC->DataBuffer, &TNC->DataBuffer[LineLen], 1 + Len - LineLen); //Copy Null + // Len -= LineLen; + // goto DataLoop; + + + if (cptr) // Have a connection + { + + // Connected + + memcpy(CallFrom, &cptr[22], 18); + cptr = strchr(CallFrom, ']'); + if (cptr) + *cptr = 0; + + if (STREAM->Connecting) + { + // Connect Complete + + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = 0; + + buffptr = GetBuff(); + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", CallFrom); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + } + else + { + // Incoming. Look for a free Stream + + STREAM->Connected = TRUE; + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = 0; + + UpdateMH(TNC, CallFrom, '+', 'I'); + + ProcessIncommingConnect(TNC, CallFrom, Stream, FALSE); + + if (HFCTEXTLEN) + { + if (HFCTEXTLEN > 1) + SendData(TNC, HFCTEXT, HFCTEXTLEN); + } + else + { + if (FULL_CTEXT) + { + int Len = CTEXTLEN, CTPaclen = 50; + int Next = 0; + + while (Len > CTPaclen) // CTEXT Paclen + { + SendData(TNC, &CTEXTMSG[Next], CTPaclen); + Next += CTPaclen; + Len -= CTPaclen; + } + SendData(TNC, &CTEXTMSG[Next], Len); + } + } + } + } + + } + + // Doesnt contain [ - just discard + + TNC->DataLen = 0; + Debugprintf(TNC->DataBuffer); + return; + +} + + + +/* + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr[1] = RXHeader->DataLength; + memcpy(&buffptr[2], Message, RXHeader->DataLength); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + + return; + + + case 'd': // Disconnected + + + + case 'C': + + // Connect. Can be Incoming or Outgoing + + // "*** CONNECTED To Station [CALLSIGN]" When the other station starts the connection + // "*** CONNECTED With [CALLSIGN]" When we started the connection + + */ + + +VOID SendData(struct TNCINFO * TNC, char * Msg, int MsgLen) +{ + // Preceed each data byte with 25 (decimal) + + char * NewMsg = malloc (MsgLen * 4); + int n; + UCHAR c; + int ExtraLen = 0; + char * ptr = NewMsg; + char * inptr = Msg; + SOCKET sock = TNCInfo[MasterPort[TNC->Port]]->TCPSock; + + TNC->Streams[0].bytesTXed += MsgLen; + + for (n = 0; n < MsgLen; n++) + { + *(ptr++) = 25; + c = *inptr++; + + if (c < 0x20 || c == 0xc0) + { + if (c != 0x0d) + { + *ptr++ = 0x0c0; + *(ptr++) = 25; + *ptr++ = c + 0x20; + ExtraLen += 2; + continue; + } + } + + *ptr++ = c; + } + + send(sock, NewMsg, MsgLen * 2 + ExtraLen, 0); + + free(NewMsg); +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + char Command[80]; + int len; + + len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a'); + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Savde till not transmitting + else + send(TNC->TCPSock, Command, len, 0); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + char Cmd[80]; + int Len; + + sprintf(Cmd, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Cmd); + + Cmd[0] = 0; + + if (TNC->MPSKInfo->DefaultMode[0]) + sprintf(Cmd, "%cDIGITAL MODE %s\x1b", '\x1a', TNC->MPSKInfo->DefaultMode); + + if (TNC->MPSKInfo->Beacon) + sprintf(&Cmd[strlen(Cmd)], "%cBEACON_ARQ_FAE\x1b", '\x1a'); + + Len = strlen(Cmd); + + if(Len) + { + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting + else + send(TNC->TCPSock, Cmd, Len, 0); + } +} + diff --git a/.svn/pristine/53/53451cf22ab7d38afa5dce4d1b4fd14822a9f3d5.svn-base b/.svn/pristine/53/53451cf22ab7d38afa5dce4d1b4fd14822a9f3d5.svn-base new file mode 100644 index 0000000..5cf2b25 --- /dev/null +++ b/.svn/pristine/53/53451cf22ab7d38afa5dce4d1b4fd14822a9f3d5.svn-base @@ -0,0 +1,1467 @@ +// C equivalents of ASM STRUCS + +// Aug 2010 Extend Applmask to 32 bit + +#ifndef _ASMSTRUCS_ +#define _ASMSTRUCS_ + +#ifdef WIN32 + +#include "winsock2.h" +#include "ws2tcpip.h" +#include "winstdint.h" + +#endif + +#define BPQICON 2 + +#define BUFFLEN 400 +#define BUFFALLOC 464 // Actual size of buffer. Extra 64 bytes for a source code tag to help find buffer leaks + + +//#define ApplStringLen 48 // Length of each config entry +#define NumberofAppls 32 // Max APPLICATIONS= values +#define ALIASLEN 48 +#define MHENTRIES 30 // Entries in MH List + +#define MaxLockedRoutes 100 + +typedef int (FAR *FARPROCY)(); + +#define NRPID 0xcf // NETROM PID + +#define L4BUSY 0x80 // BNA - DONT SEND ANY MORE +#define L4NAK 0x40 // NEGATIVE RESPONSE FLAG +#define L4MORE 0x20 // MORE DATA FOLLOWS - FRAGMENTATION FLAG +#define L4COMP 0x10 // BPQ Specific use of spare it - data is compressed + +#define L4CREQ 1 // CONNECT REQUEST +#define L4CACK 2 // CONNECT ACK +#define L4DREQ 3 // DISCONNECT REQUEST +#define L4DACK 4 // DISCONNECT ACK +#define L4INFO 5 // INFORMATION +#define L4IACK 6 // INFORMATION ACK +#define L4RESET 7 // Paula's extension + + +extern char MYCALL[]; // 7 chars, ax.25 format +extern char MYALIASTEXT[]; // 6 chars, not null terminated +extern UCHAR L3RTT[7]; // 7 chars, ax.25 format +extern UCHAR L3KEEP[7]; // 7 chars, ax.25 format +//extern int SENDNETFRAME(); +extern struct _DATABASE * DataBase; +//extern APPLCALLS APPLCALLTABLE[8]; + +extern int MAXDESTS; + +//extern VOID * GETBUFF(); +//extern VOID SETUPNODEHEADER(); +extern VOID POSTDATAAVAIL(); + + +extern int DATABASE; +extern int ENDOFDATA; +extern int L3LIVES; +extern int NUMBEROFNODES; + +struct CMDX +{ + char String[12]; // COMMAND STRING + UCHAR CMDLEN; // SIGNIFICANT LENGTH +// VOID (*CMDPROC)(struct _TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);// COMMAND PROCESSOR + VOID (*CMDPROC)();// COMMAND PROCESSOR + size_t CMDFLAG; // FLAG/VALUE Offset + +}; + +struct APPLCONFIG +{ + char Command[12]; + char CommandAlias[48]; + char ApplCall[10]; + char ApplAlias[10]; + char L2Alias[10]; + int ApplQual; +}; + +typedef struct _MHSTRUC +{ + UCHAR MHCALL[7]; + UCHAR MHDIGIS[7][8]; + time_t MHTIME; + int MHCOUNT; + BYTE MHDIGI; + char MHFreq[12]; + char MHLocator[6]; +} MHSTRUC, *PMHSTRUC; + +// +// Session Record +// + +typedef struct _TRANSPORTENTRY +{ + UCHAR L4USER[7]; // CALL OF ORIGINATING USER + + union // POINTER TO TARGET HOST/LINK/DEST/PORT (if Pactor) + { + struct PORTCONTROL * PORT; + struct _LINKTABLE * LINK; + struct DEST_LIST * DEST; + struct _BPQVECSTRUC * HOST; + struct _EXTPORTDATA * EXTPORT; + } L4TARGET; + + UCHAR L4MYCALL[7]; // CALL WE ARE USING + + UCHAR CIRCUITINDEX; // OUR CIRCUIT INFO + UCHAR CIRCUITID; // + + UCHAR FARINDEX; // + UCHAR FARID; // OTHER END'S INFO + + UCHAR L4WINDOW; // WINDOW SIZE + UCHAR L4WS; // WINDOW START - NEXT FRAME TO ACK + UCHAR TXSEQNO; // + UCHAR RXSEQNO; // TRANSPORT LEVEL SEQUENCE INFO + UCHAR L4LASTACKED; // LAST SEQUENCE ACKED + + UCHAR FLAGS; // TRANSPORT LEVEL FLAGS + UCHAR NAKBITS; // NAK & CHOKE BITS TO BE SENT + struct _TRANSPORTENTRY * L4CROSSLINK; // POINTER TO LINKED L4 SESSION ENTRY + UCHAR L4CIRCUITTYPE; // BIT SIGNIFICANT - SEE BELOW + UCHAR KAMSESSION; // Session Number on KAM Host Mode TNC + struct DATAMESSAGE * L4TX_Q; + struct _L3MESSAGEBUFFER * L4RX_Q; + struct DATAMESSAGE * L4HOLD_Q; // FRAMES WAITING TO BE ACKED + struct _L3MESSAGEBUFFER * L4RESEQ_Q; // FRAMES RECEIVED OUT OF SEQUENCE + + UCHAR L4STATE; + USHORT L4TIMER; + UCHAR L4ACKREQ; // DATA ACK NEEDED + UCHAR L4RETRIES; // RETRY COUNTER + USHORT L4KILLTIMER; // IDLE CIRCUIT TIMER + USHORT SESSIONT1; // TIMEOUT FOR SESSIONS FROM HERE + UCHAR SESSPACLEN; // PACLEN FOR THIS SESSION + UCHAR BADCOMMANDS; // SUCCESSIVE BAD COMMANDS + UCHAR STAYFLAG; // STAY CONNECTED FLAG + UCHAR SPYFLAG; // SPY - CONNECT TO NODE VIA BBS CALLSIGN + + UCHAR RTT_SEQ; // SEQUENCE NUMBER BEING TIMED + uint32_t RTT_TIMER; // TIME ABOVE SEQUENCE WAS SENT + + USHORT PASSWORD; // AUTHORISATION CODE FOR REMOTE SYSOP + + UCHAR SESS_APPLFLAGS; // APPL FLAGS FOR THIS SESSION + + UCHAR Secure_Session; // Set if Host session from BPQTerminal or BPQMailChat + + VOID * DUMPPTR; // POINTER FOR REMOTnE DUMP MODE + VOID * PARTCMDBUFFER; // Save area for incomplete commmand + + long long Frequency; // If known - for CMS Reporting Hz + char RMSCall[10]; + UCHAR Mode; // ditto + + int UNPROTO; // Unproto Mode flag - port number if in unproto mode + int UAddrLen; // + char UADDRESS[64]; // Unproto Address String - Dest + Digis + + uint64_t LISTEN; // Port Mask if in Listen Mode + + char APPL[16]; // Set if session initiated by an APPL + int L4LIMIT; // Idle time for this Session + + // Now support compressing NetRom Sessions. + // We collect as much data as possible before compressing and re-packetizing + + int AllowCompress; + + unsigned char * unCompress; // Data being saved to uncompress + int unCompressLen; + + int Sent; + int SentAfterCompression; + + int Received; + int ReceivedAfterExpansion; + + +} TRANSPORTENTRY; + +// +// CIRCUITTYPE EQUATES +// + +#define L2LINK 1 +#define SESSION 2 +#define UPLINK 4 +#define DOWNLINK 8 +#define BPQHOST 32 +#define PACTOR 64 + +typedef struct ROUTE +{ + // Adjacent Nodes + + UCHAR NEIGHBOUR_CALL[7]; // AX25 CALLSIGN + UCHAR NEIGHBOUR_DIGI1[7]; // DIGIS EN ROUTE (MAX 2 - ?? REMOVE) + UCHAR NEIGHBOUR_DIGI2[7]; // DIGIS EN ROUTE (MAX 2 - ?? REMOVE) + + UCHAR NEIGHBOUR_PORT; + UCHAR NEIGHBOUR_QUAL; + UCHAR NEIGHBOUR_FLAG; // SET IF 'LOCKED' ROUTE + +#define LOCKEDBYCONFIG 1 +#define LOCKEDBYSYSOP 2 + + struct _LINKTABLE * NEIGHBOUR_LINK; // POINTER TO LINK FOR THIS NEIGHBOUR + + USHORT NEIGHBOUR_TIME; // TIME LAST HEARD (HH MM) + + int NBOUR_IFRAMES; // FRAMES SENT/RECEIVED + int NBOUR_RETRIES; // RETRASMISSIONS + + UCHAR NBOUR_MAXFRAME; // FOR OPTIMISATION CODE + UCHAR NBOUR_FRACK; + UCHAR NBOUR_PACLEN; + + BOOL INP3Node; + BOOL NoKeepAlive; // Suppress Keepalive Processing + int LastConnectAttempt; // To stop us trying too often + + int Status; // + int LastRTT; // Last Value Reported + int RTT; // Current + int SRTT; // Smoothed RTT + int NeighbourSRTT; // Other End SRTT +// int RTTIncrement; // Average of Ours and Neighbours SRTT in 10 ms + int BCTimer; // Time to next L3RTT Broadcast + int Timeout; // Lost Response Timer + int Retries; // Lost Response Count + struct _L3MESSAGEBUFFER * Msg; // RIF being built + + int OtherendsRouteQual; // Route quality used by other end. + int OtherendLocked; // Route quality locked by ROUTES entry. + int FirstTimeFlag; // Set once quality has been set by direct receive + +} *PROUTE; + +// Status Equates + +#define GotRTTRequest 1 // Other end has sent us a RTT Packet +#define GotRTTResponse 2 // Other end has sent us a RTT Response +#define GotRIF 4 // Other end has sent RIF, so is probably an INP3 Node + // (could just be monitoring RTT for some reason +#define SentOurRIF 16 // Set when we have sent a rif for our Call and any ApplCalls + // (only sent when we have seen both a request and response) + +#pragma pack(1) + +typedef struct _L3MESSAGEBUFFER +{ +// +// NETROM LEVEL 3 MESSAGE with Buffer Header +// + struct _L3MESSAGEBUFFER * Next; + UCHAR Port; + SHORT LENGTH; + UCHAR L3PID; // PID + + UCHAR L3SRCE[7]; // ORIGIN NODE + UCHAR L3DEST[7]; // DEST NODE + UCHAR L3TTL; // TX MONITOR FIELD - TO PREVENT MESSAGE GOING + // ROUND THE NETWORK FOR EVER DUE TO ROUTING LOOP +// +// NETROM LEVEL 4 DATA +// + UCHAR L4INDEX; // TRANSPORT SESSION INDEX + UCHAR L4ID; // TRANSPORT SESSION ID + UCHAR L4TXNO; // TRANSMIT SEQUENCE NUMBER + UCHAR L4RXNO; // RECEIVE (ACK) SEQ NUMBER + UCHAR L4FLAGS; // FRAGMENTATION, ACK/NAK, FLOW CONTROL AND MSG TYPE BITS + + UCHAR L4DATA[236] ; //DATA + +} L3MESSAGEBUFFER, *PL3MESSAGEBUFFER; + + +typedef struct _L3MESSAGE +{ +// +// NETROM LEVEL 3 MESSAGE - WITHOUT L2 INFO +// + UCHAR L3SRCE[7]; // ORIGIN NODE + UCHAR L3DEST[7]; // DEST NODE + UCHAR L3TTL; // TX MONITOR FIELD - TO PREVENT MESSAGE GOING // ROUND THE NETWORK FOR EVER DUE TO ROUTING LOOP +// +// NETROM LEVEL 4 DATA +// + UCHAR L4INDEX; // TRANSPORT SESSION INDEX + UCHAR L4ID; // TRANSPORT SESSION ID + UCHAR L4TXNO; // TRANSMIT SEQUENCE NUMBER + UCHAR L4RXNO; // RECEIVE (ACK) SEQ NUMBER + UCHAR L4FLAGS; // FRAGMENTATION, ACK/NAK, FLOW CONTROL AND MSG TYPE BITS + + UCHAR L4DATA[236] ; //DATA + +} L3MESSAGE, *PL3MESSAGE; + +#define MSGHDDRLEN (USHORT)(sizeof(VOID *) + sizeof(UCHAR) + sizeof(USHORT)) + +typedef struct _MESSAGE +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGE * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + +// MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + + union + { /* array named screen */ + UCHAR L2DATA[256]; + struct _L3MESSAGE L3MSG; + }; + + UCHAR Padding[BUFFLEN - (sizeof(time_t) + (2 * sizeof(unsigned short)) + sizeof(VOID *) + 256 + MSGHDDRLEN + 16)]; // 16 = Addrs CTL PID + + time_t Timestamp; + struct _LINKTABLE * Linkptr; // For ACKMODE processing + unsigned short Process; // Process that got buffer + unsigned short GuardZone; // Should always be zero + +}MESSAGE, *PMESSAGE; + +#define MAXDATA BUFFLEN - (sizeof(time_t) + (2 * sizeof(unsigned short)) + sizeof(VOID *) + MSGHDDRLEN + 16) // 16 = Addrs CTL PID + + +typedef struct HDDRWITHDIGIS +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGE * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + UCHAR DIGIS[8][7]; + + UCHAR CTL; + UCHAR PID; + + union + { + UCHAR L2DATA[256]; + struct _L3MESSAGE L3MSG; + + }; + + UCHAR Padding[BUFFLEN - (sizeof(time_t) + (2 * sizeof(unsigned short)) + sizeof(VOID *) + 256 + 56 + MSGHDDRLEN + 16)]; // 16 = Addrs CTL PID + + time_t Timestamp; + struct _LINKTABLE * Linkptr; // For ACKMODE processing + unsigned short Process; // Process that got buffer + unsigned short GuardZone; + +} DIGIMESSAGE, *PDIGIMESSAGE; + + +typedef struct DATAMESSAGE +{ +// BASIC LINK LEVEL MESSAGE HEADERLAYOUT + + struct DATAMESSAGE * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR PID; + + UCHAR L2DATA[256]; + +} *PDATAMESSAGE; + +typedef struct MSGWITHLEN +{ + // BASIC LINK LEVEL MESSAGE HEADERLAYOUT + + struct MSGWITHLEN * Next; + size_t Len; + UCHAR Data[256]; + +} *PMSGWITHLEN; + +typedef struct MSGWITHOUTLEN +{ + // Basic Chained Buffer + + struct MSGWITHLEN * Next; + UCHAR Data[256]; + +} *PMSGWITHOUTLEN; + +// +// BPQHOST MODE VECTOR STRUC +// + +#pragma pack() + +typedef struct _BPQVECSTRUC +{ + struct _TRANSPORTENTRY * HOSTSESSION; // Pointer to Session + UCHAR HOSTFLAGS; // ALLOCATED AND STATE CHANGE FLAGS + ULONG HOSTAPPLMASK; + UCHAR HOSTAPPLFLAGS; + UCHAR HOSTSTREAM; // STREAM NUMBER + PMESSAGE HOSTTRACEQ; + HWND HOSTHANDLE; // HANDLE FOR POSTING MSGS TO + ULONG HOSTAPPLNUM; // Application Number + ULONG STREAMOWNER; // PID of Process owning stream + char PgmName[32]; // Program Name; + + +} BPQVECSTRUC, *PBPQVECSTRUC; + +typedef struct _APPLCALLS +{ + +// Application Calls/Alias Supports multiple L4 application calls + + UCHAR APPLCALL[7]; // ax.25 + char APPLALIAS_TEXT[10]; // TEXT, WITH APPENDED SPACE + + char APPLCALL_TEXT[10]; + UCHAR APPLALIAS[6]; + char Filler; // So we can use ConvtoAX25 on Alias + USHORT APPLQUAL; + struct DEST_LIST * NODEPOINTER; // Pointer to "NODES" entry for this App (if L4) + + char APPLCMD[13]; // + BOOL APPLHASALIAS; + int APPLPORT; // Port used if APPL has an Alias + char APPLALIASVAL[48]; // Alias if defined + UCHAR L2ALIAS[7]; // Additional Alias foe L2 connects + +} APPLCALLS; + +// +// We store the Time Received from our neighbour. Before using it we add our current route SRTT +// This way our times adjust to changes of neighbour SRTT. We can't cater for changes to other hop RTTs, +// But if these are significant (say 25% or 100 ms) they will be retransmitted + +typedef struct NR_DEST_ROUTE_ENTRY +{ + struct ROUTE * ROUT_NEIGHBOUR; // POINTER TO NEXT NODE IN PATH + UCHAR ROUT_QUALITY; // QUALITY + UCHAR ROUT_OBSCOUNT; + UCHAR ROUT_LOCKED; + UCHAR Padding[4]; // SO Entries are the same length +} *PNR_DEST_ROUTE_ENTRY; + +typedef struct DEST_ROUTE_ENTRY +{ + struct ROUTE * ROUT_NEIGHBOUR; // POINTER TO NEXT NODE IN PATH + USHORT LastRTT; // Last Value Reported + USHORT RTT; // Current + USHORT SRTT; // Smoothed RTT + UCHAR Hops; +} *PDEST_ROUTE_ENTRY; + +typedef struct DEST_LIST +{ + // L4 Destinations (NODES) + + struct DEST_LIST * DEST_CHAIN; // SORTED LIST CHAIN + + UCHAR DEST_CALL[7]; // DESTINATION CALLSIGN (AX25 FORMAT) + UCHAR DEST_ALIAS[6]; + + UCHAR DEST_STATE; // CONTROL BITS - SETTING UP, ACTIVE ETC + UCHAR DEST_LOCKED; + + UCHAR DEST_ROUTE; // CURRENTY ACTIVE DESTINATION + UCHAR INP3FLAGS; + + struct NR_DEST_ROUTE_ENTRY NRROUTE[3];// Best 3 NR neighbours for this dest + struct DEST_ROUTE_ENTRY ROUTE[3]; // Best 3 INP neighbours for this dest + + void * DEST_Q; // QUEUE OF FRAMES FOR THIS DESTINATION + + int DEST_RTT; // SMOOTHED ROUND TRIP TIMER + int DEST_COUNT; // FRAMES SENT + +} dest_list; + +// IMNP3FLAGS Equates + +#define NewNode 1 // Just added to table, so need to pass on + +struct XDIGI +{ + struct XDIGI * Next; // Chain + + UCHAR Call[7]; + UCHAR Alias[7]; + int Port; + BOOL UIOnly; +}; + +struct WL2KInfo +{ + struct WL2KInfo * Next; + + char * Host; + short WL2KPort; + + char RMSCall[10]; + char BaseCall[10]; + char GridSquare[7]; + char Times[80]; + char ServiceCode[17]; + + BOOL UseRigCtrlFreqs; + char WL2KFreq[12]; + char xWL2KMode; // WL2K reporting mode + char WL2KModeChar; // W or N + BOOL DontReportNarrowOnWideFreqs; + +// char NARROWMODE; +// char WIDEMODE; // Mode numbers to report to WL2K + +// struct WL2KInfo WL2KInfoList[MAXFREQS]; // Freqs for sending to WL2K + + long long Freq; + char Bandwidth; +// char * TimeList; // eg 06-10,12-15 + int mode; // see below (an integer) + int baud; // see below (an integer) + int power; // actual power if known, default to 100 for HF, 30 for VHF/UHF (an integer) + int height; // antenna height in feet if known, default to 25 + int gain; // antenna gain if known, default to 0 + int direction; // primary antenna direction in degrees if known, use 000 for omni (an integer) + BOOL RPonPTC; // Set if scanning for Robust Packet on a PTC +}; + + +typedef struct PORTCONTROL +{ + UCHAR PORTCALL[7]; + UCHAR PORTALIAS[7]; //USED FOR UPLINKS ONLY + char PORTNUMBER; + char PROTOCOL; // PORT PROTOCOL + // 0 = KISS, 2 = NETROM, 4 = BPQKISS + //; 6 = HDLC, 8 = L2 + + struct PORTCONTROL * PORTPOINTER; // NEXT IN CHAIN + + PMESSAGE PORTRX_Q; // FRAMES RECEIVED ON THIS PORT + PMESSAGE PORTTX_Q; // FRAMES TO BE SENT ON THIS PORT + + void (FAR * PORTTXROUTINE)(struct _EXTPORTDATA * PORTVEC, MESSAGE * Buffer); // POINTER TO TRANSMIT ROUTINE FOR THIS PORT + void (FAR * PORTRXROUTINE)(struct _EXTPORTDATA * PORTVEC); // POINTER TO RECEIVE ROUTINE FOR THIS PORT + void (FAR * PORTINITCODE)(struct PORTCONTROL * PortVector); // INITIALISATION ROUTINE + void (FAR * PORTTIMERCODE)(struct PORTCONTROL * PortVector); // + void (FAR * PORTCLOSECODE)(struct PORTCONTROL * PortVector); // CLOSE ROUTINE + int (FAR * PORTTXCHECKCODE)(struct PORTCONTROL * PORTVEC, int Chan); // OK to TX Check + BOOL (FAR * PORTSTOPCODE)(struct PORTCONTROL * PORT); // Temporarily Stop Port + BOOL (FAR * PORTSTARTCODE)(struct PORTCONTROL * PORT); // Restart Port + BOOL PortStopped; // STOPPORT command used + BOOL PortSuspended; // Suspended by interlock + + char PORTDESCRIPTION[31];// TEXT DESCRIPTION OF FREQ/SPEED ETC (31 so null terminated) + + UCHAR PORTQUALITY; // 'STANDARD' QUALITY FOR THIS PORT + + char PORTBBSFLAG; // NZ MEANS PORT CALL/ALIAS ARE FOR BBS + char PORTL3FLAG; // NZ RESTRICTS OUTGOING L2 CONNECTS +// +// CWID FIELDS +// + + USHORT CWID[9]; // 8 ELEMENTS + FLAG + USHORT ELEMENT; // REMAINING BITS OF CURRENT CHAR + USHORT * CWPOINTER; // POINTER TO NEXT CHAR + USHORT CWIDTIMER; // TIME TO NEXT ID + char CWSTATE; // STATE MACHINE FOR CWID + char CWTYPE; // SET TO USE ON/OFF KEYING INSTEAD OF + // FSK (FOR RUH MODEMS) + UCHAR PORTMINQUAL; // MIN QUAL TO BRAOCAST ON THIS PORT + +// STATS COUNTERS + + int L2DIGIED; + int L2FRAMES; + int L2FRAMESFORUS; + int L2FRAMESSENT; + int L2TIMEOUTS; + int L2ORUNC; // OVERRUNS + int L2URUNC; // UNDERRUNS + int L1DISCARD; // FRAMES DISCARDED (UNABLE TO TX DUE TO DCD) + int L2FRMRRX; + int L2FRMRTX; + int RXERRORS; // RECEIVE ERRORS + int L2REJCOUNT; // REJ FRAMES RECEIVED + int L2OUTOFSEQ; // FRAMES RECEIVED OUT OF SEQUENCE + int L2RESEQ; // FRAMES RESEQUENCED + + USHORT SENDING; // LINK STATUS BITS + USHORT ACTIVE; + + UCHAR AVSENDING; // LAST MINUTE + UCHAR AVACTIVE; + + char PktFlags[64]; // Decode stts rom QtSM + + char PORTTYPE; // H/W TYPE + // 0 = ASYNC, 2 = PC120, 4 = DRSI + // 6 = TOSH, 8 = QUAD, 10 = RLC100 + // 12 = RLC400 14 = INTERNAL 16 = EXTERNAL + + + USHORT IOBASE; // CONFIG PARAMS FOR HARDWARE DRIVERS + + char INTLEVEL; // NEXT 4 SAME FOR ALL H/W TYPES + int BAUDRATE; // SPEED + char CHANNELNUM; // ON MULTICHANNEL H/W + struct PORTCONTROL * INTCHAIN; // POINTER TO NEXT PORT USING THIS LEVEL + UCHAR PORTWINDOW; // L2 WINDOW FOR THIS PORT + USHORT PORTTXDELAY; // TX DELAY FOR THIS PORT + UCHAR PORTPERSISTANCE; // PERSISTANCE VALUE FOR THIS PORT + UCHAR FULLDUPLEX; // FULL DUPLEX IF SET + UCHAR SOFTDCDFLAG; // IF SET USE 'SOFT DCD' - IF MODEM CANT GIVE A REAL ONE + UCHAR PORTSLOTTIME; // SLOT TIME + UCHAR PORTTAILTIME; // TAIL TIME + UCHAR PORTT1; // L2 TIMEOUT + UCHAR PORTT2; // L2 DELAYED ACK TIMER + UCHAR PORTN2; // RETRIES + UCHAR PORTPACLEN; // DEFAULT PACLEN FOR INCOMING SESSIONS + + UINT * PORTINTERRUPT; // ADDRESS OF INTERRUPT HANDLER + + UCHAR QUAL_ADJUST; // % REDUCTION IN QUALITY IF ON SAME PORT + + char * PERMITTEDCALLS; // POINTER TO PERMITED CALLS LIST + char * PORTUNPROTO; // POINTER TO UI DEST AND DIGI LIST + UCHAR PORTDISABLED; // PORT TX DISABLE FLAG + char DIGIFLAG; // ENABLE/DISABLE/UI ONLY + UCHAR DIGIPORT; // CROSSBAND DIGI PORT + USHORT DIGIMASK; // CROSSBAND DIGI MASK + UCHAR USERS; // MAX USERS ON PORT + USHORT KISSFLAGS; // KISS SPECIAL MODE BITS + UCHAR PORTINTERLOCK; // TO DEFINE PORTS WHICH CANT TX AT SAME TIME + // + // PORTINTERLOCK was also used to prevent use of Attach'able ports at the same time + // and was the RADIO number used in rig control + // + // Now a port can have both a rx and tx radio definition, so Attach now + // Checks them (Transferred to TNC->TXRADIO and RXRADIO + // + // For backward compatibility of configs, if INTERLOCK is set on an Attachable + // port it is set as a default tx and rx radio + // + // Acually may not need the following PORT fields + + UCHAR PORTXRADIO; // TO DEFINE PORTS WHICH CANT TX Attach AT SAME TIME + UCHAR PORRXRADIO; // TO DEFINE PORTS WHICH CANT TX Attach AT SAME TIME + UCHAR NODESPACLEN; // MAX LENGTH OF 'NODES' MSG + UCHAR TXPORT; // PORT FOR SHARED TX OPERATION + MHSTRUC * PORTMHEARD; // POINTER TO MH DATA + + USHORT PARAMTIMER; // MOVED FROM HW DATA FOR SYSOPH + UCHAR PORTMAXDIGIS; // DIGIS ALLOWED ON THIS PORT + UCHAR PORTALIAS2[7]; // 2ND ALIAS FOR DIGIPEATING FOR APRS + UCHAR PORTBCALL[7]; // Source call for Beacon + char PortNoKeepAlive; // Default to no Keepalives + char PortUIONLY; // UI only port - no connects + char UICAPABLE; // Pactor-style port that can do UI + + struct WL2KInfo WL2KInfo; // WL2K Report for this Port + struct in_addr PORTIPADDR; // IP address for "KISS over UDP" + int ListenPort; // For KISS over UDP, if Different TX and RX Ports needed + BOOL KISSTCP; // TCP instead of UDP for KISS + BOOL KISSSLAVE; // TCP KISS is Slave + + char * SerialPortName; // Serial Port Name for Unix + struct XDIGI * XDIGIS; // Cross port digi setup + + BOOL NormalizeQuality; // Normalise Node Qualities + BOOL IgnoreUnlocked; // Ignore Unlocked routes + BOOL INP3ONLY; // Default to INP3 and disallow NODES + + void (* UIHook)(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); // Used for KISSARQ + struct PORTCONTROL * HookPort; + int PortSlot; // Index in Port Table + struct TNCINFO * TNC; // Associated TNC record + int HWType; // Hardware type of Driver. In here as external apps don't have access to TNC record + int RIGPort; // Linked port for freq resporting + unsigned int PERMITTEDAPPLS; // Appls allowed on this port (generalisation of BBSBANNED) + char * CTEXT; // Port Specific CText + char Hide; // Hide from port display and AGW connect menu + TRANSPORTENTRY * Session; // For Response to KISS command + time_t LastKISSCmdTime; + time_t LastSmartIDTime; // For SmartID - ID only if packets sent recently + time_t SmartIDNeeded; // Time to send next smart ID + time_t SmartIDInterval; // Smart ID Interval (Secs) + int SendtoM0LTEMap; + uint64_t PortFreq; // Configured freq + char * M0LTEMapInfo; + int QtSMPort; + BOOL QtSMConnected; + + int StatsPointer; + UCHAR * TX; // % Sending + UCHAR * BUSY; // % Active (Normally DCD active or TX) + +} PORTCONTROLX, *PPORTCONTROL; + +typedef struct FULLPORTDATA +{ + struct PORTCONTROL PORTCONTROL; + UCHAR HARDWAREDATA[300]; // WORK AREA FOR HARDWARE DRIVERS +} *PFULLPORTDATA; + +// KISS Mapping of HARDWAREDATA + +typedef struct KISSINFO +{ + struct PORTCONTROL PORT; + + int LINKSTS; // CURRENT STATE + UINT * CURALP; // CURRENT BUFFER + UINT * NEXTCHR; // + void * ASYNCMSG_Q; // RECEIVED MESSAGES + void * KISSTX_Q ; // MESSAGES TO SEND + int ESCFLAG ; // ; SET IF LAST RX CHAR WAS DLE + int ESCTXCHAR; // ; CHAR TO SEND FOLLOWING DLE IF NZ + + struct KISSINFO * FIRSTPORT; // ; FIRST PORT DEFINED FOR THIS IO ADDR + struct KISSINFO * SUBCHAIN; // ; NEXT SUBCHANNEL FOR SAME PHYSICAL PORT + + int OURCTRL; // ; CONTROL BYTE FOR THIS PORT + + int XCTRL; // CONTROL BYTE TO SEND + int REALKISSFLAGS; // ; KISS FLAGS FOR ACTIVE SUBPORT + + USHORT TXCCC; // ; NETROM/BPQKISS CHECKSUMS + USHORT RXCCC; // + + int TXACTIVE; // TIMER TO DETECT 'HUNG' SENDS + + int POLLFLAG; // POLL OUTSTANDING FOR MULTIKISS + + struct KISSINFO * POLLPOINTER; // LAST GROUP POLLED + int POLLED; // SET WHEN POLL RECEIVED + + UCHAR * KISSCMD; // Commands to be sent when port opened + int KISSCMDLEN; + + int PTTMode; // PTT Mode Flags + int PTTState; // Current State + uint64_t PTTActivemS; // For Stats + uint64_t PTTonTime; // + + uint64_t BusyActivemS; // For channel busy stats + uint64_t BusyonTime; + + char * QtSMModem; + int QtSMFreq; + int QtSMStats; // Set if stats received as KISS Command + +// UCHAR WIN32INFO[16]; // FOR WINDOWS DRIVER +} *PKISSINFO; + +// EXT Driver Mapping of HARDWAREDATA + + +typedef struct _EXTPORTDATA +{ + struct PORTCONTROL PORTCONTROL ; // REMAP HARDWARE INFO + + void * (* PORT_EXT_ADDR) (int fn, int port, PDATAMESSAGE buff); // ADDR OF RESIDENT ROUTINE + char PORT_DLL_NAME[16]; + UCHAR EXTRESTART; // FLAG FOR DRIVER REINIT + HINSTANCE DLLhandle; + int MAXHOSTMODESESSIONS; // Max Host Sessions supported (Used for KAM Pactor + ax.25 support) + struct _TRANSPORTENTRY * ATTACHEDSESSIONS[27]; // For PACTOR. etc + BOOL PERMITGATEWAY; // Set if ax.25 ports can change callsign (ie SCS, not KAM + int SCANCAPABILITIES; //Type of scan control Controller supports (None, Simple, Connect Lock) +#define NONE 0 +#define SIMPLE 1 +#define CONLOCK 2 + + void * UI_Q; // Unproto Frames for Session Mode Drivers (TRK, etc) + int FramesQueued; // TX Frames queued in Driver + +} EXTPORTDATA, *PEXTPORTDATA; + +typedef struct _HDLCDATA +{ + struct PORTCONTROL PORTCONTROL ; // REMAP HARDWARE INFO +// +// Mapping of VXD fields (mainly to simplify debugging +// + + ULONG ASIOC; // A CHAN ADDRESSES + ULONG SIO; // OUR ADDRESSES (COULD BE A OR B) + ULONG SIOC; + ULONG BSIOC; // B CHAN CONTROL + + struct _HDLCDATA * A_PTR; // PORT ENTRY FOR A CHAN + struct _HDLCDATA * B_PTR; // PORT ENTRY FOR B CHAN + + VOID (FAR * VECTOR[4]) (); // INTERRUPT VECTORS + +// UINT * IOTXCA; // INTERRUPT VECTORS +// UINT * IOTXEA; +// UINT * IORXCA; +// UINT * IORXEA; + + UCHAR LINKSTS; + + UINT * SDRNEXT; + UINT * SDRXCNT; + UINT * CURALP; + UCHAR OLOADS; // LOCAL COUNT OF BUFFERS SHORTAGES + USHORT FRAMELEN; + UINT * SDTNEXT; // POINTER to NEXT BYTE to TRANSMIT + USHORT SDTXCNT; // CHARS LEFT TO SEND + UCHAR RR0; // CURRENT RR0 + UINT * TXFRAME; // ADDRESS OF FRAME BEING SENT + + UCHAR SDFLAGS; // GENERAL FLAGS + + void * PCTX_Q; // HDLC HOLDING QUEUE + void * RXMSG_Q; // RX INTERRUPT TO SDLC BG + + +//;SOFTDCD DB 0 ; RX ACTIVE FLAG FOR 'SOFT DC + UCHAR TXDELAY; // TX KEYUP DELAY TIMER + UCHAR SLOTTIME; // TIME TO WAIT IF WE DONT SEND + UCHAR FIRSTCHAR; // CHAR TO SEND FOLLOWING TXDELAY + USHORT L1TIMEOUT; // UNABLE TO TX TIMEOUT + UCHAR PORTSLOTIMER; + + USHORT TXBRG; // FOR CARDS WITHOUT /32 DIVIDER + USHORT RXBRG; + + UCHAR WR10 ; // NRZ/NRZI FLAG + + int IRQHand; + int fd; // file descriptor for LKM + + ULONG IOLEN; // Number of bytes in IO Space + + struct PORTCONTROL * DRIVERPORTTABLE; // ADDR OF PORT TABLE ENTRY IN VXD + // Used in NT Driver for Kernel Device Pointer + +}HDLCDATA, * PHDLCDATA; + + +extern struct ROUTE * NEIGHBOURS; +extern int ROUTE_LEN; +extern int MAXNEIGHBOURS; + +extern struct DEST_LIST * DESTS; // NODE LIST +extern int DEST_LIST_LEN; +extern int MAXDESTS; // MAX NODES IN SYSTEM + +extern struct _LINKTABLE * LINKS; +extern int LINK_TABLE_LEN; +extern int MAXLINKS; +/* +L4TABLE DD 0 +MAXCIRCUITS DW 50 ; NUMBER OF L4 CIRCUITS + +NUMBEROFPORTS DW 0 + +TNCTABLE DD 0 +NUMBEROFSTREAMS DW 0 + +ENDDESTLIST DD 0 ; NODE LIST+1 +*/ + +typedef struct _LINKTABLE +{ +//; +//; LEVEL 2 LINK CONTROL TABLE +//; + + UCHAR LINKCALL[7]; // CALLSIGN OF STATION + UCHAR OURCALL[7]; // CALLSIGN OF OUR END + UCHAR DIGIS[56]; // LEVEL 2 DIGIS IN PATH + + char callingCall[10]; // for reporting. Link and Our calls depand on which end connected + char receivingCall[10]; // for reporting. Link and Our calls depand on which end connected + + char Direction[4]; // In or Out + + PPORTCONTROL LINKPORT; // PORT NUMBER + UCHAR LINKTYPE; // 1 = UP, 2= DOWN, 3 = INTERNODE + + UCHAR LINKNR; + UCHAR LINKNS; // LEV 2 SEQUENCE COUNTS + UCHAR LINKWS; // WINDOW START + UCHAR LINKOWS; // OLD (LAST ACKED) WINDOW START + UCHAR LINKWINDOW; // LEVEL 2 WINDOW SIZE + + UCHAR L2FLAGS; // CONTROL BITS + UCHAR VER1FLAG; // SET IF OTHER END RUNNING VERSION 1 + + VOID * RX_Q; // PACKETS RECEIVED ON THIS LINK + VOID * TX_Q; // PACKETS TO SEND + VOID * FRAMES[8]; // FRAMES WAITING ACK + VOID * RXFRAMES[8]; // Frames received out of sequence + + UCHAR L2STATE; // PROCESSING STATE + UCHAR Ver2point2; // Set if running 2.2 + USHORT L2TIMER; // FRAME RETRY TIMER + UCHAR L2TIME; // RETRY TIMER INITIAL VALUE + USHORT L2SLOTIM; // DELAY FOR LINK VALIDATION POLL + UCHAR L2ACKREQ; // DELAYED TEXT ACK TIMER + UCHAR REJTIMER; // TO TIME OUT REJ IN VERSION 1 + USHORT LAST_F_TIME; // TIME LAST R(F) SENT + UCHAR SDREJF; // FLAGS FOR FRMR + UCHAR SDRBYTE; // SAVED CONTROL BYTE FOR FRMR + + UCHAR SDTSLOT ; // POINTER TO NEXT TXSLOT TO USE + + UCHAR L2RETRIES; // RETRY COUNTER + + UCHAR SESSACTIVE; // SET WHEN WE ARE SURE SESSION IS UP + + UINT APPLMASK; // Used when XIR processed + VOID * ALIASPTR; + + USHORT KILLTIMER; // TIME TO KILL IDLE LINK + + VOID * CIRCUITPOINTER; // POINTER TO L4 CIRCUIT TABLE ENTRY + // (IF UP/DOWN) + PROUTE NEIGHBOUR; // POINTER TO NEIGHBOUR (IF CROSSLINK) + + VOID * L2FRAG_Q; // DEFRAGMENTATION QUEUE + + int IFrameRetryCounter; // Number of times an I frame in repeated without a frame being acked + + time_t ConnectTime; // For session stats + int bytesRXed; // Info bytes only + int bytesTXed; + + // Now support compressing L2 Sessions. + // We collect as much data as possible before compressing and re-packetizing + + int AllowCompress; + + unsigned char * unCompress; // Data being saved to uncompress + int unCompressLen; + + int Sent; + int SentAfterCompression; + + int Received; + int ReceivedAfterExpansion; + + +} LINKTABLE; + +#pragma pack(1) + +struct myin_addr { + union { + struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { u_short s_w1,s_w2; } S_un_w; + uint32_t addr; + }; +}; + +typedef struct _IPMSG +{ +// FORMAT OF IP HEADER +// +// NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + UCHAR VERLEN; // 4 BITS VERSION, 4 BITS LENGTH + UCHAR TOS; // TYPE OF SERVICE + USHORT IPLENGTH; // DATAGRAM LENGTH + USHORT IPID; // IDENTIFICATION + USHORT FRAGWORD; // 3 BITS FLAGS, 13 BITS OFFSET + UCHAR IPTTL; + UCHAR IPPROTOCOL; // HIGHER LEVEL PROTOCOL + USHORT IPCHECKSUM; // HEADER CHECKSUM + struct myin_addr IPSOURCE; + struct myin_addr IPDEST; + + UCHAR Data; + +} IPMSG, *PIPMSG; + +typedef struct _PSEUDOHEADER +{ + struct myin_addr IPSOURCE; + struct myin_addr IPDEST; + UCHAR Reserved; + UCHAR IPPROTOCOL; // HIGHER LEVEL PROTUDP/TCP Length + USHORT LENGTH; // DATAGRAM LENGTH + +} PHEADER; + +typedef struct _TCPMSG +{ + +// FORMAT OF TCP HEADER WITHIN AN IP DATAGRAM + +// NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + USHORT SOURCEPORT; + USHORT DESTPORT; + + uint32_t SEQNUM; + uint32_t ACKNUM; + + UCHAR TCPCONTROL; // 4 BITS DATA OFFSET 4 RESERVED + UCHAR TCPFLAGS; // (2 RESERVED) URG ACK PSH RST SYN FIN + + USHORT WINDOW; + USHORT CHECKSUM; + USHORT URGPTR; + + +} TCPMSG, *PTCPMSG; + +typedef struct _UDPMSG +{ + +// FORMAT OF UDP HEADER WITHIN AN IP DATAGRAM + +// NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + USHORT SOURCEPORT; + USHORT DESTPORT; + USHORT LENGTH; + USHORT CHECKSUM; + UCHAR UDPData[0]; + +} UDPMSG, *PUDPMSG; + +// ICMP MESSAGE STRUCTURE + +typedef struct _ICMPMSG +{ + // FORMAT OF ICMP HEADER WITHIN AN IP DATAGRAM + + // NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + UCHAR ICMPTYPE; + UCHAR ICMPCODE; + USHORT ICMPCHECKSUM; + + USHORT ICMPID; + USHORT ICMPSEQUENCE; + UCHAR ICMPData[0]; + +} ICMPMSG, *PICMPMSG; + +#pragma pack() + +struct SEM +{ + UINT Flag; + int Clashes; + int Gets; + int Rels; + DWORD SemProcessID; + DWORD SemThreadID; + int Line; // caller file and line + char File[MAX_PATH]; +}; + + +#define TNCBUFFLEN 8192 +#define MAXSTREAMS 32 + +// DED Emulator Stream Info + +struct StreamInfo +{ + UCHAR * Chan_TXQ; // !! Leave at front so ASM Code Finds it + // FRAMES QUEUED TO NODE + int BPQStream; + BOOL Connected; // Set if connected to Node + int CloseTimer; // Used to close session after connect failure + UCHAR MYCall[30]; + char OutgoingCall[16]; +}; + +struct TNC2StreamInfo +{ + // Not sure which of these is session specific + + UCHAR VMSR; // VIRTUAL MSR (Only Connected Bit) + int BPQPort; + BOOL MODEFLAG; // COMMAND/DATA MODE + int TPACLEN; // MAX PACKET SIZE FOR TNC GENERATED PACKETS + char RemoteCall[10]; // FOr Stream Changed Message + +/* I suspect only the above are needed + + int TRANSTIMER; // TRANPARENT MODE SEND TIMOUT + BOOL AUTOSENDFLAG; // SET WHEN TRANSMODE TIME EXPIRES + + int CMDTMR; // TRANSARENT MODE ESCAPE TIMER + int COMCOUNT; // NUMBER OF COMMAND CHARS RECEIVED + int CMDTIME ; // GUARD TIME FOR TRANS MODE EACAPE + int CMSG; // Enable CTEXT flag + int COMCHAR; // CHAR TO LEAVE CONV MODE + int PASSCHAR; // Escape char + char CTEXT[120]; + char ECHOFLAG; // ECHO ENABLED + BOOL TRACEFLAG; // MONITOR ON/OFF + BOOL FLOWFLAG; // FLOW OFF/ON + + BOOL CONOK; + BOOL CBELL; + BOOL NOMODE; // MODE CHANGE FLAGS + BOOL NEWMODE; + BOOL CONMODEFLAG; // CONNECT MODE - CONV OR TRANS + BOOL LFIGNORE; + BOOL MCON; // TRACE MODE FLAGS + BOOL MCOM; + BOOL MALL; + BOOL AUTOLF; // Add LF after CR + BOOL BBSMON; // SPECIAL SHORT MONITOR FOR BBS + BOOL MTX; // MONITOR TRANSMITTED FRAMES + BOOL MTXFORCE; // MONITOR TRANSMITTED FRAMES EVEN IF M OFF + UINT MMASK; // MONITOR PORT MASK + BOOL HEADERLN; // PUT MONITORED DATA ON NEW LINE FLAG + BOOL InEscape; // PASS Char received (treat next char as normal char not ctrl char) + + UINT APPLICATION; // APPLMASK + UINT APPLFLAGS; // FLAGS TO CONTROL APPL SYSTEM + + UINT SENDPAC; // SEND PACKET CHAR + BOOL CPACTIME; // USE PACTIME IN CONV MODE + BOOL CRFLAG ; // APPEND SENDPAC FLAG + + int TPACLEN ; // MAX PACKET SIZE FOR TNC GENERATED PACKETS + UCHAR UNPROTO[64]; // UNPROTO DEST AND DIGI STRING + + char MYCALL[10]; +*/ + +}; + + +struct TNCDATA +{ + struct TNCDATA * Next; + unsigned int Mode; // 0 = TNC2, others may follow + + UCHAR TOUSERBUFFER[TNCBUFFLEN]; // BUFFER TO USER + UCHAR TONODEBUFFER[300]; // BUFFER TO NODE + UCHAR FROMUSERBUFFER[TNCBUFFLEN]; + + char PORTNAME[80]; // for Linux Port Names + int ComPort; + BOOL VCOM; + int RTS; + int CTS; + int DCD; + int DTR; + int DSR; + int BPQPort; + int Speed; + char PortLabel[20]; + char TypeFlag[2]; + BOOL PortEnabled; + HANDLE hDevice; + BOOL NewVCOM; // Set if User Mode VCOM Port + + UCHAR VMSR; // VIRTUAL MSR + +// BIT 7 - Receive Line Signal Detect (DCD) +// 6 - Ring Indicator +// 5 - Data Set Ready +// 4 - Clear To Send +// 3 - Delta RLSD ( ie state has changed since last +// access) +// 2 - Trailing Edge Ring Detect +// 1 - Delta DSR +// 0 - Delta CTS + + + BOOL RTSFLAG; // BIT 0 SET IF RTS/DTR UP + UCHAR VLSR; // LAST RECEIVED LSR VALUE + + int RXCOUNT; // BYTES IN RX BUFFER + UCHAR * PUTPTR; // POINTER FOR LOADING BUFFER + UCHAR * GETPTR; // POINTER FOR UNLOADING BUFFER + UCHAR * CURSOR; // POSTION IN KEYBOARD BUFFER + + int MSGLEN; + int TRANSTIMER; // TRANPARENT MODE SEND TIMOUT + BOOL AUTOSENDFLAG; // SET WHEN TRANSMODE TIME EXPIRES + + int CMDTMR; // TRANSARENT MODE ESCAPE TIMER + int COMCOUNT; // NUMBER OF COMMAND CHARS RECEIVED + int CMDTIME ; // GUARD TIME FOR TRANS MODE EACAPE + int CMSG; // Enable CTEXT flag + int COMCHAR; // CHAR TO LEAVE CONV MODE + int PASSCHAR; // Escape char + int StreamSW; // Stream Switch Char + int StreamDbl; + int StreamCall; // Send call with stream switch char + int LCStream; // Stream is case independant + int Users; // Number of streams allowed + + char CTEXT[120]; + char ECHOFLAG; // ECHO ENABLED + BOOL TRACEFLAG; // MONITOR ON/OFF + BOOL FLOWFLAG; // FLOW OFF/ON + + BOOL CONOK; + BOOL CBELL; + BOOL NOMODE; // MODE CHANGE FLAGS + BOOL NEWMODE; + BOOL CONMODEFLAG; // CONNECT MODE - CONV OR TRANS + BOOL LFIGNORE; + BOOL MCON; // TRACE MODE FLAGS + BOOL MCOM; + BOOL MALL; + BOOL MUIONLY; + BOOL AUTOLF; // Add LF after CR + BOOL BBSMON; // SPECIAL SHORT MONITOR FOR BBS + BOOL MTX; // MONITOR TRANSMITTED FRAMES + BOOL MTXFORCE; // MONITOR TRANSMITTED FRAMES EVEN IF M OFF + uint64_t MMASK; // MONITOR PORT MASK + BOOL HEADERLN; // PUT MONITORED DATA ON NEW LINE FLAG + BOOL InEscape; // PASS Char received (treat next char as normal char not ctrl char) + BOOL InStreamSW; // StreamSW Char received (treat next char as new stream) + +// BOOL MODEFLAG; // TNC2 COMMAND/DATA MODE + + UINT APPLICATION; // APPLMASK + + UINT APPLFLAGS; // FLAGS TO CONTROL APPL SYSTEM + + UINT SENDPAC; // SEND PACKET CHAR + BOOL CPACTIME; // USE PACTIME IN CONV MODE + BOOL CRFLAG ; // APPEND SENDPAC FLAG + + int TPACLEN ; // MAX PACKET SIZE FOR TNC GENERATED PACKETS + UCHAR UNPROTO[64]; // UNPROTO DEST AND DIGI STRING + + char MYCALL[10]; + + // TNC2 Stream Fields + + int TXStream; // Currently Selected Stream + int RXStream; + + struct TNC2StreamInfo * TNC2Stream[26]; // For StreamSW support + + // DED Mode Fields + + int PollDelay; // Used by VCOM to slow down continuous reads on real port + + struct StreamInfo * Channels[MAXSTREAMS+1]; + char MODE; // INITIALLY TERMINAL MODE + char HOSTSTATE; // HOST STATE MACHINE + int MSGCOUNT; // LENGTH OF MESSAGE EXPECTED + int MSGLENGTH; + char MSGTYPE; + unsigned char MSGCHANNEL; + char DEDMODE; // CLUSTER MODE - DONT ALLOW DUP CONNECTS + int HOSTSTREAMS; // Default Streams + + UCHAR DEDTXBUFFER[256]; + UCHAR * DEDCURSOR; + + unsigned char MONBUFFER[258]; //="\x6"; + int MONLENGTH; + int MONFLAG; + + time_t LastDEDPollTime; // To detect lost host program + + // Kantronics Fields + + int RXBPtr; + char nextMode; // Mode after RESET + + // SCS Fields + + int FROMUSERLEN; + + BOOL Term4Mode; // Used by Airmail + BOOL PACMode; // SCS in Packet Mode + BOOL Toggle; // SCS Sequence Toggle + + char MyCall[10]; + +}; + +// Emulatiom mode equates + +#define TNC2 0 +#define DED 1 +#define KANTRONICS 2 // For future use +#define SCS 3 + + +#define MAX_ENTRIES 128 +#define MaxMHEntries 100 +#define MAX_BROADCASTS 8 +#define MAXUDPPORTS 30 + +#ifndef MAXGETHOSTSTRUCT +#define MAXGETHOSTSTRUCT 1024 +#endif + + +struct arp_table_entry +{ + unsigned char callsign[7]; + unsigned char len; // bytes to compare (6 or 7) + BOOL IPv6; + +// union +// { +// struct in_addr in_addr; +// unsigned int ipaddr; +// struct in6_addr in6_addr; +// }; + + unsigned short port; + unsigned char hostname[64]; + unsigned int error; + BOOL ResolveFlag; // True if need to resolve name + unsigned int keepalive; + unsigned int keepaliveinit; + BOOL BCFlag; // True if we want broadcasts to got to this call + BOOL AutoAdded; // Set if Entry created as a result of AUTOADDMAP + SOCKET TCPListenSock; // Listening socket if slave + SOCKET TCPSock; + int TCPMode; // TCPMaster ot TCPSlave + UCHAR * TCPBuffer; // Area for building TCP message from byte stream + int InputLen; // Bytes in TCPBuffer + + union + { + struct sockaddr_in6 destaddr6; + struct sockaddr_in destaddr; + }; + + BOOL TCPState; + pthread_t TCPThreadID; // Thread ID if TCP Master + UINT TCPOK; // Cleared when Message RXed . Incremented by timer + int SourcePort; // Used to select socket, hence from port. +// SOCKET SourceSocket; + struct AXIPPORTINFO * PORT; + BOOL noUpdate; // Don't update dest address from incoming packet + time_t LastHeard; // Last Packet received from this ststiom +}; + + +struct broadcast_table_entry +{ + unsigned char callsign[7]; + unsigned char len; // bytes to compare (6 or 7) +}; + + +struct MHTableEntry +{ + unsigned char callsign[7]; + char proto; + short port; + union + { + struct in_addr ipaddr; + struct in6_addr ipaddr6; + }; + time_t LastHeard; // Time last packet received + int Keepalive; + BOOL IPv6; +}; + + +struct AXIPPORTINFO +{ + int Port; + + struct MHTableEntry MHTable[MaxMHEntries]; + struct broadcast_table_entry BroadcastAddresses[MAX_BROADCASTS]; + + int NumberofBroadcastAddreses; + BOOL Checkifcanreply; + + int arp_table_len; + int ResolveIndex; // pointer to entry being resolved + + struct arp_table_entry arp_table[MAX_ENTRIES]; + + struct arp_table_entry default_arp; + + BOOL MHEnabled; + BOOL MHAvailable; // Enabled with config file directive + + BOOL AutoAddARP; + BOOL AutoAddBC; // Broadcast flag for autoaddmap + + unsigned char hostaddr[64]; + + HWND hResWnd, hMHWnd, ConfigWnd; + + BOOL GotMsg; + + int udpport[MAXUDPPORTS+2]; + BOOL IPv6[MAXUDPPORTS+2]; + + BOOL PortIPv6; // Set if any MAPS for IPv6 + + int NumberofUDPPorts; + + BOOL needip; + BOOL NeedResolver; + BOOL NeedTCP; + + SOCKET sock; // IP 93 Sock + SOCKET udpsock[MAXUDPPORTS+2]; + + time_t ltime,lasttime; + int baseline; + int mhbaseline; + int CurrentMHEntries; + + char buf[MAXGETHOSTSTRUCT]; + + int MaxMHWindowlength; + int MaxResWindowlength; + + BOOL ResMinimized; + BOOL MHMinimized; + + HMENU hResMenu; + HMENU hMHMenu; + + pthread_t ResolveNamesThreadId; + +}; + + +#define Disconnect(stream) SessionControl(stream,2,0) +#define Connect(stream) SessionControl(stream,1,0) + +#endif + + + diff --git a/.svn/pristine/54/548a7e33b5b7b92c51cb8e378b0bdc8c9dd5087c.svn-base b/.svn/pristine/54/548a7e33b5b7b92c51cb8e378b0bdc8c9dd5087c.svn-base new file mode 100644 index 0000000..994104e --- /dev/null +++ b/.svn/pristine/54/548a7e33b5b7b92c51cb8e378b0bdc8c9dd5087c.svn-base @@ -0,0 +1,348 @@ +/* $Id: upnpcommands.h,v 1.33 2019/02/10 12:29:25 nanard Exp $ */ +/* Miniupnp project : http://miniupnp.free.fr/ + * Author : Thomas Bernard + * Copyright (c) 2005-2018 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided within this distribution */ +#ifndef UPNPCOMMANDS_H_INCLUDED +#define UPNPCOMMANDS_H_INCLUDED + +#include "miniupnpc_declspec.h" +#include "miniupnpctypes.h" + +/* MiniUPnPc return codes : */ +#define UPNPCOMMAND_SUCCESS (0) +#define UPNPCOMMAND_UNKNOWN_ERROR (-1) +#define UPNPCOMMAND_INVALID_ARGS (-2) +#define UPNPCOMMAND_HTTP_ERROR (-3) +#define UPNPCOMMAND_INVALID_RESPONSE (-4) +#define UPNPCOMMAND_MEM_ALLOC_ERROR (-5) + +#ifdef __cplusplus +extern "C" { +#endif + +struct PortMappingParserData; + +MINIUPNP_LIBSPEC UNSIGNED_INTEGER +UPNP_GetTotalBytesSent(const char * controlURL, + const char * servicetype); + +MINIUPNP_LIBSPEC UNSIGNED_INTEGER +UPNP_GetTotalBytesReceived(const char * controlURL, + const char * servicetype); + +MINIUPNP_LIBSPEC UNSIGNED_INTEGER +UPNP_GetTotalPacketsSent(const char * controlURL, + const char * servicetype); + +MINIUPNP_LIBSPEC UNSIGNED_INTEGER +UPNP_GetTotalPacketsReceived(const char * controlURL, + const char * servicetype); + +/* UPNP_GetStatusInfo() + * status and lastconnerror are 64 byte buffers + * Return values : + * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR + * or a UPnP Error code */ +MINIUPNP_LIBSPEC int +UPNP_GetStatusInfo(const char * controlURL, + const char * servicetype, + char * status, + unsigned int * uptime, + char * lastconnerror); + +/* UPNP_GetConnectionTypeInfo() + * argument connectionType is a 64 character buffer + * Return Values : + * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR + * or a UPnP Error code */ +MINIUPNP_LIBSPEC int +UPNP_GetConnectionTypeInfo(const char * controlURL, + const char * servicetype, + char * connectionType); + +/* UPNP_GetExternalIPAddress() call the corresponding UPNP method. + * if the third arg is not null the value is copied to it. + * at least 16 bytes must be available + * + * Return values : + * 0 : SUCCESS + * NON ZERO : ERROR Either an UPnP error code or an unknown error. + * + * possible UPnP Errors : + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 501 Action Failed - See UPnP Device Architecture section on Control. */ +MINIUPNP_LIBSPEC int +UPNP_GetExternalIPAddress(const char * controlURL, + const char * servicetype, + char * extIpAdd); + +/* UPNP_GetLinkLayerMaxBitRates() + * call WANCommonInterfaceConfig:1#GetCommonLinkProperties + * + * return values : + * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR + * or a UPnP Error Code. */ +MINIUPNP_LIBSPEC int +UPNP_GetLinkLayerMaxBitRates(const char* controlURL, + const char* servicetype, + unsigned int * bitrateDown, + unsigned int * bitrateUp); + +/* UPNP_AddPortMapping() + * if desc is NULL, it will be defaulted to "libminiupnpc" + * remoteHost is usually NULL because IGD don't support it. + * + * Return values : + * 0 : SUCCESS + * NON ZERO : ERROR. Either an UPnP error code or an unknown error. + * + * List of possible UPnP errors for AddPortMapping : + * errorCode errorDescription (short) - Description (long) + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 501 Action Failed - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization and + * the sender was not authorized. + * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be + * wild-carded + * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded + * 718 ConflictInMappingEntry - The port mapping entry specified conflicts + * with a mapping assigned previously to another client + * 724 SamePortValuesRequired - Internal and External port values + * must be the same + * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports + * permanent lease times on port mappings + * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard + * and cannot be a specific IP address or DNS name + * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and + * cannot be a specific port value + * 728 NoPortMapsAvailable - There are not enough free ports available to + * complete port mapping. + * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed + * due to conflict with other mechanisms. + * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded + */ +MINIUPNP_LIBSPEC int +UPNP_AddPortMapping(const char * controlURL, const char * servicetype, + const char * extPort, + const char * inPort, + const char * inClient, + const char * desc, + const char * proto, + const char * remoteHost, + const char * leaseDuration); + +/* UPNP_AddAnyPortMapping() + * if desc is NULL, it will be defaulted to "libminiupnpc" + * remoteHost is usually NULL because IGD don't support it. + * + * Return values : + * 0 : SUCCESS + * NON ZERO : ERROR. Either an UPnP error code or an unknown error. + * + * List of possible UPnP errors for AddPortMapping : + * errorCode errorDescription (short) - Description (long) + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 501 Action Failed - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization and + * the sender was not authorized. + * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be + * wild-carded + * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded + * 728 NoPortMapsAvailable - There are not enough free ports available to + * complete port mapping. + * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed + * due to conflict with other mechanisms. + * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded + */ +MINIUPNP_LIBSPEC int +UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype, + const char * extPort, + const char * inPort, + const char * inClient, + const char * desc, + const char * proto, + const char * remoteHost, + const char * leaseDuration, + char * reservedPort); + +/* UPNP_DeletePortMapping() + * Use same argument values as what was used for AddPortMapping(). + * remoteHost is usually NULL because IGD don't support it. + * Return Values : + * 0 : SUCCESS + * NON ZERO : error. Either an UPnP error code or an undefined error. + * + * List of possible UPnP errors for DeletePortMapping : + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization + * and the sender was not authorized. + * 714 NoSuchEntryInArray - The specified value does not exist in the array */ +MINIUPNP_LIBSPEC int +UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, + const char * extPort, const char * proto, + const char * remoteHost); + +/* UPNP_DeletePortRangeMapping() + * Use same argument values as what was used for AddPortMapping(). + * remoteHost is usually NULL because IGD don't support it. + * Return Values : + * 0 : SUCCESS + * NON ZERO : error. Either an UPnP error code or an undefined error. + * + * List of possible UPnP errors for DeletePortMapping : + * 606 Action not authorized - The action requested REQUIRES authorization + * and the sender was not authorized. + * 730 PortMappingNotFound - This error message is returned if no port + * mapping is found in the specified range. + * 733 InconsistentParameters - NewStartPort and NewEndPort values are not consistent. */ +MINIUPNP_LIBSPEC int +UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype, + const char * extPortStart, const char * extPortEnd, + const char * proto, + const char * manage); + +/* UPNP_GetPortMappingNumberOfEntries() + * not supported by all routers */ +MINIUPNP_LIBSPEC int +UPNP_GetPortMappingNumberOfEntries(const char * controlURL, + const char * servicetype, + unsigned int * numEntries); + +/* UPNP_GetSpecificPortMappingEntry() + * retrieves an existing port mapping + * params : + * in extPort + * in proto + * in remoteHost + * out intClient (16 bytes) + * out intPort (6 bytes) + * out desc (80 bytes) + * out enabled (4 bytes) + * out leaseDuration (16 bytes) + * + * return value : + * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR + * or a UPnP Error Code. + * + * List of possible UPnP errors for _GetSpecificPortMappingEntry : + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 501 Action Failed - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization + * and the sender was not authorized. + * 714 NoSuchEntryInArray - The specified value does not exist in the array. + */ +MINIUPNP_LIBSPEC int +UPNP_GetSpecificPortMappingEntry(const char * controlURL, + const char * servicetype, + const char * extPort, + const char * proto, + const char * remoteHost, + char * intClient, + char * intPort, + char * desc, + char * enabled, + char * leaseDuration); + +/* UPNP_GetGenericPortMappingEntry() + * params : + * in index + * out extPort (6 bytes) + * out intClient (16 bytes) + * out intPort (6 bytes) + * out protocol (4 bytes) + * out desc (80 bytes) + * out enabled (4 bytes) + * out rHost (64 bytes) + * out duration (16 bytes) + * + * return value : + * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR + * or a UPnP Error Code. + * + * Possible UPNP Error codes : + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization + * and the sender was not authorized. + * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds + */ +MINIUPNP_LIBSPEC int +UPNP_GetGenericPortMappingEntry(const char * controlURL, + const char * servicetype, + const char * index, + char * extPort, + char * intClient, + char * intPort, + char * protocol, + char * desc, + char * enabled, + char * rHost, + char * duration); + +/* UPNP_GetListOfPortMappings() Available in IGD v2 + * + * + * Possible UPNP Error codes : + * 606 Action not Authorized + * 730 PortMappingNotFound - no port mapping is found in the specified range. + * 733 InconsistantParameters - NewStartPort and NewEndPort values are not + * consistent. + */ +MINIUPNP_LIBSPEC int +UPNP_GetListOfPortMappings(const char * controlURL, + const char * servicetype, + const char * startPort, + const char * endPort, + const char * protocol, + const char * numberOfPorts, + struct PortMappingParserData * data); + +/* IGD:2, functions for service WANIPv6FirewallControl:1 */ +MINIUPNP_LIBSPEC int +UPNP_GetFirewallStatus(const char * controlURL, + const char * servicetype, + int * firewallEnabled, + int * inboundPinholeAllowed); + +MINIUPNP_LIBSPEC int +UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype, + const char * remoteHost, + const char * remotePort, + const char * intClient, + const char * intPort, + const char * proto, + int * opTimeout); + +MINIUPNP_LIBSPEC int +UPNP_AddPinhole(const char * controlURL, const char * servicetype, + const char * remoteHost, + const char * remotePort, + const char * intClient, + const char * intPort, + const char * proto, + const char * leaseTime, + char * uniqueID); + +MINIUPNP_LIBSPEC int +UPNP_UpdatePinhole(const char * controlURL, const char * servicetype, + const char * uniqueID, + const char * leaseTime); + +MINIUPNP_LIBSPEC int +UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID); + +MINIUPNP_LIBSPEC int +UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype, + const char * uniqueID, int * isWorking); + +MINIUPNP_LIBSPEC int +UPNP_GetPinholePackets(const char * controlURL, const char * servicetype, + const char * uniqueID, int * packets); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/.svn/pristine/54/54f8f30393198d8536d0b23a74c1560215b2b693.svn-base b/.svn/pristine/54/54f8f30393198d8536d0b23a74c1560215b2b693.svn-base new file mode 100644 index 0000000..e1b048b --- /dev/null +++ b/.svn/pristine/54/54f8f30393198d8536d0b23a74c1560215b2b693.svn-base @@ -0,0 +1,340 @@ +//Microsoft Developer Studio generated resource script. +// +#include "kernelresource.h" + +#define CKernel + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" +#include "BpqTermMDI.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +BPQMAINWINDOW DIALOG DISCARDABLE 17, 25, 382, 300 +STYLE DS_3DLOOK | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "BPQ32 Console" +CLASS "BPQMAINWINDOW" +FONT 8, "Fixedsys" +BEGIN + LTEXT "",IDC_BACKGROUND,22,20,337,273 + CONTROL "",IDC_ENIGATE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | + WS_TABSTOP,57,0,8,12 + LTEXT "IGate State - Disconnected",IGATESTATE,69,0,110,12, + SS_CENTERIMAGE + LTEXT "IGATE Stats - Msgs 0 Local Stns 0",IGATESTATS,180,0, + 152,12,SS_CENTERIMAGE + LISTBOX BPQCONSOLE,1,17,359,262,LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | LBS_NOSEL | WS_VSCROLL | + WS_HSCROLL + LTEXT "GPS Off",IDC_GPS,332,0,40,12,SS_CENTERIMAGE + LTEXT "Enable IGate",IDC_STATIC,5,2,49,10 +END + +CONFIG DIALOGEX 249, 200, 160, 118 +STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION +EXSTYLE WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE +CAPTION "Configuration" +CLASS "CONFIG" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + EDITTEXT 1001,50,5,43,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + EDITTEXT 1002,50,25,100,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + EDITTEXT 1003,50,65,43,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Call",1,10,5,21,18,0,WS_EX_NOPARENTNOTIFY + LTEXT "Host",2,10,25,30,20,0,WS_EX_NOPARENTNOTIFY + LTEXT "UDP Port",3,10,65,32,15,0,WS_EX_NOPARENTNOTIFY + PUSHBUTTON "Cancel",ID_CANCEL,15,95,35,14,0,WS_EX_NOPARENTNOTIFY + PUSHBUTTON "Apply",ID_SAVE,55,95,35,14,0,WS_EX_NOPARENTNOTIFY + CONTROL "UDP Flag ",1004,"Button",BS_AUTOCHECKBOX | BS_LEFT | + WS_TABSTOP,8,45,50,14,WS_EX_RIGHT + CONTROL "Broadcast Flag ",1005,"Button",BS_AUTOCHECKBOX | + BS_LEFT | WS_TABSTOP,68,45,70,14,WS_EX_RIGHT +END + +IDD_WL2KSYSOP DIALOGEX 0, 0, 277, 355 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "WL2K Sysop Record Update" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Name",3,5,31,28,10,0,WS_EX_NOPARENTNOTIFY + EDITTEXT NAME,60,30,64,12,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Address Line 1",6,5,72,50,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT ADDR1,60,71,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Address Line 2",7,5,92,50,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT ADDR2,60,91,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "City",4,5,112,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT CITY,60,111,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "State",8,5,132,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT STATE,60,131,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Country",9,5,152,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT COUNTRY,60,151,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + PUSHBUTTON "Cancel",ID_CANCEL,115,311,47,17,0,WS_EX_NOPARENTNOTIFY + PUSHBUTTON "Apply",ID_SAVE,55,311,47,17,0,WS_EX_NOPARENTNOTIFY + LTEXT "PostCode",10,5,172,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT POSTCODE,60,171,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Email",11,5,192,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT EMAIL,60,191,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Website",12,5,212,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT WEBSITE,60,209,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Phone",13,6,232,40,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT PHONE,60,231,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Addiitional Data",14,5,252,50,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT ADDITIONALDATA,60,251,205,14,ES_AUTOHSCROLL | NOT + WS_BORDER,WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Password",15,5,13,50,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT IDC_Password,60,12,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE + LTEXT "Locator",16,5,49,50,9,0,WS_EX_NOPARENTNOTIFY + EDITTEXT IDC_Locator,60,48,133,14,ES_AUTOHSCROLL | NOT WS_BORDER, + WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE +END + +UIMAINWINDOW DIALOG DISCARDABLE 100, 100, 323, 304 +STYLE WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "BPQ32 UI Utility" +CLASS "UIMAINWINDOW" +FONT 8, "FixedSys" +BEGIN +END + +PORTPAGE DIALOG DISCARDABLE 26, 5, 366, 186 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "FixedSys" +BEGIN + LTEXT "Send Beacon Every",IDC_STATIC,17,25,68,14, + SS_CENTERIMAGE + EDITTEXT IDC_INTERVAL,90,26,20,12,ES_AUTOHSCROLL + LTEXT "Minutes to ",IDC_STATIC,117,25,55,14,SS_CENTERIMAGE + EDITTEXT IDC_UIDEST,175,26,59,12,ES_UPPERCASE | ES_AUTOHSCROLL + CONTROL "Send From File",IDC_FROMFILE,"Button",BS_AUTOCHECKBOX | + BS_VCENTER | WS_TABSTOP,17,59,68,13 + EDITTEXT IDC_FILENAME,89,58,223,12,ES_AUTOHSCROLL + PUSHBUTTON "Find File",IDC_FILE,316,58,40,12 + DEFPUSHBUTTON "Save",IDOK,122,142,40,12 + DEFPUSHBUTTON "Test",ID_TEST,205,142,40,12 + EDITTEXT IDC_MESSAGE,20,84,294,52,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN + LTEXT "",IDC_PORTNAME,12,9,139,14,SS_CENTERIMAGE + LTEXT "Path",IDC_STATIC,17,42,57,14,SS_CENTERIMAGE + EDITTEXT IDC_UIDIGIS,90,42,223,12,ES_UPPERCASE | ES_AUTOHSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAINFRAME_MENU MENU DISCARDABLE +BEGIN + POPUP "Window" + BEGIN + MENUITEM "New Terminal Window", ID_NEWWINDOW + MENUITEM "Cascade", ID_WINDOWS_CASCADE + MENUITEM "Tile", ID_WINDOWS_TILE + MENUITEM "Beacon Config", BPQUICONFIG + MENUITEM "Close all BPQ32 Programs", BPQCLOSEALL + END +END + +CONS_MENU MENU DISCARDABLE +BEGIN + POPUP "Window" + BEGIN + MENUITEM "New Terminal Window", ID_NEWWINDOW + MENUITEM "Cascade", ID_WINDOWS_CASCADE + MENUITEM "Tile", ID_WINDOWS_TILE + MENUITEM "Beacon Config", BPQUICONFIG + MENUITEM "Close all BPQ32 Programs", BPQCLOSEALL + END + POPUP "Actions" + BEGIN + MENUITEM "Save Nodes to file BPQNODES.DAT", BPQSAVENODES + MENUITEM "Save Registry Configuration", BPQSAVEREG + MENUITEM "Diagnostic Dump to file BPQDUMP", BPQDUMP + MENUITEM "Re-read Rigcontrol Config", SCANRECONFIG + MENUITEM "Re-read APRS Config", APRSRECONFIG + MENUITEM "Start Minimized", BPQSTARTMIN + MENUITEM "Minimize to Notification Area (System Tray)", BPQMINTOTRAY + MENUITEM "Update WL2K Sysop Record", IDD_WL2KSYSOP + END +END + +TERM_MENU MENU DISCARDABLE +BEGIN + POPUP "Window" + BEGIN + MENUITEM "New Terminal Window", ID_NEWWINDOW + MENUITEM "Cascade", ID_WINDOWS_CASCADE + MENUITEM "Tile", ID_WINDOWS_TILE + MENUITEM "Beacon Config", BPQUICONFIG + MENUITEM "Close all BPQ32 Programs", BPQCLOSEALL + END + POPUP "Action" + BEGIN + MENUITEM "Connect", BPQCONNECT + MENUITEM "Disconnect", BPQDISCONNECT, GRAYED + END + POPUP "Config" + BEGIN + MENUITEM "Font", ID_SETUP_FONT + MENUITEM "Enable Bells", BPQBELLS + MENUITEM "Strip Linefeeds", BPQStripLF + MENUITEM "Log Output", BPQLogOutput + MENUITEM "Send Disconnected", BPQSendDisconnected + MENUITEM "Chat Terminal Mode (Send Keppalives)", CHATTERM + MENUITEM "Restore Windows on load", ID_WINDOWS_RESTORE + MENUITEM "Beep if input too long", ID_WARNWRAP + MENUITEM "Wrap Input", ID_WRAP + MENUITEM "Flash instead of Beep on Bell", ID_FLASHONBELL + END + POPUP "Edit" + BEGIN + MENUITEM "Copy Output Window", BPQCOPYOUT + MENUITEM "Clear Output Window", BPQCLEAROUT + END + MENUITEM "Help", BPQHELP +END + +MON_MENU MENU DISCARDABLE +BEGIN + POPUP "Window" + BEGIN + MENUITEM "New Window", ID_NEWWINDOW + MENUITEM "Cascade", ID_WINDOWS_CASCADE + MENUITEM "Tile", ID_WINDOWS_TILE + MENUITEM "Beacon Config", BPQUICONFIG + MENUITEM "Close all BPQ32 Programs", BPQCLOSEALL + END + POPUP "Monitor" + BEGIN + MENUITEM "Use Local Time", MONLOCALTIME + MENUITEM "Monitor TX", BPQMTX + MENUITEM "Monitor Supervisory", BPQMCOM + MENUITEM "Monitor UI Only", MON_UI_ONLY + MENUITEM "Monitor NODES", BPQMNODES + MENUITEM "Enable Colour", MONCOLOUR + MENUITEM "Log Monitor", BPQLogMonitor + MENUITEM "Trace APRS-IS", MONITORAPRS + MENUITEM "Clear all port flags", StopALLMon + END + POPUP "Edit" + BEGIN + MENUITEM "Copy Monitor Window", BPQCOPYMON + MENUITEM "Clear Monitor Window", BPQCLEARMON + END + MENUITEM "Help", BPQHELP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// WAVE +// + +INCOMINGCALL WAVE MOVEABLE PURE "Ring.wav" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "kernelresource.h\0" + """\r\n" + "BpqTermMDI.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "#include ""BpqTermMDI.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""..\\CommonSource\\Versions.h""\r\n" + "#include ""..\\CommonSource\\StdVer.inc""\r\n" + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + "BPQMAINWINDOW", DIALOG + BEGIN + RIGHTMARGIN, 360 + END + + IDD_WL2KSYSOP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 270 + TOPMARGIN, 7 + BOTTOMMARGIN, 348 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "..\CommonSource\Versions.h" +#include "..\StdVer.inc" + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/.svn/pristine/55/559443d53653e82aaa1769a0ad570b904543a337.svn-base b/.svn/pristine/55/559443d53653e82aaa1769a0ad570b904543a337.svn-base new file mode 100644 index 0000000..57e3012 --- /dev/null +++ b/.svn/pristine/55/559443d53653e82aaa1769a0ad570b904543a337.svn-base @@ -0,0 +1,72 @@ +/* LzmaEnc.h -- LZMA Encoder + 2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZMAENC_H +#define __LZMAENC_H + +#include "types.h" + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaEncProps +{ + int level; /* 0 <= level <= 9 */ + LZ_UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version + (1 << 12) <= dictSize <= (1 << 30) for 64-bit version + default = (1 << 24) */ + int lc; /* 0 <= lc <= 8, default = 3 */ + int lp; /* 0 <= lp <= 4, default = 0 */ + int pb; /* 0 <= pb <= 4, default = 2 */ + int algo; /* 0 - fast, 1 - normal, default = 1 */ + int fb; /* 5 <= fb <= 273, default = 32 */ + int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ + int numHashBytes; /* 2, 3 or 4, default = 4 */ + LZ_UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ + unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ + int numThreads; /* 1 or 2, default = 2 */ +} CLzmaEncProps; + +void LzmaEncProps_Init(CLzmaEncProps *p); +void LzmaEncProps_Normalize(CLzmaEncProps *p); +LZ_UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); + + +/* ---------- CLzmaEncHandle Interface ---------- */ + +/* LzmaEnc_* functions can return the following exit codes: + Returns: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater in props + SZ_ERROR_WRITE - Write callback error. + SZ_ERROR_PROGRESS - some break from progress callback + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) + */ + +typedef void * CLzmaEncHandle; + +CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); +SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); +SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); +SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); +SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +/* ---------- One Call Interface ---------- */ + +/* LzmaEncode + Return code: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) + */ + +SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, + const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, + ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +#endif diff --git a/.svn/pristine/56/5668d4bc3b43670977b4daa03f05596f67ac4941.svn-base b/.svn/pristine/56/5668d4bc3b43670977b4daa03f05596f67ac4941.svn-base new file mode 100644 index 0000000..8cb4c33 --- /dev/null +++ b/.svn/pristine/56/5668d4bc3b43670977b4daa03f05596f67ac4941.svn-base @@ -0,0 +1,1136 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// Interface to allow G8BPQ switch to use Serial TNC in character mode + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifndef WIN32 +#ifndef MACBPQ +#ifndef FREEBSD +#include +#include +#endif +#endif +#endif + + +#include "cheaders.h" + + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + +#include "bpq32.h" + +#include "tncinfo.h" + +static int Socket_Data(int sock, int error, int eventcode); + +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); +BOOL SerialWriteCommBlock(struct TNCINFO * TNC); +void SerialCheckRX(struct TNCINFO * TNC); +int SerialSendData(struct TNCINFO * TNC, UCHAR * data, int txlen); +int SerialSendCommand(struct TNCINFO * TNC, UCHAR * data); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int SerialGetLine(char * buf); +int ProcessEscape(UCHAR * TXMsg); +BOOL KAMStartPort(struct PORTCONTROL * PORT); +BOOL KAMStopPort(struct PORTCONTROL * PORT); + +static char ClassName[]="SERIALSTATUS"; +static char WindowTitle[] = "SERIAL"; +static int RigControlRow = 165; + +#ifndef LINBPQ +#include +#endif + + +extern int SemHeldByAPI; + +static RECT Rect; + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr; + int len=510; + struct TNCINFO * TNC = TNCInfo[Port]; + char errbuf[256]; + + // Read Initialisation lines + + while(TRUE) + { + if (SerialGetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + else if (_memicmp(buf, "DRAGON", 6) == 0) + TNC->Dragon = TRUE; + else + strcat (TNC->InitScript, buf); + } + + return (TRUE); +} + +char * Config; +static char * ptr1, * ptr2; + +int SerialGetLine(char * buf) +{ +loop: + + if (ptr2 == NULL) + return 0; + + memcpy(buf, ptr1, ptr2 - ptr1 + 2); + buf[ptr2 - ptr1 + 2] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + if (buf[0] < 0x20) goto loop; + if (buf[0] == '#') goto loop; + if (buf[0] == ';') goto loop; + + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + buf[strlen(buf)] = 13; + + return 1; +} + +BOOL SerialReadConfigFile(int Port, int ProcLine(char * buf, int Port)) +{ + char buf[256],errbuf[256]; + + Config = PortConfig[Port]; + + if (Config) + { + // Using config from bpq32.cfg + + if (strlen(Config) == 0) + { + return TRUE; + } + + ptr1 = Config; + ptr2 = strchr(ptr1, 13); + + if (!ProcLine(buf, Port)) + { + WritetoConsoleLocal("\n"); + WritetoConsoleLocal("Bad config record "); + WritetoConsoleLocal(errbuf); + } + } + else + { + sprintf(buf," ** Error - No Configuration info in bpq32.cfg"); + WritetoConsoleLocal(buf); + } + + return (TRUE); +} + + + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static time_t ltime; + +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // Serial mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +VOID SerialChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + + datalen = sprintf(TXMsg, "MYCALL %s\r", Call); + SerialSendCommand(TNC, TXMsg); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int bytes,txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + if (TNC->hDevice == 0) + { + // Clear anything from UI_Q + + while (TNC->PortRecord->UI_Q) + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + // Try to reopen every 30 secs + + if (fn > 3 && fn < 7) + goto ok; + + TNC->ReopenTimer++; + + if (TNC->ReopenTimer < 300) + return 0; + + TNC->ReopenTimer = 0; + + if (TNC->PortRecord->PORTCONTROL.PortStopped == 0) + OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); + + if (TNC->hDevice == 0) + return 0; + +#ifndef WIN32 +#ifndef MACBPQ +#ifndef FREEBSD + + if (TNC->Dragon) + { + struct serial_struct sstruct; + + // Need to set custom baud rate + + if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0) + { + Debugprintf("Error: Dragon could not get comm ioctl\n"); + } + else + { + // set custom divisor to get 829440 baud + + sstruct.custom_divisor = 29; + sstruct.flags |= ASYNC_SPD_CUST; + + // set serial_struct + + if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0) + Debugprintf("Error: Dragon could not set custom comm baud divisor\n"); + else + Debugprintf("Dragon custom baud rate set\n"); + } + } +#endif +#endif +#endif + SendInitScript(TNC); + + } +ok: + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + SerialCheckRX(TNC); + return 0; + + case 1: // poll + + while (TNC->PortRecord->UI_Q) + { + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + ReleaseBuffer(buffptr); + continue; + } + + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + } + } + + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + SerialSendCommand(TNC, "DISCONNECT\r"); + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("Serial New Attach Stream %d", Stream); + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + + SerialChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + //sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + //MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + STREAM->bytesTXed += txlen; + + bytes=SerialSendData(TNC, data, txlen); + WritetoTrace(TNC, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->hDevice) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "No Connection to TNC\r"); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + TXMsg = &buff->L2DATA[0]; + TXMsg[txlen] = 0; + + // for now just send, but allow sending control + // characters with \\ or ^ escape + +// if (STREAM->Connected) + { + STREAM->PacketsSent++; + + if (strstr(TXMsg, "\\\\") || strchr(TXMsg, '^')) + { + txlen = ProcessEscape(TXMsg); + + if (txlen == 0) // Must be \\D + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + } + + bytes=SerialSendData(TNC, TXMsg, txlen); + TNC->Streams[Stream].BytesOutstanding += bytes; // So flow control works - will be updated by BUFFER response + STREAM->bytesTXed += bytes; +// WritetoTrace(TNC, &buff->L2DATA[0], txlen); + + return 1; + } +/* + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + char cmd[56]; + + strcpy(cmd, &buff->L2DATA[6]); + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, cnd); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + txlen = sprintf(Connect, "C %s\r", &buff->L2DATA[2]); + + SerialChangeMYC(TNC, TNC->Streams[0].MyCall); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + SerialSendCommand(TNC, Connect); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = TRUE; + return 0; + + } + + // Normal data. Send to TNC + + + SerialSendData(TNC, TXMsg, txlen); + + return 0; +*/ + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // I think we should check buffer space for all comms modes + + { + int Queued; + int Outstanding = TNC->Streams[Stream].BytesOutstanding; + + if (Stream == 0) + Queued = TNC->Streams[13].FramesQueued; // ARDOP Native Mode Send Queue + else + Queued = TNC->Streams[Stream].FramesQueued; + + Outstanding = Queued = 0; + + if (TNC->Mode == 'O') // OFDM version has more buffer space + { + if (Queued > 4 || Outstanding > 8500) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + else + { + if (Queued > 4 || Outstanding > 2000) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + + } + if (TNC->Streams[Stream].Attached == 0) + return (TNC->hDevice != 0) << 8 | 1; + + return ((TNC->hDevice != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + + case 4: // reinit7 + + return 0; + + case 5: // Close + + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on ARDOP"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (TNC->ARDOPCommsMode == 'T') // TCP Mode + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + } + else + { + // Serial Modes + + if (!TNC->HostMode) + return 0; // No connection so no interlock + } + + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + SerialSendCommand(TNC, "CONOK OFF"); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + SerialSendCommand(TNC, "CONOK ON"); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID SerialReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + SerialChangeMYC(TNC, TNC->NodeCall); + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID SerialSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ + SerialSendCommand(TNC, "CONOK OFF\r"); +} + +VOID SerialReleasePort(struct TNCINFO * TNC) +{ + SerialSendCommand(TNC, "CONOK ON\r"); +} + + +VOID * SerialExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[512]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + + sprintf(Msg,"Serial TNC %s\n", PortEntry->PORTCONTROL.SerialPortName); + WritetoConsole(Msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + if (TNCInfo[port]) // If restarting, free old config + free(TNCInfo[port]); + + TNC = TNCInfo[port] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (PortConfig[port]) // May not have config + SerialReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->PortRecord = PortEntry; + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_SERIAL; + + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (TNC->PacketChannels > 1) + TNC->PacketChannels = 1; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = SerialSuspendPort; + TNC->ReleasePortProc = SerialReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + // Set MYCALL + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + +#ifndef WIN32 +#ifndef MACBPQ +#ifndef FREEBSD + + if (TNC->Dragon) + { + struct serial_struct sstruct; + + // Need to set custom baud rate + + if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0) + { + printf("Error: Dragon could not get comm ioctl\n"); + } + else + { + // set custom divisor to get 829440 baud + + sstruct.custom_divisor = 29; + sstruct.flags |= ASYNC_SPD_CUST; + + // set serial_struct + + if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0) + Debugprintf("Error: Dragon could not set custom comm baud divisor\n"); + else + Debugprintf("Dragon custom baud rate set\n"); + } + } +#endif +#endif +#endif + + SendInitScript(TNC); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[Stream].BytesOutstanding == 0) + SerialSendCommand(TNC, "DISCONNECT\r"); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + SerialSendCommand(TNC, "DISCONNECT\r"); +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + SerialReleaseTNC(TNC); + } +} + +VOID SerialAbort(struct TNCINFO * TNC) +{ + SerialSendCommand(TNC, "ABORT\r"); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID SerialDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + return; + } + + if (TNC->ReinitState == 3) + { + return; + } +} + +VOID SendInitScript(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + char * ptr1, * ptr2; + int len; + + // Send INIT script + + ptr1 = &TNC->InitScript[0]; + + while (ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, 13); + + if (ptr2 == 0) + { + TNC->ReinitState = 3; + break; + } + + len = (int)(ptr2 - ptr1) + 1; + + memcpy(Poll, ptr1, len); + + TNC->TXLen = len; + SerialWriteCommBlock(TNC); + + Sleep(50); + + if (ptr2) + *(ptr2++) = 13; // Put CR back for next time + + ptr1 = ptr2; + } +} + + + +VOID SerialProcessTNCMessage(struct TNCINFO * TNC) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", TNC->RXBuffer); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + +} + + +void SerialCheckRX(struct TNCINFO * TNC) +{ + int Length, Len = 0; + + if (TNC->RXLen == 250) + TNC->RXLen = 0; + + if (TNC->hDevice) + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 250 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + TNC->RXBuffer[TNC->RXLen] = 0; + +// if (TNC->Streams[Stream].RXBuffer[TNC->Streams[Stream].RXLen-2] != ':') + + if (strlen(TNC->RXBuffer) < TNC->RXLen) + TNC->RXLen = 0; + + // Also need timeout for incomplete lines + +// if ((strchr(TNC->RXBuffer, 10) == 0) && (strchr(TNC->RXBuffer, 13) == 0)) +// return; // Wait for rest of frame + + +// OpenLogFile(TNC->Port); +// WriteLogLine(TNC->Port, TNC->RXBuffer, (int)strlen(TNC->RXBuffer)); +// CloseLogFile(TNC->Port); + + TNC->RXLen = 0; // Ready for next frame + + SerialProcessTNCMessage(TNC); + return; +} + +int SerialWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + return 0; +} + +int SerialSendData(struct TNCINFO * TNC, UCHAR * data, int txlen) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, data, txlen); + + return 0; +} + +int SerialSendCommand(struct TNCINFO * TNC, UCHAR * data) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, data, (int)strlen(data)); + + return 0; +} + +int ProcessEscape(UCHAR * TXMsg) +{ + UCHAR * Orig = TXMsg; + UCHAR * ptr1 = TXMsg; + UCHAR * ptr2 = strstr(TXMsg, "\\\\"); + UCHAR * ptr3 = strchr(TXMsg, '^'); + + BOOL HexEscape = FALSE; + int NewLen; + + // Now using ^C for ctrl/c, etc + // Still use \\d + // ^^ for ^, \\\ for \\ + + while (ptr2) + { + UCHAR Next; + + ptr1 = ptr2; // over stuff before escape + + ptr2 += 2; // over \\ + + Next = *ptr2; + + if (Next == '\\') + { + ptr1 = ptr2; // put \\ in message + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + } + else if (Next == 'd' || Next == 'D') + return 0; + + ptr2 = strstr(ptr2, "\\\\"); + } + + // now look for ^ + + ptr1 = Orig; + ptr2 = strchr(Orig, '^'); + + while (ptr2) + { + UCHAR Next; + + ptr1 = ptr2; // over stuff before escape + ptr2 ++; // over ^ + + Next = *ptr2; + + if (Next != '^') + { + Next &= 0x1F; // Mask to control char + HexEscape = TRUE; + } + + *ptr1 = Next; + + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + + ptr2 = strchr(ptr2, '^'); + } + + NewLen = (int)strlen(Orig); + + if (HexEscape) + { + // remove trailing CR + + NewLen --; + + Orig[NewLen] = 0; + } + + return NewLen; +} + \ No newline at end of file diff --git a/.svn/pristine/56/5680e2b61f1f5939e66004e769a816b5c28f9907.svn-base b/.svn/pristine/56/5680e2b61f1f5939e66004e769a816b5c28f9907.svn-base new file mode 100644 index 0000000..fd932a6 --- /dev/null +++ b/.svn/pristine/56/5680e2b61f1f5939e66004e769a816b5c28f9907.svn-base @@ -0,0 +1,3122 @@ +/* +Copyright 2001-2015 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// Interface to allow G8BPQ switch to use VARA Virtual TNC in a form +// of ax.25 + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + + +#include "cheaders.h" + +#ifdef WIN32 +#include +#endif + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#include "bpq32.h" + +#include "tncinfo.h" + + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +#define FEND 0xC0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +static int Socket_Data(int sock, int error, int eventcode); + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +int KillPopups(struct TNCINFO * TNC); +VOID MoveWindows(struct TNCINFO * TNC); +int SendReporttoWL2K(struct TNCINFO * TNC); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +BOOL KillOldTNC(char * Path); +int VARASendData(struct TNCINFO * TNC, UCHAR * Buff, int Len); +VOID VARASendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue); +VOID VARAProcessDataPacket(struct TNCINFO * TNC, UCHAR * Data, int Length); +void CountRestarts(struct TNCINFO * TNC); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +VOID PROCESSNODEMESSAGE(MESSAGE * Msg, struct PORTCONTROL * PORT); +VOID NETROMMSG(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG); + +#ifndef LINBPQ +BOOL CALLBACK EnumVARAWindowsProc(HWND hwnd, LPARAM lParam); +#endif + +static char ClassName[]="VARASTATUS"; +static char WindowTitle[] = "VARA"; +static int RigControlRow = 165; + +#define WINMOR +#define NARROWMODE 21 +#define WIDEMODE 22 + +#ifndef LINBPQ +#include +#endif + +extern int SemHeldByAPI; + +static RECT Rect; +extern void * TRACE_Q; + +BOOL VARAStopPort(struct PORTCONTROL * PORT) +{ + // Disable Port - close TCP Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + TNC->Streams[0].Disconnecting = FALSE; + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + KillTNC(TNC); + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Stopped"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + return TRUE; +} + +int ConnecttoVARA(int port); + +BOOL VARAStartPort(struct PORTCONTROL * PORT) +{ + // Restart Port - Open Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + ConnecttoVARA(TNC->Port); + TNC->lasttime = time(NULL);; + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Restarted"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + return TRUE; +} + + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + TNC->MaxConReq = 10; // Default + + // Read Initialisation lines + + while (TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + else if (_memicmp(buf, "VARAAC", 6) == 0) + TNC->VaraACAllowed = atoi(&buf[7]); + + else if (_memicmp(buf, "BW2300", 6) == 0) + { + TNC->ARDOPCurrentMode[0] = 'W'; // Save current state for scanning + strcat(TNC->InitScript, buf); + TNC->DefaultMode = TNC->WL2KMode = 50; + } + else if (_memicmp(buf, "BW500", 5) == 0) + { + TNC->ARDOPCurrentMode[0] = 'N'; + strcat(TNC->InitScript, buf); + TNC->DefaultMode = TNC->WL2KMode = 53; + } + else if (_memicmp(buf, "BW2750", 6) == 0) + { + TNC->ARDOPCurrentMode[0] = 'W'; // Save current state for scanning + strcat(TNC->InitScript, buf); + TNC->DefaultMode = TNC->WL2KMode = 54; + } + else if (_memicmp(buf, "FM1200", 6) == 0) + TNC->DefaultMode = TNC->WL2KMode = 51; + else if (_memicmp(buf, "FM9600", 5) == 0) + TNC->DefaultMode = TNC->WL2KMode = 52; + else if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + + } + + return (TRUE); +} + +void VARAThread(void * portptr); +int ConnecttoVARA(int port); +VOID VARAProcessReceivedData(struct TNCINFO * TNC); +VOID VARAProcessReceivedControl(struct TNCINFO * TNC); +VOID VARAReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +static time_t ltime; + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + +void doVarACSend(struct TNCINFO * TNC) +{ + int hdrlen; + int txlen = strlen(TNC->VARACMsg); + char txbuff[64]; + + txlen--; // remove cr + hdrlen = sprintf(txbuff, "%d ", txlen); + send(TNC->TCPDataSock, txbuff, hdrlen, 0); // send length + send(TNC->TCPDataSock, TNC->VARACMsg, txlen, 0); + + free (TNC->VARACMsg); + TNC->VARACMsg = 0; + TNC->VARACSize = 0; + + TNC->VarACTimer = 0; + return ; +} +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + size_t datalen; + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int bytes; + size_t txlen=0; + size_t Param; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + if (TNC->CONNECTED == 0) + { + // clear Q if not connected + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + } + + switch (fn) + { + case 8: + + return 0; + + case 7: + + // approx 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + if (TNC->VarACTimer) + { + TNC->VarACTimer--; + + if (TNC->VarACTimer == 0) + doVarACSend(TNC); + } + + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + + // Check session limit timer + + if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) + { + if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) + { + VARASendCommand(TNC, "CLEANTXBUFFER\r", TRUE); + VARASendCommand(TNC, "DISCONNECT\r", TRUE); + STREAM->Disconnecting = TRUE; + TNC->SessionTimeLimit += 120; // Don't retrigger unless things have gone horribly wrong + } + } + + // Check ATTACH time limit + + if (STREAM->Attached) + { + if (STREAM->AttachTime && TNC->AttachTimeLimit && time(NULL) > (TNC->AttachTimeLimit + STREAM->AttachTime)) + { + STREAM->ReportDISC = 1; + STREAM->AttachTime = 0; + } + } + + + while (TNC->PortRecord->UI_Q) + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + if (TNC->Busy) // Count down to clear + { + if ((TNC->BusyFlags & CDBusy) == 0) // TNC Has reported not busy + { + TNC->Busy--; + if (TNC->Busy == 0) + SetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } + } + + if (TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send + + VARASendCommand(TNC, TNC->ConnectCmd, TRUE); + TNC->Streams[0].Connecting = TRUE; + TNC->Streams[0].ConnectTime = time(NULL); + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[8], strlen(TNC->ConnectCmd)-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + free(TNC->ConnectCmd); + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].ConnectTime = time(NULL); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 39; + memcpy(buffptr->Data,"Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + return 0; + + case 1: // poll + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + VARASendCommand(TNC, "DISCONNECT\r", TRUE); + } + } + + /* + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); +*/ + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + memset(STREAM, 0, sizeof(struct STREAMINFO)); + + TNC->Streams[0].Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + // Stop Listening, and set MYCALL to user's call + + VARASendCommand(TNC, "LISTEN OFF\r", TRUE); + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + STREAM->AttachTime = time(NULL); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + } + + if (TNC->Streams[0].Attached) + CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); + + if (TNC->Streams[0].ReportDISC) + { + TNC->Streams[0].ReportDISC = FALSE; + buff->PORT = 0; + return -1; + } + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + TNC->lasttime = ltime; + ConnecttoVARA(port); + } + } + + // See if any frames for this port + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + txlen = (int)buffptr->Len; + + if(TNC->VaraACMode || TNC->VaraModeSet == 0) + { + // Send in varac format - + + // 5 Hello, 15 de G8 BPQ + + buffptr->Data[txlen] = 0; // Null terminate + + STREAM->bytesTXed += txlen; + WritetoTrace(TNC, buffptr->Data, txlen); + + // Always add to stored data and set timer. If it expires send message + + if (TNC->VARACMsg == 0) + { + TNC->VARACMsg = zalloc(4096); + TNC->VARACSize = 4096; + } + else + { + if (strlen(TNC->VARACMsg) + txlen >= (TNC->VARACSize - 10)) + { + TNC->VARACSize += 4096; + TNC->VARACMsg = realloc(TNC->VARACMsg, TNC->VARACSize); + } + } + + strcat(TNC->VARACMsg, buffptr->Data); + + TNC->VarACTimer = 10; // One second + return 0; + } + + + + else + memcpy(txbuff, buffptr->Data, txlen); + + bytes = VARASendData(TNC, &txbuff[0], txlen); + STREAM->bytesTXed += bytes; + ReleaseBuffer(buffptr); + } + + + if (TNC->WINMORtoBPQ_Q != 0) + { + buffptr=Q_REM(&TNC->WINMORtoBPQ_Q); + + datalen = buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA[0], buffptr->Data, datalen); + + datalen += sizeof(void *) + 4; + PutLengthinBuffer(buff, (int)datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + return (0); + + case 2: // send + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data,"No Connection to VARA TNC\r"); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + txlen = (int)buffptr->Len; + memcpy(txbuff, buffptr->Data, txlen); + bytes=send(TNC->TCPDataSock, buff->L2DATA, txlen, 0); + STREAM->bytesTXed += bytes; + WritetoTrace(TNC, txbuff, txlen); + ReleaseBuffer(buffptr); + } + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + + if (TNC->Streams[0].Connected) + { + unsigned char txbuff[512]; + + STREAM->PacketsSent++; + + if(TNC->VaraACMode == 0 && TNC->VaraModeSet == 1) + { + // Normal Send + + memcpy(txbuff, buff->L2DATA, txlen); + + bytes=send(TNC->TCPDataSock, txbuff, txlen, 0); + STREAM->bytesTXed += bytes; + WritetoTrace(TNC, buff->L2DATA, txlen); + return 0; + } + + // Send in varac format - len space data. No cr on end, but is implied + + // 5 Hello + + // I think we have to send a whole message (something terminated with a new line) + // may need to combine packets. Also I think we need to combine seqential sends + // (eg CTEXT and SID) + + + buff->L2DATA[txlen] = 0; // Null terminate + + STREAM->bytesTXed += txlen; + WritetoTrace(TNC, buff->L2DATA, txlen); + + // Always add to stored data and set timer. If it expires send message + + if (TNC->VARACMsg == 0) + { + TNC->VARACMsg = zalloc(4096); + TNC->VARACSize = 4096; + } + else + { + if (strlen(TNC->VARACMsg) + txlen >= (TNC->VARACSize - 10)) + { + TNC->VARACSize += 4096; + TNC->VARACMsg = realloc(TNC->VARACMsg, TNC->VARACSize); + } + } + + strcat(TNC->VARACMsg, buff->L2DATA); + + TNC->VarACTimer = 10; // One second + return 0; + } + else + { + if (_memicmp(buff->L2DATA, "D\r", 2) == 0 || _memicmp(buff->L2DATA, "BYE\r", 4) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + // See if Local command (eg RADIO) + + if (_memicmp(buff->L2DATA, "RADIO ", 6) == 0) + { + sprintf(buff->L2DATA, "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, buff->L2DATA)) + { + } + else + { + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "%s", buff->L2DATA); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(buff->L2DATA, "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "VARA} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0) + { + if (buff->L2DATA[16] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "VARA} OK\r"); + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return 0; + } + } + + + if (_memicmp(&buff->L2DATA[0], "CODEC TRUE", 9) == 0) + TNC->StartSent = TRUE; + + if (_memicmp(&buff->L2DATA[0], "BW2300", 6) == 0) + { + TNC->ARDOPCurrentMode[0] = 'W'; // Save current state for scanning + TNC->WL2KMode = 50; + } + + if (_memicmp(&buff->L2DATA[0], "BW500", 5) == 0) + { + TNC->ARDOPCurrentMode[0] = 'N'; + TNC->WL2KMode = 53; + } + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0) + { + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + return 0; + } + + // See if a Connect Command. If so set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + sprintf(Connect, "CONNECT %s %s\r", TNC->Streams[0].MyCall, &buff->L2DATA[2]); + + // Need to set connecting here as if we delay for busy we may incorrectly process OK response + + TNC->Streams[0].Connecting = TRUE; + + hookL4SessionAttempt(STREAM, &buff->L2DATA[2], TNC->Streams[0].MyCall); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + + VARASendCommand(TNC, Connect, TRUE); + TNC->Streams[0].ConnectTime = time(NULL); + + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else + { + buff->L2DATA[(txlen++) - 1] = 13; + buff->L2DATA[(txlen) - 1] = 0; + VARASendCommand(TNC, &buff->L2DATA[0], TRUE); + } + } + return (0); + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + if (TNC->Streams[0].Attached == 0) + return TNC->CONNECTED << 8 | 1; + + return (TNC->CONNECTED << 8 | TNC->Streams[0].Disconnecting << 15); // OK + + + case 4: // reinit + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + + if (TNC->PID && TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + return 0; + + case 5: // Close + + if (TNC->CONNECTED) + { + if (TNC->Streams[0].Connected) + VARASendCommand(TNC, "ABORT\r", TRUE); +// GetSemaphore(&Semaphore, 52); +// VARASendCommand(TNC, "CLOSE", FALSE); +// FreeSemaphore(&Semaphore); + Sleep(100); + } + + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + if (TNC->WeStartedTNC) + KillTNC(TNC); + + return 0; + + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (shouldn't happen) + { + Debugprintf("Scan Check Permission called on VARA"); + return 1; // OK to change + } + + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (Param == 1) // Request Permission + { + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + VARASendCommand(TNC, "LISTEN OFF\r", TRUE); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + if (!TNC->ConnectPending) + return 0; // OK to Change + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + VARASendCommand(TNC, "LISTEN ON\r", TRUE); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + if (Scan->VARAMode != TNC->ARDOPCurrentMode[0]) + { + // Mode changed + + if (TNC->ARDOPCurrentMode[0] == 'S') + { + VARASendCommand(TNC, "LISTEN ON\r", TRUE); + } + + if (Scan->VARAMode == 'W') // Set Wide Mode + { + VARASendCommand(TNC, "BW2300\r", TRUE); + TNC->WL2KMode = 50; + } + if (Scan->VARAMode == 'T') // Set Wide Mode + { + VARASendCommand(TNC, "BW2750\r", TRUE); + TNC->WL2KMode = 54; + } + else if (Scan->VARAMode == 'N') // Set Narrow Mode + { + VARASendCommand(TNC, "BW500\r", TRUE); + TNC->WL2KMode = 53; + } + else if (Scan->VARAMode == 'S') // Skip + { + VARASendCommand(TNC, "LISTEN OFF\r", TRUE); + } + + TNC->ARDOPCurrentMode[0] = Scan->VARAMode; + } + return 0; + } + return 0; +} + +void CountRestarts(struct TNCINFO * TNC) +{ + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + //MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + //strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); +} +/* +char WebProcTemplate[] = "" + "\r\n" + "" + "%s" + "" + "%s" + ""; +*/ +char WebProcTemplate[] = "" + "\r\n" + "\r\n" + "%s\r\n" + "

%s

"; + +char Menubit[] = "" + "\r\n" + "" + "Abort Session" + "Kill TNC" + "Kill and Restart TNC" + ""; + +char sliderBit[] = " TX Offset %d" + "\r\n" + "\r\n"; + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, WebProcTemplate, TNC->Port, TNC->Port, "VARA Status", "VARA Status"); + + if (LOCAL) + Len += sprintf(&Buff[Len], Menubit, TNC->TXOffset, TNC->TXOffset); + + if (TNC->TXFreq) + Len += sprintf(&Buff[Len], sliderBit, TNC->TXOffset, TNC->TXOffset); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
S/N%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +VOID VARASuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ + TNC->PortRecord->PORTCONTROL.PortSuspended = TRUE; + VARASendCommand(TNC, "LISTEN OFF\r", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Interlocked"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +} + +VOID VARAReleasePort(struct TNCINFO * TNC) +{ + TNC->PortRecord->PORTCONTROL.PortSuspended = FALSE; + VARASendCommand(TNC, "LISTEN ON\r", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); +} + + +void * VARAExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + int line; + APPLCALLS * APPL; + struct TNCINFO * TNC; + int AuxCount = 0; + char Appl[11]; + char * TempScript; + struct PORTCONTROL * PORT = &PortEntry->PORTCONTROL; + // + // Will be called once for each VARA port + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC->AutoStartDelay == 0) + TNC->AutoStartDelay = 2000; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->PortRecord = PortEntry; + + TNC->ARDOPBuffer = malloc(8192); + TNC->ARDOPDataBuffer = malloc(8192); + + if (TNC->ProgramPath) + TNC->WeStartedTNC = 1; + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_VARA; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->MAXHOSTMODESESSIONS = 1; + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + + PortEntry->PORTCONTROL.UICAPABLE = FALSE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = VARASuspendPort; + TNC->ReleasePortProc = VARAReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = VARAStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = VARAStopPort; + + TNC->ModemCentre = 1500; // WINMOR is always 1500 Offset + + if (TNC->NRNeighbour) + { + // NETROM over VARA Link + + TNC->NetRomMode = 1; + TNC->LISTENCALLS = MYNETROMCALL; + PORT->PortNoKeepAlive = 1; + TNC->DummyLink = zalloc(sizeof(struct _LINKTABLE)); + TNC->DummyLink->LINKPORT = &TNC->PortRecord->PORTCONTROL; + } + else + PORT->PORTQUALITY = 0; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + +// strcat(TempScript, "ROBUST False\r"); + + // Set MYCALL(S) + + if (TNC->LISTENCALLS) + { + sprintf(Msg, "MYCALL %s", TNC->LISTENCALLS); + } + else + { + sprintf(Msg, "MYCALL %s", TNC->NodeCall); + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + char * ptr; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + { + // *ptr++ = ' '; + *ptr = 0; + } + strcat(Msg, " "); + strcat(Msg, Appl); + AuxCount++; + if (AuxCount == 4) // Max 5 in MYCALL + break; + } + } + } + + strcat(Msg, "\r"); + + strcat(TempScript, Msg); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + + strcat(TNC->InitScript,"LISTEN ON\r"); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + // if mode hasn't been set explicitly or via WL2KREPORT set to HF Wide mode (BW2300) + + if (TNC->DefaultMode == 0) + { + if (TNC->WL2K && TNC->WL2K->mode >= 50 && TNC->WL2K->mode <= 53) // A VARA Mode + TNC->DefaultMode = TNC->WL2KMode = TNC->WL2K->mode; + else + TNC->DefaultMode = TNC->WL2KMode = 50; // Default to 2300 + } + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->HostName=malloc(10); + + if (TNC->HostName != NULL) + strcpy(TNC->HostName,"127.0.0.1"); + + } + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + line = 6; + + if (TNC->TXFreq) + { + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow + 22, PacWndProc, 550, 450, ForcedClose); + + InitCommonControls(); // loads common control's DLL + + CreateWindowEx(0, "STATIC", "TX Tune", WS_CHILD | WS_VISIBLE, 10,line,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXTUNE = CreateWindowEx(0, TRACKBAR_CLASS, "", WS_CHILD | WS_VISIBLE, 116,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXTUNEVAL = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE, 320,line,30,20, TNC->hDlg, NULL, hInstance, NULL); + + SendMessage(TNC->xIDC_TXTUNE, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG(-200, 200)); // min. & max. positions + + line += 22; + } + else + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,line,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,386,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,line,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,520,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,line,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,144,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "S/N", WS_CHILD | WS_VISIBLE,10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,line,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,line,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,line,20,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,line,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart VARA TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + Consoleprintf("VARA Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port)); + + ConnecttoVARA(port); + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + +int ConnecttoVARA(int port) +{ + if (TNCInfo[port]->CONNECTING || TNCInfo[port]->PortRecord->PORTCONTROL.PortStopped) + return 0; + + _beginthread(VARAThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID VARAThread(void * portptr) +{ + // Opens sockets and looks for data on control and data sockets. + + int port = (int)(size_t)portptr; + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + char * ptr1; + char * ptr2; + PMSGWITHLEN buffptr; + + if (TNC->HostName == NULL) + return; + + TNC->BusyFlags = 0; + + TNC->CONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + + if (TNCInfo[port]->PortRecord->PORTCONTROL.PortStopped) + { + TNC->CONNECTING = FALSE; + return; + } + +// if on Windows and Localhost see if TNC is running + +#ifdef WIN32 + + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + + if (TNC->PID == 0) + goto TNCNotRunning; + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + HANDLE hProc; + char ExeName[256] = ""; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + goto TNCRunning; + } + +#endif + +TNCNotRunning: + + // Not running or can't check, restart if we have a path + + if (TNC->ProgramPath) + { + Consoleprintf("Trying to (re)start TNC %s", TNC->ProgramPath); + + if (RestartTNC(TNC)) + CountRestarts(TNC); + + Sleep(TNC->AutoStartDelay); + } + +TNCRunning: + + if (TNC->Alerted == FALSE) + { + sprintf(TNC->WEB_COMMSSTATE, "Connecting to TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + sprintf(Msg, "Resolve Failed for VARA socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + +// closesocket(TNC->TCPSock); +// closesocket(TNC->TCPDataSock); + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for VARA socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + TNC->TCPDataSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for VARA Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + closesocket(TNC->TCPSock); + + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); +// setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // Connected successful + + goto VConnected; + } + + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + sprintf(Msg, "Connect Failed for VARA socket - error code = %d Port %d\n", + err, htons(TNC->destaddr.sin_port)); + + WritetoConsole(Msg); + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + +// printf("VARA Connect failed\n"); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + return; + +VConnected: + + // Connect Data Port + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for VARA Data socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + + RestartTNC(TNC); + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + + // Send INIT script + + // VARA needs each command in a separate send + + ptr1 = &TNC->InitScript[0]; + + // We should wait for first RDY. Cheat by queueing a null command + + GetSemaphore(&Semaphore, 52); + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + + + while (ptr1 && ptr1[0]) + { + unsigned char c; + + ptr2 = strchr(ptr1, 13); + + if (ptr2) + { + c = *(ptr2 + 1); // Save next char + *(ptr2 + 1) = 0; // Terminate string + } + VARASendCommand(TNC, ptr1, TRUE); + + if (ptr2) + *(1 + ptr2++) = c; // Put char back + + ptr1 = ptr2; + } + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to VARA TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + FreeSemaphore(&Semaphore); + + sprintf(Msg, "Connected to VARA TNC Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + + #ifndef LINBPQ +// FreeSemaphore(&Semaphore); + Sleep(1000); // Give VARA time to update Window title + EnumWindows(EnumVARAWindowsProc, (LPARAM)TNC); +// GetSemaphore(&Semaphore, 52); +#endif + + while (TNC->CONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TNC->TCPSock,&readfs); + FD_SET(TNC->TCPSock,&errorfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + timeout.tv_sec = 90; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TNC->TCPDataSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("VARA Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + VARAProcessReceivedControl(TNC); + FreeSemaphore(&Semaphore); + Debugprintf("VARA Returned from processing control packet"); + } + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + VARAProcessReceivedData(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { +Lost: + sprintf(Msg, "VARA Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + TNC->ConnectPending = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + break; + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { + sprintf(Msg, "VARA Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + break; + } + continue; + } + else + { + // 60 secs without data. Shouldn't happen + + continue; + + sprintf(Msg, "VARA No Data Timeout Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + +// sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); +// GetSemaphore(&Semaphore, 52); +// MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); +// FreeSemaphore(&Semaphore); + + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + +// GetSemaphore(&Semaphore, 52); +// VARASendCommand(TNC, "CODEC FALSE", FALSE); +// FreeSemaphore(&Semaphore); + + Sleep(100); + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + + Sleep(100); + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPDataSock); + +// if (TNC->PID && TNC->WeStartedTNC) +// { +// KillTNC(TNC); +// + break; + } + } + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + sprintf(Msg, "VARA Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + + +VOID VARAProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + Buffer[MsgLen - 1] = 0; // Remove CR + + TNC->TimeSinceLast = 0; + + if (_memicmp(Buffer, "PTT ON", 6) == 0) + { +// Debugprintf("PTT On"); + + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + TNC->PTTonTime = GetTickCount(); + + // Cancel Busy timer (stats include ptt on time in port active) + + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + + return; + } + + if (_memicmp(Buffer, "PTT OFF", 6) == 0) + { +// Debugprintf("PTT Off"); + + if (TNC->PTTonTime) + { + TNC->PTTActivemS += (GetTickCount() - TNC->PTTonTime); + TNC->PTTonTime = 0; + } + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + + return; + } + + if (_memicmp(Buffer, "SN ", 3) == 0) + { + strcpy(TNC->WEB_PROTOSTATE, &Buffer[3]); + MySetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + TNC->SNR = atof(&Buffer[3]); + return; + } + + if (_stricmp(Buffer, "BUSY ON") == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + TNC->BusyonTime = GetTickCount(); + + MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + if (_stricmp(Buffer, "BUSY OFF") == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->BusyHold) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } + + + MySetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + + if (_memicmp(&Buffer[0], "PENDING", 7) == 0) // Save Pending state for scan control + { + TNC->ConnectPending = 6; // Time out after 6 Scanintervals + Debugprintf(Buffer); +// WritetoTrace(TNC, Buffer, MsgLen - 1); + return; + } + + if (_memicmp(&Buffer[0], "CANCELPENDING", 13) == 0) + { + TNC->ConnectPending = FALSE; + Debugprintf(Buffer); + + // If a callsign is present it is the calling station - add to MH + + if (TNC->SeenCancelPending == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 1); + TNC->SeenCancelPending = 1; + } + + if (Buffer[13] == ' ') + UpdateMH(TNC, &Buffer[14], '!', 'I'); + + return; + } + + TNC->SeenCancelPending = 0; + + if (strcmp(Buffer, "OK") == 0) + { + // Need to discard response to LISTEN OFF after attach + + if (TNC->DiscardNextOK) + { + TNC->DiscardNextOK = 0; + return; + } + + if (TNC->Streams[0].Connecting == TRUE) + return; // Discard response or it will mess up connect scripts + } + + if (_memicmp(Buffer, "BUFFER", 6) == 0) + { + Debugprintf(Buffer); + + sscanf(&Buffer[7], "%d", &TNC->Streams[0].BytesOutstanding); + + if (TNC->Streams[0].BytesOutstanding == 0) + { + // all sent + + if (TNC->Streams[0].Disconnecting) // Disconnect when all sent + { + if (STREAM->NeedDisc == 0) + STREAM->NeedDisc = 60; // 6 secs + } + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %s", + STREAM->bytesTXed, STREAM->bytesRXed, &Buffer[7]); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return; + } + + if (_memicmp(Buffer, "CONNECTED ", 10) == 0) + { + char Call[11]; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + int Speed = 0; + + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 1); + + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->PacketsSent = 0; + + if (TNC->VARACMsg) + free(TNC->VARACMsg); + + TNC->VaraACMode = 0; + TNC->VARACMsg = 0; + TNC->VARACSize = 0; + if (TNC->VaraACAllowed == 0) + TNC->VaraModeSet = 1; // definitly not varaac + else + TNC->VaraModeSet = 0; // Don't know yet + + strcpy(TNC->WEB_MODE, ""); + + if (strstr(Buffer, "2300")) + { + Speed = 50; + strcpy(TNC->WEB_MODE, "2300"); + } + else if (strstr(Buffer, "NARROW")) + { + Speed = 51; + strcpy(TNC->WEB_MODE, "NARROW"); + } + else if (strstr(Buffer, "WIDE")) + { + Speed = 52; + strcpy(TNC->WEB_MODE, "WIDE"); + } + else if (strstr(Buffer, "500")) + { + Speed = 53; + strcpy(TNC->WEB_MODE, "500"); + } + else if (strstr(Buffer, "2750")) + { + Speed = 54; + strcpy(TNC->WEB_MODE, "2750"); + } + + MySetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + // Get Target Call + + ptr = strchr(&Buffer[10], ' '); + + if (ptr) + { + memcpy(TNC->TargetCall, ++ptr, 10); + strlop(TNC->TargetCall, ' '); + } + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0 || (TNC->NetRomMode && STREAM->Connecting == 0)) + { + TRANSPORTENTRY * SESS; + + // Incoming Connect + + // Stop other ports in same group + + memset(STREAM, 0, sizeof(struct STREAMINFO)); + + STREAM->ConnectTime = time(NULL); + + SuspendOtherPorts(TNC); + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Only allow VarAC mode for incomming sessions + + ProcessIncommingConnectEx(TNC, Call, 0, (TNC->NetRomMode == 0), TRUE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (Speed) + SESS->Mode = Speed; + else + SESS->Mode = TNC->WL2KMode; + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + if (SESS->Frequency == 1500) + { + // try to get from WL2K record + + if (WL2K) + SESS->Frequency = WL2K->Freq; + } + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + if (WL2K) + SESS->Frequency = WL2K->Freq; + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command((TRANSPORTENTRY *) -1, Status); + Debugprintf("VARA Call from %s rejected", Call); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(SESS->L4USER, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command((TRANSPORTENTRY *) -1, Status); + Debugprintf("VARA Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + if (TNC->NetRomMode) + { + // send any queued data + + int bytes; + + if (TNC->NetRomTxLen) + { + + STREAM->PacketsSent++; + + bytes = send(TNC->TCPDataSock, TNC->NetRomTxBuffer, TNC->NetRomTxLen, 0); + STREAM->bytesTXed += TNC->NetRomTxLen; + + free(TNC->NetRomTxBuffer); + TNC->NetRomTxBuffer = NULL; + TNC->NetRomTxLen = 0; + } + return; + } + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + char AppBuffer[64]; + + memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12); + AppName[12] = 0; + + // if SendTandRtoRelay set and Appl is RMS change to RELAY + + if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(AppBuffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(buffptr->Data, AppBuffer, MsgLen); + + Debugprintf("Calling Application %s", AppBuffer); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r"; + + // Send a Message, then a disconenct + + // Send CTEXT First + + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + int txlen = (int)buffptr->Len; + VARASendData(TNC, buffptr->Data, txlen); + ReleaseBuffer(buffptr); + } + + VARASendData(TNC, Msg, (int)strlen(Msg)); + STREAM->NeedDisc = 100; // 10 secs + } + } + + strcpy(STREAM->MyCall, TNC->TargetCall); + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + STREAM->ConnectTime = time(NULL); + + if (TNC->NetRomMode) + { + // send any queued data + + int bytes; + + if (TNC->NetRomTxLen) + { + STREAM->PacketsSent++; + + bytes = send(TNC->TCPDataSock, TNC->NetRomTxBuffer, TNC->NetRomTxLen, 0); + STREAM->bytesTXed += TNC->NetRomTxLen; + free(TNC->NetRomTxBuffer); + TNC->NetRomTxBuffer = NULL; + TNC->NetRomTxLen = 0; + } + } + else + { + TNC->VaraACMode = 0; + TNC->VaraModeSet = 1; // Don't allow connect to VaraAC + + buffptr = GetBuff(); + + if (buffptr == 0) + return; // No buffers, so ignore + + ReplyLen = sprintf(Reply, "*** Connected to %s\r", TNC->TargetCall); + + buffptr->Len = ReplyLen; + memcpy(buffptr->Data, Reply, ReplyLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + if (TNC->RIG && TNC->RIG->Valchar[0]) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, TNC->TargetCall, '+', 'O'); + return; + } + } + + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0) + { + Debugprintf(Buffer); + + TNC->ConnectPending = FALSE; // Cancel Scan Lock + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf(buffptr->Data, "VARA} Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (TNC->RestartAfterFailure) + { + if (TNC->ProgramPath) + KillTNC(TNC); + } + + return; + } + + WritetoTrace(TNC, Buffer, MsgLen - 1); + + // Release Session + + if (STREAM->Connected && STREAM->ConnectTime) + { + // Create a traffic record + + hookL4SessionDeleted(TNC, STREAM); + } + + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + if (STREAM->Disconnecting) // + VARAReleaseTNC(TNC); + + STREAM->Disconnecting = FALSE; + + return; + } + + + if (_memicmp(Buffer, "IAMALIVE", 8) == 0) + { +// strcat(Buffer, "\r\n"); +// WritetoTrace(TNC, Buffer, strlen(Buffer)); + return; + } + +// Debugprintf(Buffer); + + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 3); +// return; + } + + if (_memicmp(Buffer, "LINK REGISTERED", 9) == 0) + { + strcat(Buffer, "\r"); + WritetoTrace(TNC, Buffer, (int)strlen(Buffer)); + return; + } + + if (_memicmp(Buffer, "ENCRYPTION ", 11) == 0) + { + strcat(Buffer, "\r"); + WritetoTrace(TNC, Buffer, (int)strlen(Buffer)); + return; + } + + if (_memicmp(Buffer, "UNENCRYPTED LINK ", 11) == 0) + { + strcat(Buffer, "\r"); + WritetoTrace(TNC, Buffer, (int)strlen(Buffer)); + return; + } + + if (_memicmp(Buffer, "MISSING SOUNDCARD", 17) == 0) + { + strcat(Buffer, "\r"); + WritetoTrace(TNC, Buffer, (int)strlen(Buffer)); + return; + } + + // Others should be responses to commands + + // Return others to user (if attached but not connected) + + if (TNC->Streams[0].Attached == 0) + return; + + if (TNC->Streams[0].Connected) + return; + + if (MsgLen > 200) + MsgLen = 200; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf(buffptr->Data, "VARA} %s\r", Buffer); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); +} + + + +VOID VARAProcessReceivedData(struct TNCINFO * TNC) +{ + int InputLen; + + InputLen = recv(TNC->TCPDataSock, &TNC->ARDOPDataBuffer[TNC->DataInputLen], 8192 - TNC->DataInputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + +// closesocket(TNC->TCPSock); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + TNC->Streams[0].Disconnecting = FALSE; + + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + + return; + } + + TNC->DataInputLen += InputLen; + + if (TNC->NetRomMode) + { + // Unpack KISS frames from data stream + + unsigned char c; + int n = 0; + int Len = TNC->DataInputLen; + unsigned char KISSBuffer[600]; + int KissLen = 0; + + while (Len) + { + Len--; + + c = TNC->ARDOPDataBuffer[n++]; + + if (TNC->ESCFLAG) + { + // + // FESC received - next should be TFESC or TFEND + + TNC->ESCFLAG = FALSE; + + if (c == TFESC) + c = FESC; + + if (c == TFEND) + c = FEND; + + } + else + { + switch (c) + { + case FEND: + + // + // Either start of message or message complete + // + + if (KissLen == 0) + { + // Start of Message. + + continue; + } + + // Have a complete KISS frame - remove from buffer and process + + if (KISSBuffer[0] == 255) + { + // NODE Message + + MESSAGE * Msg = GetBuff(); + + if (Msg) + { + // Set up header + + Msg->LENGTH = KissLen + (Msg->L2DATA - (unsigned char *)Msg); + memcpy(Msg->L2DATA, KISSBuffer, KissLen); + ConvToAX25(TNC->NRNeighbour, Msg->ORIGIN); + memcpy(Msg->DEST, NETROMCALL, 7); + + PROCESSNODEMESSAGE(Msg, &TNC->PortRecord->PORTCONTROL); + + Msg->PID = 0xcf; + Msg->PORT = TNC->Port | 0x80; + Msg->CTL = 3; + Msg->DEST[6] |= 0x80; // set Command Bit + Msg->ORIGIN[6] |= 1; // set end of address + time(&Msg->Timestamp); + BPQTRACE(Msg, FALSE); + } + } + else + { + // Netrom Message + + L3MESSAGEBUFFER * L3MSG = GetBuff(); + MESSAGE * Buffer = GetBuff(); + + if (L3MSG) + { + // Set up header + + L3MSG->LENGTH = KissLen + (L3MSG->L3SRCE - (unsigned char *)L3MSG); + memcpy(L3MSG->L3SRCE, KISSBuffer, KissLen); + L3MSG->L3PID = 0xcf; + + // Create copy to pass to monitor + // To trace we need to reformat as MESSAGE + + Buffer->PID = 0xcf; + Buffer->PORT = TNC->Port; + Buffer->CTL = 3; + Buffer->LENGTH = KissLen + (Buffer->L2DATA - (unsigned char *)Buffer); + memcpy(Buffer->L2DATA, KISSBuffer, KissLen); + + ConvToAX25(TNC->NRNeighbour, Buffer->DEST); + memcpy(Buffer->ORIGIN, NETROMCALL, 7); + Buffer->ORIGIN[6] |= 1; // set end of address + Buffer->DEST[6] |= 0x80; // set Command Bit + + time(&Buffer->Timestamp); + BPQTRACE(Buffer, FALSE); // TRACE + NETROMMSG(TNC->DummyLink, L3MSG); + } + } + + if (Len == 0) // All used + { + TNC->DataInputLen = 0; + } + else + { + memmove(TNC->ARDOPDataBuffer, &TNC->ARDOPDataBuffer[n], Len); + TNC->DataInputLen = Len; + KissLen = 0; + n = 0; + } + + continue; + + case FESC: + + TNC->ESCFLAG = TRUE; + continue; + + } + } + + // Ok, a normal char + + KISSBuffer[KissLen++] = c; + + if (KissLen > 590) + KissLen = 0; + + } + + // End of input - if there is stuff left in the input buffer we will add the next block to it + + return; + } + + VARAProcessDataPacket(TNC, TNC->ARDOPDataBuffer, TNC->DataInputLen); + TNC->DataInputLen = 0; + return; +} + + + +VOID VARAProcessReceivedControl(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[8192]; + + // shouldn't get several messages per packet, as each should need an ack + // May get message split over packets + + if (TNC->InputLen > 8000) // Shouldnt have packets longer than this + TNC->InputLen=0; + + InputLen=recv(TNC->TCPSock, &TNC->ARDOPBuffer[TNC->InputLen], 8191 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + + TNC->CONNECTED = FALSE; + + if (TNC->Streams[0].Connecting || TNC->Streams[0].Connected) + TNC->Streams[0].ReportDISC = TRUE; + + TNC->Streams[0].Disconnecting = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->InputLen += InputLen; + + TNC->ARDOPBuffer[TNC->InputLen] = 0; + Debugprintf("VARA Processing buffer - %s", TNC->ARDOPBuffer); + +loop: + + ptr = memchr(TNC->ARDOPBuffer, '\r', TNC->InputLen); + + if (ptr == 0) // CR in buffer + { + Debugprintf("VARA Part Packet Received - Waiting for rest"); + return; // Wait for it + } + + ptr2 = &TNC->ARDOPBuffer[TNC->InputLen]; + + if ((ptr2 - ptr) == 1) // CR + { + // Usual Case - single msg in buffer + + VARAProcessResponse(TNC, TNC->ARDOPBuffer, TNC->InputLen); + TNC->InputLen=0; + return; + } + else + { + MsgLen = TNC->InputLen - (int)(ptr2-ptr) + 1; // Include CR + + memcpy(Buffer, TNC->ARDOPBuffer, MsgLen); + + VARAProcessResponse(TNC, Buffer, MsgLen); + + if (TNC->InputLen < MsgLen) + { + TNC->InputLen = 0; + Debugprintf("VARA Corrupt multi command input"); + return; + } + memmove(TNC->ARDOPBuffer, ptr + 1, TNC->InputLen - MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + return; +} + + + +VOID VARAProcessDataPacket(struct TNCINFO * TNC, UCHAR * Data, int Length) +{ + // Info on Data Socket - just packetize and send on + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int PacLen = 236; + PMSGWITHLEN buffptr; + + TNC->TimeSinceLast = 0; + + STREAM->bytesRXed += Length; + + Data[Length] = 0; +// Debugprintf("VARA: RXD %d bytes", Length); + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->bytesTXed, STREAM->bytesRXed,STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + // if VARAAC Mode, remove byte count from front and add cr + // could possibly be longer than buffer size + + if (TNC->VaraModeSet == 0) // Could be normal or VaraAC + { + unsigned char *ptr = memchr(Data, ' ', Length); // contains a space + + if (ptr) + { + int ACLen = atoi(Data); + int lenLen = (ptr - Data) + 1; + + if (ACLen == (Length - lenLen)) + TNC->VaraACMode = 1; // AC Mode + } + TNC->VaraModeSet = 1; // Know which mode + } + + if (TNC->VaraACMode) + { + char * lenp; + char * msg; + int len; + + lenp = Data; + msg = strlop(lenp, ' '); + + len = atoi(lenp); + if (len != strlen(msg)) + return; + + msg[len++] = 13; + msg[len] = 0; + + Length = len; + memmove(Data, msg, len + 1); + + } + + // May need to fragment + + while (Length) + { + int Fraglen = Length; + + if (Length > PACLEN) + Fraglen = PACLEN; + + Length -= Fraglen; + + buffptr = GetBuff(); + + if (buffptr == 0) + return; // No buffers, so ignore + + memcpy(buffptr->Data, Data, Fraglen); + + WritetoTrace(TNC, Data, Fraglen); + + Data += Fraglen; + + buffptr->Len = Fraglen; + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + } + return; +} + +static VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[0].BytesOutstanding == 0) + VARASendCommand(TNC, "DISCONNECT\r", TRUE); +} + +static VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + char Abort[] = "ABORT\r"; + + VARASendCommand(TNC, Abort, TRUE); + WritetoTrace(TNC, Abort, 5); +} + +static VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + VARAReleaseTNC(TNC); + TNC->ARDOPCurrentMode[0] = 0; // Force Mode select on next scan change + + // Also reset mode in case incoming call has changed it + + if (TNC->DefaultMode == 50) + VARASendCommand(TNC, "BW2300\r", TRUE); + else if (TNC->DefaultMode == 53) + VARASendCommand(TNC, "BW500\r", TRUE); + else if (TNC->DefaultMode == 54) + VARASendCommand(TNC, "BW2750\r", TRUE); + +} + + +VOID VARASendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue) +{ + int SentLen; + + if (Buff[0] == 0) // Terminal Keepalive? + return; + + if (memcmp(Buff, "LISTEN O", 8) == 0) + TNC->DiscardNextOK = TRUE; // Responding to LISTEN messes up forwarding + + if (TNC->CONNECTED == 0) + return; + + if (TNC->TCPSock) + { + SentLen = send(TNC->TCPSock, Buff, (int)strlen(Buff), 0); + + if (SentLen != strlen(Buff)) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "VARA Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTED = FALSE; + return; + } + } + return; +} + +int VARASendData(struct TNCINFO * TNC, UCHAR * Buff, int Len) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int bytes=send(TNC->TCPDataSock,(const char FAR *)Buff, Len, 0); + STREAM->bytesTXed += bytes; + WritetoTrace(TNC, Buff, Len); + return bytes; +} + +#ifndef LINBPQ + +BOOL CALLBACK EnumVARAWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[128]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + int n; + + n = GetWindowText(hwnd, wtext, 127); + + if (memcmp(wtext,"VARA", 4) == 0) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + if (TNC->PID == ProcessId) + { + // Our Process + + char msg[512]; + char ID[64] = ""; + int i = 29; + + memcpy(ID, TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION, 30); + + while (ID[i] == ' ') + ID[i--] = 0; + + wtext[n] = 0; + sprintf (msg, "BPQ %s - %s", ID, wtext); + SetWindowText(hwnd, msg); + return FALSE; + } + } + + return (TRUE); +} +#endif + +VOID VARAReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + +// ARDOPChangeMYC(TNC, TNC->NodeCall); + + VARASendCommand(TNC, "LISTEN ON\r", TRUE); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Start Scanner + + if (TNC->DefaultRadioCmd) + { + sprintf(TXMsg, "%d %s", TNC->Port, TNC->DefaultRadioCmd); + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + } + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + ReleaseOtherPorts(TNC); +} + +void SendVARANetrom(struct TNCINFO * TNC, unsigned char * Data, int Len) +{ + // Check that PID is 0xcf, then just send the data portion of packet + + // We need to delimit packets, and KISS encoding seems as good as any. Also + // need to buffer to avoid sending lots of small packets and maybe to wait for + // link to connect + + unsigned char Kiss[600]; + int KissLen; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + if (TNC->CONNECTED == 0) + return; // Don't Queue if no connection to TNC + + KissLen = KissEncode(Data, Kiss, Len); + + TNC->NetRomTxBuffer = realloc(TNC->NetRomTxBuffer, TNC->NetRomTxLen + KissLen); + memcpy(&TNC->NetRomTxBuffer[TNC->NetRomTxLen], Kiss, KissLen); + TNC->NetRomTxLen += KissLen; + + if (STREAM->Connected) + { + int bytes; + + STREAM->PacketsSent++; + + bytes = send(TNC->TCPDataSock, TNC->NetRomTxBuffer, TNC->NetRomTxLen, 0); + STREAM->bytesTXed += TNC->NetRomTxLen; + + free(TNC->NetRomTxBuffer); + TNC->NetRomTxBuffer = NULL; + TNC->NetRomTxLen = 0; + return; + } + + if (TNC->Streams[0].Connecting == 0 && TNC->Streams[0].Connected == 0) + { + // Try to connect to Neighbour + + char Connect[32]; + + TNC->NetRomMode = 1; + + sprintf(Connect, "CONNECT %s %s\r", MYNETROMCALL, TNC->NRNeighbour); + + // Need to set connecting here as if we delay for busy we may incorrectly process OK response + + TNC->Streams[0].Connecting = TRUE; + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return; + } + } + TNC->OverrideBusy = FALSE; + + VARASendCommand(TNC, Connect, TRUE); + TNC->Streams[0].ConnectTime = time(NULL); + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, MYNETROMCALL); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } +} + +void SendVARANetromNodes(struct TNCINFO * TNC, MESSAGE *Buffer) +{ + // Check that PID is 0xcf, then just send the data portion of packet + + int Len = Buffer->LENGTH - (Buffer->L2DATA - (unsigned char *)Buffer); + + if (Buffer->PID != 0xcf) + { + ReleaseBuffer(Buffer); + return; + } + + SendVARANetrom(TNC, Buffer->L2DATA, Len); + time(&Buffer->Timestamp); + + C_Q_ADD(&TRACE_Q, Buffer); + +} + +void SendVARANetromMsg(struct TNCINFO * TNC, L3MESSAGEBUFFER * MSG) +{ + MESSAGE * Buffer = (MESSAGE *)MSG; + + int Len = MSG->LENGTH - (MSG->L3SRCE - (unsigned char *)MSG); + + if (MSG->L3PID != 0xcf) + { + ReleaseBuffer(MSG); + return; + } + + SendVARANetrom(TNC, MSG->L3SRCE, Len); + + memmove(Buffer->L2DATA, MSG->L3SRCE, Len); + + // To trace we need to reformat as MESSAGE + + Buffer->PID = 0xcf; + Buffer->PORT = TNC->Port | 0x80; + Buffer->CTL = 3; + Buffer->LENGTH = Len + (Buffer->L2DATA - (unsigned char *)Buffer); + ConvToAX25(TNC->NRNeighbour, Buffer->DEST); + memcpy(Buffer->ORIGIN, NETROMCALL, 7); + Buffer->ORIGIN[6] |= 1; // set end of address + Buffer->DEST[6] |= 0x80; // set Command Bit + + + time(&Buffer->Timestamp); + + C_Q_ADD(&TRACE_Q, Buffer); + +} + + + + + + diff --git a/.svn/pristine/56/56e7feaf8f487b8936b3931a13f84cccbd62335f.svn-base b/.svn/pristine/56/56e7feaf8f487b8936b3931a13f84cccbd62335f.svn-base new file mode 100644 index 0000000..7792329 --- /dev/null +++ b/.svn/pristine/56/56e7feaf8f487b8936b3931a13f84cccbd62335f.svn-base @@ -0,0 +1,1905 @@ +/* +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 +*/ + +// +// DLL to inteface HAL Communications Corp Clover/Pacor controllers to BPQ32 switch +// +// Uses BPQ EXTERNAL interface +// + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include "time.h" + +#include "cheaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + +#define HAL 1 + +#define SetMYCALL 0x13 +#define ConnectEnable 0x52 +#define ConnectDisable 0x42 +#define SetEAS 0x59 // Echo as Sent +#define SetTones 0xec +#define ClearOnDisc 0x57 + +static char ClassName[]="HALSTATUS"; + +static char WindowTitle[] = "HAL"; +static int RigControlRow = 185; + +#define SOH 0x01 // CONTROL CODES +#define ETB 0x17 +#define DLE 0x10 + +//int MaxStreams = 0; + +#ifndef LINBPQ +extern HFONT hFont; +#endif + +static char status[23][50] = {"IDLE", "TFC", "RQ", "ERR", "PHS", "OVER", "FSK TX", + "FSK RX", "P-MODE100", "P-MODE200", "HUFMAN ON", "HUFMAN OFF", "P-MODE SBY(LISTEN ON)", + "P-MODE SBY(LISTEN OFF)", "ISS", "IRS", + "AMTOR SBY(LISTEN ON)", "AMTOR SBY(LISTEN OFF)", "AMTOR FEC TX", "AMTOR FEC RX", "P-MODE FEC TX", + "FREE SIGNAL TX (AMTOR)", "FREE SIGNAL TX TIMED OUT (AMTOR)"}; + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL SetupConnection(int); +static BOOL WriteCommBlock(struct TNCINFO * TNC); +static void CheckRX(struct TNCINFO * TNC); +VOID HALPoll(int Port); +VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); +VOID ProcessTermModeResponse(struct TNCINFO * TNC); +static VOID DoTNCReinit(struct TNCINFO * TNC); +VOID DoTermModeTimeout(struct TNCINFO * TNC); +VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length); +VOID ProcessHALCmd(struct TNCINFO * TNC); +VOID ProcessHALData(struct TNCINFO * TNC); +VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); +VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); + +BOOL HALConnected(struct TNCINFO * TNC, char * Call); +VOID HALDisconnected(struct TNCINFO * TNC); + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +int DLEDecode(UCHAR * inbuff, UCHAR * outbuff, int len); + +VOID COMClearDTR(HANDLE fd); +VOID COMClearRTS(HANDLE fd); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); + + + +//static HANDLE LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + +//char * Logs[4] = {"1", "2", "3", "4"}; + +//char BaseDir[]="c:"; + +static VOID CloseLogfile(int Flags) +{ +// CloseHandle(LogHandle[Flags]); +// LogHandle[Flags] = INVALID_HANDLE_VALUE; +} + +static VOID OpenLogfile(int Flags) +{ +/* +UCHAR FN[MAX_PATH]; + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(FN,"%s\\HALLog_%02d%02d%02d_%s.bin", BaseDir, tm->tm_mday, tm->tm_hour, tm->tm_min, Logs[Flags]); + + LogHandle[Flags] = CreateFile(FN, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + SetFilePointer(LogHandle[Flags], 0, 0, FILE_END); + + return (LogHandle[Flags] != INVALID_HANDLE_VALUE); +*/ +} + +static void WriteLogLine(int Flags, char * Msg, int MsgLen) +{ +// int cnt; +// WriteFile(LogHandle[Flags] ,Msg , MsgLen, &cnt, NULL); +} + + + +int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + ptr = strtok(NULL, " \t\n\r"); + + if (_stricmp(buf, "APPL") == 0) // Using BPQ32 COnfig + { + BPQport = Port; + p_cmd = ptr; + } + else + if (_stricmp(buf, "PORT") != 0) // Using Old Config + { + // New config without a PORT or APPL - this is a Config Command + + strcpy(buf, errbuf); + strcat(buf, "\r"); + + BPQport = Port; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + goto ConfigLine; + } + else + + { + + // Old Config from file + + BPQport=0; + BPQport = atoi(ptr); + + p_cmd = strtok(NULL, " \t\n\r"); + + if (Port && Port != BPQport) + { + // Want a particular port, and this isn't it + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + } + } + } + if(BPQport > 0 && BPQport < 33) + { + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_cmd != NULL) + { + if (p_cmd[0] != ';' && p_cmd[0] != '#') + TNC->ApplCmd=_strdup(p_cmd); + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; +ConfigLine: + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + { + TNC->WL2K = DecodeWL2KReportLine(buf); + continue; + } + if (_memicmp(buf, "NEEDXONXOFF", 10) == 0) + { + TNC->XONXOFF = TRUE; + continue; + } + + if (_memicmp(buf, "TONES", 5) == 0) + { + int tone1 = 0, tone2 = 0; + + ptr = strtok(&buf[6], " ,/\t\n\r"); + if (ptr) + { + tone1 = atoi(ptr); + ptr = strtok(NULL, " ,/\t\n\r"); + if (ptr) + { + tone2 = atoi(ptr); + ptr = &TNC->InitScript[TNC->InitScriptLen]; + + // Try putting into FSK mode first + + *(ptr++) = 0x84; + *(ptr++) = SetTones; // Set Tones (Mark, Space HI byte first) + *(ptr++) = tone1 >> 8; + *(ptr++) = tone1 & 0xff; + *(ptr++) = tone2 >> 8; + *(ptr++) = tone2 & 0xff; + + TNC->InitScriptLen += 6; + + continue; + } + } + goto BadLine; + } + if (_memicmp(buf, "DEFAULTMODE ", 12) == 0) + { + + ptr = strtok(&buf[12], " ,\t\n\r"); + if (ptr) + { + if (_stricmp(ptr, "CLOVER") == 0) + TNC->DefaultMode = Clover; + else if (_stricmp(ptr, "PACTOR") == 0) + TNC->DefaultMode = Pactor; + else if (_stricmp(ptr, "AMTOR") == 0) + TNC->DefaultMode = AMTOR; + else goto BadLine; + + continue; + } + goto BadLine; + } + } + BadLine: + WritetoConsole(" Bad config record "); + WritetoConsole(errbuf); + WritetoConsole("\r\n"); + } + + return (TRUE); +} + +static size_t ExtProc(int fn, int port , PDATAMESSAGE buff) +{ + int txlen = 0; + PMSGWITHLEN buffptr; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM; + int Stream; + + if (TNC == NULL) + return 0; + + if (fn < 4 || fn > 5) + if (TNC->hDevice == 0) + return 0; // Port not open + + STREAM = &TNC->Streams[0]; + + switch (fn) + { + case 1: // poll + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = 0; + + return -1; + } + } + + CheckRX(TNC); + HALPoll(port); + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (STREAM->PACTORtoBPQ_Q !=0) + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = 0; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + + ReleaseBuffer(buffptr); + + return (1); + } + } + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + // Find TNC Record + + Stream = buff->PORT; + + if (!TNC->TNCOK) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 27; + memcpy(&buffptr->Data[0], "No Connection to PACTOR TNC\r", 27); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4); + + buffptr->Len = txlen; + memcpy(&buffptr->Data[0], &buff->L2DATA[0], txlen); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + + STREAM->FramesQueued++; + + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + + if (STREAM->FramesQueued > 4) + return (1 | TNC->HostMode << 8); + + return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + case 4: // reinit + + return (0); + + case 5: // Close + + CloseCOMPort(TNCInfo[port]->hDevice); + return (0); + + case 6: // Scan Control + + return 0; // None Yet + + } + return 0; + +} + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "HAL Status

HAL Status

"); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_STATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TXRX); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], "", TNC->WEB_LEDS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Status%s
TX/RX State%s
Traffic%s
LEDSSTBY CALL LINK ERROR TX RX
%s
"); + + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +VOID * HALExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * ptr; + int len; + char Msg[512]; +#ifndef LINBPQ + HWND x; +#endif + // + // Will be called once for each Pactor Port + // The COM port number is in IOBASE + // + + sprintf(msg,"HAL Driver %s", PortEntry->PORTCONTROL.SerialPortName); + WritetoConsole(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port"); + WritetoConsole(msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->PortRecord = PortEntry; + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_HAL; + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->MAXHOSTMODESESSIONS = 1; // Default + + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + { + memcpy(TNC->NodeCall, MYNODECALL, 10); + } + else + { + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + } + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + if (TNC->DefaultMode) + TNC->CurrentMode = TNC->DefaultMode; + else + TNC->CurrentMode = Clover; + + TNC->PollDelay = 999999999; + + // Set Disable +?, ExpandedStatus , Channel Stats Off, ClearOnDisc, EAS and MYCALL + + len = sprintf(Msg, "%c%c%c%c%c%c%s", 0xcc, 0x56, 0x41, ClearOnDisc, SetEAS, SetMYCALL, TNC->NodeCall); + len++; // We include the NULL + + memcpy(&TNC->InitScript[TNC->InitScriptLen], Msg, len); + TNC->InitScriptLen += len; + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 510; + TNC->WebWinY = 280; + + TNC->WEB_COMMSSTATE = zalloc(512); + TNC->WEB_TNCSTATE = zalloc(100); + strcpy(TNC->WEB_TNCSTATE, "Free"); + TNC->WEB_MODE = zalloc(100); + TNC->WEB_TRAFFIC = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_STATE = zalloc(100); + TNC->WEB_TXRX = zalloc(100); + TNC->WEB_LEDS = zalloc(100); + strcpy(TNC->WEB_LEDS, " X X X X X X"); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 233, ForcedClose); + + x = CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + x = TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + x = CreateWindowEx(0, "STATIC", "LEDS", WS_CHILD | WS_VISIBLE,10,138,60,20, TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + x = CreateWindowEx(0, "STATIC", "STBY CALL LINK ERROR TX RX", WS_CHILD | WS_VISIBLE,116,138,280,20, TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + x = TNC->xIDC_LEDS = CreateWindowEx(0, "STATIC", " X X X X X X", WS_CHILD | WS_VISIBLE,116,158,280,20 , TNC->hDlg, NULL, hInstance, NULL); + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + + TNC->ClientHeight = 233; + TNC->ClientWidth = 500; + + MoveWindows(TNC); +#endif + + OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + + SendCmd(TNC, "\x09" , 1); // Reset + + WritetoConsole("\n"); + + return ExtProc; +} + + + +static VOID KISSCLOSE(int Port) +{ + struct TNCINFO * conn = TNCInfo[Port]; + + // drop DTR and RTS + + COMClearDTR(conn->hDevice); + COMClearRTS(conn->hDevice); + + // purge any outstanding reads/writes and close device handle + + CloseCOMPort(conn->hDevice); + + return; +} + + +static void CheckRX(struct TNCINFO * TNC) +{ + int Length, Len; + UCHAR * Xptr; + + // only try to read number of bytes in queue + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + // We need to konw whether data is received or echoed, so we can't split commands and data here. + // Pass everything to the Command Handler. It will check that there are enough bytes for the command, + // and wait for more if not. + + // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 + + // If USB version, we might get unescaped xon and xoff, which we must ignore + + if (TNC->XONXOFF) + { + Xptr = memchr(&TNC->RXBuffer, 0x11, Length); + + while(Xptr) + { + Debugprintf("XON Port %d", TNC->Port); + memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); + Xptr = memchr(&TNC->RXBuffer, 0x11, Length); + } + + Xptr = memchr(&TNC->RXBuffer, 0x13, Length); + + while(Xptr) + { + Debugprintf("XOFF Port %d", TNC->Port); + memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr)); + Xptr = memchr(&TNC->RXBuffer, 0x13, Length); + } + + Xptr = memchr(&TNC->RXBuffer, 0x91, Length); // See if packet contains 0x91 escape + + if (Xptr) + + // Make sure we have the escaped char as well + + if ((Xptr - &TNC->RXBuffer[0]) == Length - 1) // x91 is last char + return; + } + + ProcessHALBuffer(TNC, Length); + + TNC->RXLen = 0; + + return; + +} + + + +static BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + return TRUE; +} + +VOID HALPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + UCHAR * Poll = TNC->TXBuffer; + char Status[80]; + UCHAR TXMsg[1000]; + int datalen; + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + // Timed Out + + TNC->TNCOK = FALSE; + TNC->HostMode = 0; + + sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[0]) // Connected + { + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + } + } + + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (TNC->TNCOK) + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + STREAM->Attached = TRUE; + + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->BytesAcked = 0; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + + datalen = sprintf(TXMsg, "%c%s", SetMYCALL, STREAM->MyCall); + SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + + SendCmd(TNC, "\x42", 1); // Connect Enable off + + return; + + } + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + + if (STREAM->Attached) + CheckForDetach(TNC, 0, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (TNC->NeedPACTOR) + { + TNC->NeedPACTOR--; + + if (TNC->NeedPACTOR == 0) + { + int datalen; + + UCHAR TXMsg[80]; + + datalen = sprintf(TXMsg, "%c%s", SetMYCALL, TNC->NodeCall); + SendCmd(TNC, TXMsg, datalen + 1); // Send the NULL + + // Set Listen Mode + + switch (TNC->CurrentMode) + { + case Pactor: + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + SendCmd(TNC, "\x58", 1); // Listen + + break; + + case Clover: + + SendCmd(TNC, "\x80", 1); // Clover + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x41", 1); // No Statistics + SendCmd(TNC, "\x60\x09", 2); // Robust Retries + SendCmd(TNC, "\x61\x09", 2); // Normal Retries + + break; + } + + SendCmd(TNC, "\x52", 1); // ConnectEnable + + // Restart Scanning + + sprintf(Status, "%d SCANSTART 15", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Status); + + return; + } + } + +#define MAXHALTX 256 + + //for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q && (STREAM->bytesTXed - STREAM->BytesAcked < 600)) + { + int datalen; + PMSGWITHLEN buffptr; + UCHAR * MsgPtr; + unsigned char TXMsg[500]; + + buffptr = (PMSGWITHLEN)STREAM->BPQtoPACTOR_Q; + datalen = buffptr->Len; + MsgPtr = buffptr->Data; + + if (STREAM->Connected) + { + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; + if (strstr(MsgPtr, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + STREAM->FramesQueued--; + return; + } + } + + // Must send data in small chunks - the Hal has limited buffer space + + // If in IRS force a turnround + + if (TNC->TXRXState == 'R' && TNC->CurrentMode != Clover) + { + if (TNC->TimeInRX++ > 15) + SendCmd(TNC, "\x87", 1); // Changeover to ISS + else + goto Poll; + } + + TNC->TimeInRX = 0; + + EncodeAndSend(TNC, MsgPtr, datalen); + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + ReleaseBuffer(buffptr); + WriteLogLine(2, MsgPtr, datalen); + + STREAM->bytesTXed += datalen; + STREAM->FramesQueued--; + + ShowTraffic(TNC); + + return; + } + else + { + buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); + STREAM->FramesQueued--; + + // Command. Do some sanity checking and look for things to process locally + + datalen--; // Exclude CR + MsgPtr[datalen] = 0; // Null Terminate + _strupr(MsgPtr); + + if (memcmp(MsgPtr, "RADIO ", 6) == 0) + { + sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]); + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &MsgPtr[40])) + { + ReleaseBuffer(buffptr); + } + else + { + buffptr->Len = sprintf((UCHAR *)buffptr->Data, "%s", &MsgPtr[40]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + return; + } + + if (memcmp(MsgPtr, "MODE CLOVER", 11) == 0) + { + TNC->CurrentMode = Clover; + buffptr->Len = sprintf((UCHAR *)buffptr->Data,"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + MySetWindowText(TNC->xIDC_MODE, "Clover"); + strcpy(TNC->WEB_MODE, "Clover"); + + SendCmd(TNC, "\x80", 1); // Clover + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x41", 1); // No Statistics + + return; + } + + if (memcmp(MsgPtr, "MODE PACTOR", 11) == 0) + { + TNC->CurrentMode = Pactor; + buffptr->Len = sprintf((UCHAR *)buffptr->Data,"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + SendCmd(TNC, "\x48", 1); // Listen Off + + return; + } + if (memcmp(MsgPtr, "MODE AMTOR", 11) == 0) + { + TNC->CurrentMode = AMTOR; + buffptr->Len = sprintf((UCHAR *)buffptr->Data,"HAL} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return; + } + + if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect + { + memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); + + switch (TNC->CurrentMode) + { + case Pactor: + + SendCmd(TNC, "\x84", 1); // FSK + SendCmd(TNC, "\x83", 1); // Select P-MODE Standby + + datalen = sprintf(TXMsg, "\x19%s", STREAM->RemoteCall); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - PACTOR", STREAM->MyCall, STREAM->RemoteCall); + + // DOnt set connecting till we get the 19 response so we can trap listen as a fail + break; + + case Clover: + + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", STREAM->MyCall, STREAM->RemoteCall); + + break; + } + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + SendCmd(TNC, TXMsg, datalen + 1); // Include NULL + + ReleaseBuffer(buffptr); + + return; + } + + if (memcmp(MsgPtr, "CLOVER ", 7) == 0) + { + memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); + + SendCmd(TNC, "\x54", 1); // Enable adaptive Clover format + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall); + SendCmd(TNC, TXMsg, datalen + 1); // Include NULL + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", + STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + ReleaseBuffer(buffptr); + + return; + } + + if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect + { + SendCmd(TNC, "\x07", 1); // Normal Disconnect + TNC->NeedPACTOR = 50; + + STREAM->Connecting = FALSE; + STREAM->ReportDISC = TRUE; + ReleaseBuffer(buffptr); + + return; + } + + // Other Command ?? Treat as HEX string + + datalen = sscanf(MsgPtr, "%X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X", + (UINT *)&TXMsg[0], (UINT *)&TXMsg[1], (UINT *)&TXMsg[2], (UINT *)&TXMsg[3], (UINT *)&TXMsg[4], + (UINT *)&TXMsg[5], (UINT *)&TXMsg[6], (UINT *)&TXMsg[7], (UINT *)&TXMsg[8], (UINT *)&TXMsg[9], + (UINT *)&TXMsg[10], (UINT *)&TXMsg[11], (UINT *)&TXMsg[12], (UINT *)&TXMsg[13], + (UINT *)&TXMsg[14], (UINT *)&TXMsg[15]); + +// SendCmd(TNC, TXMsg, datalen); + ReleaseBuffer(buffptr); + TNC->InternalCmd = 0; + } + } + } +Poll: + // Nothing doing - send Poll (but not too often) + + TNC->PollDelay++; + + if (TNC->PollDelay < 20) + return; + + TNC->PollDelay = 0; + + if (TNC->TNCOK) + SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll + else + SendCmd(TNC, "\x09" , 1); // Reset + + TNC->Timeout = 100; + + return; +} + +static VOID DoTNCReinit(struct TNCINFO * TNC) +{ + // TNC Has Restarted, send init commands (can probably send all at once) + +// TNC->TXBuffer[0] = 0x1b; +// TNC->TXLen = 1; + + WriteCommBlock(TNC); + + SendCmd(TNC, TNC->InitScript, TNC->InitScriptLen); + + TNC->HostMode = TRUE; // Should now be in Host Mode + TNC->NeedPACTOR = 20; // Need to set Calls and start scan + + TNC->DataMode = RXDATA; // Start with RX Data + + SendCmd(TNC, "\x7d" , 1); // Use Get LEDS as Poll +// SendCmd(TNC, "\xc9" , 1); // Huffman Off + SendCmd(TNC, "\x57", 1); // Enable TX buffer clear on disconnect + + SendCmd(TNC, "\x60\x06", 2); // Robust Mode Retries + +// SendCmd(TNC, "\x6f\x03" , 2); // Undocumented XON/XOFF On - used to see if old or new style modem + + TNC->Timeout = 50; + + return; + +} + +VOID ProcessHALData(struct TNCINFO * TNC) +{ + // Received Data just pass to Appl + + PMSGWITHLEN buffptr; + int Len = TNC->DataLen; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + TNC->DataLen = 0; + + if (TNC->DataMode == TXDATA) + { + STREAM->BytesAcked += Len; +// Debugprintf("Acked %d", Len); + + if (STREAM->BytesAcked > STREAM->bytesTXed) + Debugprintf("Too Much Acked"); + + if ((STREAM->BPQtoPACTOR_Q == 0) && STREAM->BytesAcked >= STREAM->bytesTXed) + { + // All sent + + if (STREAM->Disconnecting) + TidyClose(TNC, 0); + else + if (TNC->CurrentMode != Clover) + + // turn round link + + SendCmd(TNC, "\x0c" , 1); // Turnround + + } + } + else + { + if (TNC->DataMode == RXDATA) + { +// Debugprintf("RXed %d", Len); + buffptr = GetBuff(); + if (buffptr == NULL) + return; // No buffers, so ignore + + buffptr->Len = Len; // Length + + WriteLogLine(1, TNC->DataBuffer, Len); + + STREAM->bytesRXed += Len; + + memcpy(buffptr->Data, TNC->DataBuffer, Len); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + } + + ShowTraffic(TNC); + + return; +} + + + +VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length) +{ + UCHAR Char; + UCHAR * inptr; + UCHAR * cmdptr; + UCHAR * dataptr; + BOOL CmdEsc, DataEsc; + + inptr = TNC->RXBuffer; + + cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; + dataptr = &TNC->DataBuffer[TNC->DataLen]; + CmdEsc = TNC->CmdEsc; + DataEsc = TNC->DataEsc; + + // HAL uses HEX 80 as a command escape, 81 to ESCAPE 80 and 81 + + // The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91 + + // Command Responses can be variable length + + // Command Handler will check for each command/response if it has enough - if not it will wait till more arrives + + while(Length--) + { + Char = *(inptr++); + + if (CmdEsc) + { + CmdEsc = FALSE; + + if (TNC->XONXOFF && Char == 0x91) + { + // XON/XOFF escape. We ensured above that data follows so we can process it inline + + Length--; + Char = *(inptr++) - 0x20; + } + *(cmdptr++) = Char; + } + else if (DataEsc) + { + DataEsc = FALSE; + goto DataChar; + } + else +NotData: + if (Char == 0x80) // Next Char is Command + CmdEsc = TRUE; + else if (Char == 0x81) // Next Char is escaped data (80 or 81) + DataEsc = TRUE; + else + { + // This is a Data Char. We must process any Commands received so far, so we know the type of data + + DataChar: + + TNC->CmdLen = cmdptr - TNC->CmdBuffer; + ProcessHALCmd(TNC); + cmdptr = &TNC->CmdBuffer[TNC->CmdLen]; + dataptr = &TNC->DataBuffer[TNC->DataLen]; + + *(dataptr++) = Char; // Normal Data + + // Now process any other data chars + + while(Length--) + { + Char = *(inptr++); + + if (TNC->XONXOFF && Char == 0x91) + { + // XON/XOFF escape within data. We ensured above that data follows so we + // can process it here + + Length--; + Char = *(inptr++) - 0x20; + } + + if (Char == 0x80 || Char == 0x81) + { + // Process any data we have, then loop back + + TNC->DataLen = dataptr - TNC->DataBuffer; + ProcessHALData(TNC); + + goto NotData; + } + *(dataptr++) = Char; // Normal Data + } + + // Used all data + + TNC->DataLen = dataptr - TNC->DataBuffer; + + ProcessHALData(TNC); + TNC->CmdEsc = CmdEsc; + TNC->DataEsc = DataEsc; + + return; + } + } + + // Save State + + TNC->CmdLen = cmdptr - TNC->CmdBuffer; + + TNC->CmdEsc = CmdEsc; + TNC->DataEsc = DataEsc; + + if (TNC->DataLen) + ProcessHALData(TNC); + + if (TNC->CmdLen) + ProcessHALCmd(TNC); +} + +VOID mySetWindowText(struct TNCINFO * TNC, char * Msg) +{ + MySetWindowText(TNC->xIDC_STATE, Msg); + strcpy(TNC->WEB_STATE, Msg); +} + +VOID ProcessHALCmd(struct TNCINFO * TNC) +{ + char * Call; + int Stream = 0; + int Opcode; + int StatusByte; + int Leds; + int Len; + int Used; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + +CmdLoop: + + Opcode = TNC->CmdBuffer[0]; + Len = TNC->CmdLen; + + if (Len == 0) + return; + + TNC->TNCOK = TRUE; + TNC->Timeout = 0; + + sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // We may have more than one response in the buffer, and only each cmd/response decoder knows how many it needs + + switch(Opcode) + { + case 0x09: //Hardware Reset - equivalent to power on reset + + // Hardware has reset - need to reinitialise + + TNC->HostMode = 0; // Force Reinit + + Used = 1; + break; + + case 0x7a: // FSK Modes Status + + // Mixture of mode and state - eg listen huffman on/off irs/iss, so cant just display + + if (Len < 2) return; // Wait for more + + StatusByte = TNC->CmdBuffer[1]; + + switch (StatusByte) + { + case 0x06: // FSK TX (RTTY) + case 0x07: // FSK RX (RTTY) + case 0x10: // AMTOR STANDBY (LISTEN ON) + case 0x11: // AMTOR STANDBY (LISTEN OFF) + case 0x12: // AMTOR FEC TX (AMTOR) + case 0x13: // AMTOR FEC RX (AMTOR) + case 0x14: // P-MODE FEC TX (P-MODE) + case 0x15: // FREE SIGNAL TX (AMTOR) + case 0x16: // FREE SIGNAL TX TIMED OUT (AMTOR) + + // Diaplay Linke Status + + MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + break; + + case 0x0C: // P-MODE STANDBY (LISTEN ON) + case 0x0D: // P-MODE STANDBY (LISTEN OFF) + + // if we were connecting, this means connect failed. + + MySetWindowText(TNC->xIDC_MODE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + if (STREAM->Connecting) + HALDisconnected(TNC); + + break; + + case 0x0E: // ISS (AMTOR/P-MODE) + + MySetWindowText(TNC->xIDC_TXRX,"ISS"); + strcpy(TNC->WEB_TXRX, "ISS"); + TNC->TXRXState = 'S'; + break; + + case 0x0F: // IRS (AMTOR/P-MODE) + + MySetWindowText(TNC->xIDC_TXRX,"IRS"); + strcpy(TNC->WEB_TXRX, "IRS"); + TNC->TXRXState = 'R'; + break; + + case 0x00: // IDLE (AMTOR/P-MODE) + case 0x01: // TFC (AMTOR/P-MODE) + case 0x02: // RQ (AMTOR/P-MODE) + case 0x03: // ERR (AMTOR/P-MODE) + case 0x04: // PHS (AMTOR/P-MODE) + case 0x05: // OVER (AMTOR/P-MODE) (not implemented) + + MySetWindowText(TNC->xIDC_STATE, status[StatusByte]); + strcpy(TNC->WEB_MODE, status[StatusByte]); + + + +//$807A $8008 P-MODE100 (P-MODE) +//$807A $8009 P-MODE200 (P-MODE) +//$807A $800A HUFFMAN ON (P-MODE) +//$807A $800B HUFFMAN OFF (P-MODE) + ; + } + Used = 2; + break; + + + case 0x7d: // Get LED Status + + // We use Get LED Status as a Poll + + if (Len < 2) return; // Wait for more + + Leds = TNC->CmdBuffer[1]; + sprintf(TNC->WEB_LEDS," %c %c %c %c %c %c ", + (Leds & 0x20)? 'X' : ' ', + (Leds & 0x10)? 'X' : ' ', + (Leds & 0x08)? 'X' : ' ', + (Leds & 0x04)? 'X' : ' ', + (Leds & 0x02)? 'X' : ' ', + (Leds & 0x01)? 'X' : ' '); + +// STBY CALL LINK ERROR TX RX + MySetWindowText(TNC->xIDC_LEDS, TNC->WEB_LEDS); + + Used = 2; + break; + + case 0x21: // Monitored FEC CCB + case 0x22: // Monitored ARQ CCB + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = (int)strlen(Call) + 2; // Opcode and Null + + UpdateMH(TNC, Call, '!', 0); + + break; + + case 0x27: // Clover ARQ LINK REQUEST status message + + //indicates an incoming link request to either MYCALL ($8027 $8000), or MYALTCALL ($8027 $8001). + + if (Len < 2) return; // Wait for more + + // Don't need to do anything (but may eventally use ALTCALL as an APPLCALL + Used = 2; + break; + + case 0x2D: // FSK ARQ Link Request status message + + // $802D $8001 $8000 CLOVER Link Request (not implemented) + // $802D $8002 $8000 AMTOR CCIR-476 Link Request + // $802D $8003 $8000 AMTOR CCIR-625 Link Request + // $802D $8004 $8000 P-MODE Link Request + + if (Len < 3) return; // Wait for more + + // Don't need to do anything (but may save Session type later + + Used = 3; + break; + + + case 0x28: // Monitored Call + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = strlen(Call) + 2; // Opcode and Null + + // Could possibly be used for APPLCALLS by changing MYCALL when we see a call to one of our calls + + break; + + + case 0x20: // Clover Linked with - Call Connected + case 0x29: // The Linked 476 message indicates the start of a CCIR 476 linked session. + case 0x2A: // The Linked 625 message indicates the start of a CCIR 625 linked session to . + case 0x2B: // P-MODE link to + + // As the reply is variable, make sure we have the terminating NULL + + if (memchr(TNC->CmdBuffer, 0, Len) == NULL) + return; // Wait for more + + Call = &TNC->CmdBuffer[1]; + Used = (int)strlen(Call) + 2; // Opcode and Null + + HALConnected(TNC, Call); + + break; + + case 0x23: // Normal Disconnected - followed by $8000 + case 0x24: // Link failed (any of the link errors) + case 0x25: // Signal Lost (LOS) + + if (Len < 2) return; // Wait for more + + HALDisconnected(TNC); + + Used = 2; + break; + + + // Stream Switch Reports - we will need to do something with these if Echo as Sent is set + // or we do something with the secondary port + + case 0x30: // Switch to Receive Data characters + case 0x31: // Switch to Transmit Data characters + case 0x32: // Switch to RX data from secondary port + + TNC->DataMode = Opcode; + Used = 1; + break; + + case 0x33: // Send TX data to modem + case 0x34: // Send TX data to secondary port + + TNC->TXMode = Opcode; + Used = 1; + break; + + case 0x70: // Channel Spectra Data + // $807F $80xx $8030 Invalid or unimplemented command code + if (Len < 9) return; // Wait for more + + Used = 9; + break; + + case 0x71: // SelCall On/Off + + if (Len < 2) return; // Wait for more + + Used = 2; + break; + + case 0x72: // Channel Spectra Data + // $807F $80xx $8030 Invalid or unimplemented command code + if (Len < 15) return; // Wait for more + + Used = 15; + break; + + case 0x73: // Clover Link state + + if (Len < 2) return; // Wait for more + + StatusByte = TNC->CmdBuffer[1]; + + switch (StatusByte) + { + case 0x00: mySetWindowText(TNC, "Channel idle"); break; + case 0x01: mySetWindowText(TNC, "Channel occupied with non-Clover signal"); break; + case 0x42: mySetWindowText(TNC, "Linked stations monitored"); break; + case 0x64: mySetWindowText(TNC, "Attempting normal link"); break; + case 0x65: mySetWindowText(TNC, "Attempting robust link"); break; + case 0x66: mySetWindowText(TNC, "Calling ARQ CQ"); break; + case 0x78: mySetWindowText(TNC, "Clover Control Block (CCB) send retry"); break; + case 0x79: mySetWindowText(TNC, "Clover Control Block (CCB) receive retry"); break; + case 0x7D: mySetWindowText(TNC, "Clover Control Block (CCB) received successfully"); break; + case 0x8A: mySetWindowText(TNC, "TX data block sent"); break; + case 0x8B: mySetWindowText(TNC, "RX data block received ok (precedes data block)"); break; + case 0x8C: mySetWindowText(TNC, "TX data block re-sent"); break; + case 0x8D: mySetWindowText(TNC, "RX data block decode failed (precedes data block)"); break; + case 0x8E: mySetWindowText(TNC, "TX idle"); break; + case 0x8F: mySetWindowText(TNC, "RX idle"); break; + case 0x9C: mySetWindowText(TNC, "Link failed: CCB send retries exceeded"); break; + case 0x9D: mySetWindowText(TNC, "Link failed: CCB receive retries exceeded"); break; + case 0x9E: mySetWindowText(TNC, "Link failed: protocol error"); break; + case 0xA0: mySetWindowText(TNC, "Receiving FEC SYNC sequence"); break; + } + + Used = 2; + break; + + case 0x75: // Clover waveform format + + if (Len < 5) return; // Wait for more + + Used = 5; + break; + + case 0x7F: // Error $80xx $80yy Error in command $80xx of type $80yy + // $807F $80xx $8030 Invalid or unimplemented command code + // $807F $80xx $8031 Invalid parameter value + // $807F $80xx $8032 Not allowed when connected + // $807F $80xx $8033 Not allowed when disconnected + // $807F $80xx $8034 Not valid in this mode + // $807F $80xx $8035 Not valid in this code + // $807F $8096 $8036 EEPROM write error + + if (Len < 3) return; // Wait for more + + if (TNC->CmdBuffer[1] == 0x6f && TNC->CmdBuffer[2] == 0x31) + { + // Reject of XON/XOFF enable + +// TNC->XONXOFF = FALSE; +// Debugprintf("BPQ32 HAL Port %d - Disabling XON/XOFF mode", TNC->Port); + } + else + Debugprintf("HAL Port %d Command Error Cmd %X Error %X", TNC->Port, TNC->CmdBuffer[1], TNC->CmdBuffer[2]); + + Used = 3; + break; + + // Following are all immediate commands - response is echo of command + + case 0x6f: // XON/XOFF on + +// TNC->XONXOFF = TRUE; // And drop through +// Debugprintf("BPQ32 HAL Port %d - Enabling XON/XOFF mode", TNC->Port); + + case 0x19: // Call P-MODE to + case 0x10: // Robust Link to using MYCALL + case 0x11: // Normal Link to using MYCALL + + STREAM->Connecting = TRUE; + + case 0x00: // P Load LOD file + case 0x01: // P Load S28 file + case 0x02: //Check Unit Error Status + case 0x03: //F Check System Clock + case 0x04: //C Close PTT and transmit Clover waveform + case 0x05: //Open PTT and stop transmit test + case 0x06: //Immediate Abort (Panic Kill) + case 0x07: //Normal disconnect (wait for ACK) + case 0x08: //Software reset - restore all program defaults + case 0x0A: //Send CW ID + case 0x0B: //Close PTT and transmit Single Tone + case 0x0C: //F Normal OVER (AMTOR,P-MODE) + case 0x0D: //F Force RTTY TX (Baudot/ASCII) + case 0x0E: //F Go to RTTY RX (Baudot/ASCII) + case 0x0F: //Go to LOD/S28 file loader + case SetMYCALL: // Set MYCALL Response + + case 0x1E: // Set MYALTCALL Response + + case 0x41: + case 0x42: + case 0x46: + case 0x47: + case 0x48: + case 0x4d: + case 0x52: // Enable adaptive Clover format + case 0x54: // Enable adaptive Clover format + + case 0x56: // Expanded Link State Reports OFF/ON + case 0x57: // Clear buffers on disc + case 0x58: + case 0x59: + case 0x60: // Robust Mode Retries + case 0x61: // Normal Mode Retries + case 0x80: //Switch to CLOVER mode + case 0x81: //Select AMTOR Standby + case 0x82: //Select AMTOR FEC + case 0x83: //Select P-MODE Standby + case 0x84: //Switch to FSK modes + case 0x85: //Select Baudot + case 0x86: //Select ASCII + case 0x87: //Forced OVER (AMTOR, P-MODE) + case 0x88: //Forced END (AMTOR, P-MODE) + case 0x89: //Force LTRS shift + case 0x8A: //Force FIGS shift + case 0x8B: //Send MARK tone + case 0x8C: //Send SPACE tone + case 0x8D: //Send MARK/SPACE tones + case 0x8E: //Received first character on line + case 0x8F: //Close PTT only (no tones) + + case 0xC9: //Huffman Off/On + case 0xCC: + case 0xD9: //Close PTT only (no tones) + + case SetTones: + + Used = 1; + break; + + case 0x91: // ???? + +// if (Len < 2) return; // Wait for more + + Used = 1; + break; + + default: + + // We didn't recognise command, so don't know how long it is - disaster! + + Debugprintf("HAL Port %d Unrecognised Command %x", TNC->Port, Opcode); + TNC->CmdLen = 0; + + return; + } + + if (Used == Len) + { + // All used - most likely case + + TNC->CmdLen = 0; + return; + } + + // Move Command Down buffer, and reenter + + TNC->CmdLen -= Used; + + memmove(TNC->CmdBuffer, &TNC->CmdBuffer[Used], TNC->CmdLen); + + goto CmdLoop; + + +} + + +VOID HALDisconnected(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + CloseLogfile(0); + CloseLogfile(1); + CloseLogfile(2); + + if ((STREAM->Connecting | STREAM->Connected) == 0) + { + // Not connected or Connecting. Probably response to going into Pactor Listen Mode + + return; + } + + if (STREAM->Connecting && STREAM->Disconnecting == FALSE) + { + PMSGWITHLEN buffptr; + + // Connect Failed - actually I think HAL uses another code for connect failed, but leave here for now + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "*** Failure with %s\r", STREAM->RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // In case! + STREAM->FramesQueued = 0; + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + } + + // Connected, or Disconnecting - Release Session + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->FramesQueued = 0; + + if (STREAM->Disconnecting == FALSE) + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + + // Need to reset Pactor Call in case it was changed + + TNC->NeedPACTOR = 20; +} + +BOOL HALConnected(struct TNCINFO * TNC, char * Call) +{ + char Msg[80]; + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char CallCopy[80]; + + strcpy(CallCopy, Call); + strcat(CallCopy, " "); // Some routines expect 10 char calls + + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->BytesAcked = 0; + STREAM->ConnectTime = time(NULL); + + // Stop Scanner + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + + ShowTraffic(TNC); + + TNC->DataMode = RXDATA; + + OpenLogfile(0); + OpenLogfile(1); + OpenLogfile(2); + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + // Incoming Connect + + ProcessIncommingConnect(TNC, CallCopy, 0, TRUE); + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", STREAM->RemoteCall, TNC->NodeCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + if (TNC->CurrentMode != Clover) + SendCmd(TNC, "\x87", 1); // Changeover to ISS + + // If an autoconnect APPL is defined, send it + + if (TNC->ApplCmd) + { + buffptr = GetBuff(); + if (buffptr == 0) return TRUE; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)buffptr->Data, "%s\r", TNC->ApplCmd); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + + return TRUE; + } + + if (FULL_CTEXT && HFCTEXTLEN == 0) + { + EncodeAndSend(TNC, CTEXTMSG, CTEXTLEN); + WriteLogLine(2, CTEXTMSG, CTEXTLEN); + + STREAM->bytesTXed += CTEXTLEN; + } + return TRUE; + } + + // Connect Complete + + buffptr = GetBuff(); + if (buffptr == 0) return TRUE; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)buffptr->Data, "*** Connected to %s\r", Call);; + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + STREAM->Connecting = FALSE; + STREAM->Connected = TRUE; // Subsequent data to data channel + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->NodeCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, CallCopy, '+', 'O'); + + + return TRUE; +} + + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) +{ + // Send A Packet With DLE Encoding Encoding + + TNC->TXLen = DLEEncode(txbuffer, TNC->TXBuffer, Len); + + WriteCommBlock(TNC); +} + +VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len) +{ + // Send A Packet With Command Encoding (preceed each with 0x80 + + int i,txptr=0; + UCHAR * outbuff = TNC->TXBuffer; + + for (i=0; iTXLen = txptr; + WriteCommBlock(TNC); +} + +int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + + // Escape x80 and x81 with x81 + +// outbuff[0] = 0x80; +// outbuff[1] = 0x33; // Send data to modem + + for (i=0;iNeedPACTOR = 30; +} + + + + diff --git a/.svn/pristine/57/57abeaa0ba65351540ed33cdf31ee3cd3453f1e8.svn-base b/.svn/pristine/57/57abeaa0ba65351540ed33cdf31ee3cd3453f1e8.svn-base new file mode 100644 index 0000000..0032c1b Binary files /dev/null and b/.svn/pristine/57/57abeaa0ba65351540ed33cdf31ee3cd3453f1e8.svn-base differ diff --git a/.svn/pristine/57/57f006c2815d5f085b912b9bbe3dfda00051a6b5.svn-base b/.svn/pristine/57/57f006c2815d5f085b912b9bbe3dfda00051a6b5.svn-base new file mode 100644 index 0000000..608ea2c --- /dev/null +++ b/.svn/pristine/57/57f006c2815d5f085b912b9bbe3dfda00051a6b5.svn-base @@ -0,0 +1,828 @@ + +/* png.c - location for general purpose libpng functions + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#define PNG_NO_EXTERN +#include "png.h" + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef version_1_2_8 Your_png_h_is_not_version_1_2_8; + +/* Version information for C files. This had better match the version + * string defined in png.h. */ + +#ifdef PNG_USE_GLOBAL_ARRAYS +/* png_libpng_ver was changed to a function in version 1.0.5c */ +const char png_libpng_ver[18] = PNG_LIBPNG_VER_STRING; + +/* png_sig was changed to a function in version 1.0.5c */ +/* Place to hold the signature string for a PNG file. */ +const png_byte FARDATA png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + +/* Invoke global declarations for constant strings for known chunk types */ +PNG_IHDR; +PNG_IDAT; +PNG_IEND; +PNG_PLTE; +PNG_bKGD; +PNG_cHRM; +PNG_gAMA; +PNG_hIST; +PNG_iCCP; +PNG_iTXt; +PNG_oFFs; +PNG_pCAL; +PNG_sCAL; +PNG_pHYs; +PNG_sBIT; +PNG_sPLT; +PNG_sRGB; +PNG_tEXt; +PNG_tIME; +PNG_tRNS; +PNG_zTXt; + +/* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + +/* start of interlace block */ +const int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + +/* offset to next interlace block */ +const int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + +/* start of interlace block in the y direction */ +const int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + +/* offset to next interlace block in the y direction */ +const int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + +/* width of interlace block (used in assembler routines only) */ +#ifdef PNG_HAVE_ASSEMBLER_COMBINE_ROW +const int FARDATA png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; +#endif + +/* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h +const int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; +*/ + +/* Mask to determine which pixels are valid in a pass */ +const int FARDATA png_pass_mask[] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; + +/* Mask to determine which pixels to overwrite while displaying */ +const int FARDATA png_pass_dsp_mask[] + = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; + +#endif /* PNG_USE_GLOBAL_ARRAYS */ + +/* Tells libpng that we have already handled the first "num_bytes" bytes + * of the PNG file signature. If the PNG data is embedded into another + * stream we can set num_bytes = 8 so that libpng will not attempt to read + * or write any of the magic bytes before it starts on the IHDR. + */ + +void PNGAPI +png_set_sig_bytes(png_structp png_ptr, int num_bytes) +{ + png_debug(1, "in png_set_sig_bytes\n"); + if (num_bytes > 8) + png_error(png_ptr, "Too many bytes for PNG signature."); + + png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes); +} + +/* Checks whether the supplied bytes match the PNG signature. We allow + * checking less than the full 8-byte signature so that those apps that + * already read the first few bytes of a file to determine the file type + * can simply check the remaining bytes for extra assurance. Returns + * an integer less than, equal to, or greater than zero if sig is found, + * respectively, to be less than, to match, or be greater than the correct + * PNG signature (this is the same behaviour as strcmp, memcmp, etc). + */ +int PNGAPI +png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + if (num_to_check > 8) + num_to_check = 8; + else if (num_to_check < 1) + return (0); + + if (start > 7) + return (0); + + if (start + num_to_check > 8) + num_to_check = 8 - start; + + return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check))); +} + +/* (Obsolete) function to check signature bytes. It does not allow one + * to check a partial signature. This function might be removed in the + * future - use png_sig_cmp(). Returns true (nonzero) if the file is a PNG. + */ +int PNGAPI +png_check_sig(png_bytep sig, int num) +{ + return ((int)!png_sig_cmp(sig, (png_size_t)0, (png_size_t)num)); +} + +/* Function to allocate memory for zlib and clear it to 0. */ +#ifdef PNG_1_0_X +voidpf PNGAPI +#else +voidpf /* private */ +#endif +png_zalloc(voidpf png_ptr, uInt items, uInt size) +{ + png_voidp ptr; + png_structp p=png_ptr; + png_uint_32 save_flags=p->flags; + png_uint_32 num_bytes; + + if (items > PNG_UINT_32_MAX/size) + { + png_warning (png_ptr, "Potential overflow in png_zalloc()"); + return (NULL); + } + num_bytes = (png_uint_32)items * size; + + p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes); + p->flags=save_flags; + +#if defined(PNG_1_0_X) && !defined(PNG_NO_ZALLOC_ZERO) + if (ptr == NULL) + return ((voidpf)ptr); + + if (num_bytes > (png_uint_32)0x8000L) + { + png_memset(ptr, 0, (png_size_t)0x8000L); + png_memset((png_bytep)ptr + (png_size_t)0x8000L, 0, + (png_size_t)(num_bytes - (png_uint_32)0x8000L)); + } + else + { + png_memset(ptr, 0, (png_size_t)num_bytes); + } +#endif + return ((voidpf)ptr); +} + +/* function to free memory for zlib */ +#ifdef PNG_1_0_X +void PNGAPI +#else +void /* private */ +#endif +png_zfree(voidpf png_ptr, voidpf ptr) +{ + png_free((png_structp)png_ptr, (png_voidp)ptr); +} + +/* Reset the CRC variable to 32 bits of 1's. Care must be taken + * in case CRC is > 32 bits to leave the top bits 0. + */ +void /* PRIVATE */ +png_reset_crc(png_structp png_ptr) +{ + png_ptr->crc = crc32(0, Z_NULL, 0); +} + +/* Calculate the CRC over a section of data. We can only pass as + * much data to this routine as the largest single buffer size. We + * also check that this data will actually be used before going to the + * trouble of calculating it. + */ +void /* PRIVATE */ +png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length) +{ + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + if (need_crc) + png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length); +} + +/* Allocate the memory for an info_struct for the application. We don't + * really need the png_ptr, but it could potentially be useful in the + * future. This should be used in favour of malloc(png_sizeof(png_info)) + * and png_info_init() so that applications that want to use a shared + * libpng don't have to be recompiled if png_info changes size. + */ +png_infop PNGAPI +png_create_info_struct(png_structp png_ptr) +{ + png_infop info_ptr; + + png_debug(1, "in png_create_info_struct\n"); + if(png_ptr == NULL) return (NULL); +#ifdef PNG_USER_MEM_SUPPORTED + info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO, + png_ptr->malloc_fn, png_ptr->mem_ptr); +#else + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); +#endif + if (info_ptr != NULL) + png_info_init_3(&info_ptr, png_sizeof(png_info)); + + return (info_ptr); +} + +/* This function frees the memory associated with a single info struct. + * Normally, one would use either png_destroy_read_struct() or + * png_destroy_write_struct() to free an info struct, but this may be + * useful for some applications. + */ +void PNGAPI +png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr) +{ + png_infop info_ptr = NULL; + + png_debug(1, "in png_destroy_info_struct\n"); + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + png_info_destroy(png_ptr, info_ptr); + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn, + png_ptr->mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } +} + +/* Initialize the info structure. This is now an internal function (0.89) + * and applications using it are urged to use png_create_info_struct() + * instead. + */ +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +#undef png_info_init +void PNGAPI +png_info_init(png_infop info_ptr) +{ + /* We only come here via pre-1.0.12-compiled applications */ + png_info_init_3(&info_ptr, 0); +} +#endif + +void PNGAPI +png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size) +{ + png_infop info_ptr = *ptr_ptr; + + png_debug(1, "in png_info_init_3\n"); + + if(png_sizeof(png_info) > png_info_struct_size) + { + png_destroy_struct(info_ptr); + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); + *ptr_ptr = info_ptr; + } + + /* set everything to 0 */ + png_memset(info_ptr, 0, png_sizeof (png_info)); +} + +#ifdef PNG_FREE_ME_SUPPORTED +void PNGAPI +png_data_freer(png_structp png_ptr, png_infop info_ptr, + int freer, png_uint_32 mask) +{ + png_debug(1, "in png_data_freer\n"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if(freer == PNG_DESTROY_WILL_FREE_DATA) + info_ptr->free_me |= mask; + else if(freer == PNG_USER_WILL_FREE_DATA) + info_ptr->free_me &= ~mask; + else + png_warning(png_ptr, + "Unknown freer parameter in png_data_freer."); +} +#endif + +void PNGAPI +png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask, + int num) +{ + png_debug(1, "in png_free_data\n"); + if (png_ptr == NULL || info_ptr == NULL) + return; + +#if defined(PNG_TEXT_SUPPORTED) +/* free text item num or (if num == -1) all text items */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) +#else +if (mask & PNG_FREE_TEXT) +#endif +{ + if (num != -1) + { + if (info_ptr->text && info_ptr->text[num].key) + { + png_free(png_ptr, info_ptr->text[num].key); + info_ptr->text[num].key = NULL; + } + } + else + { + int i; + for (i = 0; i < info_ptr->num_text; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); + png_free(png_ptr, info_ptr->text); + info_ptr->text = NULL; + info_ptr->num_text=0; + } +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +/* free any tRNS entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_TRNS) && (png_ptr->flags & PNG_FLAG_FREE_TRNS)) +#endif +{ + png_free(png_ptr, info_ptr->trans); + info_ptr->valid &= ~PNG_INFO_tRNS; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; +#endif + info_ptr->trans = NULL; +} +#endif + +#if defined(PNG_sCAL_SUPPORTED) +/* free any sCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) +#else +if (mask & PNG_FREE_SCAL) +#endif +{ +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, info_ptr->scal_s_width); + png_free(png_ptr, info_ptr->scal_s_height); + info_ptr->scal_s_width = NULL; + info_ptr->scal_s_height = NULL; +#endif + info_ptr->valid &= ~PNG_INFO_sCAL; +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +/* free any pCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) +#else +if (mask & PNG_FREE_PCAL) +#endif +{ + png_free(png_ptr, info_ptr->pcal_purpose); + png_free(png_ptr, info_ptr->pcal_units); + info_ptr->pcal_purpose = NULL; + info_ptr->pcal_units = NULL; + if (info_ptr->pcal_params != NULL) + { + int i; + for (i = 0; i < (int)info_ptr->pcal_nparams; i++) + { + png_free(png_ptr, info_ptr->pcal_params[i]); + info_ptr->pcal_params[i]=NULL; + } + png_free(png_ptr, info_ptr->pcal_params); + info_ptr->pcal_params = NULL; + } + info_ptr->valid &= ~PNG_INFO_pCAL; +} +#endif + +#if defined(PNG_iCCP_SUPPORTED) +/* free any iCCP entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) +#else +if (mask & PNG_FREE_ICCP) +#endif +{ + png_free(png_ptr, info_ptr->iccp_name); + png_free(png_ptr, info_ptr->iccp_profile); + info_ptr->iccp_name = NULL; + info_ptr->iccp_profile = NULL; + info_ptr->valid &= ~PNG_INFO_iCCP; +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +/* free a given sPLT entry, or (if num == -1) all sPLT entries */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) +#else +if (mask & PNG_FREE_SPLT) +#endif +{ + if (num != -1) + { + if(info_ptr->splt_palettes) + { + png_free(png_ptr, info_ptr->splt_palettes[num].name); + png_free(png_ptr, info_ptr->splt_palettes[num].entries); + info_ptr->splt_palettes[num].name = NULL; + info_ptr->splt_palettes[num].entries = NULL; + } + } + else + { + if(info_ptr->splt_palettes_num) + { + int i; + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i); + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = NULL; + info_ptr->splt_palettes_num = 0; + } + info_ptr->valid &= ~PNG_INFO_sPLT; + } +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) +#else +if (mask & PNG_FREE_UNKN) +#endif +{ + if (num != -1) + { + if(info_ptr->unknown_chunks) + { + png_free(png_ptr, info_ptr->unknown_chunks[num].data); + info_ptr->unknown_chunks[num].data = NULL; + } + } + else + { + int i; + + if(info_ptr->unknown_chunks_num) + { + for (i = 0; i < (int)info_ptr->unknown_chunks_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i); + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + info_ptr->unknown_chunks_num = 0; + } + } +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +/* free any hIST entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_HIST) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_HIST) && (png_ptr->flags & PNG_FLAG_FREE_HIST)) +#endif +{ + png_free(png_ptr, info_ptr->hist); + info_ptr->hist = NULL; + info_ptr->valid &= ~PNG_INFO_hIST; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_HIST; +#endif +} +#endif + +/* free any PLTE entry that was internally allocated */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_PLTE) && (png_ptr->flags & PNG_FLAG_FREE_PLTE)) +#endif +{ + png_zfree(png_ptr, info_ptr->palette); + info_ptr->palette = NULL; + info_ptr->valid &= ~PNG_INFO_PLTE; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; +#endif + info_ptr->num_palette = 0; +} + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* free any image bits attached to the info structure */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) +#else +if (mask & PNG_FREE_ROWS) +#endif +{ + if(info_ptr->row_pointers) + { + int row; + for (row = 0; row < (int)info_ptr->height; row++) + { + png_free(png_ptr, info_ptr->row_pointers[row]); + info_ptr->row_pointers[row]=NULL; + } + png_free(png_ptr, info_ptr->row_pointers); + info_ptr->row_pointers=NULL; + } + info_ptr->valid &= ~PNG_INFO_IDAT; +} +#endif + +#ifdef PNG_FREE_ME_SUPPORTED + if(num == -1) + info_ptr->free_me &= ~mask; + else + info_ptr->free_me &= ~(mask & ~PNG_FREE_MUL); +#endif +} + +/* This is an internal routine to free any memory that the info struct is + * pointing to before re-using it or freeing the struct itself. Recall + * that png_free() checks for NULL pointers for us. + */ +void /* PRIVATE */ +png_info_destroy(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_info_destroy\n"); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + png_ptr->num_chunk_list=0; + } +#endif + + png_info_init_3(&info_ptr, png_sizeof(png_info)); +} + +/* This function returns a pointer to the io_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy() or png_read_destroy() are called. + */ +png_voidp PNGAPI +png_get_io_ptr(png_structp png_ptr) +{ + return (png_ptr->io_ptr); +} + +#if !defined(PNG_NO_STDIO) +/* Initialize the default input/output functions for the PNG file. If you + * use your own read or write routines, you can call either png_set_read_fn() + * or png_set_write_fn() instead of png_init_io(). If you have defined + * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't + * necessarily available. + */ +void PNGAPI +png_init_io(png_structp png_ptr, png_FILE_p fp) +{ + png_debug(1, "in png_init_io\n"); + png_ptr->io_ptr = (png_voidp)fp; +} +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +/* Convert the supplied time into an RFC 1123 string suitable for use in + * a "Creation Time" or other text-based time string. + */ +png_charp PNGAPI +png_convert_to_rfc1123(png_structp png_ptr, png_timep ptime) +{ + static PNG_CONST char short_months[12][4] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if (png_ptr->time_buffer == NULL) + { + png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29* + png_sizeof(char))); + } + +#if defined(_WIN32_WCE) + { + wchar_t time_buf[29]; + wsprintf(time_buf, TEXT("%d %S %d %02d:%02d:%02d +0000"), + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + WideCharToMultiByte(CP_ACP, 0, time_buf, -1, png_ptr->time_buffer, 29, + NULL, NULL); + } +#else +#ifdef USE_FAR_KEYWORD + { + char near_time_buf[29]; + sprintf(near_time_buf, "%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + png_memcpy(png_ptr->time_buffer, near_time_buf, + 29*png_sizeof(char)); + } +#else + sprintf(png_ptr->time_buffer, "%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); +#endif +#endif /* _WIN32_WCE */ + return ((png_charp)png_ptr->time_buffer); +} +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + +#if 0 +/* Signature string for a PNG file. */ +png_bytep PNGAPI +png_sig_bytes(void) +{ + return ((png_bytep)"\211\120\116\107\015\012\032\012"); +} +#endif + +png_charp PNGAPI +png_get_copyright(png_structp png_ptr) +{ + if (&png_ptr != NULL) /* silence compiler warning about unused png_ptr */ + return ((png_charp) "\n libpng version 1.2.8 - December 3, 2004\n\ + Copyright (c) 1998-2004 Glenn Randers-Pehrson\n\ + Copyright (c) 1996-1997 Andreas Dilger\n\ + Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\n"); + return ((png_charp) ""); +} + +/* The following return the library version as a short string in the + * format 1.0.0 through 99.99.99zz. To get the version of *.h files + * used with your application, print out PNG_LIBPNG_VER_STRING, which + * is defined in png.h. + * Note: now there is no difference between png_get_libpng_ver() and + * png_get_header_ver(). Due to the version_nn_nn_nn typedef guard, + * it is guaranteed that png.c uses the correct version of png.h. + */ +png_charp PNGAPI +png_get_libpng_ver(png_structp png_ptr) +{ + /* Version of *.c files used when building libpng */ + if (&png_ptr != NULL) /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); + return ((png_charp) ""); +} + +png_charp PNGAPI +png_get_header_ver(png_structp png_ptr) +{ + /* Version of *.h files used when building libpng */ + if (&png_ptr != NULL) /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); + return ((png_charp) ""); +} + +png_charp PNGAPI +png_get_header_version(png_structp png_ptr) +{ + /* Returns longer string containing both version and date */ + if (&png_ptr != NULL) /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_HEADER_VERSION_STRING); + return ((png_charp) ""); +} + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +int PNGAPI +png_handle_as_unknown(png_structp png_ptr, png_bytep chunk_name) +{ + /* check chunk_name and return "keep" value if it's on the list, else 0 */ + int i; + png_bytep p; + if((png_ptr == NULL && chunk_name == NULL) || png_ptr->num_chunk_list<=0) + return 0; + p=png_ptr->chunk_list+png_ptr->num_chunk_list*5-5; + for (i = png_ptr->num_chunk_list; i; i--, p-=5) + if (!png_memcmp(chunk_name, p, 4)) + return ((int)*(p+4)); + return 0; +} +#endif + +/* This function, added to libpng-1.0.6g, is untested. */ +int PNGAPI +png_reset_zstream(png_structp png_ptr) +{ + return (inflateReset(&png_ptr->zstream)); +} + +/* This function was added to libpng-1.0.7 */ +png_uint_32 PNGAPI +png_access_version_number(void) +{ + /* Version of *.c files used when building libpng */ + return((png_uint_32) PNG_LIBPNG_VER); +} + + +#if !defined(PNG_1_0_X) +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) + /* GRR: could add this: && defined(PNG_MMX_CODE_SUPPORTED) */ +/* this INTERNAL function was added to libpng 1.2.0 */ +void /* PRIVATE */ +png_init_mmx_flags (png_structp png_ptr) +{ + png_ptr->mmx_rowbytes_threshold = 0; + png_ptr->mmx_bitdepth_threshold = 0; + +# if (defined(PNG_USE_PNGVCRD) || defined(PNG_USE_PNGGCCRD)) + + png_ptr->asm_flags |= PNG_ASM_FLAG_MMX_SUPPORT_COMPILED; + + if (png_mmx_support() > 0) { + png_ptr->asm_flags |= PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU +# ifdef PNG_HAVE_ASSEMBLER_COMBINE_ROW + | PNG_ASM_FLAG_MMX_READ_COMBINE_ROW +# endif +# ifdef PNG_HAVE_ASSEMBLER_READ_INTERLACE + | PNG_ASM_FLAG_MMX_READ_INTERLACE +# endif +# ifndef PNG_HAVE_ASSEMBLER_READ_FILTER_ROW + ; +# else + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB + | PNG_ASM_FLAG_MMX_READ_FILTER_UP + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ; + + png_ptr->mmx_rowbytes_threshold = PNG_MMX_ROWBYTES_THRESHOLD_DEFAULT; + png_ptr->mmx_bitdepth_threshold = PNG_MMX_BITDEPTH_THRESHOLD_DEFAULT; +# endif + } else { + png_ptr->asm_flags &= ~( PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU + | PNG_MMX_READ_FLAGS + | PNG_MMX_WRITE_FLAGS ); + } + +# else /* !((PNGVCRD || PNGGCCRD) && PNG_ASSEMBLER_CODE_SUPPORTED)) */ + + /* clear all MMX flags; no support is compiled in */ + png_ptr->asm_flags &= ~( PNG_MMX_FLAGS ); + +# endif /* ?(PNGVCRD || PNGGCCRD) */ +} + +#endif /* !(PNG_ASSEMBLER_CODE_SUPPORTED) */ + +/* this function was added to libpng 1.2.0 */ +#if !defined(PNG_USE_PNGGCCRD) && \ + !(defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_USE_PNGVCRD)) +int PNGAPI +png_mmx_support(void) +{ + return -1; +} +#endif +#endif /* PNG_1_0_X */ + +#ifdef PNG_SIZE_T +/* Added at libpng version 1.2.6 */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +png_size_t PNGAPI +png_convert_size(size_t size) +{ + if (size > (png_size_t)-1) + PNG_ABORT(); /* We haven't got access to png_ptr, so no png_error() */ + return ((png_size_t)size); +} +#endif /* PNG_SIZE_T */ diff --git a/.svn/pristine/58/585b3520b6bc3dd8a5913958b2b90a6104e51707.svn-base b/.svn/pristine/58/585b3520b6bc3dd8a5913958b2b90a6104e51707.svn-base new file mode 100644 index 0000000..264e4d0 --- /dev/null +++ b/.svn/pristine/58/585b3520b6bc3dd8a5913958b2b90a6104e51707.svn-base @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2018 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * https://www.eclipse.org/legal/epl-2.0/ + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#if !defined(SUBOPTS_H) +#define SUBOPTS_H + +/** The MQTT V5 subscribe options, apart from QoS which existed before V5. */ +typedef struct MQTTSubscribe_options +{ + /** The eyecatcher for this structure. Must be MQSO. */ + char struct_id[4]; + /** The version number of this structure. Must be 0. + */ + int struct_version; + /** To not receive our own publications, set to 1. + * 0 is the original MQTT behaviour - all messages matching the subscription are received. + */ + unsigned char noLocal; + /** To keep the retain flag as on the original publish message, set to 1. + * If 0, defaults to the original MQTT behaviour where the retain flag is only set on + * publications sent by a broker if in response to a subscribe request. + */ + unsigned char retainAsPublished; + /** 0 - send retained messages at the time of the subscribe (original MQTT behaviour) + * 1 - send retained messages on subscribe only if the subscription is new + * 2 - do not send retained messages at all + */ + unsigned char retainHandling; +} MQTTSubscribe_options; + +#define MQTTSubscribe_options_initializer { {'M', 'Q', 'S', 'O'}, 0, 0, 0, 0 } + +#endif diff --git a/.svn/pristine/59/590b2c8c5d5f8aa880e881391f05668625bf23d3.svn-base b/.svn/pristine/59/590b2c8c5d5f8aa880e881391f05668625bf23d3.svn-base new file mode 100644 index 0000000..e9043fd --- /dev/null +++ b/.svn/pristine/59/590b2c8c5d5f8aa880e881391f05668625bf23d3.svn-base @@ -0,0 +1,7185 @@ +/* +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 +*/ + + +// +// Telnet Driver for BPQ Switch +// +// Uses BPQ EXTERNAL interface +// + +//#define WIN32_LEAN_AND_MEAN + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include "time.h" + +#include "kernelresource.h" + +#define IDM_DISCONNECT 2000 +#define IDM_LOGGING 2100 + +#include "cheaders.h" +#include "tncinfo.h" + +#ifdef WIN32 +#include +#include "WS2tcpip.h" +#else +//#define TIOCOUTQ 0x5411 +#define SIOCOUTQ TIOCOUTQ /* output queue size (not sent + not acked) */ +#endif + +#include "adif.h" +#include "telnetserver.h" + +#define MAX_PENDING_CONNECTS 4 + +extern UCHAR LogDirectory[]; + + +static char ClassName[]="TELNETSERVER"; +static char WindowTitle[] = "Telnet Server"; +static int RigControlRow = 190; + +UCHAR * APIENTRY GetLogDirectory(); +static BOOL OpenSockets(struct TNCINFO * TNC); +static BOOL OpenSockets6(struct TNCINFO * TNC); +void ProcessHTTPMessage(void * conn); +static VOID SetupListenSet(struct TNCINFO * TNC); +int WritetoConsoleLocal(char * buff); +BOOL TelSendPacket(int Stream, struct STREAMINFO * STREAM, PMSGWITHLEN buffptr, struct ADIF * ADIF); +int GetCMSHash(char * Challenge, char * Password); +int IsUTF8(unsigned char *cpt, int len); +int Convert437toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1251toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1252toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +VOID initUTF8(); +int TrytoGuessCode(unsigned char * Char, int Len); +DllExport struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); +extern BPQVECSTRUC * TELNETMONVECPTR; +BOOL SendWL2KSessionRecord(ADIF * ADIF, int BytesSent, int BytesReceived); +int DataSocket_ReadSync(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream); +VOID SendWL2KRegisterHybrid(struct TNCINFO * TNC); +int IntSetTraceOptionsEx(uint64_t mask, int mtxparam, int mcomparam, int monUIOnly); +int DataSocket_ReadDRATS(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream); +void processDRATSFrame(unsigned char * Message, int Len, struct ConnectionInfo * sockptr); +void DRATSConnectionLost(struct ConnectionInfo * sockptr); +int BuildRigCtlPage(char * _REPLYBUFFER); +void ProcessWebmailWebSockThread(void * conn); +void RHPThread(void * Params); +void ProcessRHPWebSockClosed(SOCKET socket); +int ProcessSNMPPayload(UCHAR * Msg, int Len, UCHAR * Reply, int * OffPtr); +int RHPProcessHTTPMessage(struct ConnectionInfo * conn, char * response, char * Method, char * URL, char * request, BOOL LOCAL, BOOL COOKIE); + + +#ifndef LINBPQ +extern HKEY REGTREE; +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HMENU hWndMenu; +extern HFONT hFont; +extern HBRUSH bgBrush; + +extern HWND ClientWnd, FrameWnd; + +extern HANDLE hInstance; +static RECT Rect; + +LRESULT CALLBACK TelWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +#endif + +extern int REALTIMETICKS; + +#define MaxSockets 26 + +struct UserRec RelayUser; +struct UserRec SyncUser = {"","Sync"}; +struct UserRec CMSUser; +struct UserRec HostUser = {"","Host"}; +struct UserRec TriModeUser; + +static char AttemptsMsg[] = "Too many attempts - Disconnected\r\n"; +static char disMsg[] = "Disconnected by SYSOP\r\n"; + +static char BlankCall[]=" "; + +BOOL LogEnabled = FALSE; +BOOL CMSLogEnabled = TRUE; +extern BOOL IncludesMail; + +extern int HTTPPort; + +static HMENU hMenu, hPopMenu, hPopMenu2, hPopMenu3; // handle of menu + +static int ProcessLine(char * buf, int Port); +VOID __cdecl Debugprintf(const char * format, ...); + + +int DisplaySessions(struct TNCINFO * TNC); +int DoStateChange(int Stream); +int Connected(int Stream); +int Disconnected(int Stream); +//int DeleteConnection(con); +static int Socket_Accept(struct TNCINFO * TNC, SOCKET SocketId, int Port); +static int Socket_Data(struct TNCINFO * TNC, SOCKET SocketId,int error, int eventcode); +static int DataSocket_Read(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM); +int DataSocket_ReadFBB(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream); +int DataSocket_ReadRelay(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM); +int DataSocket_ReadHTTP(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream); +int DataSocket_Write(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET TCPSock); +int DataSocket_Disconnect(struct TNCINFO * TNC, struct ConnectionInfo * sockptr); +BOOL ProcessTelnetCommand(struct ConnectionInfo * sockptr, byte * Msg, int * Len); +int ShowConnections(struct TNCINFO * TNC); +int Terminate(); +int SendtoSocket(SOCKET TCPSock,char * Msg); +int WriteLog(char * msg); +VOID WriteCMSLog(char * msg); +byte * EncodeCall(byte * Call); +VOID SendtoNode(struct TNCINFO * TNC, int Stream, char * Msg, int MsgLen); +DllExport int APIENTRY GetNumberofPorts(); + +BOOL CheckCMS(struct TNCINFO * TNC); +int TCPConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * STREAM, char * Host, int Port, BOOL FBB); +int CMSConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * STREAM, int Stream); +int Telnet_Connected(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Error); +BOOL ProcessConfig(); +VOID FreeConfig(); +VOID SaveCMSHostInfo(int port, struct TCPINFO * TCP, int CMSNo); +VOID GetCMSCachedInfo(struct TNCINFO * TNC); +BOOL CMSCheck(struct TNCINFO * TNC, struct TCPINFO * TCP); +VOID Tel_Format_Addr(struct ConnectionInfo * sockptr, char * dst); +VOID ProcessTrimodeCommand(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, char * MsgPtr); +VOID ProcessTrimodeResponse(struct TNCINFO * TNC, struct STREAMINFO * STREAM, unsigned char * MsgPtr, int Msglen); +VOID ProcessTriModeDataMessage(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM); + + +static int LogAge = 13; + + + +#ifdef WIN32 + +int DeleteLogFile(char * Log); + +void DeleteTelnetLogFiles() +{ + DeleteLogFile("/logs/Telnet*.log"); + DeleteLogFile("/logs/CMSAccess_*.log"); + DeleteLogFile("/logs/ConnectLog_*.log"); +} + +int DeleteLogFile(char * Log) +{ + + + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + char File[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + DWORD dwError=0; + LARGE_INTEGER ft; + time_t now = time(NULL); + int Age; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetLogDirectory()); + strcat(szDir, Log); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + return dwError; + + // List all the files in the directory with some info about them. + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugString(ffd.cFileName); + } + else + { + ft.HighPart = ffd.ftCreationTime.dwHighDateTime; + ft.LowPart = ffd.ftCreationTime.dwLowDateTime; + + ft.QuadPart -= 116444736000000000; + ft.QuadPart /= 10000000; + + Age = (int)((now - ft.LowPart) / 86400); + + if (Age > LogAge) + { + sprintf(File, "%s/logs/%s%c", GetLogDirectory(), ffd.cFileName, 0); + Debugprintf("Deleting %s", File); + DeleteFile(File); + } + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + return dwError; +} + +#else + +#include + +int TelFilter(const struct dirent * dir) +{ + return (memcmp(dir->d_name, "CMSAccess", 9) == 0 + || memcmp(dir->d_name, "Telnet", 6) == 0 + || memcmp(dir->d_name, "ConnectLog", 6) == 0) + && strstr(dir->d_name, ".log"); +} + +int DeleteTelnetLogFiles() +{ + struct dirent **namelist; + int n; + struct stat STAT; + time_t now = time(NULL); + int Age = 0, res; + char FN[256]; + + n = scandir("logs", &namelist, TelFilter, alphasort); + + if (n < 0) + perror("scandir"); + else + { + while(n--) + { + sprintf(FN, "logs/%s", namelist[n]->d_name); + if (stat(FN, &STAT) == 0) + { + Age = (now - STAT.st_mtime) / 86400; + + if (Age > LogAge) + { + Debugprintf("Deleting %s\n", FN); + unlink(FN); + } + } + free(namelist[n]); + } + free(namelist); + } + return 0; +} +#endif + + + +void BuffertoNode(struct ConnectionInfo * sockptr, char * MsgPtr, int InputLen) +{ + // Queue to Node. Data may arrive it large quatities, possibly exceeding node buffer capacity + + if (sockptr->FromHostBuffPutptr + InputLen > sockptr->FromHostBufferSize) + { + if (InputLen > 10000) + sockptr->FromHostBufferSize += InputLen; + else + sockptr->FromHostBufferSize += 10000; + + sockptr->FromHostBuffer = realloc(sockptr->FromHostBuffer, sockptr->FromHostBufferSize); + } + + memcpy(&sockptr->FromHostBuffer[sockptr->FromHostBuffPutptr], MsgPtr, InputLen); + + sockptr->FromHostBuffPutptr += InputLen; + sockptr->InputLen = 0; + + return; + } + +BOOL SendAndCheck(struct ConnectionInfo * sockptr, unsigned char * MsgPtr, int len, int flags) +{ + int err; + int sent = send(sockptr->socket, MsgPtr, len, flags); + + if (sent == len) + return TRUE; // OK + + err = WSAGetLastError(); + + Debugprintf("TCP Send Failed - Sent %d should be %d err %d - requeue data", sent, len, err); + + if (err == 10035 || err == 115 || err == 36) //EWOULDBLOCK + { + // Save unsent data + + if (sent == -1) // -1 means none sent + sent = 0; + + sockptr->ResendBuffer = malloc(len - sent); + sockptr->ResendLen = len - sent; + + memmove(sockptr->ResendBuffer, MsgPtr + sent, len - sent); + } + return FALSE; +} + +VOID SendPortsForMonitor(SOCKET sock, int Secure) +{ + UCHAR PortInfo[3000] = {0xff, 0xff}; + UCHAR * ptr = &PortInfo[2]; + char ID[31] = ""; + struct PORTCONTROL * PORT; + int i, n, p; + + // Sends the ID of each Port to TermTCP + + p = GetNumberofPorts(); + + if (IncludesMail && Secure) + p++; + + ptr += sprintf(ptr, "%d|", p); + + if (IncludesMail && Secure) + { + p--; + ptr += sprintf(ptr,"0 Mail Monitor|"); + } + + for (n=1; n <= p; n++) + { + PORT = GetPortTableEntryFromSlot(n); + + memcpy(ID, PORT->PORTDESCRIPTION, 30); + + for (i = 29; i; i--) + { + if (ID[i] != ' ') + break; + + ID[i] = 0; + } + + ptr += sprintf(ptr,"%d %s|", PORT->PORTNUMBER, ID); + } + + + send(sock, PortInfo, (int)(ptr - PortInfo), 0); +} + +int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr; + UCHAR * ptr1; + + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int len=510; + char errbuf[256]; + char * param; + char * value; + char *User, *Pwd, *UserCall, *Secure, * Appl; + int End = (int)strlen(buf) -1; + struct TNCINFO * TNC; + struct TCPINFO * TCP; + + strcpy(errbuf,buf); // save in case of error + + if (buf[End] == 10) + buf[End]=0; // remove newline + + if(buf[0] =='#') return (TRUE); // comment + + if(buf[0] ==';') return (TRUE); // comment + + ptr=strchr(buf,'='); + + if (!ptr) + ptr=strchr(buf,' '); + + if (!ptr) + return 0; + + if (TNCInfo[Port] == NULL) + { + TNC = TNCInfo[Port] = zalloc(sizeof(struct TNCINFO)); + TCP = TNC->TCPInfo = zalloc(sizeof (struct TCPINFO)); // Telnet Server Specific Data + + TCP->MaxSessions = 10; // Default Values + TNC->Hardware = H_TELNET; + TCP->IPV4 = TRUE; + TCP->SecureTelnet = 1; + strcpy(TCP->CMSServer, "cms.winlink.org"); + } + + TNC = TNCInfo[Port]; + TCP = TNC->TCPInfo; + + param=buf; + *(ptr)=0; + value=ptr+1; + + if (_stricmp(param, "IPV4") == 0) + TCP->IPV4 = atoi(value); + + else if (_stricmp(param, "IPV6") == 0) + TCP->IPV6 = atoi(value); + + else if (_stricmp(param, "CMS") == 0) + TCP->CMS = atoi(value); + + else if (_stricmp(param, "CMSPASS") == 0) + { + char Temp[80]; + + if (strlen(value) > 79) + value[79] = 0; + + strcpy(Temp, value); + strlop(Temp, 32); + strlop(Temp, 13); + strcpy(TCP->SecureCMSPassword, Temp); + + } + + else if (_stricmp(param, "CMSCALL") == 0) + { + if (strlen(value) > 9) + value[9] = 0; + strcpy(TCP->GatewayCall, value); + strlop(TCP->GatewayCall, 13); + _strupr(TCP->GatewayCall); + } + + else if (_stricmp(param, "CMSLOC") == 0) + { + if (strlen(value) > 9) + value[9] = 0; + strcpy(TCP->GatewayLoc, value); + strlop(TCP->GatewayLoc, 13); + _strupr(TCP->GatewayLoc); + } + + else if (_stricmp(param,"ReportHybrid") == 0) + TCP->ReportHybrid = atoi(value); + + else if (_stricmp(param, "HybridServiceCode") == 0) + { + TCP->HybridServiceCode = _strdup(value); + strlop(TCP->HybridServiceCode, 13); + strlop(TCP->HybridServiceCode, ';'); + _strupr(TCP->HybridServiceCode); + } + + else if (_stricmp(param, "HybridFrequencies") == 0) + { + TCP->HybridFrequencies = _strdup(value); + strlop(TCP->HybridFrequencies, 13); + strlop(TCP->HybridFrequencies, ' '); + _strupr(TCP->HybridFrequencies); + } + + else if (_stricmp(param, "HybridCoLocatedRMS") == 0) + { + TCP->HybridCoLocatedRMS = _strdup(value); + strlop(TCP->HybridCoLocatedRMS, 13); + strlop(TCP->HybridCoLocatedRMS, ' '); + _strupr(TCP->HybridCoLocatedRMS); + } + + else if (_stricmp(param,"LOGGING") == 0) + LogEnabled = atoi(value); + + else if (_stricmp(param,"CMSLOGGING") == 0) + CMSLogEnabled = atoi(value); + + else if (_stricmp(param,"DisconnectOnClose") == 0) + TCP->DisconnectOnClose = atoi(value); + + else if (_stricmp(param,"ReportRelayTraffic") == 0) + TCP->ReportRelayTraffic = atoi(value); + + else if (_stricmp(param,"SecureTelnet") == 0) + TCP->SecureTelnet = atoi(value); + + else if (_stricmp(param,"CloseOnDisconnect") == 0) + TCP->DisconnectOnClose = atoi(value); + + else if (_stricmp(param,"TCPPORT") == 0) + TCP->TCPPort = atoi(value); + + else if (_stricmp(param,"DRATSPORT") == 0) + TCP->DRATSPort = atoi(value); + + else if (_stricmp(param,"TRIMODEPORT") == 0) + TCP->TriModePort = atoi(value); + + else if (_stricmp(param,"HTTPPORT") == 0) + HTTPPort = TCP->HTTPPort = atoi(value); + + else if (_stricmp(param,"APIPORT") == 0) + TCP->APIPort = atoi(value); + + else if (_stricmp(param,"SYNCPORT") == 0) + TCP->SyncPort = atoi(value); + + else if (_stricmp(param,"SNMPPORT") == 0) + TCP->SNMPPort = atoi(value); + + else if ((_stricmp(param,"CMDPORT") == 0) || (_stricmp(param,"LINUXPORT") == 0)) + { + int n = 0; + char * context; + char * ptr = strtok_s(value, ", ", &context); + + while (ptr && n < 33) + { + TCP->CMDPort[n++] = atoi(ptr); + ptr = strtok_s(NULL, ", ", &context); + } + } + + else if (_stricmp(param,"CMSSERVER") == 0) + { + int n = 0; + char * context; + char * ptr = strtok_s(value, ", \r", &context); + + strcpy(TCP->CMSServer, ptr); + } + + else if (_stricmp(param,"RELAYHOST") == 0) + { + int n = 0; + char * context; + char * ptr = strtok_s(value, ", \r", &context); + + strcpy(TCP->RELAYHOST, ptr); + } + + + else if (_stricmp(param,"FALLBACKTORELAY") == 0) + { + int n = 0; + char * context; + char * ptr = strtok_s(value, ", \r", &context); + + TCP->FallbacktoRelay = atoi(value); + } + + else if (_stricmp(param,"FBBPORT") == 0) + { + int n = 0; + char * context; + char * ptr = strtok_s(value, ", ", &context); + + while (ptr && n < 99) + { + TCP->FBBPort[n++] = atoi(ptr); + ptr = strtok_s(NULL, ", ", &context); + } + } + + else if (_stricmp(param,"RELAYPORT") == 0) + TCP->RelayPort = atoi(value); + + else if (_stricmp(param,"RELAYAPPL") == 0) + { + if (TCP->RelayPort == 0) + TCP->RelayPort = 8772; + strcpy(TCP->RelayAPPL, value); + strcat(TCP->RelayAPPL, "\r"); + } + + else if (_stricmp(param,"SYNCAPPL") == 0) + { + if (TCP->SyncPort == 0) + TCP->SyncPort = 8780; + strcpy(TCP->SyncAPPL, value); + strcat(TCP->SyncAPPL, "\r"); + } + + // if (strcmp(param,"LOGINRESPONSE") == 0) cfgLOGINRESPONSE = value; + // if (strcmp(param,"PASSWORDRESPONSE") == 0) cfgPASSWORDRESPONSE = value; + + else if (_stricmp(param,"LOGINPROMPT") == 0) + { + ptr1 = strchr(value, 13); + if (ptr1) *ptr1 = 0; + strcpy(TCP->LoginMsg,value); + } + + else if (_stricmp(param,"PASSWORDPROMPT") == 0) + { + ptr1 = strchr(value, 13); + if (ptr1) *ptr1 = 0; + strcpy(TCP->PasswordMsg, value); + } + + else if (_stricmp(param,"HOSTPROMPT") == 0) + strcpy(TCP->cfgHOSTPROMPT, value); + + else if (_stricmp(param,"LOCALECHO") == 0) + strcpy(TCP->cfgLOCALECHO, value); + + else if (_stricmp(param,"MAXSESSIONS") == 0) + { + TCP->MaxSessions = atoi(value); + if (TCP->MaxSessions > MaxSockets ) TCP->MaxSessions = MaxSockets; + } + + else if (_stricmp(param,"CTEXT") == 0 ) + { + int len = (int)strlen (value); + + if (value[len -1] == ' ') + value[len -1] = 0; + + strcpy(TCP->cfgCTEXT, value); + + // Replace \n Signon string with cr lf + + ptr = &TCP->cfgCTEXT[0]; + +scanCTEXT: + + ptr = strstr(ptr, "\\n"); + + if (ptr) + { + *(ptr++)=13; // put in cr + *(ptr++)=10; // put in lf + + goto scanCTEXT; + } + } + + else if (_stricmp(param,"LOCALNET") == 0) + { + uint32_t Network, Mask; + uint32_t IPMask; + char * Netptr, * MaskPtr, *Context; + struct LOCALNET * LocalNet; + int Bits = 24; + + Netptr = strtok_s(value, " /;", &Context); + + if (Netptr) + { + Network = inet_addr(Netptr); + MaskPtr = strtok_s(NULL, " /;", &Context); + + if (MaskPtr) + { + Bits = atoi(MaskPtr); + + if (Bits > 32) + Bits = 32; + } + + if (Bits == 0) + IPMask = 0; + else + IPMask = (0xFFFFFFFF) << (32 - Bits); + + Mask = htonl(IPMask); // Needs to be Network order + + LocalNet = (struct LOCALNET *)zalloc(sizeof(struct LOCALNET)); + + LocalNet->Network = Network; + LocalNet->Mask = Mask; + LocalNet->Next = TNC->TCPInfo->LocalNets; + + TNC->TCPInfo->LocalNets = LocalNet; + + } + } + + + + + else if (_stricmp(param,"USER") == 0) + { + struct UserRec * USER; + char Param[8][256]; + char * ptr1, * ptr2; + int n = 0; + + // USER=user,password,call,appl,SYSOP + + memset(Param, 0, 2048); + strlop(value, 13); + strlop(value, ';'); + + ptr1 = value; + + while (ptr1 && *ptr1 && n < 8) + { + ptr2 = strchr(ptr1, ','); + if (ptr2) *ptr2++ = 0; + + strcpy(&Param[n][0], ptr1); + strlop(Param[n++], ' '); + ptr1 = ptr2; + while(ptr1 && *ptr1 && *ptr1 == ' ') + ptr1++; + } + + + User = &Param[0][0]; + + if (_stricmp(User, "ANON") == 0) + { + strcpy(&Param[2][0], "ANON"); + strcpy(&Param[4][0], ""); // Dont allow SYSOP if ANON + } + + Pwd = &Param[1][0]; + UserCall = &Param[2][0]; + Appl = &Param[3][0]; + Secure = &Param[4][0]; + + if (User[0] == 0 || Pwd[0] == 0 || UserCall[0] == 0) // invalid record + return 0; + + _strupr(UserCall); + + if (TCP->NumberofUsers == 0) + TCP->UserRecPtr = zalloc(sizeof(void *)); + else + TCP->UserRecPtr = realloc(TCP->UserRecPtr, (TCP->NumberofUsers+1) * sizeof(void *)); + + USER = zalloc(sizeof(struct UserRec)); + + TCP->UserRecPtr[TCP->NumberofUsers] = USER; + + USER->Callsign = _strdup(UserCall); + USER->Password = _strdup(Pwd); + USER->UserName = _strdup(User); + USER->Appl = zalloc(32); + USER->Secure = FALSE; + + if (_stricmp(Secure, "SYSOP") == 0) + USER->Secure = TRUE; + + if (Appl[0] && strcmp(Appl, "\"\"") != 0) + { + strcpy(USER->Appl, _strupr(Appl)); + strcat(USER->Appl, "\r\n"); + } + TCP->NumberofUsers += 1; + } + else if (_memicmp(errbuf, "WL2KREPORT", 10) == 0) + TNC->WL2K = DecodeWL2KReportLine(errbuf); + else if (_stricmp(param,"WebTermCSS") == 0) + { + TCP->WebTermCSS = _strdup(value); + } + else + return FALSE; + + return TRUE; +} + +static int MaxStreams = 26; + +void CheckRX(struct TNCINFO * TNC); +VOID TelnetPoll(int Port); +VOID ProcessTermModeResponse(struct TNCINFO * TNC); +VOID DoTNCReinit(struct TNCINFO * TNC); +VOID DoTermModeTimeout(struct TNCINFO * TNC); + +VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); + + +static VOID WritetoTrace(int Stream, char * Msg, int Len, struct ADIF * ADIF, char Dirn) +{ + int index = 0; + UCHAR * ptr1 = Msg, * ptr2; + UCHAR Line[1000]; + int LineLen, i; + char logmsg[200]; + + Msg[Len] = 0; + +lineloop: + + if (Len > 0) + { + // copy text to file a line at a time + + ptr2 = memchr(ptr1, 13 , Len); + + if (ptr2) + { + ptr2++; + LineLen = (int)(ptr2 - ptr1); + Len -= LineLen; + + if (LineLen > 900) + goto Skip; + + memcpy(Line, ptr1, LineLen); + memcpy(&Line[LineLen - 1], "", 4); + LineLen += 3; + + if ((*ptr2) == 10) + { + memcpy(&Line[LineLen], "", 4); + LineLen += 4; + ptr2++; + Len --; + } + + Line[LineLen] = 0; + + // If line contains any data above 7f, assume binary and dont display + + for (i = 0; i < LineLen; i++) + { + if (Line[i] > 127 || Line[i] < 32) + goto Skip; + } + + if (strlen(Line) < 100) + { + sprintf(logmsg,"%d %s\r\n", Stream, Line); + WriteCMSLog(logmsg); + UpdateADIFRecord(ADIF, Line, Dirn); + } + +Skip: + ptr1 = ptr2; + goto lineloop; + } + + // No CR + + for (i = 0; i < Len; i++) + { + if (ptr1[i] > 127) + break; + } + + if (i == Len) // No binary data + { + if (strlen(ptr1) < 100) + { + sprintf(logmsg,"%d %s\r\n", Stream, ptr1); + WriteCMSLog(logmsg); + UpdateADIFRecord(ADIF, ptr1, Dirn); + } + } + } +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int txlen = 0, n; + PMSGWITHLEN buffptr; + struct TNCINFO * TNC = TNCInfo[port]; + struct TCPINFO * TCP; + + int Stream; + struct ConnectionInfo * sockptr; + struct STREAMINFO * STREAM; + + if (TNC == NULL) + return 0; // Not configured + + switch (fn) + { + case 7: + + // 100 mS Timer. Now needed, as Poll can be called more frequently in some circuymstances + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + TCP = TNC->TCPInfo; + + if (TCP->CMS) + { + TCP->CheckCMSTimer++; + + if (TCP->CMSOK) + { + if (TCP->CheckCMSTimer > 600 * 15) + CheckCMS(TNC); + } + else + { + if (TCP->CheckCMSTimer > 600 * 2) + CheckCMS(TNC); + } + } + + // We now use persistent HTTP sessions, so need to close after a reasonable time + + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->SocketActive) + { + if (sockptr->HTTPMode) + { + if (sockptr->WebSocks == 0) + { + if (sockptr->LastSendTime && (time(NULL) - sockptr->LastSendTime) > 150) // ~ 2.5 mins + { + closesocket(sockptr->socket); + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + } + } + else if (memcmp(sockptr->WebURL, "rhp", 3) == 0) + { + // RHP Sockets (Used for WhatsPack) Need a timeout + // Normally keepalives are sent each way around every 9 mins + // Keepalives aren't sent when connecting so may need a bit longer + + if (sockptr->LastSendTime && (time(NULL) - sockptr->LastSendTime) > 20 * 60) // 20mins + { + ProcessRHPWebSockClosed(sockptr->socket); + closesocket(sockptr->socket); + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + } + } + } + else + { + // Time out Login + + if (sockptr->LoginState < 2 && (time(NULL) - sockptr->ConnectTime) > 30) + { + closesocket(sockptr->socket); + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + } + } + } + } + + + + return 0; + + case 1: // poll + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + TRANSPORTENTRY * SESS; + struct ConnectionInfo * sockptr = TNC->Streams[Stream].ConnectionInfo; + + if (sockptr && sockptr->UserPointer == &CMSUser) // Connected to CMS + { + SESS = TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]; + + if (SESS) + { + n = SESS->L4KILLTIMER; + if (n < (SESS->L4LIMIT - 120)) + { + SESS->L4KILLTIMER = SESS->L4LIMIT - 120; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = SESS->L4LIMIT - 120; + } + } + } + + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + STREAM->ReportDISC = TRUE; + } + } + + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + } + + TelnetPoll(port); + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->PACTORtoBPQ_Q !=0) + { + int datalen; + + buffptr = Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + return (1); + } + } + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + // Find TNC Record + + Stream = buff->PORT; + STREAM = &TNC->Streams[Stream]; + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + + buffptr->Len = txlen; + memcpy(&buffptr->Data, &buff->L2DATA, txlen); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + STREAM->FramesQueued++; + + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + + STREAM = &TNC->Streams[Stream]; + + if (STREAM->FramesQueued > 40) + return (257); // Busy + + return 256; // OK + +#define SD_BOTH 0x02 + + case 4: // reinit + { + struct _EXTPORTDATA * PortRecord; + +#ifndef LINBPQ + HWND SavehDlg, SaveCMS, SaveMonitor; + HMENU SaveMenu1, SaveMenu2, SaveMenu3; +#endif + int n, i; + + if (!ProcessConfig()) + { + Consoleprintf("Failed to reread config file - leaving config unchanged"); + break; + } + + FreeConfig(); + + for (n = 1; n <= TNC->TCPInfo->CurrentSockets; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + sockptr->SocketActive = FALSE; + closesocket(sockptr->socket); + } + + TCP = TNC->TCPInfo; + + shutdown(TCP->TCPSock, SD_BOTH); + shutdown(TCP->sock6, SD_BOTH); + + n = 0; + while (TCP->FBBsock[n]) + shutdown(TCP->FBBsock[n++], SD_BOTH); + + shutdown(TCP->Relaysock, SD_BOTH); + shutdown(TCP->HTTPsock, SD_BOTH); + shutdown(TCP->HTTPsock6, SD_BOTH); + + + n = 0; + while (TCP->FBBsock6[n]) + shutdown(TCP->FBBsock[n++], SD_BOTH); + + shutdown(TCP->Relaysock6, SD_BOTH); + Sleep(500); + + closesocket(TCP->TCPSock); + closesocket(TCP->sock6); + + n = 0; + while (TCP->FBBsock[n]) + closesocket(TCP->FBBsock[n++]); + + n = 0; + while (TCP->FBBsock6[n]) + closesocket(TCP->FBBsock6[n++]); + + closesocket(TCP->Relaysock); + closesocket(TCP->Relaysock6); + closesocket(TCP->HTTPsock); + closesocket(TCP->HTTPsock6); + + // Save info from old TNC record + + n = TNC->Port; + PortRecord = TNC->PortRecord; +#ifndef LINBPQ + SavehDlg = TNC->hDlg; + SaveCMS = TCP->hCMSWnd; + SaveMonitor = TNC->hMonitor; + SaveMenu1 = TCP->hActionMenu; + SaveMenu2 = TCP->hDisMenu; + SaveMenu3 = TCP->hLogMenu; +#endif + // Free old TCP Session Stucts + + for (i = 0; i <= TNC->TCPInfo->MaxSessions; i++) + { + free(TNC->Streams[i].ConnectionInfo); + } + + ReadConfigFile(TNC->Port, ProcessLine); + + TNC = TNCInfo[n]; + TNC->Port = n; + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_TELNET; + TNC->RIG = &TNC->DummyRig; // Not using Rig control, so use Dummy + + // Get Menu Handles + + TCP = TNC->TCPInfo; +#ifndef LINBPQ + TNC->hDlg = SavehDlg; + TCP->hCMSWnd = SaveCMS; + TNC->hMonitor = SaveMonitor; + TCP->hActionMenu = SaveMenu1; + TCP->hDisMenu = SaveMenu2; + TCP->hLogMenu = SaveMenu3; + + CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TNC->TCPInfo->CMS<<3); + CheckMenuItem(TCP->hLogMenu, 0, MF_BYPOSITION | LogEnabled<<3); + CheckMenuItem(TCP->hLogMenu, 1, MF_BYPOSITION | CMSLogEnabled<<3); +#endif + // Malloc TCP Session Stucts + + for (i = 0; i <= TNC->TCPInfo->MaxSessions; i++) + { + TNC->Streams[i].ConnectionInfo = zalloc(sizeof(struct ConnectionInfo)); + TCP->CurrentSockets = i; //Record max used to save searching all entries +#ifndef LINBPQ + if (i > 0) + ModifyMenu(TCP->hDisMenu,i - 1 ,MF_BYPOSITION | MF_STRING,IDM_DISCONNECT + 1, "."); +#endif + } + + TNC->PortRecord = PortRecord; + + Sleep(500); + OpenSockets(TNC); + OpenSockets6(TNC); + SetupListenSet(TNC); + CheckCMS(TNC); + ShowConnections(TNC); + } + + break; + + case 5: // Close + + TCP = TNC->TCPInfo; + + for (n = 1; n <= TCP->CurrentSockets; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + closesocket(sockptr->socket); + } + + shutdown(TCP->TCPSock, SD_BOTH); + + n = 0; + while (TCP->FBBsock[n]) + shutdown(TCP->FBBsock[n++], SD_BOTH); + + shutdown(TCP->Relaysock, SD_BOTH); + shutdown(TCP->HTTPsock, SD_BOTH); + shutdown(TCP->HTTPsock6, SD_BOTH); + + shutdown(TCP->sock6, SD_BOTH); + + n = 0; + while (TCP->FBBsock6[n]) + shutdown(TCP->FBBsock6[n++], SD_BOTH); + + shutdown(TCP->Relaysock6, SD_BOTH); + + Sleep(500); + closesocket(TCP->TCPSock); + + n = 0; + while (TCP->FBBsock[n]) + closesocket(TCP->FBBsock[n++]); + + closesocket(TCP->Relaysock); + + closesocket(TCP->sock6); + + n = 0; + while (TCP->FBBsock6[n]) + closesocket(TCP->FBBsock6[n++]); + + closesocket(TCP->Relaysock6); + closesocket(TCP->HTTPsock); + closesocket(TCP->HTTPsock6); + + return (0); + + case 6: // Scan Control + + return 0; // None Yet + + } + return 0; + +} + + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len; + char msg[256]; + struct ConnectionInfo * sockptr; + int i,n; + + char CMSStatus[80] = ""; + + if (TNC->TCPInfo->CMS) + { + if (TNC->TCPInfo->CMSOK) + strcpy(CMSStatus, "CMS Ok"); + else + strcpy(CMSStatus, "No CMS"); + } + + Len = sprintf(Buff, "" + "Telnet StatusTelnet Status         %s", CMSStatus); + + Len += sprintf(&Buff[Len], ""); + + + Len += sprintf(&Buff[Len], ""); + + for (n = 1; n <= TNC->TCPInfo->CurrentSockets; n++) + { + sockptr=TNC->Streams[n].ConnectionInfo; + + if (!sockptr->SocketActive) + { + strcpy(msg,""); + } + else + { + if (sockptr->UserPointer == 0) + { + if (sockptr->HTTPMode) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + if (sockptr->WebSocks) + sprintf(msg,"", Addr); + else + sprintf(msg,"", Addr); + } + else if (sockptr->DRATSMode) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + sprintf(msg,"", Addr); + } + else + strcpy(msg,""); + } + else + { + i=sprintf(msg,"", + sockptr->UserPointer->UserName, sockptr->Callsign, + sockptr->FromHostBuffPutptr - sockptr->FromHostBuffGetptr); + } + } + Len += sprintf(&Buff[Len], "%s", msg); + } + + Len += sprintf(&Buff[Len], "
UserCallsignQueue
Idle  
Websock<%s  
HTTP<%s  
DRATS<%s  
Logging in  
%s%s%d
"); + return Len; +} + + + +void * TelnetExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + struct TCPINFO * TCP; + int port; + char * ptr; + int i; + HWND x=0; + +/* + UCHAR NC[257]; + WCHAR WC[1024]; + + int WLen, NLen; + + UINT UTF[256] = {0}; + UINT UTFL[256]; + + int n, u; + + for (n = 0; n < 256; n++) + NC[n] =n ; + + n = MultiByteToWideChar(437, 0, NC, 256, &WC[0], 1024); + + for (n = 0; n < 256; n++) + UTFL[n] = WideCharToMultiByte(CP_UTF8, 0, &WC[n], 1, &UTF[n], 1024 , NULL, NULL); + + // write UTF8 data as source + + { + HANDLE Handle; + int i, n, Len; + char Line [256]; + + + Handle = CreateFile("c:\\UTF8437Data.c", GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + n = wsprintf (Line, "unsigned int CP437toUTF8Data[128] = {\r\n"); + + WriteFile(Handle, Line ,n , &n, NULL); + + if (Handle != INVALID_HANDLE_VALUE) + { + for (i = 128; i < 256; i += 4) + { + n = wsprintf (Line, " %d, %d, %d, %d, \r\n", + UTF[i], UTF[i+1], UTF[i+2], UTF[i+3]); + WriteFile(Handle, Line ,n , &n, NULL); + + } + + WriteFile(Handle, "};\r\n", 4, &n, NULL); + } + n = wsprintf (Line, "unsigned int CP437toUTF8DataLen[128] = {\r\n"); + + WriteFile(Handle, Line ,n , &n, NULL); + + if (Handle != INVALID_HANDLE_VALUE) + { + for (i = 128; i < 256;i += 4) + { + n = wsprintf (Line, " %d, %d, %d, %d, \r\n", + UTFL[i], UTFL[i+1], UTFL[i+2], UTFL[i+3]); + WriteFile(Handle, Line ,n , &n, NULL); + + } + + WriteFile(Handle, "};\r\n", 4, &n, NULL); + + SetEndOfFile(Handle); + + CloseHandle(Handle); + } + } +*/ + + DeleteTelnetLogFiles(); + + initUTF8(); + + sprintf(msg,"Telnet Server "); + WritetoConsoleLocal(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + WritetoConsoleLocal("\n"); + return ExtProc; + } + + TCP = TNC->TCPInfo; + + TNC->Port = port; + TNC->PortRecord = PortEntry; + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_TELNET; + + PortEntry->MAXHOSTMODESESSIONS = TNC->TCPInfo->MaxSessions + 1; // Default + + if (PortEntry->PORTCONTROL.PORTCALL[0] != 0) + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + PortEntry->PORTCONTROL.PROTOCOL = 10; // WINMOR/Pactor + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // No Scan Control + PortEntry->PERMITGATEWAY = TRUE; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TELNETMONVECPTR->HOSTAPPLFLAGS = 0x80; // Requext Monitoring + + if (TCP->LoginMsg[0] == 0) + strcpy(TCP->LoginMsg, "user:"); + if (TCP->PasswordMsg[0] == 0) + strcpy(TCP->PasswordMsg, "password:"); + if (TCP->cfgCTEXT[0] == 0) + { + char Call[10]; + memcpy(Call, MYNODECALL, 10); + strlop(Call, ' '); + + sprintf(TCP->cfgCTEXT, "Connected to %s's Telnet Server\r\n\r\n", Call); + } + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 260; + TNC->WebWinY = 325; + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, TelWndProc, 400, 300, NULL); + + TCP->hCMSWnd = CreateWindowEx(0, "STATIC", "CMS OK ", WS_CHILD | WS_VISIBLE, + 240,0,60,16, TNC->hDlg, NULL, hInstance, NULL); + + SendMessage(TCP->hCMSWnd, WM_SETFONT, (WPARAM)hFont, 0); + + x = CreateWindowEx(0, "STATIC", " User Callsign Queue", WS_CHILD | WS_VISIBLE, + 0,0,240,16, TNC->hDlg, NULL, hInstance, NULL); + + SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0); + + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | WS_VSCROLL, + 0,20,400,2800, TNC->hDlg, NULL, hInstance, NULL); + + SendMessage(TNC->hMonitor, WM_SETFONT, (WPARAM)hFont, 0); + + hPopMenu = CreatePopupMenu(); + hPopMenu2 = CreatePopupMenu(); + hPopMenu3 = CreatePopupMenu(); + + AppendMenu(hPopMenu, MF_STRING + MF_POPUP, (UINT)hPopMenu2,"Logging Options"); + AppendMenu(hPopMenu, MF_STRING + MF_POPUP, (UINT)hPopMenu3,"Disconnect User"); + AppendMenu(hPopMenu, MF_STRING, TELNET_RECONFIG, "ReRead Config"); + AppendMenu(hPopMenu, MF_STRING, CMSENABLED, "CMS Access Enabled"); + AppendMenu(hPopMenu, MF_STRING, USECACHEDCMS, "Using Cached CMS Addresses"); + + AppendMenu(hPopMenu2, MF_STRING, IDM_LOGGING, "Log incoming connections"); + AppendMenu(hPopMenu2, MF_STRING, IDM_CMS_LOGGING, "Log CMS Connections"); + + AppendMenu(hPopMenu3, MF_STRING, IDM_DISCONNECT, "1"); + + TCP->hActionMenu = hPopMenu; + + CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3); + + TCP->hLogMenu = hPopMenu2; + TCP->hDisMenu = hPopMenu3; + + CheckMenuItem(TCP->hLogMenu, 0, MF_BYPOSITION | LogEnabled<<3); + CheckMenuItem(TCP->hLogMenu, 1, MF_BYPOSITION | CMSLogEnabled<<3); + +// ModifyMenu(hMenu, 1, MF_BYPOSITION | MF_OWNERDRAW | MF_STRING, 10000, 0); + +#endif + + // Malloc TCP Session Stucts + + for (i = 0; i <= TNC->TCPInfo->MaxSessions; i++) + { + TNC->Streams[i].ConnectionInfo = zalloc(sizeof(struct ConnectionInfo)); + TNC->Streams[i].ConnectionInfo->Number = i; + TCP->CurrentSockets = i; //Record max used to save searching all entries + + sprintf(msg,"%d", i); + +#ifndef LINBPQ + if (i > 1) + AppendMenu(TCP->hDisMenu, MF_STRING, IDM_DISCONNECT ,msg); +#endif + } + + OpenSockets(TNC); + OpenSockets6(TNC); + SetupListenSet(TNC); + TNC->RIG = &TNC->DummyRig; // Not using Rig control, so use Dummy + + if (TCP->CMS) + CheckCMS(TNC); + + WritetoConsoleLocal("\n"); + + ShowConnections(TNC); + + if (TCP->ReportHybrid) + SendWL2KRegisterHybrid(TNC); + + + return ExtProc; +} + +SOCKET OpenUDPSocket(struct TNCINFO * TNC) +{ + u_long param = 1; + struct sockaddr_in sinx; + int err, ret; + struct TCPINFO * TCP = TNC->TCPInfo; + char Msg[80]; + + TCP->SNMPsock = socket(AF_INET,SOCK_DGRAM,0); + + if (TCP->SNMPsock == INVALID_SOCKET) + { + WritetoConsoleLocal("Failed to create SNMP UDP socket"); + return 0; + } + + ioctl (TCP->SNMPsock, FIONBIO, ¶m); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + + sinx.sin_port = htons(TCP->SNMPPort); + + ret = bind(TCP->SNMPsock, (struct sockaddr *) &sinx, sizeof(sinx)); + + if (ret != 0) + { + // Bind Failed + + err = WSAGetLastError(); + sprintf(Msg, "Bind Failed for SNMP UDP socket %d - error code = %d", TCP->SNMPPort, err); + WritetoConsoleLocal(Msg); + return 0; + } + + return TCP->SNMPsock; +} + + + +SOCKET OpenSocket4(struct TNCINFO * xTNC, int port) +{ + struct sockaddr_in local_sin; /* Local socket - internet style */ + struct sockaddr_in * psin; + SOCKET sock = 0; + u_long param=1; + + char szBuff[80]; + + psin=&local_sin; + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = INADDR_ANY; + + if (port) + { + sock = socket(AF_INET, SOCK_STREAM, 0); + + if (sock == INVALID_SOCKET) + { + sprintf(szBuff, "socket() failed error %d\n", WSAGetLastError()); + WritetoConsoleLocal(szBuff); + return 0; + } + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *)¶m,4); + + + psin->sin_port = htons(port); // Convert to network ordering + + if (bind( sock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) + { + sprintf(szBuff, "bind(sock) failed port %d Error %d\n", port, WSAGetLastError()); + WritetoConsoleLocal(szBuff); + closesocket( sock ); + return FALSE; + } + + if (listen( sock, MAX_PENDING_CONNECTS ) < 0) + { + sprintf(szBuff, "listen(sock) failed port %d Error %d\n", port, WSAGetLastError()); + WritetoConsoleLocal(szBuff); + return FALSE; + } + ioctl(sock, FIONBIO, ¶m); + } + + return sock; +} + +BOOL OpenSockets(struct TNCINFO * TNC) +{ + struct sockaddr_in local_sin; /* Local socket - internet style */ + struct sockaddr_in * psin; + u_long param=1; + struct TCPINFO * TCP = TNC->TCPInfo; + int n; + + if (!TCP->IPV4) + return TRUE; + + psin=&local_sin; + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = INADDR_ANY; + + if (TCP->TCPPort) + TCP->TCPSock = OpenSocket4(TNC, TCP->TCPPort); + + n = 0; + + while (TCP->FBBPort[n]) + { + TCP->FBBsock[n] = OpenSocket4(TNC, TCP->FBBPort[n]); + n++; + } + + if (TCP->RelayPort) + TCP->Relaysock = OpenSocket4(TNC, TCP->RelayPort); + + if (TCP->TriModePort) + { + TCP->TriModeSock = OpenSocket4(TNC, TCP->TriModePort); + TCP->TriModeDataSock = OpenSocket4(TNC, TCP->TriModePort + 1); + } + + if (TCP->HTTPPort) + TCP->HTTPsock = OpenSocket4(TNC, TCP->HTTPPort); + + if (TCP->APIPort) + TCP->APIsock = OpenSocket4(TNC, TCP->APIPort); + + if (TCP->SyncPort) + TCP->Syncsock = OpenSocket4(TNC, TCP->SyncPort); + + if (TCP->DRATSPort) + TCP->DRATSsock = OpenSocket4(TNC, TCP->DRATSPort); + + if (TCP->SNMPPort) + TCP->SNMPsock = OpenUDPSocket(TNC); + + CMSUser.UserName = _strdup("CMS"); + + TriModeUser.Secure = TRUE; + TriModeUser.UserName = _strdup("TRIMODE"); + TriModeUser.Callsign = zalloc(10); + + return TRUE; +} + +SOCKET OpenSocket6(struct TNCINFO * TNC, int port) +{ + + struct sockaddr_in6 local_sin; /* Local socket - internet style */ + struct sockaddr_in6 * psin; + SOCKET sock; + char szBuff[80]; + u_long param=1; + + memset(&local_sin, 0, sizeof(local_sin)); + + psin=&local_sin; + psin->sin6_family = AF_INET6; + psin->sin6_addr = in6addr_any; + psin->sin6_flowinfo = 0; + psin->sin6_scope_id = 0; + + sock = socket(AF_INET6, SOCK_STREAM, 0); + +#ifndef WIN32 + + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ¶m, sizeof(param)) < 0) + { + perror("setting option IPV6_V6ONLY"); + } + +#endif + + if (sock == INVALID_SOCKET) + { + sprintf(szBuff, "IPV6 socket() failed error %d\n", WSAGetLastError()); + WritetoConsoleLocal(szBuff); + return FALSE; + } + + psin->sin6_port = htons(port); // Convert to network ordering + + if (bind(sock, (struct sockaddr FAR *)psin, sizeof(local_sin)) == SOCKET_ERROR) + { + sprintf(szBuff, "IPV6 bind(sock) failed Port %d Error %d\n", port, WSAGetLastError()); + WritetoConsoleLocal(szBuff); + closesocket( sock ); + + return FALSE; + } + + if (listen( sock, MAX_PENDING_CONNECTS ) < 0) + { + sprintf(szBuff, "IPV6 listen(sock) failed Error %d\n", WSAGetLastError()); + WritetoConsoleLocal(szBuff); + + return FALSE; + } + + ioctl(sock, FIONBIO, ¶m); + return sock; +} + + +BOOL OpenSockets6(struct TNCINFO * TNC) +{ + struct sockaddr_in6 local_sin; /* Local socket - internet style */ + + struct sockaddr_in6 * psin; + struct TCPINFO * TCP = TNC->TCPInfo; + int n; + u_long param=1; + + if (!TCP->IPV6) + return TRUE; + + memset(&local_sin, 0, sizeof(local_sin)); + + psin=&local_sin; + psin->sin6_family = AF_INET6; + psin->sin6_addr = in6addr_any; + psin->sin6_flowinfo = 0; + psin->sin6_scope_id = 0; + + + if (TCP->TCPPort) + TCP->sock6 = OpenSocket6(TNC, TCP->TCPPort); + + n = 0; + + while (TCP->FBBPort[n]) + { + TCP->FBBsock6[n] = OpenSocket6(TNC, TCP->FBBPort[n]); + n++; + } + + if (TCP->RelayPort) + TCP->Relaysock6 = OpenSocket6(TNC, TCP->RelayPort); + + if (TCP->HTTPPort) + TCP->HTTPsock6 = OpenSocket6(TNC, TCP->HTTPPort); + + if (TCP->APIPort) + TCP->APIsock6 = OpenSocket6(TNC, TCP->APIPort); + + if (TCP->SyncPort) + TCP->Syncsock6 = OpenSocket6(TNC, TCP->SyncPort); + + if (TCP->DRATSPort) + TCP->DRATSsock6 = OpenSocket6(TNC, TCP->DRATSPort); + + + CMSUser.UserName = _strdup("CMS"); + + return TRUE; +} + +static VOID SetupListenSet(struct TNCINFO * TNC) +{ + // Set up master set of fd's for checking for incoming calls + + struct TCPINFO * TCP = TNC->TCPInfo; + SOCKET maxsock = 0; + int n; + fd_set * readfd = &TCP->ListenSet; + SOCKET sock; + + FD_ZERO(readfd); + + n = 0; + while (n < 100) + { + sock = TCP->FBBsock[n++]; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + } + + sock = TCP->TCPSock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->Relaysock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->HTTPsock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->APIsock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->Syncsock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->DRATSsock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->TriModeSock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->TriModeDataSock; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + n = 0; + while (n < 100) + { + sock = TCP->FBBsock6[n++]; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + } + + sock = TCP->sock6; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->Relaysock6; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->HTTPsock6; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->APIsock6; + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + + sock = TCP->DRATSsock6; + + if (sock) + { + FD_SET(sock, readfd); + if (sock > maxsock) + maxsock = sock; + } + TCP->maxsock = maxsock; +} + + +VOID TelnetPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + UCHAR * Poll = TNC->TXBuffer; + struct TCPINFO * TCP = TNC->TCPInfo; + struct STREAMINFO * STREAM; + int Msglen; + int Stream; + + // we now poll for incoming connections + + struct timeval timeout; + int retval; + int n; + struct ConnectionInfo * sockptr; + SOCKET sock; + int Active = 0; + SOCKET maxsock; + + fd_set readfd, writefd, exceptfd; + timeout.tv_sec = 0; + timeout.tv_usec = 0; // poll + + if (TCP->maxsock == 0) + goto nosocks; + + memcpy(&readfd, &TCP->ListenSet, sizeof(fd_set)); + + retval = select((int)(TCP->maxsock) + 1, &readfd, NULL, NULL, &timeout); + + if (retval == -1) + { + retval = WSAGetLastError(); + perror("listen select"); + } + + if (retval) + { + n = 0; + while (TCP->FBBsock[n]) + { + sock = TCP->FBBsock[n]; + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->FBBPort[n]); + + n++; + } + + sock = TCP->TCPSock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->TCPPort); + } + + sock = TCP->TriModeSock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->TriModePort); + } + sock = TCP->TriModeDataSock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->TriModePort + 1); + } + + sock = TCP->Relaysock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->RelayPort); + } + + sock = TCP->HTTPsock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->HTTPPort); + } + + sock = TCP->DRATSsock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->DRATSPort); + } + + sock = TCP->Syncsock; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->SyncPort); + } + + + + n = 0; + + while (TCP->FBBsock6[n]) + { + sock = TCP->FBBsock6[n]; + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->FBBPort[n]); + n++; + } + + sock = TCP->sock6; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->TCPPort); + } + + sock = TCP->Relaysock6; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->RelayPort); + } + + sock = TCP->HTTPsock6; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->HTTPPort); + } + + sock = TCP->DRATSsock6; + if (sock) + { + if (FD_ISSET(sock, &readfd)) + Socket_Accept(TNC, sock, TCP->DRATSPort); + } + } + + // look for data on any active sockets + + maxsock = 0; + + FD_ZERO(&readfd); + FD_ZERO(&writefd); + FD_ZERO(&exceptfd); + + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + // Should we use write event after a blocked write ???? + + if (sockptr->SocketActive) + { +// if (sockptr->socket == 0) +// { +// Debugprintf("Active Session but zero socket"); +// DataSocket_Disconnect(TNC, sockptr); +// return; +// } + + if (TNC->Streams[n].Connecting) + { + // look for complete or failed + + FD_SET(sockptr->socket, &writefd); + FD_SET(sockptr->socket, &exceptfd); + } + else + { + FD_SET(sockptr->socket, &readfd); + FD_SET(sockptr->socket, &exceptfd); + } + Active++; + if (sockptr->socket > maxsock) + maxsock = sockptr->socket; + + if (sockptr->TriModeDataSock) + { + FD_SET(sockptr->TriModeDataSock, &readfd); + FD_SET(sockptr->TriModeDataSock, &exceptfd); + + if (sockptr->TriModeDataSock > maxsock) + maxsock = sockptr->TriModeDataSock; + } + } + } + + if (Active) + { + retval = select((int)maxsock + 1, &readfd, &writefd, &exceptfd, &timeout); + + if (retval == -1) + { + perror("data select"); + Debugprintf("Telnet Select Error %d Active %d", WSAGetLastError(), Active); + + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + if (sockptr->SocketActive) + Debugprintf("Active Session %d socket %d", n, sockptr->socket); + } + } + else + { + if (retval) + { + // see who has data + + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->SocketActive) + { + sock = sockptr->socket; + + if (sockptr->TriModeDataSock) + { + if (FD_ISSET(sockptr->TriModeDataSock, &readfd)) + { + ProcessTriModeDataMessage(TNC, sockptr, sockptr->TriModeDataSock, &TNC->Streams[n]); + } + } + + if (FD_ISSET(sock, &readfd)) + { + if (sockptr->RelayMode) + DataSocket_ReadRelay(TNC, sockptr, sock, &TNC->Streams[n]); + else if (sockptr->HTTPMode) + DataSocket_ReadHTTP(TNC, sockptr, sock, n); + else if (sockptr->SyncMode) + DataSocket_ReadSync(TNC, sockptr, sock, n); + else if (sockptr->FBBMode) + DataSocket_ReadFBB(TNC, sockptr, sock, n); + else if (sockptr->DRATSMode) + DataSocket_ReadDRATS(TNC, sockptr, sock, n); + else + DataSocket_Read(TNC, sockptr, sock, &TNC->Streams[n]); + } + + if (FD_ISSET(sock, &exceptfd)) + { + Debugprintf("exceptfd set"); + Telnet_Connected(TNC, sockptr, sock, 1); + } + + if (FD_ISSET(sock, &writefd)) + Telnet_Connected(TNC, sockptr, sock, 0); + + } + } + } + } + } + + +nosocks: + + // Try SNMP + + if (TCP->SNMPsock) + { + struct sockaddr_in rxaddr; + char rxbuff[500]; + int addrlen = sizeof(struct sockaddr_in); + int Offset = 0; + + int len = recvfrom(TCP->SNMPsock, rxbuff, 500, 0,(struct sockaddr *)&rxaddr, &addrlen); + + if (len > 0) + { + UCHAR Reply[256]; + int SendLen; + + SendLen = ProcessSNMPPayload(rxbuff, len, Reply, &Offset); + + if (SendLen == 0) + return; + + sendto(TCP->SNMPsock, &Reply[Offset], SendLen, 0, (struct sockaddr *)&rxaddr, addrlen); + return; + } + } + while (TELNETMONVECPTR->HOSTTRACEQ) + { + int len; + time_t stamp; + BOOL MonitorNODES = FALSE; + MESSAGE * monbuff; + UCHAR * monchars; + + unsigned char buffer[1024] = "\xff\x1b\xb"; + + monbuff = Q_REM((void **)&TELNETMONVECPTR->HOSTTRACEQ); + monchars = (UCHAR *)monbuff; + + stamp = monbuff->Timestamp; + + if (monbuff->PORT & 0x80) // TX + buffer[2] = 91; + else + buffer[2] = 17; + + for (Stream = 0; Stream <= TCP->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) + { + struct ConnectionInfo * sockptr = STREAM->ConnectionInfo; + + if (sockptr->BPQTermMode) + { + if (sizeof(void *) > 4) + monchars += 4; + + if (!sockptr->MonitorNODES && monchars[21] == 3 && monchars[22] == 0xcf && monchars[23] == 0xff) + { + len = 0; + } + else + { + unsigned long long SaveMMASK = MMASK; + BOOL SaveMTX = MTX; + BOOL SaveMCOM = MCOM; + BOOL SaveMUI = MUIONLY; + + IntSetTraceOptionsEx(sockptr->MMASK, sockptr->MTX, sockptr->MCOM, sockptr->MUIOnly); + len = IntDecodeFrame((MESSAGE *)monbuff, &buffer[3], stamp, sockptr->MMASK, FALSE, FALSE); + IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); + + if (len) + { + len += 3; + buffer[len++] = 0xfe; + send(STREAM->ConnectionInfo->socket, buffer, len, 0); + } + } + } + } + } + + ReleaseBuffer(monbuff); + } + + for (Stream = 0; Stream <= TCP->MaxSessions; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + STREAM->Attached = TRUE; + STREAM->FramesQueued= 0; + STREAM->NoCMSFallback = 0; + + continue; + } + + if (STREAM->Attached) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0 && STREAM->Attached) + { + // Node has disconnected - clear any connection + + struct ConnectionInfo * sockptr = TNC->Streams[Stream].ConnectionInfo; + SOCKET sock = sockptr->socket; + char Msg[80]; + PMSGWITHLEN buffptr; + + STREAM->Attached = FALSE; + STREAM->Connected = FALSE; + STREAM->NoCMSFallback = FALSE; + + sockptr->FromHostBuffPutptr = sockptr->FromHostBuffGetptr = 0; // clear any queued data + + while(TNC->Streams[Stream].BPQtoPACTOR_Q) + { + buffptr = (PMSGWITHLEN)Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + if (TelSendPacket(Stream, &TNC->Streams[Stream], buffptr, sockptr->ADIF) == FALSE) + { + // Send failed, and has saved packet + // free saved and discard any more on queue + + free(sockptr->ResendBuffer); + sockptr->ResendBuffer = NULL; + sockptr->ResendLen = 0; + + while(TNC->Streams[Stream].BPQtoPACTOR_Q) + { + buffptr=Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + ReleaseBuffer(buffptr); + } + break; + } + } + + while(TNC->Streams[Stream].PACTORtoBPQ_Q) + { + buffptr=Q_REM(&TNC->Streams[Stream].PACTORtoBPQ_Q); + ReleaseBuffer(buffptr); + } + + if (LogEnabled) + { + char logmsg[120]; + sprintf(logmsg,"%d Disconnected. Bytes Sent = %d Bytes Received %d\n", + sockptr->Number, STREAM->bytesTXed, STREAM->bytesRXed); + + WriteLog (logmsg); + } + + if (!sockptr->FBBMode) + { + sprintf(Msg,"*** Disconnected from Stream %d\r\n",Stream); + send(sock, Msg, (int)strlen(Msg),0); + } + + if (sockptr->UserPointer == &TriModeUser) + { + // Always Disconnect + + send(sockptr->socket, "DISCONNECTED\r\n", 14, 0); + return; + } + + if (sockptr->UserPointer == &CMSUser) + { + if (CMSLogEnabled) + { + char logmsg[120]; + sprintf(logmsg,"%d Disconnected. Bytes Sent = %d Bytes Received %d Time %d Seconds\r\n", + sockptr->Number, STREAM->bytesTXed, STREAM->bytesRXed, (int)(time(NULL) - sockptr->ConnectTime)); + + WriteCMSLog (logmsg); + } + + // Don't report if Internet down unless ReportRelayTraffic set) + + if (sockptr->RelaySession == FALSE || TCP->ReportRelayTraffic) + SendWL2KSessionRecord(sockptr->ADIF, STREAM->bytesTXed, STREAM->bytesRXed); + + WriteADIFRecord(sockptr->ADIF); + + if (sockptr->ADIF) + free(sockptr->ADIF); + + sockptr->ADIF = NULL; + + // Always Disconnect CMS Socket + + DataSocket_Disconnect(TNC, sockptr); + return; + } + + if (sockptr->RelayMode) + { + // Always Disconnect Relay Socket + + Sleep(100); + DataSocket_Disconnect(TNC, sockptr); + return; + } + + if (sockptr->Signon[0] || sockptr->ClientSession) // Outward Connect + { + Sleep(1000); + DataSocket_Disconnect(TNC, sockptr); + return; + } + + + if (TCP->DisconnectOnClose) + { + Sleep(1000); + DataSocket_Disconnect(TNC, sockptr); + } + else + { + char DisfromNodeMsg[] = "Disconnected from Node - Telnet Session kept\r\n"; + send(sockptr->socket, DisfromNodeMsg, (int)strlen(DisfromNodeMsg),0); + } + } + } + } + + for (Stream = 0; Stream <= TCP->MaxSessions; Stream++) + { + struct ConnectionInfo * sockptr = TNC->Streams[Stream].ConnectionInfo; + STREAM = &TNC->Streams[Stream]; + + if (sockptr->SocketActive && sockptr->Keepalive && L4LIMIT) + { + if ((time(NULL) - sockptr->LastSendTime) > (L4LIMIT - 60)) // PC Ticks are about 10% slow + { + // Send Keepalive + + sockptr->LastSendTime = time(NULL); + BuffertoNode(sockptr, "Keepalive\r", 10); + } + } + + if (sockptr->ResendBuffer) + { + // Data saved after EWOULDBLOCK returned to send + + UCHAR * ptr = sockptr->ResendBuffer; + sockptr->ResendBuffer = NULL; + + SendAndCheck(sockptr, ptr, sockptr->ResendLen, 0); + free(ptr); + + continue; + } + + while (STREAM->BPQtoPACTOR_Q) + { + int datalen; + PMSGWITHLEN buffptr; + UCHAR * MsgPtr; + + // Make sure there is space. Linux TCP buffer is quite small + // Windows doesn't support SIOCOUTQ + +#ifndef WIN32 + int value = 0, error; + + error = ioctl(sockptr->socket, SIOCOUTQ, &value); + + if (value > 1500) + break; +#endif + buffptr = (PMSGWITHLEN)Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + STREAM->FramesQueued--; + datalen = (int)(buffptr->Len); + MsgPtr = &buffptr->Data[0]; + + if (STREAM->ConnectionInfo->TriMode) + { + ProcessTrimodeResponse(TNC, STREAM, MsgPtr, datalen); + ReleaseBuffer(buffptr); + return; + } + + + if (TNC->Streams[Stream].Connected) + { + if (sockptr->SyncMode) + { + // Suppress Conected and SID - Relay doesn't understand them + + if (strstr(buffptr->Data, "Connected to") || memcmp(buffptr->Data, "[BPQ-", 5) == 0) + { + ReleaseBuffer(buffptr); + return; + } + } + + if (TelSendPacket(Stream, STREAM, buffptr, sockptr->ADIF) == FALSE) + { + // Send failed, and has requeued packet + // Dont send any more + + break; + } + } + else // Not Connected + { + // Command. Do some sanity checking and look for things to process locally + + datalen--; // Exclude CR + MsgPtr[datalen] = 0; // Null Terminate + + if (_stricmp(MsgPtr, "NoFallback") == 0) + { + TNC->Streams[Stream].NoCMSFallback = TRUE; + buffptr->Len = sprintf(&buffptr->Data[0], "Ok\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return; + } + + if (_memicmp(MsgPtr, "D", 1) == 0) + { + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + ReleaseBuffer(buffptr); + return; + } + + if ((_memicmp(MsgPtr, "C", 1) == 0) && MsgPtr[1] == ' ' && datalen > 2) // Connect + { + char Host[100] = ""; + char P2[100] = ""; + char P3[100] = ""; + char P4[100] = ""; + char P5[100] = ""; + char P6[100] = ""; + char P7[100] = ""; + unsigned int Port = 0; + int n; + + n = sscanf(&MsgPtr[2], "%s %s %s %s %s %s %s", + &Host[0], &P2[0], &P3[0], &P4[0], &P5[0], &P6[0], &P7[0]); + + sockptr->Signon[0] = 0; // Not outgoing; + sockptr->Keepalive = FALSE; // No Keepalives + sockptr->NoCallsign = FALSE; + sockptr->UTF8 = 0; // Not UTF8 + + if (_stricmp(Host, "HOST") == 0) + { + Port = atoi(P2); + + if (Port > MaxBPQPortNo || TCP->CMDPort[Port] == 0) + { + buffptr->Len = sprintf(&buffptr->Data[0], "Error - Invalid HOST Port\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + STREAM->NeedDisc = 10; + return; + } + + STREAM->Connecting = TRUE; + sockptr->CMSSession = FALSE; + sockptr->FBBMode = FALSE; + + if (P3[0] == 'K' || P4[0] == 'K' || P5[0] == 'K' || P6[0] == 'K') + { + sockptr->Keepalive = TRUE; + sockptr->LastSendTime = time(NULL); + } + + if (P3[0] == 'S' || P4[0] == 'S' || P5[0] == 'S' || P6[0] == 'S') + { + // Set Say flag on partner session + + struct _TRANSPORTENTRY * Sess = TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]->L4CROSSLINK; + if (Sess) + Sess->STAYFLAG = 1; + } + + if (_stricmp(P3, "NOCALL") == 0 || _stricmp(P4, "NOCALL") == 0 || _stricmp(P5, "NOCALL") == 0 || _stricmp(P6, "NOCALL") == 0) + sockptr->NoCallsign = TRUE; + + if (_stricmp(P3, "TRANS") == 0 || _stricmp(P4, "TRANS") == 0 || _stricmp(P5, "TRANS") == 0 || _stricmp(P6, "TRANS") == 0) + { + sockptr->FBBMode = TRUE; + sockptr->NeedLF = TRUE; + } + + TCPConnect(TNC, TCP, STREAM, "127.0.0.1", TCP->CMDPort[Port], sockptr->FBBMode); + ReleaseBuffer(buffptr); + return; + } + + if (_stricmp(Host, "RELAY") == 0) + { + if (P2[0] == 0) + { + strcpy(P2, TCP->RELAYHOST); + strcpy(P3, "8772"); + } + + if (P2[0]) + { + STREAM->Connecting = TRUE; + STREAM->ConnectionInfo->CMSSession = TRUE; + STREAM->ConnectionInfo->RelaySession = TRUE; + TCPConnect(TNC, TCP, STREAM, P2, atoi(P3), TRUE); + ReleaseBuffer(buffptr); + return; + } + } + + if (_stricmp(Host, "SYNC") == 0) + { + if (P2[0] == 0) + { + strcpy(P2, TCP->RELAYHOST); + strcpy(P3, "8780"); + } + + if (P2[0]) + { + sockptr->CMSSession = FALSE; + STREAM->Connecting = TRUE; + STREAM->ConnectionInfo->SyncMode = TRUE; + TCPConnect(TNC, TCP, STREAM, P2, atoi(P3), TRUE); + ReleaseBuffer(buffptr); + return; + } + } + + + if (_stricmp(Host, "CMS") == 0) + { + if (TCP->CMS == 0 || !TCP->CMSOK) + { + if (TCP->RELAYHOST[0] && TCP->FallbacktoRelay && STREAM->NoCMSFallback == 0) + { + STREAM->Connecting = TRUE; + STREAM->ConnectionInfo->CMSSession = TRUE; + STREAM->ConnectionInfo->RelaySession = TRUE; + TCPConnect(TNC, TCP, STREAM, TCP->RELAYHOST, 8772, TRUE); + ReleaseBuffer(buffptr); + return; + } + + buffptr->Len = sprintf(&buffptr->Data[0], "Error - CMS Not Available\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + STREAM->NeedDisc = 10; + CheckCMS(TNC); + + return; + } + + STREAM->Connecting = TRUE; + STREAM->ConnectionInfo->CMSSession = TRUE; + STREAM->ConnectionInfo->RelaySession = FALSE; + CMSConnect(TNC, TCP, STREAM, Stream); + ReleaseBuffer(buffptr); + + return; + } + + // Outward Connect. + + // Only Allow user specified host if Secure Session + + if (TCP->SecureTelnet) + { + struct _TRANSPORTENTRY * Sess = TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]; + +// if (Sess && Sess->L4CROSSLINK) +// Sess = Sess->L4CROSSLINK; + + if (Sess && !Sess->Secure_Session) + { + buffptr->Len = sprintf(&buffptr->Data[0], "Error - Telnet Outward Connect needs SYSOP Status\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + STREAM->NeedDisc = 10; + return; + } + } + + Port = atoi(P2); + + if (Port) + { + int useFBBMode = TRUE; + + STREAM->Connecting = TRUE; + STREAM->ConnectionInfo->CMSSession = FALSE; + STREAM->ConnectionInfo->RelaySession = FALSE; + + if (_stricmp(P3, "TELNET") == 0) + { +// // FBB with CRLF + + STREAM->ConnectionInfo->NeedLF = TRUE; + } + else if (_stricmp(P3, "REALTELNET") == 0) + { +// // Telnet Mode with CRLF + + useFBBMode = FALSE; + STREAM->ConnectionInfo->NeedLF = TRUE; + } + else + STREAM->ConnectionInfo->NeedLF = FALSE; + + + STREAM->ConnectionInfo->FBBMode = TRUE; + + if (_stricmp(P3, "NOCALL") == 0 || _stricmp(P4, "NOCALL") == 0 || _stricmp(P5, "NOCALL") == 0 || _stricmp(P6, "NOCALL") == 0) + { + STREAM->ConnectionInfo->NoCallsign = TRUE; + } + else + { + if (_stricmp(P3, "NEEDLF") == 0 || STREAM->ConnectionInfo->NeedLF) + { + // Send LF after each param + + if (P6[0]) + sprintf(STREAM->ConnectionInfo->Signon, "%s\r\n%s\r\n%s\r\n", P4, P5, P6); + else + if (P5[0]) + sprintf(STREAM->ConnectionInfo->Signon, "%s\r\n%s\r\n", P4, P5); + else + if (P4[0]) + sprintf(STREAM->ConnectionInfo->Signon, "%s\r\n", P4); + } + else + { + if (P5[0]) + sprintf(STREAM->ConnectionInfo->Signon, "%s\r%s\r%s\r", P3, P4, P5); + else + if (P4[0]) + sprintf(STREAM->ConnectionInfo->Signon, "%s\r%s\r", P3, P4); + else + if (P3[0]) + sprintf(STREAM->ConnectionInfo->Signon, "%s\r", P3); + } + } + + TCPConnect(TNC, TCP, STREAM, Host, Port, useFBBMode); + ReleaseBuffer(buffptr); + return; + } + } + + buffptr->Len = sprintf(&buffptr->Data[0], "Error - Invalid Command\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + STREAM->NeedDisc = 10; + return; + } + } + + Msglen = sockptr->FromHostBuffPutptr - sockptr->FromHostBuffGetptr; + + if (Msglen) + { + int Paclen = 0; + int Queued = 0; + TRANSPORTENTRY * Sess1 = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + TRANSPORTENTRY * Sess2 = NULL; + + if (Sess1) + Sess2 = Sess1->L4CROSSLINK; + + // Can't use TXCount - it is Semaphored= + + Queued = C_Q_COUNT(&TNC->Streams[Stream].PACTORtoBPQ_Q); + Queued += C_Q_COUNT((UINT *)&TNC->PortRecord->PORTCONTROL.PORTRX_Q); + + if (Sess2) + Queued += CountFramesQueuedOnSession(Sess2); + + if (Sess1) + Queued += CountFramesQueuedOnSession(Sess1); + + if (Queued > 15) + continue; + + if (Sess1) + Paclen = Sess1->SESSPACLEN; + + if (Paclen == 0) + Paclen = 256; + + ShowConnections(TNC); + + if (Msglen > Paclen) + Msglen = Paclen; + + if (Sess1) Sess1->L4KILLTIMER = 0; + if (Sess2) Sess2->L4KILLTIMER = 0; + + SendtoNode(TNC, Stream, &sockptr->FromHostBuffer[sockptr->FromHostBuffGetptr], Msglen); + sockptr->FromHostBuffGetptr += Msglen; + sockptr->LastSendTime = time(NULL); + } + } +} + +#ifndef LINBPQ + +LRESULT CALLBACK TelWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + struct ConnectionInfo * sockptr; + SOCKET sock; + int i, n; + struct TNCINFO * TNC; + struct TCPINFO * TCP; + struct _EXTPORTDATA * PortRecord; + HWND SavehDlg, SaveCMS, SaveMonitor; + HMENU SaveMenu1, SaveMenu2, SaveMenu3; + MINMAXINFO * mmi; + + LPMEASUREITEMSTRUCT lpmis; // pointer to item of data + LPDRAWITEMSTRUCT lpdis; // pointer to item drawing data + +// struct ConnectionInfo * ConnectionInfo; + + for (i=1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->hDlg == hWnd) + break; + } + + if (TNC == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) + { +// case WM_SIZING: + case WM_SIZE: + { + RECT rcClient; + int ClientHeight, ClientWidth; + + GetClientRect(TNC->hDlg, &rcClient); + + ClientHeight = rcClient.bottom; + ClientWidth = rcClient.right; + + MoveWindow(TNC->hMonitor, 0,20 ,ClientWidth-4, ClientHeight-25, TRUE); + break; + } + + case WM_GETMINMAXINFO: + + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = 400; + mmi->ptMaxSize.y = 500; + mmi->ptMaxTrackSize.x = 400; + mmi->ptMaxTrackSize.y = 500; + break; + + + case WM_MDIACTIVATE: + { + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)hPopMenu, "Actions"); + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + // call DrawMenuBar after the menu items are set + DrawMenuBar(FrameWnd); + + return TRUE; //DefMDIChildProc(hWnd, message, wParam, lParam); + + } + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case SC_RESTORE: + + TNC->Minimized = FALSE; + SendMessage(ClientWnd, WM_MDIRESTORE, (WPARAM)hWnd, 0); + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case SC_MINIMIZE: + + TNC->Minimized = TRUE; + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_COMMAND: + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + // Parse the menu selections: + + if (wmId > IDM_DISCONNECT && wmId < IDM_DISCONNECT+MaxSockets+1) + { + // disconnect user + + sockptr = TNC->Streams[wmId-IDM_DISCONNECT].ConnectionInfo; + + if (sockptr->SocketActive) + { + sock=sockptr->socket; + + send(sock,disMsg, (int)strlen(disMsg),0); + + Sleep (1000); + + shutdown(sock,2); + + DataSocket_Disconnect(TNC, sockptr); + + TNC->Streams[wmId-IDM_DISCONNECT].ReportDISC = TRUE; //Tell Node + + return 0; + } + } + + switch (wmId) + { + case CMSENABLED: + + // Toggle CMS Enabled Flag + + TCP = TNC->TCPInfo; + + TCP->CMS = !TCP->CMS; + CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3); + + if (TCP->CMS) + CheckCMS(TNC); + else + { + TCP->CMSOK = FALSE; + SetWindowText(TCP->hCMSWnd, "CMS Off"); + } + break; + + case IDM_LOGGING: + + // Toggle Logging Flag + + LogEnabled = !LogEnabled; + CheckMenuItem(TNC->TCPInfo->hLogMenu, 0, MF_BYPOSITION | LogEnabled<<3); + + break; + + case IDM_CMS_LOGGING: + + // Toggle Logging Flag + + LogEnabled = !LogEnabled; + CheckMenuItem(TNC->TCPInfo->hLogMenu, 1, MF_BYPOSITION | CMSLogEnabled<<3); + + break; + + case TELNET_RECONFIG: + + if (!ProcessConfig()) + { + Consoleprintf("Failed to reread config file - leaving config unchanged"); + break; + } + + FreeConfig(); + + for (n = 1; n <= TNC->TCPInfo->CurrentSockets; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + sockptr->SocketActive = FALSE; + closesocket(sockptr->socket); + } + + TCP = TNC->TCPInfo; + + shutdown(TCP->TCPSock, SD_BOTH); + shutdown(TCP->sock6, SD_BOTH); + + n = 0; + while (TCP->FBBsock[n]) + shutdown(TCP->FBBsock[n++], SD_BOTH); + + shutdown(TCP->Relaysock, SD_BOTH); + shutdown(TCP->HTTPsock, SD_BOTH); + shutdown(TCP->HTTPsock6, SD_BOTH); + + + n = 0; + while (TCP->FBBsock6[n]) + shutdown(TCP->FBBsock[n++], SD_BOTH); + + shutdown(TCP->Relaysock6, SD_BOTH); + + Sleep(500); + + closesocket(TCP->TCPSock); + closesocket(TCP->sock6); + + n = 0; + while (TCP->FBBsock[n]) + closesocket(TCP->FBBsock[n++]); + + n = 0; + while (TCP->FBBsock6[n]) + closesocket(TCP->FBBsock6[n++]); + + closesocket(TCP->Relaysock); + closesocket(TCP->Relaysock6); + closesocket(TCP->HTTPsock); + closesocket(TCP->HTTPsock6); + + // Save info from old TNC record + + n = TNC->Port; + PortRecord = TNC->PortRecord; + SavehDlg = TNC->hDlg; + SaveCMS = TCP->hCMSWnd; + SaveMonitor = TNC->hMonitor; + SaveMenu1 = TCP->hActionMenu; + SaveMenu2 = TCP->hDisMenu; + SaveMenu3 = TCP->hLogMenu; + + // Free old TCP Session Stucts + + for (i = 0; i <= TNC->TCPInfo->MaxSessions; i++) + { + free(TNC->Streams[i].ConnectionInfo); + } + + ReadConfigFile(TNC->Port, ProcessLine); + + TNC = TNCInfo[n]; + TNC->Port = n; + TNC->PortRecord = PortRecord; + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_TELNET; + + TNC->hDlg = SavehDlg; + TNC->RIG = &TNC->DummyRig; // Not using Rig control, so use Dummy + + // Get Menu Handles + + TCP = TNC->TCPInfo; + + TCP->hCMSWnd = SaveCMS; + TNC->hMonitor = SaveMonitor; + TCP->hActionMenu = SaveMenu1; + TCP->hDisMenu = SaveMenu2; + TCP->hLogMenu = SaveMenu3; + + CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TNC->TCPInfo->CMS<<3); + CheckMenuItem(TCP->hLogMenu, 0, MF_BYPOSITION | LogEnabled<<3); + CheckMenuItem(TCP->hLogMenu, 1, MF_BYPOSITION | CMSLogEnabled<<3); + + // Malloc TCP Session Stucts + + for (i = 0; i <= TNC->TCPInfo->MaxSessions; i++) + { + TNC->Streams[i].ConnectionInfo = zalloc(sizeof(struct ConnectionInfo)); + TNC->Streams[i].ConnectionInfo->Number = i; + + TCP->CurrentSockets = i; //Record max used to save searching all entries + + if (i > 0) + ModifyMenu(TCP->hDisMenu,i - 1 ,MF_BYPOSITION | MF_STRING,IDM_DISCONNECT + 1, "."); + } + + Sleep(500); + OpenSockets(TNC); + OpenSockets6(TNC); + SetupListenSet(TNC); + CheckCMS(TNC); + ShowConnections(TNC); + + break; + default: + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + break; + + // case WM_SIZE: + +// if (wParam == SIZE_MINIMIZED) +// return (0); + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + + if (TNC->TCPInfo->hCMSWnd == (HWND)lParam) + { + if (TNC->TCPInfo->CMSOK) + SetTextColor(hdcStatic, RGB(0, 128, 0)); + else + SetTextColor(hdcStatic, RGB(255, 0, 0)); + } + + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + case WM_MEASUREITEM: + + // Retrieve pointers to the menu item's + // MEASUREITEMSTRUCT structure and MYITEM structure. + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + lpmis->itemWidth = 300; + + return TRUE; + + case WM_DRAWITEM: + + // Get pointers to the menu item's DRAWITEMSTRUCT + // structure and MYITEM structure. + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + // If the user has selected the item, use the selected + // text and background colors to display the item. + + SetTextColor(lpdis->hDC, RGB(0, 128, 0)); + + if (TNC->TCPInfo->CMS) + { + if (TNC->TCPInfo->CMSOK) + TextOut(lpdis->hDC, 340, lpdis->rcItem.top + 2, "CMS OK", 6); + else + { + SetTextColor(lpdis->hDC, RGB(255, 0, 0)); + TextOut(lpdis->hDC, 340, lpdis->rcItem.top + 2, "NO CMS", 6); + } + } + else + TextOut(lpdis->hDC, 340, lpdis->rcItem.top + 2, " ", 13); + + return TRUE; + + case WM_DESTROY: + + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + +} +#endif + +int Socket_Accept(struct TNCINFO * TNC, SOCKET SocketId, int Port) +{ + int n, addrlen = sizeof(struct sockaddr_in6); + struct sockaddr_in6 sin6; + + struct ConnectionInfo * sockptr; + SOCKET sock; + char Negotiate[6]={IAC,WILL,suppressgoahead,IAC,WILL,echo}; +// char Negotiate[3]={IAC,WILL,echo}; + struct TCPINFO * TCP = TNC->TCPInfo; + HMENU hDisMenu = TCP->hDisMenu; + u_long param=1; + + // if for TriModeData Session, use the TriMode Control connection entry + + if (SocketId == TCP->TriModeDataSock) + { + sockptr = TNC->TCPInfo->TriModeControlSession; + sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen); + sockptr->TriModeDataSock = sock; + ioctl(sock, FIONBIO, ¶m); + + return 0; + } + +// Find a free Session + + for (n = 1; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->SocketActive == FALSE) + { + sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen); + + if (sock == INVALID_SOCKET) + { + Debugprintf("BPQ32 Telnet accept() failed Error %d", WSAGetLastError()); + return FALSE; + } + + // Log, including port + + if (LogEnabled) + { + char Addr[256]; + char logmsg[512]; + + Tel_Format_Addr(sockptr, Addr); + + sprintf(logmsg,"%d %s Incoming Connect on Port %d\n", sockptr->Number, Addr, Port); + WriteLog (logmsg); + } + + + +// Debugprintf("BPQ32 Telnet accept() Sock %d", sock); + + ioctl(sock, FIONBIO, ¶m); + + sockptr->socket = sock; + sockptr->SocketActive = TRUE; + sockptr->InputLen = 0; + sockptr->Number = n; + sockptr->LoginState = 0; + sockptr->UserPointer = 0; + sockptr->DoEcho = FALSE; + sockptr->BPQTermMode = FALSE; + sockptr->ConnectTime = time(NULL); + sockptr->Keepalive = FALSE; + sockptr->UTF8 = 0; + + TNC->Streams[n].bytesRXed = TNC->Streams[n].bytesTXed = 0; + TNC->Streams[n].FramesQueued = 0; + + sockptr->HTTPMode = FALSE; + sockptr->APIMode = FALSE; + sockptr->SyncMode = FALSE; + sockptr->DRATSMode = FALSE; + sockptr->FBBMode = FALSE; + sockptr->RelayMode = FALSE; + sockptr->ClientSession = FALSE; + sockptr->NeedLF = FALSE; + sockptr->TNC = TNC; + sockptr->WebSocks = 0; + + if (sockptr->ADIF == NULL) + sockptr->ADIF = malloc(sizeof(struct ADIF)); + + memset(sockptr->ADIF, 0, sizeof(struct ADIF)); + + if (SocketId == TCP->HTTPsock || SocketId == TCP->HTTPsock6) + sockptr->HTTPMode = TRUE; + + if (SocketId == TCP->APIsock || SocketId == TCP->APIsock6) + { + sockptr->HTTPMode = TRUE; // API is a type of HTTP socket + sockptr->APIMode = TRUE; + } + else if (SocketId == TCP->Syncsock || SocketId == TCP->Syncsock6) + sockptr->SyncMode = TRUE; + else if (SocketId == TCP->DRATSsock || SocketId == TCP->DRATSsock6) + sockptr->DRATSMode = TRUE; + + else if (SocketId == TCP->Relaysock || SocketId == TCP->Relaysock6) + { + sockptr->RelayMode = TRUE; + sockptr->FBBMode = TRUE; + } + else if (SocketId == TCP->TriModeSock) + { + sockptr->TriMode = TRUE; + sockptr->FBBMode = TRUE; + TNC->TCPInfo->TriModeControlSession = sockptr; + sockptr->TriModeConnected = FALSE; + sockptr->TriModeDataSock = 0; + } + else if (SocketId != TCP->TCPSock && SocketId != TCP->sock6) // We can have several listening FBB mode sockets + sockptr->FBBMode = TRUE; +#ifndef LINBPQ + ModifyMenu(hDisMenu, n - 1, MF_BYPOSITION | MF_STRING, IDM_DISCONNECT + n, "."); + DrawMenuBar(TNC->hDlg); +#endif + ShowConnections(TNC); + + if (sockptr->HTTPMode) + return 0; + + if (sockptr->DRATSMode) + { + send(sock, "100 Authentication not required\n", 33, 0); + sockptr->LoginState = 2; + return 0; + } + + if (sockptr->SyncMode) + { + char MyCall[16] = ""; + char Hello[32]; + int Len; + memcpy(MyCall, MYNODECALL, 10); + strlop(MyCall, ' '); + strlop(MyCall, '-'); + + Len = sprintf(Hello, "POSYNCHELLO %s\r", MyCall); + + send(sock, Hello, Len, 0); + return 0; + } + else + if (sockptr->RelayMode) + { + send(sock,"\r\rCallsign :\r", 13,0); + } + else + if (sockptr->TriMode) + { + // Trimode emulator Control Connection. + + sockptr->UserPointer = &TriModeUser; + + + send(sock,"CMD\r\n", 5,0); + sockptr->LoginState = 5; + } + else + if (sockptr->FBBMode == FALSE) + { + send(sock, Negotiate, 6, 0); + send(sock, TCP->LoginMsg, (int)strlen(TCP->LoginMsg), 0); + } + + if (sockptr->FromHostBuffer == 0) + { + sockptr->FromHostBuffer = malloc(10000); + sockptr->FromHostBufferSize = 10000; + } + + sockptr->FromHostBuffPutptr = sockptr->FromHostBuffGetptr = 0; + + return 0; + } + } + + // No free sessions. Must accept() then close + + sock = accept(SocketId, (struct sockaddr *)&sin6, &addrlen); + + send(sock,"No Free Sessions\r\n", 18,0); + Debugprintf("No Free Telnet Sessions"); + + Sleep (1000); + closesocket(sock); + + return 0; +} + +/* +int Socket_Data(struct TNCINFO * TNC, int sock, int error, int eventcode) +{ + int n; + struct ConnectionInfo * sockptr; + struct TCPINFO * TCP = TNC->TCPInfo; + HMENU hDisMenu = TCP->hDisMenu; + + // Find Connection Record + + for (n = 0; n <= TCP->CurrentSockets; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->socket == sock && sockptr->SocketActive) + { +#ifndef LINBPQ + switch (eventcode) + { + case FD_READ: + + if (sockptr->RelayMode) + return DataSocket_ReadRelay(TNC, sockptr, sock, &TNC->Streams[n]); + + if (sockptr->HTTPMode) + return DataSocket_ReadHTTP(TNC, sockptr, sock, n); + + if (sockptr->FBBMode) + return DataSocket_ReadFBB(TNC, sockptr, sock, n); + else + return DataSocket_Read(TNC, sockptr, sock, &TNC->Streams[n]); + + case FD_WRITE: + + return 0; + + case FD_OOB: + + return 0; + + case FD_ACCEPT: + + return 0; + + case FD_CONNECT: + + return 0; + + case FD_CLOSE: + + TNC->Streams[n].ReportDISC = TRUE; //Tell Node + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + return 0; +#endif + } + + } + + return 0; +} + +*/ +#define PACLEN 100 + +VOID SendtoNode(struct TNCINFO * TNC, int Stream, char * Msg, int MsgLen) +{ + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == NULL) + return; // No buffers, so ignore + + if (TNC->Streams[Stream].Connected == 0) + { + // Connection Closed - Get Another + + struct ConnectionInfo * sockptr = TNC->Streams[Stream].ConnectionInfo; + + if (ProcessIncommingConnect(TNC, sockptr->Callsign, sockptr->Number, FALSE) == FALSE) + { + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + return; + } + + if (sockptr->UserPointer) + TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]->Secure_Session = sockptr->UserPointer->Secure; + } + + buffptr->Len= MsgLen; // Length + + memcpy(&buffptr->Data, Msg, MsgLen); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); +} + +int InnerProcessData(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM, int len); + + +int DataSocket_Read(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM) +{ + int len=0, maxlen; + char NLMsg[3]={13,10,0}; + byte * IACptr; + BOOL wait; + + struct TCPINFO * TCP = TNC->TCPInfo; + int SendIndex = 0; + byte * TelCommand; + int beforeIAC = 0; + int rest = 0; + + ioctl(sock,FIONREAD,&len); + + maxlen = InputBufferLen - sockptr->InputLen; + + if (len > maxlen) len=maxlen; + + len = recv(sock, &sockptr->InputBuffer[sockptr->InputLen], len, 0); + + if (len == SOCKET_ERROR || len ==0) + { + // Failed or closed - clear connection + + TNC->Streams[sockptr->Number].ReportDISC = TRUE; //Tell Node + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + + // If message contains Telnet Commands we should process the data in the buffer first + // and not echo the commands! + + + IACptr = memchr(sockptr->InputBuffer, IAC, sockptr->InputLen + len); + + if (IACptr) + { + beforeIAC = IACptr - sockptr->InputBuffer; + rest = (sockptr->InputLen + len) - beforeIAC; + + if (beforeIAC) + { + TelCommand = malloc(rest); + memcpy(TelCommand, IACptr, rest); + InnerProcessData(TNC, sockptr, sock, STREAM, beforeIAC); + + // There may still be data in buffer, but it may be less than before + // Put IAC and following into buffer + + memcpy(&sockptr->InputBuffer[sockptr->InputLen], TelCommand, rest); + len -= sockptr->InputLen; + free(TelCommand); + } + } + +IACLoop: + + IACptr = memchr(sockptr->InputBuffer, IAC, sockptr->InputLen + len); + + if (IACptr) + { + // There still may be data in the buffer. + + wait = ProcessTelnetCommand(sockptr, IACptr, &rest); + + if (wait) + { + // Need more. + + sockptr->InputLen += len; + return 0; // wait for more chars + } + + // If ProcessTelnet Command returns FALSE, then it has removed the IAC and its + // params from the buffer. There may still be more to process. + + if (rest == 0) + return 0; // Nothing Left + + memmove(&sockptr->InputBuffer[sockptr->InputLen], IACptr + len - rest, rest); + len = rest; + + goto IACLoop; // There may be more + } + + return InnerProcessData(TNC, sockptr, sock, STREAM, len); +} + + +int InnerProcessData(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM, int len) +{ + int InputLen, MsgLen, i, n,charsAfter; + char NLMsg[3]={13,10,0}; + byte * CRPtr; + byte * LFPtr; + byte * BSptr; + byte * MsgPtr; + char logmsg[1000]; + struct UserRec * USER; + struct TCPINFO * TCP = TNC->TCPInfo; + int SendIndex = 0; + + // echo data just read + + if (sockptr->DoEcho && sockptr->LoginState != 1) // Password + send(sockptr->socket,&sockptr->InputBuffer[sockptr->InputLen], len, 0); + + sockptr->InputLen += len; + + // look for backspaces in data just read + + BSptr = memchr(&sockptr->InputBuffer[0], 8, sockptr->InputLen); + + if (BSptr == NULL) + BSptr = memchr(&sockptr->InputBuffer[0], 127, sockptr->InputLen); + + if (BSptr != 0) + { + // single char or BS as last is most likely, and worth treating as a special case + + int n; + + charsAfter = sockptr->InputLen - (int)((BSptr-&sockptr->InputBuffer[0])) - 1; + + if (charsAfter == 0) + { + sockptr->InputLen--; + if (sockptr->InputLen > 0) + sockptr->InputLen--; //Remove last char + + goto noBS; + } + // more than single char. Copy stuff after bs over char before + + memmove(BSptr-1, BSptr+1, charsAfter); + + n = sockptr->InputLen; + + sockptr->InputLen -= 2; // drop bs and char before + + // see if more bs chars +BSCheck: + BSptr = memchr(&sockptr->InputBuffer[0], 8, sockptr->InputLen); + + if (BSptr == NULL) + BSptr = memchr(&sockptr->InputBuffer[0], 127, sockptr->InputLen); + + if (BSptr == NULL) + goto noBS; + + charsAfter = sockptr->InputLen - (int)((BSptr-&sockptr->InputBuffer[0])) - 1; + + if (charsAfter == 0) + { + sockptr->InputLen--; // Remove BS + if (sockptr->InputLen > 0) + sockptr->InputLen--; //Remove last char if not at start + goto noBS; + } + + memmove(BSptr-1, BSptr+1, charsAfter); + sockptr->InputLen--; // Remove BS + if (sockptr->InputLen > 0) sockptr->InputLen--; //Remove last char if not at start + + goto BSCheck; // may be more bs chars + } + +noBS: + + // Extract lines from input stream + + MsgPtr = &sockptr->InputBuffer[0]; + InputLen = sockptr->InputLen; + +MsgLoop: + + // if in Client Mode, accept CR, CR Null, CR LF or LF, and replace with CR + // Also send immediately to client - dont wait for complete lines + + if (sockptr->ClientSession) + { + int n = InputLen; + char * ptr = MsgPtr; + char * Start = MsgPtr; + char c; + int len = 0; + char NodeLine[300]; + char * optr = NodeLine; + + while (n--) + { + c = *(ptr++); + + if (c == 0) + // Ignore Nulls + continue; + + len++; + *(optr++) = c; + + if (c == 13) + { + // See if next is lf or null + + if (n) + { + // Some Left + + if ((*ptr) == 0 || *(ptr) == 10) + { + // skip next + + n--; + ptr++; + } + } + } + else if (c == 10) + { + *(optr - 1) = 13; + } + else + { + // Normal Char + + if (len >= PACLEN) + { + BuffertoNode(sockptr, NodeLine, len); + optr = NodeLine; + len = 0; + } + } + } + + // All scanned - send anything outstanding + + if (len) + BuffertoNode(sockptr, NodeLine, len); + + sockptr->InputLen = 0; + ShowConnections(TNC); + + return 0; + } + + + // Server Mode + + CRPtr=memchr(MsgPtr, 13, InputLen); + + if (CRPtr) + { + // Convert CR Null to CR LF + + LFPtr=memchr(MsgPtr, 0, InputLen); + + if (LFPtr && *(LFPtr - 1) == 13) // Convert CR NULL to CR LF + { + *LFPtr = 10; // Replace NULL with LF + send(sockptr->socket, LFPtr, 1, 0); // And echo it + } + } + + // could just have LF?? + + LFPtr=memchr(MsgPtr, 10, InputLen); + + if (LFPtr == 0) + if (CRPtr) + { + LFPtr = ++CRPtr; + InputLen++; + } + if (LFPtr == 0) + { + // Check Paclen + + if (InputLen > PACLEN) + { + if (sockptr->LoginState != 2) // Normal Data + { + // Long message received when waiting for user or password - just ignore + + sockptr->InputLen=0; + + return 0; + } + + // Send to Node + + // Line could be up to 500 chars if coming from a program rather than an interative user + // Limit send to node to 255 + + while (InputLen > 255) + { + SendtoNode(TNC, sockptr->Number, MsgPtr, 255); + sockptr->InputLen -= 255; + InputLen -= 255; + + memmove(MsgPtr,MsgPtr+255,InputLen); + } + + SendtoNode(TNC, sockptr->Number, MsgPtr, InputLen); + + sockptr->InputLen = 0; + + } // PACLEN + + return 0; // No CR + } + + // Got a LF + + // Process data up to the cr + + MsgLen = (int)(LFPtr-MsgPtr); // Include the CR but not LF + + switch (sockptr->LoginState) + { + + case 2: + + // Normal Data State + + STREAM->bytesRXed += MsgLen; + SendIndex = 0; + + // Line could be up to 500 chars if coming from a program rather than an interative user + // Limit send to node to 255. Should really use PACLEN instead of 255.... + + while (MsgLen > 255) + { + SendtoNode(TNC, sockptr->Number, MsgPtr + SendIndex, 255); + SendIndex += 255; + MsgLen -= 255; + } + + SendtoNode(TNC, sockptr->Number, MsgPtr + SendIndex, MsgLen); + + MsgLen += SendIndex; + + // If anything left, copy down buffer, and go back + + InputLen=InputLen-MsgLen-1; + + sockptr->InputLen=InputLen; + + if (InputLen > 0) + { + memmove(MsgPtr,LFPtr+1,InputLen); + + goto MsgLoop; + } + + return 0; + + case 0: + + // Check Username + // + + *(LFPtr-1)=0; // remove cr + + // send(sock, NLMsg, 2, 0); + + if (LogEnabled) + { + char Addr[256]; + + Tel_Format_Addr(sockptr, Addr); + + if (strlen(MsgPtr) > 64) + { + MsgPtr[64] = 0; + Debugprintf("Telnet Bad User Name %s", MsgPtr); + } + + sprintf(logmsg,"%d %s User=%s\n", sockptr->Number, Addr, MsgPtr); + WriteLog (logmsg); + } + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (USER == NULL) + continue; + + if (_stricmp(USER->UserName, "ANON") == 0) + { + // Anon Login - Callsign is supplied as user + + sockptr->UserPointer = USER; //' Save pointer for checking password + strcpy(sockptr->Callsign, _strupr(MsgPtr)); //' for *** linked + } + else if (strcmp(MsgPtr,USER->UserName) == 0) + { + sockptr->UserPointer = USER; //' Save pointer for checking password + strcpy(sockptr->Callsign, USER->Callsign); //' for *** linked + } + else + continue; + + send(sock, TCP->PasswordMsg, (int)strlen(TCP->PasswordMsg),0); + + sockptr->Retries = 0; + + sockptr->LoginState = 1; + sockptr->InputLen = 0; + + n=sockptr->Number; +#ifndef LINBPQ + ModifyMenu(TCP->hDisMenu, n - 1, MF_BYPOSITION | MF_STRING, IDM_DISCONNECT + n, MsgPtr); +#endif + ShowConnections(TNC); + return 0; + } + + // Not found + + + if (sockptr->Retries++ == 4) + { + send(sock,AttemptsMsg,sizeof(AttemptsMsg),0); + Sleep (1000); + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + } + else + { + send(sock, TCP->LoginMsg, (int)strlen(TCP->LoginMsg), 0); + sockptr->InputLen=0; + } + + return 0; + + case 1: + + *(LFPtr-1)=0; // remove cr + + send(sock, NLMsg, 2, 0); // Need to echo NL, as password is not echoed + + if (LogEnabled) + { + char Addr[256]; + + Tel_Format_Addr(sockptr, Addr); + + if (strlen(MsgPtr) > 64) + { + MsgPtr[64] = 0; + Debugprintf("Telnet Bad Password %s", MsgPtr); + } + + + sprintf(logmsg,"%d %s Password=%s\n", sockptr->Number, Addr, MsgPtr); + WriteLog (logmsg); + } + + if (strcmp(MsgPtr, sockptr->UserPointer->Password) == 0) { + char * ct = TCP->cfgCTEXT; + char * Appl; + int ctlen = (int)strlen(ct); + + if (ProcessIncommingConnect(TNC, sockptr->Callsign, sockptr->Number, FALSE) == FALSE) + { + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + return 0; + } + + TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]->Secure_Session = sockptr->UserPointer->Secure; + + sockptr->LoginState = 2; + sockptr->InputLen = 0; + + if (ctlen > 0) send(sock, ct, ctlen, 0); + + STREAM->bytesTXed = ctlen; + + if (LogEnabled) + { + char Addr[100]; + + Tel_Format_Addr(sockptr, Addr); + sprintf(logmsg,"%d %s Call Accepted Callsign=%s\n", sockptr->Number, Addr, sockptr->Callsign); + WriteLog (logmsg); + } + + Appl = sockptr->UserPointer->Appl; + + if (Appl[0]) + SendtoNode(TNC, sockptr->Number, Appl, (int)strlen(Appl)); + + ShowConnections(TNC); + + return 0; + } + + // Bad Password + + if (sockptr->Retries++ == 4) + { + send(sock,AttemptsMsg, (int)strlen(AttemptsMsg),0); + Sleep (1000); + DataSocket_Disconnect (TNC, sockptr); //' Tidy up + } + else + { + send(sock, TCP->PasswordMsg, (int)strlen(TCP->PasswordMsg), 0); + sockptr->InputLen=0; + } + + return 0; + + default: + + return 0; + + } + + return 0; +} + +int DataSocket_ReadRelay(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM) +{ + int len=0, maxlen, InputLen, MsgLen, n; + char NLMsg[3]={13,10,0}; + byte * LFPtr; + byte * MsgPtr; + char logmsg[256]; + char RelayMsg[] = "No CMS connection available - using local BPQMail\r"; + struct TCPINFO * TCP = TNC->TCPInfo; + + ioctl(sock,FIONREAD,&len); + + maxlen = InputBufferLen - sockptr->InputLen; + + if (len > maxlen) len=maxlen; + + len = recv(sock, &sockptr->InputBuffer[sockptr->InputLen], len, 0); + + + if (len == SOCKET_ERROR || len ==0) + { + // Failed or closed - clear connection + + TNC->Streams[sockptr->Number].ReportDISC = TRUE; //Tell Node + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + + sockptr->InputLen+=len; + + // Extract lines from input stream + + MsgPtr = &sockptr->InputBuffer[0]; + InputLen = sockptr->InputLen; + + STREAM->bytesRXed += InputLen; + + if (sockptr->LoginState == 2) + { + // Data. FBB is binary + + // Send to Node + + // Queue to Node. Data may arrive it large quatities, possibly exceeding node buffer capacity + + STREAM->bytesRXed += InputLen; + + if (sockptr->FromHostBuffPutptr + InputLen > sockptr->FromHostBufferSize) + { + if (InputLen > 10000) + sockptr->FromHostBufferSize += InputLen; + else + sockptr->FromHostBufferSize += 10000; + + sockptr->FromHostBuffer = realloc(sockptr->FromHostBuffer, sockptr->FromHostBufferSize); + } + + memcpy(&sockptr->FromHostBuffer[sockptr->FromHostBuffPutptr], MsgPtr, InputLen); + + sockptr->FromHostBuffPutptr += InputLen; + sockptr->InputLen = 0; + + return 0; + } +/* + if (InputLen > 256) + { + SendtoNode(TNC, sockptr->Number, MsgPtr, 256); + sockptr->InputLen -= 256; + + InputLen -= 256; + + memmove(MsgPtr,MsgPtr+256,InputLen); + + goto MsgLoop; + } + + SendtoNode(TNC, sockptr->Number, MsgPtr, InputLen); + sockptr->InputLen = 0; + + return 0; + } +*/ + if (InputLen > 256) + { + // Long message received when waiting for user or password - just ignore + + sockptr->InputLen=0; + + return 0; + } + + LFPtr=memchr(MsgPtr, 13, InputLen); + + if (LFPtr == 0) + return 0; // Waitr for more + + // Got a CR + + // Process data up to the cr + + MsgLen = (int)(LFPtr-MsgPtr); + + switch (sockptr->LoginState) + { + + case 0: + + // Check Username + // + + *(LFPtr)=0; // remove cr + + if (*MsgPtr == '.') + MsgPtr++; + + if (strlen(MsgPtr) == 0) + { + DataSocket_Disconnect(TNC, sockptr); // Silently disconnect - should only be used for automatic systems + return 0; + } + + if (LogEnabled) + { + unsigned char work[4]; + memcpy(work, &sockptr->sin.sin_addr.s_addr, 4); + sprintf(logmsg,"%d %d.%d.%d.%d User=%s\n", + sockptr->Number, + work[0], work[1], work[2], work[3], + MsgPtr); + + WriteLog (logmsg); + } + + strcpy(sockptr->Callsign, _strupr(MsgPtr)); + + // Save callsign for *** linked + + send(sock, "Password :\r", 11,0); + + sockptr->Retries = 0; + sockptr->LoginState = 1; + sockptr->InputLen = 0; + + n=sockptr->Number; +#ifndef LINBPQ + ModifyMenu(TCP->hDisMenu, n - 1, MF_BYPOSITION | MF_STRING, IDM_DISCONNECT + n, MsgPtr); +#endif + ShowConnections(TNC); + + return 0; + + + case 1: + + *(LFPtr)=0; // remove cr + + if (strlen(MsgPtr) == 0) + { + DataSocket_Disconnect(TNC, sockptr); // Silently disconnect - should only be used for automatic systems + return 0; + } + + if (LogEnabled) + { + unsigned char work[4]; + memcpy(work, &sockptr->sin.sin_addr.s_addr, 4); + sprintf(logmsg,"%d %d.%d.%d.%d Password=%s\n", + sockptr->Number, + work[0], work[1], work[2], work[3], + MsgPtr); + + WriteLog (logmsg); + } + + if (strchr(MsgPtr, '$')) + { + // Special format Password for PAT Gateway Mode + + char * Port = strlop(MsgPtr, '$'); + char * Call; + int PortNo; + char ConMsg[80]; + + if (Port) + { + Call = strlop(Port, '$'); + + if (Call) + { + struct PORTCONTROL * PORT; + + PortNo = atoi(Port); + PORT = GetPortTableEntryFromPortNum(PortNo); + + if (PORT == NULL || PORT->PROTOCOL < 10) + sprintf(ConMsg, "C %s %s", Port, Call); + else + sprintf(ConMsg, "ATT %s %s", Port, Call); + + } + + if (ProcessIncommingConnect(TNC, sockptr->Callsign, sockptr->Number, FALSE) == 0) + { + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + return 0; + } + + sockptr->LoginState = 2; + + sockptr->InputLen = 0; + + if (LogEnabled) + { + unsigned char work[4]; + memcpy(work, &sockptr->sin.sin_addr.s_addr, 4); + sprintf(logmsg,"%d %d.%d.%d.%d Gateway Connect Call=%s Command=%s\n", + sockptr->Number, + work[0], work[1], work[2], work[3], + sockptr->Callsign,ConMsg); + + WriteLog (logmsg); + } + + // Send Command to Node + + strcat(ConMsg, "\r"); + SendtoNode(TNC, sockptr->Number, ConMsg, (int)strlen(ConMsg)); + } + + return 0; + } + + sockptr->UserPointer = &RelayUser; + + if (ProcessIncommingConnectEx(TNC, sockptr->Callsign, sockptr->Number, FALSE, TRUE) == 0) + { + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + return 0; + } + + if (TCP->FallbacktoRelay) + send(sock, RelayMsg, (int)strlen(RelayMsg), 0); + + sockptr->LoginState = 2; + + sockptr->InputLen = 0; + + if (LogEnabled) + { + unsigned char work[4]; + memcpy(work, &sockptr->sin.sin_addr.s_addr, 4); + sprintf(logmsg,"%d %d.%d.%d.%d Call Accepted Callsign =%s\n", + sockptr->Number, + work[0], work[1], work[2], work[3], + sockptr->Callsign); + + WriteLog (logmsg); + } + + ShowConnections(TNC); + + sockptr->InputLen = 0; + + // Connect to the BBS + + SendtoNode(TNC, sockptr->Number, TCP->RelayAPPL, (int)strlen(TCP->RelayAPPL)); + + ShowConnections(TNC); + + return 0; + + default: + + return 0; + + } + + return 0; +} +#define ZEXPORT WINAPI + +#include "zlib.h" + +int DataSocket_ReadSync(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream) +{ + int len=0, maxlen, InputLen; + byte * MsgPtr; + struct TCPINFO * TCP = TNC->TCPInfo; + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + TRANSPORTENTRY * Sess1 = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + TRANSPORTENTRY * Sess2 = NULL; + + ioctl(sock,FIONREAD,&len); + + maxlen = InputBufferLen - sockptr->InputLen; + + if (len > maxlen) len = maxlen; + + len = recv(sock, &sockptr->InputBuffer[sockptr->InputLen], len, 0); + + if (len == SOCKET_ERROR || len == 0) + { + // Failed or closed - clear connection + + TNC->Streams[sockptr->Number].ReportDISC = TRUE; //Tell Node + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + + sockptr->InputLen+=len; + MsgPtr = &sockptr->InputBuffer[0]; + InputLen = sockptr->InputLen; + MsgPtr[InputLen] = 0; + + STREAM->bytesRXed += InputLen; + + if (sockptr->LoginState == 0) // Initial connection + { + // First Message should be POSYNCLOGON CALL + + // Extract the callsign + + char * call = strlop(MsgPtr, ' '); + + if (call == NULL || strcmp(MsgPtr, "POSYNCLOGON") !=0) + { + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + return 0; + } + + strcpy(sockptr->Callsign, call); + + call --; + *(call) = ' '; + + sockptr->UserPointer = &SyncUser; + + SendtoNode(TNC, sockptr->Number, TCP->SyncAPPL, (int)strlen(TCP->SyncAPPL)); + BuffertoNode(sockptr, MsgPtr, InputLen); + STREAM->RelaySyncStream = 1; + sockptr->LoginState = 2; + + ShowConnections(TNC); + return 0; + } + + // Queue to Node. Data may arrive in large quantities, possibly exceeding node buffer capacity + + BuffertoNode(sockptr, MsgPtr, InputLen); + sockptr->InputLen = 0; + + return 0; +} + + + +int DataSocket_ReadFBB(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream) +{ + int len=0, maxlen, InputLen, MsgLen, i, n; + char NLMsg[3]={13,10,0}; + byte * CRPtr; + byte * MsgPtr; + char logmsg[1000]; + struct UserRec * USER; + struct TCPINFO * TCP = TNC->TCPInfo; + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + TRANSPORTENTRY * Sess1 = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + TRANSPORTENTRY * Sess2 = NULL; + + ioctl(sock,FIONREAD,&len); + + maxlen = InputBufferLen - sockptr->InputLen; + + if (len > maxlen) len = maxlen; + + len = recv(sock, &sockptr->InputBuffer[sockptr->InputLen], len, 0); + + if (len == SOCKET_ERROR || len == 0) + { + // Failed or closed - clear connection + + TNC->Streams[sockptr->Number].ReportDISC = TRUE; //Tell Node + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + + sockptr->InputLen+=len; + + // Extract lines from input stream + + MsgPtr = &sockptr->InputBuffer[0]; + InputLen = sockptr->InputLen; + MsgPtr[InputLen] = 0; + + if (sockptr->LoginState == 0) + { + // Look for FLMSG Header + + if (InputLen > 10 && memcmp(MsgPtr, "... start\n", 10) == 0) + { + MsgPtr[9] = 13; // Convert to CR + sockptr->LoginState = 2; // Set Logged in + + SendtoNode(TNC, Stream, "..FLMSG\r", 8); // Dummy command to command handler + + } + } + +MsgLoop: + + if (sockptr->LoginState == 2) + { + // Data. FBB is binary + + int Paclen = 0; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) + Paclen = TNC->PortRecord->ATTACHEDSESSIONS[Stream]->SESSPACLEN; + +// if (Paclen == 0) + Paclen = 256; + + if (sockptr->BPQTermMode) + { + if (memcmp(MsgPtr, "\\\\\\\\", 4) == 0) + { + // Monitor Control + + int P8 = 0; + + int n = sscanf(&MsgPtr[4], "%llx %x %x %x %x %x %x %x", + &sockptr->MMASK, &sockptr->MTX, &sockptr->MCOM, &sockptr->MonitorNODES, + &sockptr->MonitorColour, &sockptr->MUIOnly, &sockptr->UTF8, &P8); + + if (n == 5) + sockptr->MUIOnly = sockptr->UTF8 = 0; + + if (n == 6) + sockptr->UTF8 = 0; + + if (P8 == 1) + SendPortsForMonitor(sock, sockptr->UserPointer->Secure); + sockptr->InputLen = 0; + return 0; + } + } + + if (sockptr->UserPointer == &CMSUser) + { + WritetoTrace(Stream, MsgPtr, InputLen, sockptr->ADIF, 'R'); + } + + if (InputLen == 8 && memcmp(MsgPtr, ";;;\r\n", 8) == 0) + { + // CMS Keepalive + + sockptr->InputLen = 0; + return 0; + } + + // Queue to Node. Data may arrive it large quantities, possibly exceeding node buffer capacity + + STREAM->bytesRXed += InputLen; + BuffertoNode(sockptr, MsgPtr, InputLen); + sockptr->InputLen = 0; + + return 0; + } + + if (InputLen > 256) + { + // Long message received when waiting for user or password - just ignore + + sockptr->InputLen=0; + + return 0; + } + + if (MsgPtr[0] == 10) // LF + { + // Remove the LF + + InputLen--; + sockptr->InputLen--; + + memmove(MsgPtr, MsgPtr+1, InputLen); + } + + CRPtr = memchr(MsgPtr, 13, InputLen); + + if (CRPtr == 0) + return 0; // Waitr for more + + // Got a CR + + // Process data up to the cr + + MsgLen = (int)(CRPtr - MsgPtr); + + if (MsgLen == 0) // Just CR + { + MsgPtr++; // Skip it + InputLen--; + sockptr->InputLen--; + goto MsgLoop; + } + + + switch (sockptr->LoginState) + { + case 5: + + // Trimode Emulator Command + + *CRPtr = 0; + + ProcessTrimodeCommand(TNC, sockptr, MsgPtr); + + MsgLen++; + + InputLen -= MsgLen; + + memmove(MsgPtr, MsgPtr+MsgLen, InputLen); + sockptr->InputLen = InputLen ; + MsgPtr[InputLen] = 0; + + + goto MsgLoop; + + case 3: + + // CMS Signon + + strlop(MsgPtr, 13); + + sprintf(logmsg,"%d %s\r\n", Stream, MsgPtr); + WriteCMSLog (logmsg); + + if (strstr(MsgPtr, "Callsign :")) + { + char Msg[80]; + int Len; + + if (sockptr->LogonSent) + { + sockptr->InputLen=0; + return TRUE; + } + + sockptr->LogonSent = TRUE; + + if (TCP->SecureCMSPassword[0] && sockptr->RelaySession == 0) + Len = sprintf(Msg, "%s %s\r", TNC->Streams[sockptr->Number].MyCall, TCP->GatewayCall); + else + Len = sprintf(Msg, "%s\r", TNC->Streams[sockptr->Number].MyCall); + + if (sockptr->ADIF == NULL) + { + sockptr->ADIF = malloc(sizeof(struct ADIF)); + memset(sockptr->ADIF, 0, sizeof(struct ADIF)); + } + + strcpy(sockptr->ADIF->CMSCall, TCP->GatewayCall); + + send(sock, Msg, Len, 0); + sprintf(logmsg,"%d %s\n", Stream, Msg); + WriteCMSLog (logmsg); + + sockptr->InputLen=0; + + return TRUE; + } + if (memcmp(MsgPtr, ";SQ: ", 5) == 0) + { + // Secure CMS challenge + + char Msg[80]; + int Len; + int Response = GetCMSHash(&MsgPtr[5], TCP->SecureCMSPassword); + char RespString[12]; + long long Freq = 0; + int Mode = 0; + ADIF * ADIF = sockptr->ADIF; + + strcat(MsgPtr,""); + UpdateADIFRecord(ADIF, MsgPtr, 'R'); + + if (Sess1) + { + Sess2 = Sess1->L4CROSSLINK; + + if (Sess2) + { + // if Session has report info, use it + + if (Sess2->Mode) + { + ADIF->Freq = Freq = Sess2->Frequency; + ADIF->Mode = Mode = Sess2->Mode; + } + else + { + // See if L2 session - if so, get info from WL2K report line + + if (Sess2->L4CIRCUITTYPE & L2LINK) + { + LINKTABLE * LINK = Sess2->L4TARGET.LINK; + PORTCONTROLX * PORT = LINK->LINKPORT; + + ADIF->Freq = Freq = PORT->WL2KInfo.Freq; + ADIF->Mode = Mode = PORT->WL2KInfo.mode; + } + else + { + if (Sess2->RMSCall[0]) + { + ADIF->Freq = Freq = Sess2->Frequency; + ADIF->Mode = Mode = Sess2->Mode; + } + } + } + } + } + + sprintf(RespString, "%010d", Response); + + Len = sprintf(Msg, ";SR: %s %lld %d\r", &RespString[2], Freq, Mode); + + send(sock, Msg, Len,0); + sprintf(logmsg,"%d %s\n", Stream, Msg); + WriteCMSLog (logmsg); + + strcat(Msg,""); + UpdateADIFRecord(ADIF, Msg, 'S'); + + sockptr->InputLen=0; + sockptr->LoginState = 2; // Data + sockptr->LogonSent = FALSE; + + return TRUE; + } + + if (strstr(MsgPtr, "Password :")) + { + // Send "CMSTelnet" + gateway callsign + frequency + emission type if info is available + + TRANSPORTENTRY * Sess1 = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + TRANSPORTENTRY * Sess2 = NULL; + char Passline[80] = "CMSTELNET\r"; + int len = 10; + ADIF * ADIF = sockptr->ADIF; + + + if (Sess1) + { + Sess2 = Sess1->L4CROSSLINK; + + if (Sess2) + { + // if Session has report info, use it + + if (Sess2->Mode) + { + ADIF->Freq = Sess2->Frequency; + ADIF->Mode = Sess2->Mode; + } + else + { + // See if L2 session - if so, get info from WL2K report line + + if (Sess2->L4CIRCUITTYPE & L2LINK) + { + LINKTABLE * LINK = Sess2->L4TARGET.LINK; + PORTCONTROLX * PORT = LINK->LINKPORT; + + if (PORT->WL2KInfo.Freq) + { + len = sprintf(Passline, "CMSTELNET %s %lld %d\r", PORT->WL2KInfo.RMSCall, PORT->WL2KInfo.Freq, PORT->WL2KInfo.mode); + ADIF->Freq = PORT->WL2KInfo.Freq; + ADIF->Mode = PORT->WL2KInfo.mode; + } + } + else + { + if (Sess2->RMSCall[0]) + { + len = sprintf(Passline, "CMSTELNET %s %lld %d\r", Sess2->RMSCall, Sess2->Frequency, Sess2->Mode); + ADIF->Mode = Sess2->Frequency; + ADIF->Mode = Sess2->Mode; + } + } + } + } + } + send(sock, Passline, len, 0); + sockptr->LoginState = 2; // Data + sockptr->InputLen=0; + sockptr->LogonSent = FALSE; + + if (CMSLogEnabled) + { + char logmsg[120]; + sprintf(logmsg,"%d %s\r\n", sockptr->Number, Passline); + WriteCMSLog (logmsg); + } + + return TRUE; + } + + return TRUE; + + case 0: + + // Check Username + // + + *(CRPtr)=0; // remove cr + + if (LogEnabled) + { + char Addr[256]; + Tel_Format_Addr(sockptr, Addr); + + if (strlen(MsgPtr) > 64) + { + MsgPtr[64] = 0; + Debugprintf("Telnet Bad User Name %s", MsgPtr); + } + + sprintf(logmsg,"%d %s User=%s\n", sockptr->Number, Addr, MsgPtr); + WriteLog (logmsg); + } + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (USER == NULL) + continue; + + if (_stricmp(USER->UserName, "ANON") == 0) + { + // Anon Login - Callsign is supplied as user + + sockptr->UserPointer = USER; //' Save pointer for checking password + strcpy(sockptr->Callsign, _strupr(MsgPtr)); //' for *** linked + } + else if (strcmp(MsgPtr,USER->UserName) == 0) + { + sockptr->UserPointer = USER; //' Save pointer for checking password + strcpy(sockptr->Callsign, USER->Callsign); //' for *** linked + + } + else + continue; + + sockptr->Retries = 0; + + sockptr->LoginState = 1; + sockptr->InputLen = 0; + + n=sockptr->Number; + +#ifndef LINBPQ + ModifyMenu(TCP->hDisMenu, n - 1, MF_BYPOSITION | MF_STRING, IDM_DISCONNECT + n, MsgPtr); +#endif + + ShowConnections(TNC); + + InputLen=InputLen-(MsgLen+1); + + sockptr->InputLen=InputLen; + + if (InputLen > 0) + { + memmove(MsgPtr, CRPtr+1, InputLen); + goto MsgLoop; + } + + return 0; + } + + // User Not found + + if (sockptr->Retries++ == 4) + { + send(sock,AttemptsMsg,sizeof(AttemptsMsg),0); + Sleep (1000); + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + } + else + { + send(sock, TCP->LoginMsg, (int)strlen(TCP->LoginMsg), 0); + sockptr->InputLen=0; + + } + + return 0; + + case 1: + + *(CRPtr)=0; // remove cr + + if (LogEnabled) + { + char Addr[256]; + Tel_Format_Addr(sockptr, Addr); + + if (strlen(MsgPtr) > 64) + { + MsgPtr[64] = 0; + Debugprintf("Telnet Bad Password %s", MsgPtr); + } + + sprintf(logmsg,"%d %s Password=%s\n", sockptr->Number, Addr, MsgPtr); + WriteLog (logmsg); + } + if (strcmp(MsgPtr, sockptr->UserPointer->Password) == 0) + { + char * Appl; + + if (ProcessIncommingConnect(TNC, sockptr->Callsign, sockptr->Number, FALSE) == FALSE) + { + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + return 0; + } + + TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]->Secure_Session = sockptr->UserPointer->Secure; + + sockptr->LoginState = 2; + + sockptr->InputLen = 0; + + if (LogEnabled) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + sprintf(logmsg,"%d %s Call Accepted. Callsign=%s\n", + sockptr->Number, Addr,sockptr->Callsign); + + WriteLog (logmsg); + } + + ShowConnections(TNC); + InputLen=InputLen-(MsgLen+1); + + sockptr->InputLen=InputLen; + + // What is left is the Command to connect to the BBS + + if (InputLen > 1) + { + if (*(CRPtr+1) == 10) + { + CRPtr++; + InputLen--; + } + + memmove(MsgPtr, CRPtr+1, InputLen); + + if (_memicmp(MsgPtr, "BPQTermTCP", 10) == 0) + { + send(sock, "Connected to TelnetServer\r", 26, 0); + sockptr->BPQTermMode = TRUE; + sockptr->MMASK = 0; // Make sure defaults to off + sockptr->InputLen -= 11; + + if (sockptr->InputLen) + { + // Monitor control info may arrive in same packet + + int P8 = 0; + + memmove(MsgPtr, &MsgPtr[11], InputLen); + if (memcmp(MsgPtr, "\\\\\\\\", 4) == 0) + { + // Monitor Control + + int n = sscanf(&MsgPtr[4], "%llx %x %x %x %x %x %x %x", + &sockptr->MMASK, &sockptr->MTX, &sockptr->MCOM, &sockptr->MonitorNODES, + &sockptr->MonitorColour, &sockptr->MUIOnly, &sockptr->UTF8, &P8); + + if (n == 5) + sockptr->MUIOnly = sockptr->UTF8 = 0; + + if (n == 6) + sockptr->UTF8 = 0; + + if (P8 == 1) + SendPortsForMonitor(sock, sockptr->UserPointer->Secure); + + + sockptr->InputLen = 0; + } + } + } + else + { + MsgPtr[InputLen] = 13; + SendtoNode(TNC, sockptr->Number, MsgPtr, InputLen+1); + } + sockptr->InputLen = 0; + } + + Appl = sockptr->UserPointer->Appl; + + if (Appl[0]) + SendtoNode(TNC, sockptr->Number, Appl, (int)strlen(Appl)); + + return 0; + } + // Bad Password + + if (sockptr->Retries++ == 4) + { + send(sock,AttemptsMsg, (int)strlen(AttemptsMsg),0); + Sleep (1000); + DataSocket_Disconnect(TNC, sockptr); //' Tidy up + } + else + { + send(sock, TCP->PasswordMsg, (int)strlen(TCP->PasswordMsg), 0); + sockptr->InputLen=0; + } + + return 0; + + default: + + return 0; + } + return 0; +} + +extern char * RigWebPage; + +struct RHPParamBlock +{ + unsigned char * Msg; + int Len; + SOCKET Socket; + struct ConnectionInfo * sockptr; +}; + + + +int DataSocket_ReadHTTP(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream) +{ + int w =1, x= 1, len=0, y = 2, maxlen, InputLen, ret; + char NLMsg[3]={13,10,0}; + UCHAR * MsgPtr; + UCHAR * CRLFCRLF; + UCHAR * LenPtr; + int BodyLen, ContentLen; + struct ConnectionInfo * sockcopy; + + ret = ioctl(sock,FIONREAD,&w); + + maxlen = InputBufferLen - sockptr->InputLen; + + if (w > maxlen) w = maxlen; + + len = recv(sock, &sockptr->InputBuffer[sockptr->InputLen], w, 0); + + if (len == SOCKET_ERROR || len == 0) + { + // Failed or closed - clear connection + + // if Websock connection till app + + if (sockptr->WebSocks) + { + if (memcmp(sockptr->WebURL, "rhp", 3) == 0) + { + ProcessRHPWebSockClosed(sockptr->socket); + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + } + else + { + TNC->Streams[sockptr->Number].ReportDISC = TRUE; //Tell Node + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + } + + MsgPtr = &sockptr->InputBuffer[0]; + sockptr->InputLen += len; + InputLen = sockptr->InputLen; + + MsgPtr[InputLen] = 0; + + if (sockptr->WebSocks) + { + // Websocks message + + int i, j; + int Fin, Opcode, Len, Mask; + char MaskingKey[4]; + char * ptr; + char * Payload; + + /* + +-+-+-+-+-------+-+-------------+-------------------------------+ + |F|R|R|R| opcode|M| Payload len | Extended payload length | + |I|S|S|S| (4) |A| (7) | (16/64) | + |N|V|V|V| |S| | (if payload len==126/127) | + | |1|2|3| |K| | | + +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + | Extended payload length continued, if payload len == 127 | + + - - - - - - - - - - - - - - - +-------------------------------+ + | |Masking-key, if MASK set to 1 | + +-------------------------------+-------------------------------+ + | Masking-key (continued) | Payload Data | + +-------------------------------- - - - - - - - - - - - - - - - + + : Payload Data continued ... : + + Octet i of the transformed data ("transformed-octet-i") is the XOR of + octet i of the original data ("original-octet-i") with octet at index + i modulo 4 of the masking key ("masking-key-octet-j"): + + j = i MOD 4 + transformed-octet-i = original-octet-i XOR masking-key-octet-j +*/ + Fin = MsgPtr[0] >> 7; + Opcode = MsgPtr[0] & 15; + Mask = MsgPtr[1] >> 7; + Len = MsgPtr[1] & 127; + + if (Len == 126) // Two Byte Len + { + Len = (MsgPtr[2] << 8) + MsgPtr[3]; + memcpy(MaskingKey, &MsgPtr[4], 4); + ptr = &MsgPtr[8]; + } + else + { + memcpy(MaskingKey, &MsgPtr[2], 4); + ptr = &MsgPtr[6]; + } + + Payload = ptr; + + for (i = 0; i < Len; i++) + { + j = i & 3; + + *ptr = *ptr ^ MaskingKey[j]; + ptr++; + } + + if (Opcode == 8) + { + Debugprintf("WebSock Close"); + } + else if (Opcode == 1) + { + if (strcmp(sockptr->WebURL, "RIGCTL") == 0) + { + // PTT Message + + char RigCMD[64]; + + sprintf(RigCMD, "%s PTT", Payload); + Rig_Command( (TRANSPORTENTRY *) -1, RigCMD); + } + else if (memcmp(sockptr->WebURL, "WMRefresh", 9) == 0) + { + sockcopy = malloc(sizeof(struct ConnectionInfo)); + sockptr->TNC = TNC; + sockptr->LastSendTime = time(NULL); + + memcpy(sockcopy, sockptr, sizeof(struct ConnectionInfo)); + + _beginthread(ProcessWebmailWebSockThread, 2048000, (VOID *)sockcopy); // Needs big stack + return 0; + } + else if (memcmp(sockptr->WebURL, "rhp", 3) == 0) + { + // Run in thread as it may block; + + struct RHPParamBlock * ParamBlock = malloc(sizeof(struct RHPParamBlock)); + + ParamBlock->sockptr = sockptr; + ParamBlock->Socket = sockptr->socket; + ParamBlock->Len = Len; + ParamBlock->Msg = malloc(Len + 10); + memcpy(ParamBlock->Msg, Payload, Len); + sockptr->LastSendTime = time(NULL); + + _beginthread(RHPThread, 0, (VOID *)ParamBlock); + + sockptr->InputLen = 0; + return 0; + } + } + else + Debugprintf("WebSock Opcode %d Msg %s", Opcode, &MsgPtr[6]); + + sockptr->InputLen = 0; + return 0; + } + + // Make sure request is complete - should end crlfcrlf, and if a post have the required input message + + + CRLFCRLF = strstr(MsgPtr, "\r\n\r\n"); + + if (CRLFCRLF == 0) + return 0; + + LenPtr = strstr(MsgPtr, "Content-Length:"); + + if (LenPtr) + { + ContentLen = atoi(LenPtr + 15); + BodyLen = InputLen - (int)((CRLFCRLF + 4 - MsgPtr)); + + if (BodyLen < ContentLen) + return 0; + } + + sockcopy = malloc(sizeof(struct ConnectionInfo)); + sockptr->TNC = TNC; + sockptr->LastSendTime = time(NULL); + + memcpy(sockcopy, sockptr, sizeof(struct ConnectionInfo)); + + if(strstr(MsgPtr, "Upgrade: websocket")) + { + int LOCAL = 0, COOKIE = 0; + char * HostPtr; + char * ptr; + + sockptr->WebSocks = 1; + ShowConnections(TNC); + + memcpy(sockptr->WebURL, &MsgPtr[5], 31); + strlop(sockptr->WebURL, ' '); + if (RigWebPage) + RigWebPage[0] = 0; + + HostPtr = strstr(MsgPtr, "Host: "); + + if (HostPtr) + { + uint32_t Host; + char Hostname[32]= ""; + struct LOCALNET * LocalNet = sockptr->TNC->TCPInfo->LocalNets; + + HostPtr += 6; + memcpy(Hostname, HostPtr, 31); + strlop(Hostname, ':'); + Host = inet_addr(Hostname); + + if (strcmp(Hostname, "127.0.0.1") == 0) + LOCAL = TRUE; + else + { + if (sockptr->sin.sin_family != AF_INET6) + { + while(LocalNet) + { + uint32_t MaskedHost = sockptr->sin.sin_addr.s_addr & LocalNet->Mask; + if (MaskedHost == LocalNet->Network) + { + LOCAL = 1; + break; + } + LocalNet = LocalNet->Next; + } + } + + ptr = strstr(MsgPtr, "BPQSessionCookie=N"); + + if (ptr) + COOKIE = TRUE; + } + sockptr->WebSecure = LOCAL | COOKIE; + } + } + + + _beginthread(ProcessHTTPMessage, 2048000, (VOID *)sockcopy); // Needs big stack + + sockptr->InputLen = 0; + return 0; +} + +int DataSocket_ReadDRATS(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Stream) +{ + int len=0, maxlen; + + ioctl(sock,FIONREAD,&len); + + maxlen = InputBufferLen - sockptr->InputLen; + + if (len > maxlen) len = maxlen; + + len = recv(sock, &sockptr->InputBuffer[sockptr->InputLen], len, 0); + + if (len == SOCKET_ERROR || len == 0) + { + // Failed or closed - clear connection + + DRATSConnectionLost(sockptr); + DataSocket_Disconnect(TNC, sockptr); + return 0; + } + + // Make sure request is complete - should end [EOB] + + processDRATSFrame(&sockptr->InputBuffer[sockptr->InputLen], len, sockptr); + return 0; +} + + +int DataSocket_Disconnect(struct TNCINFO * TNC, struct ConnectionInfo * sockptr) +{ + int n; + + if (sockptr->SocketActive) + { + if (sockptr->socket) + closesocket(sockptr->socket); + + n = sockptr->Number; +#ifndef LINBPQ + ModifyMenu(TNC->TCPInfo->hDisMenu, n - 1, MF_BYPOSITION | MF_STRING, IDM_DISCONNECT + n, "."); +#endif + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + } + return 0; +} + +int ShowConnections(struct TNCINFO * TNC) +{ +#ifndef LINBPQ + char msg[80]; + struct ConnectionInfo * sockptr; + int i,n; + + SendMessage(TNC->hMonitor,LB_RESETCONTENT,0,0); + + for (n = 1; n <= TNC->TCPInfo->CurrentSockets; n++) + { + sockptr=TNC->Streams[n].ConnectionInfo; + + if (!sockptr->SocketActive) + { + strcpy(msg,"Idle"); + } + else + { + if (sockptr->UserPointer == 0) + { + if (sockptr->HTTPMode) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + + if (sockptr->WebSocks) + sprintf(msg, "Websock From %s", Addr); + else + sprintf(msg, "HTTP From %s", Addr); + } + else if (sockptr->DRATSMode) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + sprintf(msg, "DRATS From %s", Addr); + } + else + strcpy(msg,"Logging in"); + } + else + { + i=sprintf(msg,"%-10s %-10s %2d", + sockptr->UserPointer->UserName, sockptr->Callsign, sockptr->FromHostBuffPutptr - sockptr->FromHostBuffGetptr); + } + } + SendMessage(TNC->hMonitor, LB_ADDSTRING ,0, (LPARAM)msg); + } +#endif + return 0; +} +byte * EncodeCall(byte * Call) +{ + static char axcall[10]; + + ConvToAX25(Call, axcall); + return &axcall[0]; +} +BOOL ProcessTelnetCommand(struct ConnectionInfo * sockptr, byte * Msg, int * Len) +{ + int cmd, TelOption; + int used; + char WillSupGA[3]={IAC,WILL,suppressgoahead}; + char WillEcho[3]={IAC,WILL,echo}; + char Wont[3]={IAC,WONT,echo}; + char Dont[3]={IAC,DONT,echo}; + + // Note Msg points to the IAC, which may not be at the start of the receive buffer + // Len is number of bytes left in buffer including the IAC + + if (*Len < 2) return TRUE; //' Wait for more + + cmd = Msg[1]; + + if (cmd == DOx || cmd == DONT || cmd == WILL || cmd == WONT) + if (*Len < 3) return TRUE; //' wait for option + + TelOption = Msg[2]; + + switch (cmd) + { + case DOx: + + switch (TelOption) + { + case echo: + sockptr->DoEcho = TRUE; + send(sockptr->socket,WillEcho,3,0); + break; + + case suppressgoahead: + + send(sockptr->socket,WillSupGA,3,0); + break; + + default: + + Wont[2] = TelOption; + send(sockptr->socket,Wont,3,0); + } + + used=3; + + break; + + case DONT: + + // Debug.Print "DONT"; TelOption + + switch (TelOption) + { + case echo: + sockptr->DoEcho = FALSE; + break; + } + + Wont[2] = TelOption; + send(sockptr->socket,Wont,3,0); + + used=3; + + break; + + case WILL: + + // Debug.Print "WILL"; TelOption + +// if (TelOption == echo) sockptr->DoEcho = TRUE; + + Dont[2] = TelOption; + send(sockptr->socket, Dont, 3, 0); + + used=3; + + break; + + case WONT: + +// Debug.Print "WONT"; TelOption + + used=3; + + break; + + default: + + used=2; + + } + + // remove the processed command from the buffer + + *Len -= used; + + return FALSE; +} + + +int WriteLog(char * msg) +{ + FILE *file; + char timebuf[128]; + time_t ltime; + + UCHAR Value[MAX_PATH]; + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + if (LogDirectory[0] == 0) + { + strcpy(Value, "logs/Telnet_"); + } + else + { + strcpy(Value, LogDirectory); + strcat(Value, "/"); + strcat(Value, "logs/Telnet_"); + } + + sprintf(Value, "%s%02d%02d%02d.log", Value, + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + + if ((file = fopen(Value, "a")) == NULL) + return FALSE; + + time(<ime); + +#ifdef LINBPQ + { + struct tm * tmp = localtime(<ime); + strftime( timebuf, 128, + "%d/%m/%Y %H:%M:%S ", tmp ); + } +#else + { + struct tm * today; + + today = localtime(<ime); + strftime(timebuf, 128, "%d/%m/%Y %H:%M:%S ", today); + } +#endif + fputs(timebuf, file); + fputs(msg, file); + fclose(file); + return 0; +} + +char LastCMSLog[256]; + +VOID WriteCMSLog(char * msg) +{ + UCHAR Value[MAX_PATH]; + time_t T; + struct tm * tm; + FILE * Handle; + char LogMsg[256]; + int MsgLen; + + if (CMSLogEnabled == FALSE) + return; + + T = time(NULL); + tm = gmtime(&T); + + if (LogDirectory[0] == 0) + { + strcpy(Value, "logs/CMSAccess"); + } + else + { + strcpy(Value, LogDirectory); + strcat(Value, "/"); + strcat(Value, "logs/CMSAccess"); + } + + sprintf(Value, "%s_%04d%02d%02d.log", Value, + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday); + + Handle = fopen(Value, "ab"); + + if (Handle == NULL) + return; + + MsgLen = sprintf(LogMsg, "%02d:%02d:%02d %s", tm->tm_hour, tm->tm_min, tm->tm_sec, msg); + + fwrite(LogMsg , 1, MsgLen, Handle); + + fclose(Handle); + +#ifndef WIN32 + + if (strcmp(Value, LastCMSLog)) + { + UCHAR SYMLINK[MAX_PATH]; + + sprintf(SYMLINK,"%s/CMSAccessLatest.log", BPQDirectory); + unlink(SYMLINK); + strcpy(LastCMSLog, Value); + symlink(Value, SYMLINK); + } + +#endif + + return; +} + + + + + + +int Telnet_Connected(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, int Error) +{ + struct TCPINFO * TCP = TNC->TCPInfo; + PMSGWITHLEN buffptr; + int Stream = sockptr->Number; + char Signon[80]; + int errlen = 4; + + buffptr = (PMSGWITHLEN)GetBuff(); + if (buffptr == 0) return 0; // No buffers, so ignore + +#ifndef WIN32 + +// SO_ERROR codes + +//#define ETIMEDOUT 110 /* Connection timed out */ +//#define ECONNREFUSED 111 /* Connection refused */ +//#define EHOSTDOWN 112 /* Host is down */ +//#define EHOSTUNREACH 113 /* No route to host */ +//#define EALREADY 114 /* Operation already in progress */ +//#define EINPROGRESS 115 /* Operation now in progress */ + + if (Error == 0) + getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&Error, &errlen); + +// Debugprintf("Except Event Error after opts = %d", Error); +#endif + + if (Error) + { + if (sockptr->CMSSession && sockptr->RelaySession == 0) + { + // Try Next + + TCP->CMSFailed[sockptr->CMSIndex] = TRUE; + + if (CMSConnect(TNC, TNC->TCPInfo, &TNC->Streams[Stream], Stream)) + return 0; + + // Connect failure - if no more servers to check look for FALLBACKTORELAY + + return 0; + } + else + { + int err = 0; + int errlen = 4; + + getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); + + buffptr->Len = sprintf(&buffptr->Data[0], "*** Failed to Connect\r"); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + closesocket(sock); + TNC->Streams[Stream].Connecting = FALSE; + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + TNC->Streams[Stream].NeedDisc = 10; + return 0; + } + } + + sockptr->LogonSent = FALSE; + + if (sockptr->CMSSession) + { + sockptr->LoginState = 3; // Password State + + sockptr->UserPointer = &CMSUser; + strcpy(sockptr->Callsign, TNC->Streams[Stream].MyCall); + + sockptr->DoEcho = FALSE; + sockptr->FBBMode = TRUE; + sockptr->RelayMode = FALSE; + sockptr->ClientSession = FALSE; + sockptr->SyncMode = FALSE; + + if (TCP->CMS) + SaveCMSHostInfo(TNC->Port, TNC->TCPInfo, sockptr->CMSIndex); + + if (CMSLogEnabled) + { + char logmsg[120]; + + if (sockptr->RelaySession) + sprintf(logmsg,"%d %s Connected to RELAY\r\n", sockptr->Number, TNC->Streams[Stream].MyCall); + else + sprintf(logmsg,"%d %s Connected to CMS\r\n", sockptr->Number, TNC->Streams[Stream].MyCall); + + WriteCMSLog (logmsg); + } + + if (sockptr->RelaySession) + buffptr->Len = sprintf(&buffptr->Data[0], "*** %s Connected to RELAY\r", TNC->Streams[Stream].MyCall); + else + buffptr->Len = sprintf(&buffptr->Data[0], "*** %s Connected to CMS\r", TNC->Streams[Stream].MyCall); + } + else + { + sockptr->LoginState = 2; // Data State + sockptr->UserPointer = &HostUser; + strcpy(sockptr->Callsign, TNC->Streams[Stream].MyCall); + sockptr->DoEcho = FALSE; + sockptr->ClientSession = TRUE; + + if (sockptr->SyncMode) + { + char Addr[256]; + Tel_Format_Addr(sockptr, Addr); + + buffptr->Len = sprintf(&buffptr->Data[0], "*** Connected to SYNC %s:%d\r", Addr, htons(sockptr->sin.sin_port)); + send(sockptr->socket, sockptr->Signon, (int)strlen(sockptr->Signon), 0); + } + else + { + if (sockptr->Signon[0]) + { + buffptr->Len = sprintf(&buffptr->Data[0], "*** Connected to Server\r"); + send(sockptr->socket, sockptr->Signon, (int)strlen(sockptr->Signon), 0); + } + else + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4CROSSLINK->APPL[0]) + buffptr->Len = sprintf(&buffptr->Data[0], "*** Connected to %s\r", + TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4CROSSLINK->APPL); + else + buffptr->Len = sprintf(&buffptr->Data[0], "*** Connected to APPL\r"); + + if (sockptr->NoCallsign == FALSE) + send(sockptr->socket, Signon, sprintf(Signon, "%s\r\n", TNC->Streams[Stream].MyCall), 0); + } + } + } + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + sockptr->SocketActive = TRUE; + sockptr->InputLen = 0; +// sockptr->Number = Stream; + sockptr->RelayMode = FALSE; + sockptr->ConnectTime = time(NULL); + TNC->Streams[Stream].Connecting = FALSE; + TNC->Streams[Stream].Connected = TRUE; + + if (sockptr->ADIF == NULL) + sockptr->ADIF = malloc(sizeof(struct ADIF)); + + memset(sockptr->ADIF, 0, sizeof(struct ADIF)); + + strcpy(sockptr->ADIF->Call, TNC->Streams[Stream].MyCall); + + ShowConnections(TNC); + + if (sockptr->FromHostBuffer == 0) + { + sockptr->FromHostBuffer = malloc(10000); + sockptr->FromHostBufferSize = 10000; + } + + sockptr->FromHostBuffPutptr = sockptr->FromHostBuffGetptr = 0; + + TNC->Streams[Stream].bytesRXed = TNC->Streams[Stream].bytesTXed = 0; + + return 0; +} + +VOID ReportError(struct STREAMINFO * STREAM, char * Msg) +{ + PMSGWITHLEN buffptr; + + buffptr = (PMSGWITHLEN)GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "Error - %s\r", Msg); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); +} + +VOID Report(struct STREAMINFO * STREAM, char * Msg) +{ + PMSGWITHLEN buffptr; + + buffptr = (PMSGWITHLEN)GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "%s\r", Msg); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); +} + +void CheckCMSThread(void * TNC); + +BOOL CheckCMS(struct TNCINFO * TNC) +{ + if (TNC->TCPInfo->CMS) + { + TNC->TCPInfo->CheckCMSTimer = 0; + _beginthread(CheckCMSThread, 0, (void *)TNC); + } + return 0; +} + +void CheckCMSThread(void * TNCPtr) +{ + // Resolve Name and check connectivity to each address + + struct TNCINFO * TNC = (struct TNCINFO *)TNCPtr; + struct TCPINFO * TCP = TNC->TCPInfo; +// struct hostent * HostEnt; + struct in_addr addr; + struct hostent *remoteHost; + char **pAlias; int i = 0; + BOOL INETOK = FALSE; + struct addrinfo hints, *res = 0, *saveres; + int n; + unsigned long cms; + + TCP->UseCachedCMSAddrs = FALSE; + + // if TCP->CMSServer is an ip address use it + + cms = inet_addr(TCP->CMSServer); + + if (cms != INADDR_NONE) + { + Debugprintf("Using %s for CMS Server", TCP->CMSServer); + TCP->CMSAddr[0].s_addr = cms; + TCP->CMSFailed[0] = FALSE; + TCP->CMSName[0] = _strdup(TCP->CMSServer); // Save Host Name + TCP->NumberofCMSAddrs = 1; + goto CheckServers; + } + + // First make sure we have a functioning DNS + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET6; // use IPv4 or IPv6, whichever + hints.ai_socktype = SOCK_DGRAM; + + n = getaddrinfo("a.root-servers.net", NULL, &hints, &res); + + if (n == 0) + goto rootok; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever + hints.ai_socktype = SOCK_DGRAM; + n = getaddrinfo("b.root-servers.net", NULL, &hints, &res); + + if (n == 0) + goto rootok; + + Debugprintf("Resolve root nameserver failed"); + + // Most likely is a local Internet Outage, but we could have Internet, but no name servers + // Either way, switch to using cached CMS addresses. CMS Validation will check connectivity + + TCP->UseCachedCMSAddrs = TRUE; + goto CheckServers; + +rootok: + + freeaddrinfo(res); + + INETOK = TRUE; // We have connectivity + + res = 0; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever + hints.ai_socktype = SOCK_DGRAM; + n = getaddrinfo(TCP->CMSServer, NULL, &hints, &res); + + if (n || !res || res->ai_next == 0) // Resolve Failed, or Returned only one Host + { + // Switch to Cached Servers + + if (res) + { + // If Host is amazonaws, allow it + + remoteHost = gethostbyaddr((char *) &res->ai_addr->sa_data[2], 4, AF_INET); + + if (remoteHost && strstr(_strlwr(remoteHost->h_name), "amazonaws")) + goto resok; + + Debugprintf("Resolve CMS returned only one host"); + freeaddrinfo(res); + } + else + Debugprintf("Resolve CMS Failed"); + + TCP->UseCachedCMSAddrs = TRUE; + + goto CheckServers; + } + +resok: + + saveres = res; + + while (res) + { + memcpy(&addr.s_addr, &res->ai_addr->sa_data[2], 4); + TCP->CMSAddr[i] = addr; + TCP->CMSFailed[i] = FALSE; + i++; + res = res->ai_next; + } + + freeaddrinfo(saveres); + + TCP->NumberofCMSAddrs = i; + + i = 0; + + while (i < TCP->NumberofCMSAddrs) + { + if (TCP->CMSName[i]) + free(TCP->CMSName[i]); + + remoteHost = gethostbyaddr((char *) &TCP->CMSAddr[i], 4, AF_INET); + + if (remoteHost == NULL) + { + int dwError = WSAGetLastError(); + + TCP->CMSName[i] = NULL; + + if (dwError != 0) + { + if (dwError == HOST_NOT_FOUND) + Debugprintf("CMS - Host not found"); + else if (dwError == NO_DATA) + Debugprintf("CMS No data record found"); + else + Debugprintf("CMS Gethost failed %d", dwError); + } + } + else + { + Debugprintf("CMS #%d %s Official name : %s",i, inet_ntoa(TCP->CMSAddr[i]), remoteHost->h_name); + + TCP->CMSName[i] = _strdup(remoteHost->h_name); // Save Host Name + + for (pAlias = remoteHost->h_aliases; *pAlias != 0; pAlias++) + { + Debugprintf("\tAlternate name #%d: %s\n", i, *pAlias); + } + } + i++; + } + + TCP->NumberofCMSAddrs = i; + +CheckServers: +#ifndef LINBPQ + CheckMenuItem(TNC->TCPInfo->hActionMenu, 4, MF_BYPOSITION | TCP->UseCachedCMSAddrs<<3); +#endif + if (TCP->UseCachedCMSAddrs) + { + // Get Cached Servers from CMSInfo.txt + + GetCMSCachedInfo(TNC); + } + + if (TCP->NumberofCMSAddrs == 0) + { + TCP->CMSOK = FALSE; +#ifndef LINBPQ + SetWindowText(TCP->hCMSWnd, "NO CMS"); +#endif + return; + } + + // if we don't know we have Internet connectivity, make sure we can connect to at least one of them + + TCP->CMSOK = INETOK | CMSCheck(TNC, TCP); // If we know we have Inet, dont check connectivity + +#ifndef LINBPQ + if (TCP->CMSOK) + MySetWindowText(TCP->hCMSWnd, "CMS OK"); + else + MySetWindowText(TCP->hCMSWnd, "NO CMS"); +#endif + return; +} + +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_NAME 255 +#define MAX_VALUE_DATA 255 + + + +VOID GetCMSCachedInfo(struct TNCINFO * TNC) +{ + struct TCPINFO * TCP = TNC->TCPInfo; + ULONG IPAD; + char inname[256]; + + FILE *in; + char Buffer[2048]; + char *buf = Buffer; + char *ptr1, *ptr2, *context; + int i = 0; + + if (LogDirectory[0] == 0) + { + strcpy(inname, "logs/CMSInfo.txt"); + } + else + { + strcpy(inname, LogDirectory); + strcat(inname, "/"); + strcat(inname, "logs/CMSInfo.txt"); + } + + TCP->NumberofCMSAddrs = 0; + + in = fopen(inname, "r"); + + if (!(in)) return; + + while(fgets(buf, 128, in)) + { + ptr1 = strtok_s(buf, ", ", &context); + ptr2 = strtok_s(NULL, ", ", &context); // Skip Time + ptr2 = strtok_s(NULL, ", ", &context); + + if (ptr1[0] < 32 || ptr1[0] > 127 || ptr2 == NULL) + continue; + + IPAD = inet_addr(ptr2); + + memcpy(&TCP->CMSAddr[i], &IPAD, 4); + + TCP->CMSFailed[i] = FALSE; + + if (TCP->CMSName[i]) + free(TCP->CMSName[i]); + + TCP->CMSName[i] = _strdup(ptr1); // Save Host Name + i++; + + if (i >= MaxCMS) + break; + } + + fclose(in); + + TCP->NumberofCMSAddrs = i; + + return; +} + +BOOL CMSCheck(struct TNCINFO * TNC, struct TCPINFO * TCP) +{ + // Make sure at least one CMS can be connected to + + u_long param=1; + BOOL bcopt=TRUE; + SOCKET sock; + struct sockaddr_in sinx; + struct sockaddr_in destaddr; + int addrlen=sizeof(sinx); + int n = 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(8772); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + for (n = 0; n < TCP->NumberofCMSAddrs; n++) + { + sock = socket(AF_INET, SOCK_STREAM, 0); + + if (sock == INVALID_SOCKET) + return FALSE; + + memcpy(&destaddr.sin_addr.s_addr, &TCP->CMSAddr[n], 4); + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + if (bind(sock, (struct sockaddr *) &sinx, addrlen) != 0 ) + return FALSE; + + if (connect(sock,(struct sockaddr *) &destaddr, sizeof(destaddr)) == 0) + { + closesocket(sock); + return TRUE; + } + + // Failed - try next + + if (TCP->CMSName[n]) + Debugprintf("Check CMS Failed for %s", TCP->CMSName[n]); + closesocket(sock); + } + return FALSE; +} + + + +int CMSConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * STREAM, int Stream) +{ + int err; + u_long param=1; + BOOL bcopt=TRUE; + struct ConnectionInfo * sockptr; + SOCKET sock; + struct sockaddr_in sinx; + struct sockaddr_in destaddr; + int addrlen=sizeof(sinx); + int n; + char Msg[80]; + + sockptr = STREAM->ConnectionInfo; + + sock = sockptr->socket = socket(AF_INET, SOCK_STREAM, 0); + + if (sock == INVALID_SOCKET) + { + ReportError(STREAM, "Create Socket Failed"); + return FALSE; + } + + if (sockptr->ADIF == NULL) + sockptr->ADIF = malloc(sizeof(struct ADIF)); + + memset(sockptr->ADIF, 0, sizeof(struct ADIF)); + + sockptr->SocketActive = TRUE; + sockptr->InputLen = 0; + sockptr->LoginState = 2; + sockptr->UserPointer = 0; + sockptr->DoEcho = FALSE; + sockptr->BPQTermMode = FALSE; + + sockptr->FBBMode = TRUE; // Raw Data + sockptr->NeedLF = FALSE; + + if (sockptr->ADIF == NULL) + sockptr->ADIF = malloc(sizeof(struct ADIF)); + + memset(sockptr->ADIF, 0, sizeof(struct ADIF)); + + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(8772); + + // See if current CMS is down + + n = 0; + + while (TCP->CMSFailed[TCP->NextCMSAddr]) + { + TCP->NextCMSAddr++; + if (TCP->NextCMSAddr >= TCP->NumberofCMSAddrs) TCP->NextCMSAddr = 0; + n++; + + if (n == TCP->NumberofCMSAddrs) + { + TCP->CMSOK = FALSE; +#ifndef LINBPQ + DrawMenuBar(TNC->hDlg); +#endif + ReportError(STREAM, "All CMS Servers are inaccessible"); + closesocket(sock); + + if (TCP->RELAYHOST[0] && TCP->FallbacktoRelay && STREAM->NoCMSFallback == 0) + { + STREAM->Connecting = TRUE; + STREAM->ConnectionInfo->CMSSession = TRUE; + STREAM->ConnectionInfo->RelaySession = TRUE; + return TCPConnect(TNC, TCP, STREAM, TCP->RELAYHOST, 8772, TRUE); + } + + STREAM->NeedDisc = 10; + TNC->Streams[Stream].Connecting = FALSE; + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + return FALSE; + } + } + + sockptr->CMSIndex = TCP->NextCMSAddr; + + sprintf(Msg, "Trying %s", TCP->CMSName[TCP->NextCMSAddr]); + + memcpy(&destaddr.sin_addr.s_addr, &TCP->CMSAddr[TCP->NextCMSAddr++], 4); + + if (TCP->NextCMSAddr >= TCP->NumberofCMSAddrs) + TCP->NextCMSAddr = 0; + + ioctl(sockptr->socket, FIONBIO, ¶m); + + setsockopt (sockptr->socket, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sockptr->socket, (struct sockaddr *) &sinx, addrlen) != 0 ) + { + ReportError(STREAM, "Bind Failed"); + return FALSE; + } + +#ifndef LINBPQ + ModifyMenu(TCP->hDisMenu, Stream - 1, MF_BYPOSITION | MF_STRING, IDM_DISCONNECT + Stream, "CMS"); +#endif + + Report(STREAM, Msg); + + if (connect(sockptr->socket,(struct sockaddr *) &destaddr, sizeof(destaddr)) == 0) + { + // + // Connected successful + // + + ReportError(STREAM, "*** Connected"); + return TRUE; + } + else + { + err=WSAGetLastError(); + + if (err == 10035 || err == 115 || err == 36 || err == 150) //EWOULDBLOCK + { + // Connect in Progress + + sockptr->UserPointer = &CMSUser; + return TRUE; + } + else + { + // Connect failed + + closesocket(sockptr->socket); + + if (sockptr->CMSSession && sockptr->RelaySession == 0) + { + // Try Next + + TCP->CMSFailed[sockptr->CMSIndex] = TRUE; + Debugprintf("Connect Failed %d, trying next", err); + CMSConnect(TNC, TNC->TCPInfo, &TNC->Streams[Stream], Stream); + return 0; + } + + ReportError(STREAM, "Connect Failed"); + CheckCMS(TNC); + + STREAM->Connecting = FALSE; + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + STREAM->NeedDisc = 10; + + return FALSE; + } + } + return FALSE; + +} + +VOID SaveCMSHostInfo(int port, struct TCPINFO * TCP, int CMSNo) +{ + char Info[256]; + char inname[256]; + char outname[256]; + + unsigned char work[4]; + FILE *in, *out; + char Buffer[2048]; + char *buf = Buffer; + + if (TCP->CMS == 0) + return; + + if (CMSNo > 9 || CMSNo < 0 || TCP->CMSName[CMSNo] == 0) + { + Debugprintf("SaveCMSHostInfo invalid CMS Number %d", CMSNo); + return; + } + + if (LogDirectory[0] == 0) + { + strcpy(inname, "logs/CMSInfo.txt"); + } + else + { + strcpy(inname, LogDirectory); + strcat(inname, "/"); + strcat(inname, "logs/CMSInfo.txt"); + } + + if (LogDirectory[0] == 0) + { + strcpy(outname, "logs/CMSInfo.tmp"); + } + else + { + strcpy(outname, LogDirectory); + strcat(outname, "/"); + strcat(outname, "logs/CMSInfo.tmp"); + } + + memcpy(work, &TCP->CMSAddr[CMSNo].s_addr, 4); + + sprintf(Info,"%s %d %d.%d.%d.%d\n", TCP->CMSName[CMSNo], (int)time(NULL), + work[0], work[1], work[2], work[3]); + + + in = fopen(inname, "r"); + + if (!(in)) + { + in = fopen(inname, "w"); + + if (!(in)) + { + perror("Failed to create CMSInfo.txt"); + Debugprintf("Failed to create CMSInfo.txt"); + return; + } + fclose(in); + in = fopen(inname, "r"); + } + + if (!(in)) return; + + out = fopen(outname, "w"); + + if (!(out)) return; + + while(fgets(buf, 128, in)) + { + char addr[256]; + time_t t; + char ip[256]; + int n; + + if (sizeof(time_t) == 4) + n = sscanf(buf,"%s %d %s", addr, (int *)&t, ip); + else + n = sscanf(buf, "%s %lld %s", addr, &t, ip); + + if (n == 3) + { + time_t age = time(NULL) - t; + + // if not current server and not too old, copy across + + if (addr[0] > 31 && addr[0] < 127) + if ((age < 86400 * 30) && strcmp(addr, TCP->CMSName[CMSNo]) != 0) + fputs(buf, out); + } + } + + fputs(Info, out); + + fclose(in); + fclose(out); + + remove(inname); + rename(outname, inname); + + return; +} + +int TCPConnect(struct TNCINFO * TNC, struct TCPINFO * TCP, struct STREAMINFO * STREAM, char * Host, int Port, BOOL FBB) +{ + int err; + u_long param=1; + BOOL bcopt=TRUE; + struct ConnectionInfo * sockptr; + SOCKET sock; + struct sockaddr_in sinx; + struct sockaddr_in destaddr; + int addrlen=sizeof(sinx); + int i; + + sockptr = STREAM->ConnectionInfo; + + sock = sockptr->socket = socket(AF_INET, SOCK_STREAM, 0); + + if (sock == INVALID_SOCKET) + { + ReportError(STREAM, "Create Socket Failed"); + return FALSE; + } + + sockptr->SocketActive = TRUE; + sockptr->InputLen = 0; + sockptr->LoginState = 2; + sockptr->UserPointer = 0; + sockptr->DoEcho = FALSE; + + sockptr->FBBMode = FBB; // Raw Data + + if (sockptr->ADIF == NULL) + sockptr->ADIF = malloc(sizeof(struct ADIF)); + + memset(sockptr->ADIF, 0, sizeof(struct ADIF)); + + + // Resolve Name if needed + + sockptr->sin.sin_family = AF_INET; + sockptr->sin.sin_port = htons(Port); + + sockptr->sin.sin_addr.s_addr = inet_addr(Host); + + if (sockptr->sin.sin_addr.s_addr == INADDR_NONE) + { + struct hostent * HostEnt; + + // Resolve name to address + + HostEnt = gethostbyname(Host); + + if (!HostEnt) + { + ReportError(STREAM, "Resolve HostName Failed"); + return FALSE; // Resolve failed + } + i = 0; + while (HostEnt->h_addr_list[i] != 0) + { + struct in_addr addr; + addr.s_addr = *(u_long *) HostEnt->h_addr_list[i++]; + } + memcpy(&sockptr->sin.sin_addr.s_addr, HostEnt->h_addr, 4); + } + + ioctl (sockptr->socket, FIONBIO, ¶m); + + setsockopt (sockptr->socket, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (bind(sockptr->socket, (struct sockaddr *) &sinx, addrlen) != 0 ) + { + ReportError(STREAM, "Bind Failed"); + return FALSE; + } + + if (LogEnabled) + { + char logmsg[512]; + + sprintf(logmsg,"%d Outward Connect to %s Port %d\n", sockptr->Number, Host, Port); + WriteLog (logmsg); + } + + + if (connect(sockptr->socket,(struct sockaddr *) &sockptr->sin, sizeof(destaddr)) == 0) + { + // + // Connected successful + // + + ReportError(STREAM, "*** Connected"); + + // Get Send Buffer Size + + return TRUE; + } + else + { + err=WSAGetLastError(); + + if (err == 10035 || err == 115 || err == 36) //EWOULDBLOCK + { + // Connect in Progress + + sockptr->UserPointer = &HostUser; + return TRUE; + } + else + { + // Connect failed + + closesocket(sockptr->socket); + ReportError(STREAM, "Connect Failed"); + STREAM->Connecting = FALSE; + sockptr->SocketActive = FALSE; + ShowConnections(TNC); + STREAM->NeedDisc = 10; + + return FALSE; + } + } + + return FALSE; + +} + + +VOID Tel_Format_Addr(struct ConnectionInfo * sockptr, char * dst) +{ + unsigned char * src; + char zeros[12] = ""; + char * ptr; + struct + { + int base, len; + } best, cur; + unsigned int words[8]; + int i; + + if (sockptr->sin.sin_family != AF_INET6) + { + unsigned char work[4]; + memcpy(work, &sockptr->sin.sin_addr.s_addr, 4); + sprintf(dst,"%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + return; + } + + src = (unsigned char *)&sockptr->sin6.sin6_addr; + + // See if Encapsulated IPV4 addr + + if (src[12] != 0) + { + if (memcmp(src, zeros, 12) == 0) // 12 zeros, followed by non-zero + { + sprintf(dst,"::%d.%d.%d.%d", src[12], src[13], src[14], src[15]); + return; + } + } + + // Convert 16 bytes to 8 words + + for (i = 0; i < 16; i += 2) + words[i / 2] = (src[i] << 8) | src[i + 1]; + + // Look for longest run of zeros + + best.base = -1; + cur.base = -1; + + for (i = 0; i < 8; i++) + { + if (words[i] == 0) + { + if (cur.base == -1) + cur.base = i, cur.len = 1; // New run, save start + else + cur.len++; // Continuation - increment length + } + else + { + // End of a run of zeros + + if (cur.base != -1) + { + // See if this run is longer + + if (best.base == -1 || cur.len > best.len) + best = cur; + + cur.base = -1; // Start again + } + } + } + + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + + if (best.base != -1 && best.len < 2) + best.base = -1; + + ptr = dst; + + for (i = 0; i < 8; i++) + { + /* Are we inside the best run of 0x00's? */ + + if (best.base != -1 && i >= best.base && i < (best.base + best.len)) + { + // Just output one : for whole string of zeros + + *ptr++ = ':'; + i = best.base + best.len - 1; // skip rest of zeros + continue; + } + + /* Are we following an initial run of 0x00s or any real hex? */ + + if (i != 0) + *ptr++ = ':'; + + ptr += sprintf (ptr, "%x", words[i]); + + // Was it a trailing run of 0x00's? + } + + if (best.base != -1 && (best.base + best.len) == 8) + *ptr++ = ':'; + + *ptr++ = '\0'; +} + +BOOL TelSendPacket(int Stream, struct STREAMINFO * STREAM, PMSGWITHLEN buffptr, struct ADIF * ADIF) +{ + int datalen; + UCHAR * MsgPtr; + SOCKET sock; + struct ConnectionInfo * sockptr = STREAM->ConnectionInfo; + + datalen = (int)buffptr->Len; + MsgPtr = &buffptr->Data[0]; + + STREAM->bytesTXed += datalen; + + sock = sockptr->socket; + + if (sockptr->UserPointer == &CMSUser) + { + WritetoTrace(Stream, MsgPtr, datalen, ADIF, 'S'); + } + + if (sockptr->UTF8) + { + // Convert any non-utf8 chars + + if (IsUTF8(MsgPtr, datalen) == FALSE) + { + unsigned char UTF[1024]; + int u, code; + + // Try to guess encoding + + code = TrytoGuessCode(MsgPtr, datalen); + + if (code == 437) + u = Convert437toUTF8(MsgPtr, datalen, UTF); + else if (code == 1251) + u = Convert1251toUTF8(MsgPtr, datalen, UTF); + else + u = Convert1252toUTF8(MsgPtr, datalen, UTF); + + SendAndCheck(sockptr, UTF, u, 0); + ReleaseBuffer(buffptr); + return TRUE; + } + } + + if (sockptr->FBBMode && sockptr->NeedLF == FALSE) + { +/* + // if Outward Connect to FBB, Replace ff with ffff + + if (0) // if we use this need to fix retry + { + char * ptr2, * ptr = &MsgPtr[0]; + int i; + do + { + ptr2 = memchr(ptr, 255, datalen); + + if (ptr2 == 0) + { + // no ff, so just send as is + + xxxsend(sock, ptr, datalen, 0); + i=0; + break; + } + + i=ptr2+1-ptr; + + xxsend(sock,ptr,i,0); + xxsend(sock,"\xff",1,0); + + datalen-=i; + ptr=ptr2+1; + } + while (datalen>0); + } +*/ + // Normal FBB Mode path + + BOOL ret = SendAndCheck(sockptr, MsgPtr, datalen, 0); + ReleaseBuffer(buffptr); + return ret; + } + + // Not FBB mode, or FBB and NEEDLF Replace cr with crlf + + { + unsigned char Out[1024]; + unsigned char c; + unsigned char * ptr2 = Out; + unsigned char * ptr = &MsgPtr[0]; + + while (datalen--) + { + c = (*ptr++); + + if (c == 13) + { + *(ptr2++) = 13; + *(ptr2++) = 10; + } + else + *(ptr2++) = c; + } + + ReleaseBuffer(buffptr); + return SendAndCheck(sockptr, Out, (int)(ptr2 - Out), 0); + } +} + +VOID ProcessTrimodeCommand(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, char * MsgPtr) +{ + struct STREAMINFO * STREAM = &TNC->Streams[sockptr->Number]; + int Port = 4; + + Debugprintf(MsgPtr); + + if (strcmp(MsgPtr, "CLOSE") == 0) + { + if (STREAM->Connected) + { + STREAM->ReportDISC = TRUE; + } + } + +// MYCALLSIGN XE2BNC + else + if (memcmp(MsgPtr, "MYCALLSIGN", 10) == 0) + { + char * call = &MsgPtr[11]; + + if (strlen(call) > 9) + call[9] = 0; + + memcpy(STREAM->MyCall, call, 10); + + ConvToAX25(call, &TNC->PortRecord->ATTACHEDSESSIONS[sockptr->Number]->L4USER[0]); + + strcpy(&TNCInfo[Port]->Streams[0].MyCall[0], call); + } + + +// TARGETCALLSIGN KE7XO + else + if (memcmp(MsgPtr, "TARGETCALLSIGN", 14) == 0) + { + char * call = &MsgPtr[15]; + + if (strlen(call) > 9) + call[9] = 0; + + memcpy(STREAM->RemoteCall, call, 10); + } +// INITIATECALL 50 + else + if (memcmp(MsgPtr, "INITIATECALL", 12) == 0) + { + char Cmd[80]; + int n; + + n = sprintf(Cmd,"C %s\r", STREAM->RemoteCall); + + SendtoNode(TNC, sockptr->Number, Cmd, n); + } + + +// CHANNEL 3586500,None,None + else + if (memcmp(MsgPtr, "CHANNEL", 7) == 0) + { + double Freq = atof(&MsgPtr[8]); + char Radiocmd[80]; + int n; + + strcpy(sockptr->Callsign, "G8BPQ"); + + n = sprintf(Radiocmd,"RADIO %f %s\r", Freq/1000000, "USB"); + + SendtoNode(TNC, sockptr->Number, Radiocmd, n); + } + + else + if (memcmp(MsgPtr, "PROTOCOL", 8) == 0) + { + // Attach the relevant port + + SendtoNode(TNC, sockptr->Number, "ATTACH 4\r", 9); + } + + else + if (strcmp(MsgPtr, "BUSY") == 0) + send(sockptr->socket, "BUSY False\r\n", 12,0); + + + send(sockptr->socket, "CMD\r\n", 5,0); + +// SendtoNode(TNC, sockptr->Number, NodeLine, len); +} + + +VOID ProcessTrimodeResponse(struct TNCINFO * TNC, struct STREAMINFO * STREAM, unsigned char * MsgPtr, int Msglen) +{ + MsgPtr[Msglen] = 0; + + if (STREAM->ConnectionInfo->TriModeConnected) + { + // Send over the Data Socket + + send(STREAM->ConnectionInfo->TriModeDataSock, MsgPtr, Msglen, 0); + + return; + } + + strlop(MsgPtr, 13); + Debugprintf(MsgPtr); + + if (memcmp(MsgPtr, "*** Connected to ", 17) == 0) + { + char Cmd[80]; + int n; + + n = sprintf(Cmd,"CONNECTED %s\r", &MsgPtr[17]); + + STREAM->ConnectionInfo->TriModeConnected = TRUE; + + send(STREAM->ConnectionInfo->socket, Cmd, n, 0); + } +} + +VOID ProcessTriModeDataMessage(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCKET sock, struct STREAMINFO * STREAM) +{ + int len=0; + char NLMsg[3]={13,10,0}; + char RelayMsg[] = "No CMS connection available - using local BPQMail\r"; + struct TCPINFO * TCP = TNC->TCPInfo; + unsigned char Buffer[256]; + + ioctl(sock,FIONREAD,&len); + + if (len > 256) len = 256; + + len = recv(sock, Buffer, len, 0); + + if (len == SOCKET_ERROR || len ==0) + { + // Failed or closed - clear connection + + closesocket(sock); + return; + } + + SendtoNode(TNC, sockptr->Number, Buffer, len); +} + +extern struct DATAMESSAGE * REPLYBUFFER; +char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...); + + +VOID RECONFIGTELNET (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + int Port = 0, index =0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + struct TNCINFO * TNC; + char * ptr1, * ptr2; + char buf[256],errbuf[256]; + char * Config; + struct TCPINFO * TCP; + + 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; + } + + TNC = TNCInfo[Port]; + + if (TNC == NULL || TNC->Hardware != H_TELNET) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + TCP = TNC->TCPInfo; + + ptr = strtok_s(NULL, " ", &Context); + + if (ptr && _stricmp(ptr, "ALL") == 0) + { + // Use EXTRESTART Code + + PEXTPORTDATA PORTVEC = (PEXTPORTDATA) PORT; + PORTVEC->EXTRESTART = 1; + + Bufferptr = Cmdprintf(Session, Bufferptr, "Reconfig Telnet Ok\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (ptr && _stricmp(ptr, "USERS") == 0) + { + // Reconfig Users + + if (!ProcessConfig()) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Failed to reread config file - leaving config unchanged\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Config = PortConfig[Port]; + + if (Config == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "No Config Entries found\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // Don't free old user records - sessions may have pointers to them + + // Free the header + + if (TCP->UserRecPtr) + { + free(TCP->UserRecPtr); + TCP->UserRecPtr = NULL; + } + + TCP->NumberofUsers = 0; + + // Look for USER lines + + ptr1 = Config; + ptr2 = strchr(ptr1, 13); + + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1 + 1); + buf[ptr2 - ptr1 + 1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + strcpy(errbuf,buf); // save in case of erro + + if (_memicmp(buf, "USER=", 5) == 0 || _memicmp(buf, "USER ", 5) == 0) + { + char *User, *Pwd, *UserCall, *Secure, * Appl; + int End = (int)strlen(buf) -1; + struct UserRec * USER; + char Param[8][256]; + char * ptr1, * ptr2; + int n = 0; + char * value = &buf[5]; + + // USER=user,password,call,appl,SYSOP + + memset(Param, 0, 2048); + strlop(value, 13); + strlop(value, ';'); + + ptr1 = value; + + while (ptr1 && *ptr1 && n < 8) + { + ptr2 = strchr(ptr1, ','); + if (ptr2) *ptr2++ = 0; + + strcpy(&Param[n][0], ptr1); + strlop(Param[n++], ' '); + ptr1 = ptr2; + while(ptr1 && *ptr1 && *ptr1 == ' ') + ptr1++; + } + + + User = &Param[0][0]; + + if (_stricmp(User, "ANON") == 0) + { + strcpy(&Param[2][0], "ANON"); + strcpy(&Param[4][0], ""); // Dont allow SYSOP if ANON + } + + Pwd = &Param[1][0]; + UserCall = &Param[2][0]; + Appl = &Param[3][0]; + Secure = &Param[4][0]; + + if (User[0] == 0 || Pwd[0] == 0 || UserCall[0] == 0) // invalid record + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Bad USER Record %s\r", errbuf); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + _strupr(UserCall); + + if (TCP->NumberofUsers == 0) + TCP->UserRecPtr = malloc(sizeof(void *)); + else + TCP->UserRecPtr = realloc(TCP->UserRecPtr, (TCP->NumberofUsers+1) * sizeof(void *)); + + USER = zalloc(sizeof(struct UserRec)); + + TCP->UserRecPtr[TCP->NumberofUsers] = USER; + + USER->Callsign = _strdup(UserCall); + USER->Password = _strdup(Pwd); + USER->UserName = _strdup(User); + USER->Appl = zalloc(32); + USER->Secure = FALSE; + + if (_stricmp(Secure, "SYSOP") == 0) + USER->Secure = TRUE; + + if (Appl[0] && strcmp(Appl, "\"\"") != 0) + { + strcpy(USER->Appl, _strupr(Appl)); + strcat(USER->Appl, "\r\n"); + } + TCP->NumberofUsers++; + } + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Reread Telnet Users Ok - %d USER Records\r", TCP->NumberofUsers); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid parameter - use either USERS or ALL \r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID SHOWTELNET(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + // DISPLAY Telnet Server Status Mheard + + int Port = 0, index =0; + char * ptr, *Context; + struct PORTCONTROL * PORT = NULL; + int txlen = 0, n; + struct TNCINFO * TNC; + char msg[80]; + struct ConnectionInfo * sockptr; + int i; + char CMS[] = "CMS Disabled"; + + 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; + } + + TNC = TNCInfo[Port]; + + if (TNC == NULL || TNC->Hardware != H_TELNET) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet port\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (TNC->TCPInfo->CMS) + if (TNC->TCPInfo->CMSOK) + strcpy(CMS, "CMS Ok"); + else + strcpy(CMS, "No CMS"); + + Bufferptr = Cmdprintf(Session, Bufferptr, "Telnet Status for Port %d %s\r", Port, CMS); + + for (n = 1; n <= TNC->TCPInfo->CurrentSockets; n++) + { + sockptr=TNC->Streams[n].ConnectionInfo; + + if (!sockptr->SocketActive) + { + strcpy(msg,"Idle"); + } + else + { + if (sockptr->UserPointer == 0) + { + if (sockptr->HTTPMode) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + + if (sockptr->WebSocks) + sprintf(msg, "Websock From %s", Addr); + else + sprintf(msg, "HTTP From %s", Addr); + + } + else if (sockptr->DRATSMode) + { + char Addr[100]; + Tel_Format_Addr(sockptr, Addr); + sprintf(msg, "DRATS From %s", Addr); + } + else + strcpy(msg,"Logging in"); + } + else + { + i=sprintf(msg,"%-10s %-10s %2d", + sockptr->UserPointer->UserName,sockptr->Callsign,sockptr->BPQStream); + } + } + Bufferptr = Cmdprintf(Session, Bufferptr, "%s\r", msg); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + + +// Refresh any Web Socket Webmail index display +// Called whenever message database is changed + +#ifdef LINBPQ + +int DoRefreshWebMailIndex(); + +int RefreshWebMailIndex() +{ + DoRefreshWebMailIndex(); + return 0; +} + +#else + +// Have to pass request from BPQMail to DLL as socket can only be accessed in calling process +// Pass request back to WebMail via pipe + +// Code must run in bpq32 process, so set flag here and call code from Timer Routine + +extern BOOL NeedWebMailRefresh; + + +DllExport int APIENTRY RefreshWebMailIndex() +{ + NeedWebMailRefresh = 1; + return 0; +} + +#endif + +int DoRefreshWebMailIndex() +{ + // Loop through all sockets and pick out WebMail Index Connections + + int i, n; + struct ConnectionInfo * sockptr; + struct ConnectionInfo * sockcopy; + struct TNCINFO * TNC; + struct TCPINFO * TCP; + +#ifndef LINBPQ + NeedWebMailRefresh = 0; +#endif + + for (i = 0; i < 33; i++) + { + TNC = TNCInfo[i]; + + if (TNC && TNC->Hardware == H_TELNET) + { + TCP = TNC->TCPInfo; + + if (TCP) + { + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->SocketActive) + { + if (sockptr->HTTPMode && sockptr->WebSocks && memcmp(sockptr->WebURL, "WMRefresh", 9) == 0) + { + sockcopy = malloc(sizeof(struct ConnectionInfo)); + sockptr->TNC = TNC; + sockptr->LastSendTime = time(NULL); + + memcpy(sockcopy, sockptr, sizeof(struct ConnectionInfo)); + + _beginthread(ProcessWebmailWebSockThread, 2048000, (VOID *)sockcopy); // Needs big stack + } + } + } + } + } + } + return 0; +} + diff --git a/.svn/pristine/59/59384357fa6a02765ac89b40ca4dffa3b1f95c8a.svn-base b/.svn/pristine/59/59384357fa6a02765ac89b40ca4dffa3b1f95c8a.svn-base new file mode 100644 index 0000000..ff0669c --- /dev/null +++ b/.svn/pristine/59/59384357fa6a02765ac89b40ca4dffa3b1f95c8a.svn-base @@ -0,0 +1,32 @@ +/* Alloc.h -- Memory allocation functions +2008-03-13 +Igor Pavlov +Public domain */ + +#ifndef __COMMON_ALLOC_H +#define __COMMON_ALLOC_H + +#include + +void *MyAlloc(size_t size); +void MyFree(void *address); + +#ifdef _WIN32 + +void SetLargePageSize(); + +void *MidAlloc(size_t size); +void MidFree(void *address); +void *BigAlloc(size_t size); +void BigFree(void *address); + +#else + +#define MidAlloc(size) MyAlloc(size) +#define MidFree(address) MyFree(address) +#define BigAlloc(size) MyAlloc(size) +#define BigFree(address) MyFree(address) + +#endif + +#endif diff --git a/.svn/pristine/59/593f9fd7c086ef89df25fa16ce36362d4ee4c8dd.svn-base b/.svn/pristine/59/593f9fd7c086ef89df25fa16ce36362d4ee4c8dd.svn-base new file mode 100644 index 0000000..c406aa9 --- /dev/null +++ b/.svn/pristine/59/593f9fd7c086ef89df25fa16ce36362d4ee4c8dd.svn-base @@ -0,0 +1,4369 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// Interface to allow G8BPQ switch to use FreeData TNC + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifndef WIN32 +#ifndef MACBPQ +#include +#endif +#endif + +#include "cheaders.h" +#include "bpq32.h" +#include "tncinfo.h" + +#define SD_BOTH 0x02 + +#define FREEDATABUFLEN 16384 // TCP buffer size + +int KillTNC(struct TNCINFO * TNC); +static int RestartTNC(struct TNCINFO * TNC); + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); +static int Socket_Data(int sock, int error, int eventcode); +VOID MoveWindows(struct TNCINFO * TNC); +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SendInitScript(struct TNCINFO * TNC); +int ProcessEscape(UCHAR * TXMsg); +static void SendPoll(struct TNCINFO * TNC); +void SendMode(struct TNCINFO * TNC); +static int ConnecttoFreeData(int port); +void ConnectTNCPort(struct TNCINFO * TNC); +int FreeDataSendCommand(struct TNCINFO * TNC, char * data); +static void SendPing(struct TNCINFO * TNC, char * Call); +static void SendCQ(struct TNCINFO * TNC); +char * stristr (char *ch1, char *ch2); +int zEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned); +static void SendDataMsg(struct TNCINFO * TNC, char * Call, char * Msg, int Len); +static int SendAsRaw(struct TNCINFO * TNC, char * Call, char * myCall, char * Msg, int Len); +static int SendAsFile(struct TNCINFO * TNC, char * Call, char * Msg, int Len); +char * byte_base64_encode(char *str, int len); +void xdecodeblock( unsigned char in[4], unsigned char out[3] ); +void FlushData(struct TNCINFO * TNC); +void CountRestarts(struct TNCINFO * TNC); +void StopTNC(struct TNCINFO * TNC); +int FreeDataConnect(struct TNCINFO * TNC, char * Call); +int FreeDataDisconnect(struct TNCINFO * TNC); +int FreeGetData(struct TNCINFO * TNC); +static void SendBeacon(struct TNCINFO * TNC, int Interval); +void buildParamString(struct TNCINFO * TNC, char * line); +VOID FreeDataSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC); +VOID FreeDataReleasePort(struct TNCINFO * TNC); + + +static char ClassName[]="FREEDATASTATUS"; +static char WindowTitle[] = "FreeData Modem"; +static int RigControlRow = 205; + +#ifndef WIN32 +#include +#define MAXPNAMELEN 32 +#else +#include +#endif + + +extern int SemHeldByAPI; + +static RECT Rect; + +static int ProcessLine(char * buf, int Port); + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +#define MAXRXSIZE 512000 // Sets max size for file transfer (less base64 overhead + +int CaptureCount = 0; +int PlaybackCount = 0; + +int CaptureIndex = -1; // Card number +int PlayBackIndex = -1; + + + +char CaptureNames[16][MAXPNAMELEN + 2] = { "" }; +char PlaybackNames[16][MAXPNAMELEN + 2] = { "" }; + + +#ifdef WIN32 + +#include + +#pragma comment(lib, "winmm.lib") + +WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, 1, 12000, 24000, 2, 16, 0 }; + +WAVEOUTCAPS pwoc; +WAVEINCAPS pwic; + + +char * CaptureDevices = NULL; +char * PlaybackDevices = NULL; + +HWAVEOUT hWaveOut = 0; +HWAVEIN hWaveIn = 0; + +#endif + + +char * gen_uuid() +{ + char v[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + int i; + + //3fb17ebc-bc38-4939-bc8b-74f2443281d4 + //8 dash 4 dash 4 dash 4 dash 12 + + static char buf[37] = {0}; + + //gen random for all spaces because lazy + for (i = 0; i < 36; ++i) + { + buf[i] = v[rand()%16]; + } + + //put dashes in place + buf[8] = '-'; + buf[13] = '-'; + buf[18] = '-'; + buf[23] = '-'; + + //needs end byte + buf[36] = '\0'; + + return buf; +} + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC = TNCInfo[Port]; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if (ptr == NULL) return (TRUE); + + if (*ptr =='#') return (TRUE); // comment + + if (*ptr ==';') return (TRUE); // comment + + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->FreeDataInfo = zalloc(sizeof(struct FreeDataINFO)); + +// TNC->FreeDataInfo->useBaseCall = 1; // Default + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + TNC->MaxConReq = 10; // Default + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->TCPPort = WINMORport; + + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "CAPTURE", 7) == 0) + { + TNC->FreeDataInfo->Capture = _strupr(_strdup(&buf[8])); + strlop(TNC->FreeDataInfo->Capture, 13); + } + else if (_memicmp(buf, "PLAYBACK", 8) == 0) + { + TNC->FreeDataInfo->Playback = _strupr(_strdup(&buf[9])); + strlop(TNC->FreeDataInfo->Playback, 13); + } + + else if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + + else if (_memicmp(buf, "HAMLIBHOST", 10) == 0) + { + TNC->FreeDataInfo->hamlibHost = _strdup(&buf[11]); + strlop(TNC->FreeDataInfo->hamlibHost, 13); + } + + else if (_memicmp(buf, "TuningRange", 11) == 0) + TNC->FreeDataInfo->TuningRange = atoi(&buf[12]); + + else if (_memicmp(buf, "TXLevel", 6) == 0) + TNC->FreeDataInfo->TXLevel = atoi(&buf[7]); + + else if (_memicmp(buf, "Explorer", 8) == 0) + TNC->FreeDataInfo->Explorer = atoi(&buf[9]); + + + else if (_memicmp(buf, "LimitBandWidth", 14) == 0) + TNC->FreeDataInfo->LimitBandWidth = atoi(&buf[15]); + + else if (_memicmp(buf, "HAMLIBPORT", 10) == 0) + TNC->FreeDataInfo->hamlibPort = atoi(&buf[11]); + + else if (_memicmp(buf, "USEBASECALL", 11) == 0) + TNC->FreeDataInfo->useBaseCall = atoi(&buf[12]); + + else if (_memicmp(buf, "RXDIRECTORY", 11) == 0) + { + TNC->FreeDataInfo->RXDir = _strdup(&buf[12]); + strlop(TNC->FreeDataInfo->RXDir, 13); + } + + else if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + + } + + return (TRUE); +} + +char * Config; +static char * ptr1, * ptr2; + +int FreeDataGetLine(char * buf) +{ +loop: + + if (ptr2 == NULL) + return 0; + + memcpy(buf, ptr1, ptr2 - ptr1 + 2); + buf[ptr2 - ptr1 + 2] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + if (buf[0] < 0x20) goto loop; + if (buf[0] == '#') goto loop; + if (buf[0] == ';') goto loop; + + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0; + buf[strlen(buf)] = 13; + + return 1; +} + + +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + + + +static time_t ltime; + + + +static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + if (TNC->hDevice) + { + // FreeData mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + return; + } +} + + +VOID FreeDataChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + + datalen = sprintf(TXMsg, "MYCALL %s\r", Call); +// FreeDataSendCommand(TNC, TXMsg); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int txlen = 0; + UCHAR * TXMsg; + + size_t Param; + int Stream = 0; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + switch (fn) + { + case 7: + + // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + + SendPoll(TNC); + + // Check for buffered data to send + + if (TNC->FreeDataInfo->toSendTimeout) + { + TNC->FreeDataInfo->toSendTimeout--; + if (TNC->FreeDataInfo->toSendTimeout <= 0) + FlushData(TNC); + } + + return 0; + + case 1: // poll + +// FreeDataCheckRX(TNC); + + if (TNC->TNCCONNECTED == FALSE && TNC->TNCCONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 19 ) + { + TNC->lasttime = ltime; + ConnecttoFreeData(port); + } + } + + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + int CallLen; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + +/* if (TNC->CONNECTED == 0 || + TNC->Streams[0].Connecting || + TNC->Streams[0].Connected) + { + // discard if TNC not connected or sesison active + + ReleaseBuffer(buffptr); + continue; + } +*/ + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + // Frame has ax.25 format header. Convert to Text + + CallLen = ConvFromAX25(Buffer + 7, Call); // Origin + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + *ptr++ = '!'; + + CallLen = ConvFromAX25(Buffer, Call); // Dest + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + Buffer += 14; // TO Digis + datalen -= 14; + + while ((Buffer[-1] & 1) == 0) + { + *ptr++ = ','; + CallLen = ConvFromAX25(Buffer, Call); + memcpy(ptr, Call, CallLen); + ptr += CallLen; + Buffer += 7; // End of addr + datalen -= 7; + } + + *ptr++ = '_'; + *ptr++ = 'U'; // UI Frame + *ptr++ = 0; // delimit calls + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + } + +// FreeDataSendSingleData(TNC, FECMsg, Buffer, datalen); + + ReleaseBuffer(buffptr); + } + + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + } + } + + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + FreeDataDisconnect(TNC); + strcpy(TNC->WEB_TNCSTATE, "Disconnecting"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("FreeData New Attach Stream %d", Stream); + + STREAM->Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + // Stop Listening, and set MYCALL to user's call + + FreeDataSuspendPort(TNC, TNC); + FreeDataChangeMYC(TNC, TNC->Streams[0].MyCall); + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + } + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + // See if any frames for this port + + for (Stream = 0; Stream <= 2; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + + SendAsFile(TNC, TNC->FreeDataInfo->farCall, data, txlen); + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->TNCCONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "No Connection to TNC\r"); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID + TXMsg = &buff->L2DATA[0]; + TXMsg[txlen] = 0; + + if (STREAM->Connected) + { + STREAM->PacketsSent++; + + SendDataMsg(TNC, TNC->FreeDataInfo->farCall, buff->L2DATA, txlen); + return 1; + } + + if (TNC->FreeDataInfo->Chat) + { + // Chat Mode - Send to other end + + char reply[512] = "m"; + char * p; + int Len; + + if (_stricmp(TXMsg, "/ex\r") == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + TNC->FreeDataInfo->Chat = 0; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Chat with %s ended. \r", TNC->FreeDataInfo->ChatCall); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + // Send as chat message + + //m�;send_message�;123�;64730c5c-d32c-47b4-9b11-c958fd07a185�;hhhhhhhhhhhhhhhhhh + //�;�;plain/text�; + + strlop(TXMsg, 13); + + strcpy(&reply[2], ";send_message"); + strcpy(&reply[16], ";123"); + reply[21] = ';'; + strcpy(&reply[22], gen_uuid()); + sprintf(&reply[59], ";%s\n", TXMsg); + + p = strchr(&reply[59], 0); + + p[1] = ';'; + strcpy(&p[3], ";plain/text"); + p[15] = ';'; + + Len = &p[16] - reply; + + SendAsRaw(TNC, TNC->FreeDataInfo->ChatCall, "", reply, Len); + + return 0; + } + + + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + TNC->FreeDataInfo->Chat = 0; + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "PING ", 5) == 0) + { + char * Call = &buff->L2DATA[5]; + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + strlop(Call, 13); + strlop(Call, ' '); + SendPing(TNC, _strupr(Call)); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Ping Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + if (_memicmp(&buff->L2DATA[0], "CQ", 2) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + SendCQ(TNC); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} CQ Sent\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "TXLEVEL ", 8) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + int Level = atoi(&buff->L2DATA[8]); + char TXL[] = "{\"type\" : \"set\", \"command\" : \"tx_audio_level\", \"value\": \"%d\"}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, TXL, Level); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} TXLevel Set\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "SENDTEST", 8) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + char TXF[] = "{\"type\" : \"set\", \"command\" : \"send_test_frame\"}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, "%s", TXF); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Test Frame Requested\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "CHAT ", 5) == 0) + { + char * Call = &buff->L2DATA[5]; + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + strlop(Call, 13); + strlop(Call, ' '); + + TNC->FreeDataInfo->Chat = 1; + memcpy(TNC->FreeDataInfo->ChatCall, _strupr(Call), 10); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Chat with %s. Enter /ex to exit\r", Call); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + + if (_memicmp(&TXMsg[0], "BEACON ", 7) == 0) + { + int Interval = atoi(&TXMsg[7]); + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + SendBeacon(TNC, Interval); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Ok\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + } + + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char loppedCall[10]; + + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + // FreeDATA doesn't have the concept of a connection, so need to simulate it between the nodes + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + strcpy(TNC->FreeDataInfo->toCall, &buff->L2DATA[2]); + strcpy(loppedCall, TNC->FreeDataInfo->toCall); + if (TNC->FreeDataInfo->useBaseCall) + strlop(loppedCall, '-'); + strcpy(TNC->FreeDataInfo->farCall, loppedCall); + + // MYCALL and Target call are end to end concepts - the TNC cam can only use one call, set at TNC start. and no SSID's + // Messages are sent at TNC level to the tnc call, so we send our tnc call to the other end + + + txlen = sprintf(Connect, "C %s %s %s ", &buff->L2DATA[2], STREAM->MyCall, TNC->FreeDataInfo->ourCall); + + // See if Busy +/* + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } +*/ + TNC->OverrideBusy = FALSE; + + memset(STREAM->RemoteCall, 0, 10); + strcpy(STREAM->RemoteCall, &buff->L2DATA[2]); + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->PacketsSent = 0; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +// FreeDataSendCommand(TNC, Connect); + FreeDataConnect(TNC, STREAM->RemoteCall); + STREAM->Connecting = TRUE; + return 0; + + } + + // Normal data. Send to TNC + + // The TNC doesn't have any commands, so send error message + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "FreeData} Not connected\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + // strlop(buff->L2DATA, 13); + // txlen = SendDataMsg(TNC, TNC->, buff->L2DATA); + // FreeDataSendData(TNC, TXMsg, txlen); + + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // FreeData TNC can buffer unlimited data + + if (TNC->Streams[Stream].Attached == 0) + return (TNC->TNCCONNECTED != 0) << 8 | 1; + + return ((TNC->TNCCONNECTED != 0) << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + case 5: // Close + + StopTNC(TNC); + + // Drop through + + case 4: // reinit7 + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + printf("FreeData Closing %d\n", TNC->WeStartedTNC); + + if (TNC->WeStartedTNC) + { + printf("Calling KillTNC\n"); + KillTNC(TNC); + } + + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on FreeDATA"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + FreeDataSuspendPort(TNC, TNC); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + FreeDataReleasePort(TNC); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + return 0; + } + return 0; +} + +VOID FreeDataReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + FreeDataChangeMYC(TNC, TNC->NodeCall); + + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + FreeDataReleasePort(TNC); + ReleaseOtherPorts(TNC); + +} + +VOID FreeDataSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ +// char CMD[] = "{\"type\" : \"set\", \"command\" : \"listen\", \"state\": \"False\"}\n"; +// send(TNC->TCPDataSock, CMD, strlen(CMD), 0); +} + +VOID FreeDataReleasePort(struct TNCINFO * TNC) +{ + char CMD[] = "{\"type\" : \"set\", \"command\" : \"listen\", \"state\": \"True\"}\n"; + send(TNC->TCPDataSock, CMD, strlen(CMD), 0); +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "FreDATA Status" + "

FreeData Status" + "

", + TNC->Port); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +#ifndef LINBPQ + +#define BGCOLOUR RGB(236,233,216) +static HBRUSH RedBrush = NULL; +HBRUSH GreenBrush; +HBRUSH BlueBrush; +static HBRUSH bgBrush = NULL; + +extern HWND ClientWnd, FrameWnd; +extern int OffsetH, OffsetW; + +extern HMENU hMainFrameMenu; +extern HMENU hBaseMenu; +extern HANDLE hInstance; + +extern HKEY REGTREE; + + +/* +static LRESULT CALLBACK PacWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + MINMAXINFO * mmi; + PAINTSTRUCT ps; + HDC hdc; + + int i; + struct TNCINFO * TNC; + + HKEY hKey; + char Key[80]; + int retCode, disp; + + for (i=0; i<41; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->hDlg == hWnd) + break; + } + + if (TNC == NULL) + return DefMDIChildProc(hWnd, message, wParam, lParam); + + switch (message) { + + case WM_CREATE: + + break; + + case WM_PAINT: + + hdc = BeginPaint(hWnd, &ps); + +// SelectObject(ps.hdc, RedBrush); + SelectObject(ps.hdc, GreenBrush); +// SelectObject(ps.hdc, GetStockObject(GRAY_BRUSH)); + + EndPaint(hWnd, &ps); + break; + + case WM_GETMINMAXINFO: + + if (TNC->ClientHeight) + { + mmi = (MINMAXINFO *)lParam; + mmi->ptMaxSize.x = TNC->ClientWidth; + mmi->ptMaxSize.y = TNC->ClientHeight; + mmi->ptMaxTrackSize.x = TNC->ClientWidth; + mmi->ptMaxTrackSize.y = TNC->ClientHeight; + } + + break; + + + case WM_MDIACTIVATE: + { + + // Set the system info menu when getting activated + + if (lParam == (LPARAM) hWnd) + { + // Activate + + RemoveMenu(hBaseMenu, 1, MF_BYPOSITION); + + if (TNC->hMenu) + AppendMenu(hBaseMenu, MF_STRING + MF_POPUP, (UINT)TNC->hMenu, "Actions"); + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hBaseMenu, (LPARAM) hWndMenu); + +// SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) TNC->hMenu, (LPARAM) TNC->hWndMenu); + } + else + { + // Deactivate + + SendMessage(ClientWnd, WM_MDISETMENU, (WPARAM) hMainFrameMenu, (LPARAM) NULL); + } + + // call DrawMenuBar after the menu items are set + DrawMenuBar(FrameWnd); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + + + case WM_INITMENUPOPUP: + + if (wParam == (WPARAM)TNC->hMenu) + { + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, " TNC") || strstr(TNC->ProgramPath, "ARDOP") || strstr(TNC->ProgramPath, "VARA")) + { + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_ENABLED); + + break; + } + } + EnableMenuItem(TNC->hMenu, WINMOR_RESTART, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(TNC->hMenu, WINMOR_KILL, MF_BYCOMMAND | MF_GRAYED); + } + + break; + + case WM_COMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + case WINMOR_KILL: + + KillTNC(TNC); + break; + + case WINMOR_RESTART: + + KillTNC(TNC); + RestartTNC(TNC); + break; + + case WINMOR_RESTARTAFTERFAILURE: + + TNC->RestartAfterFailure = !TNC->RestartAfterFailure; + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\PACTOR\\PORT%d", TNC->Port); + + retCode = RegCreateKeyEx(REGTREE, Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + RegSetValueEx(hKey,"TNC->RestartAfterFailure",0,REG_DWORD,(BYTE *)&TNC->RestartAfterFailure, 4); + RegCloseKey(hKey); + } + break; + + case ARDOP_ABORT: + + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SIZING: + case WM_SIZE: + + MoveWindows(TNC); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_SYSCOMMAND: + + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (wmId) + { + + case SC_RESTORE: + + TNC->Minimized = FALSE; + break; + + case SC_MINIMIZE: + + TNC->Minimized = TRUE; + break; + } + + return DefMDIChildProc(hWnd, message, wParam, lParam); + + case WM_CTLCOLORDLG: + return (LONG)bgBrush; + + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + case WM_DESTROY: + + break; + } + return DefMDIChildProc(hWnd, message, wParam, lParam); +} +*/ + +#endif + + + +VOID * FreeDataExtInit(EXTPORTDATA * PortEntry) +{ + int port; + char Msg[255]; + char * ptr; + struct TNCINFO * TNC; + char * TempScript; + u_long param = 1; + int line; + int i; + int n = 0; + + srand(time(NULL)); + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + if (TNC->FreeDataInfo->TuningRange == 0) + TNC->FreeDataInfo->TuningRange = 50; + + if (TNC->FreeDataInfo->TXLevel == 0) + TNC->FreeDataInfo->TXLevel = 50; + + if (TNC->AutoStartDelay == 0) + TNC->AutoStartDelay = 2000; + +#ifndef LINBPQ + + if (bgBrush == NULL) + { + bgBrush = CreateSolidBrush(BGCOLOUR); + RedBrush = CreateSolidBrush(RGB(255,0,0)); + GreenBrush = CreateSolidBrush(RGB(0,255,0)); + BlueBrush = CreateSolidBrush(RGB(0,0,255)); + } + +#endif + + Consoleprintf("FreeData Host %s %d", TNC->HostName, TNC->TCPPort); + + TNC->Port = port; + TNC->PortRecord = PortEntry; + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_FREEDATA; + + TNC->WeStartedTNC = 1; + + TNC->ARDOPDataBuffer = malloc(MAXRXSIZE); + TNC->ARDOPBuffer = malloc(FREEDATABUFLEN); + + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + memcpy(TNC->FreeDataInfo->ourCall, TNC->NodeCall, 10); + strlop(TNC->FreeDataInfo->ourCall, ' '); + if (TNC->FreeDataInfo->useBaseCall) + strlop(TNC->FreeDataInfo->ourCall, '-'); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + if (TNC->PacketChannels > 1) + TNC->PacketChannels = 1; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = FreeDataSuspendPort; + TNC->ReleasePortProc = FreeDataReleasePort; + +// PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; +// PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; + + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = zalloc(1000); + + // cant think of any yet + + if (TNC->InitScript) + { + strcat(TempScript, TNC->InitScript); + free(TNC->InitScript); + } + + TNC->InitScript = TempScript; + + // Set MYCALL + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Already decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + PortEntry->PORTCONTROL.TNC = TNC; + + // Build SSID List + + if (TNC->LISTENCALLS) + { + strcpy(TNC->FreeDataInfo->SSIDList, TNC->LISTENCALLS); + } + else + { + APPLCALLS * APPL; + char Appl[11] = ""; + char * List = TNC->FreeDataInfo->SSIDList; + char * SSIDptr; + int SSID; + int Listptr; + + // list is a set of numbers separated by spaces eg 0 2 10 + + SSIDptr = strchr(TNC->NodeCall, '-'); + if (SSIDptr) + SSID = atoi(SSIDptr + 1); + else + SSID = 0; + + Listptr = sprintf(List, "%d", SSID); + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + + SSIDptr = strchr(Appl, '-'); + if (SSIDptr) + SSID = atoi(SSIDptr + 1); + else + SSID = 0; + + Listptr += sprintf(&List[Listptr], " %d", SSID); + } + } + List[Listptr] = 0; + } + + // Build SSID List for Linux + + TNC->FreeDataInfo->SSIDS[n] = _strdup(TNC->FreeDataInfo->SSIDList); + + while (TNC->FreeDataInfo->SSIDS[n]) + { + TNC->FreeDataInfo->SSIDS[n+1] = strlop(TNC->FreeDataInfo->SSIDS[n], ' '); + n++; + } + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + +#ifndef LINBPQ + + line = 6; + + if (TNC->TXFreq) + { + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow + 22, PacWndProc, 500, 450, ForcedClose); + + InitCommonControls(); // loads common control's DLL + + CreateWindowEx(0, "STATIC", "TX Tune", WS_CHILD | WS_VISIBLE, 10,line,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXTUNE = CreateWindowEx(0, TRACKBAR_CLASS, "", WS_CHILD | WS_VISIBLE, 116,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXTUNEVAL = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE, 320,line,30,20, TNC->hDlg, NULL, hInstance, NULL); + + SendMessage(TNC->xIDC_TXTUNE, TBM_SETRANGE, (WPARAM) TRUE, (LPARAM) MAKELONG(-200, 200)); // min. & max. positions + + line += 22; + } + else + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,line,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,450,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,line,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,520,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,line,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,line,144,20, TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,line,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,line,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,line,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + line += 22; + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,line,20,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,line,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,line,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill Freedata TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart Freedata TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + + strcpy(TNC->WEB_CHANSTATE, "Idle"); + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + + // Convert sound card name to index + +#ifdef WIN32 + + if (CaptureDevices == NULL) // DOn't do it again if more that one port + { + CaptureCount = waveInGetNumDevs(); + + CaptureDevices = malloc((MAXPNAMELEN + 2) * CaptureCount); + CaptureDevices[0] = 0; + + printf("Capture Devices"); + + for (i = 0; i < CaptureCount; i++) + { + waveInOpen(&hWaveIn, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveInGetDevCaps((UINT_PTR)hWaveIn, &pwic, sizeof(WAVEINCAPS)); + + if (CaptureDevices) + strcat(CaptureDevices, ","); + strcat(CaptureDevices, pwic.szPname); + Debugprintf("%d %s", i + 1, pwic.szPname); + memcpy(&CaptureNames[i][0], pwic.szPname, MAXPNAMELEN); + _strupr(&CaptureNames[i][0]); + } + + PlaybackCount = waveOutGetNumDevs(); + + PlaybackDevices = malloc((MAXPNAMELEN + 2) * PlaybackCount); + PlaybackDevices[0] = 0; + + Debugprintf("Playback Devices"); + + for (i = 0; i < PlaybackCount; i++) + { + waveOutOpen(&hWaveOut, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveOutGetDevCaps((UINT_PTR)hWaveOut, &pwoc, sizeof(WAVEOUTCAPS)); + + if (PlaybackDevices[0]) + strcat(PlaybackDevices, ","); + strcat(PlaybackDevices, pwoc.szPname); + Debugprintf("%i %s", i + 1, pwoc.szPname); + memcpy(&PlaybackNames[i][0], pwoc.szPname, MAXPNAMELEN); + _strupr(&PlaybackNames[i][0]); + waveOutClose(hWaveOut); + } + } + +#endif + + time(&TNC->lasttime); // Get initial time value + + ConnecttoFreeData(port); + return ExtProc; +} + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // We don't get data acks, so can't check for bytes outstanding + + FreeDataDisconnect(TNC); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + FreeDataDisconnect(TNC); +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + FreeDataReleaseTNC(TNC); + } +} + +VOID FreeDataAbort(struct TNCINFO * TNC) +{ + FreeDataSendCommand(TNC, "ABORT\r"); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID FreeDataDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + return; + } + + if (TNC->ReinitState == 3) + { + return; + } +} + +static RECT Rect1 = {30, 160, 400, 195}; + +int zEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned) +{ + // Replace forbidden chars with =xx + + unsigned char * ptr = out; + unsigned char c; + + while (len--) + { + c = *(in++); + + if (strchr(&Banned[0], c)) + { + *(out++) = '='; + *(out++) = (c >> 4) + 'A'; + *(out++) = (c & 15) + 'A'; + } + else + *(out++) = c; + } + + return (out - ptr); +} + + + +VOID FreeDataProcessTNCMessage(struct TNCINFO * TNC, char * Call, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * toCall, * fromCall, * tncCall, *Context; + char * ptr; + char a, b; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + // First Byte of Message is Type. Messages can be commands or short (<120) data packets + // Data is encoded with =xx replacing restricted chars + + Msg[Len] = 0; + + switch (Msg[0]) + { + case 'C': + + // Connect Request. C G8BPQ-10 GM8BPQ-2 (Target, Origin) + + toCall = strtok_s(&Msg[2], " ", &Context); + fromCall = strtok_s(NULL, " ", &Context); + tncCall = strtok_s(NULL, " ", &Context); + + strcpy(TNC->FreeDataInfo->farCall, tncCall); + + ConvToAX25Ex(fromCall, axcall); // Allow -T and -R SSID's for MPS + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(axcall) == FALSE) + { + Debugprintf("FreeData Call from %s rejected", fromCall); + + // Send 'd' + + Sleep(1000); + FreeDataDisconnect(TNC); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(axcall, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + Sleep(1000); + FreeDataSendCommand(TNC, "d"); + + Debugprintf("FreeDara Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(toCall, Appl) == 0) + break; + } + + if (App < 32) + { + + memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12); + AppName[12] = 0; + + // if SendTandRtoRelay set and Appl is RMS change to RELAY + + if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (!CheckAppl(TNC, AppName)) + { + Sleep(1000); + FreeDataSendCommand(TNC, "dApplication not Available"); + return; + } + } + + ProcessIncommingConnectEx(TNC, fromCall, 0, TRUE, TRUE); + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + // if connect to an application, send command + + if (AppName[0]) + { + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "%s\r", AppName); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + } + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, toCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + if (SESS->Frequency == 1500) + { + // try to get from WL2K record + + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, toCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->PacketsSent = 0; + STREAM->Connected = TRUE; + + // Send Connect ACK + Sleep(1000); + FreeDataSendCommand(TNC, "c"); + return; + + case 'c': + + // Connect ACK + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", TNC->FreeDataInfo->toCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + + return; + + case 'D': + + // Disconnect Command + + FreeDataSendCommand(TNC, "d"); + + // Drop through to disconnect this end + + case 'd': + + // Disconnect complete (response to sending "D") + // Or connect refused in response to "C" + + if (STREAM->Connecting) + { + // Connection Refused - If there is a message, pass to appl + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + if (Msg[1]) + buffptr->Len = sprintf(buffptr->Data, "Connect Rejected - %s\r", &Msg[1]); + else + buffptr->Len = sprintf(buffptr->Data, "Connect Rejected\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + return; + } + + // Release Session + + if (STREAM->Connected) + { + // Create a traffic record + + hookL4SessionDeleted(TNC, STREAM); + } + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + + case 'B': + + // Was Base64, but has been expanded - just send to User + + // If len > blocksize, fragment + + Len--; + Msg++; // Remove Type + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Msg, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Msg, 256); + Len -= 256; + Msg += 256; + STREAM->bytesRXed += 256; + + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Msg, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Msg, Len); + STREAM->bytesRXed += Len; + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->bytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->bytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return; + + case 'I': + + // Encoded Response + + // Undo = transparency + + ptr = Msg + 1; + + while (ptr = strchr(ptr, '=')) + { + // Next two chars are a hex value + + a = ptr[1] - 'A'; + b = ptr[2] - 'A'; + memmove(ptr, ptr + 2, Len); + ptr[0] = (a << 4) + b; + ptr++; + } + + buffptr = GetBuff(); + buffptr->Len = sprintf(buffptr->Data, "%s", &Msg[1]); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + + case 'f': + + // FreeDATA File Transfer + + // Seems to be f null fn null data + //f.;Makefile.;.;123123123.; + { + char * FN; + time_t CRC; + int FileLen; + char Filename[256]; + FILE * fp1; + unsigned char * ptr; + char Text[64]; + int textLen; + + + if (TNC->FreeDataInfo->RXDir == NULL) + { + Debugprintf("FreeDATA RXDIRECTORY not set - file transfer ignored"); + return; + } + + FN = _strdup(&Msg[3]); + ptr = &Msg[4] + strlen(FN); + + ptr = ptr + strlen(ptr) + 2; + CRC = atoi(ptr); + ptr = ptr + strlen(ptr) + 2; + + FileLen = Len - (ptr - Msg); + + sprintf(Filename, "%s\\%s", TNC->FreeDataInfo->RXDir, FN); + + fp1 = fopen(Filename, "wb"); + + if (fp1) + { + fwrite(ptr, 1, FileLen, fp1); + fclose(fp1); + textLen = sprintf(Text, "File %s received from %s \r", FN, Call); + WritetoTrace(TNC, Text, textLen); + } + else + Debugprintf("FreeDATA - File %s create failed %s", Filename); + + + free(FN); + return; + + } + + + } + + if (STREAM->Attached == 0) + return; + + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", Msg); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + +} + + +VOID FreeDataProcessNewConnect(struct TNCINFO * TNC, char * fromCall, char * toCall) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + char * ptr; + unsigned char axcall[7]; + char AppName[13] = ""; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + strcpy(TNC->FreeDataInfo->farCall, fromCall); + + ConvToAX25Ex(fromCall, axcall); // Allow -T and -R SSID's for MPS + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(axcall) == FALSE) + { + Debugprintf("FreeData Call from %s rejected", fromCall); + + // Send 'd' + + Sleep(1000); + FreeDataDisconnect(TNC); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(axcall, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + Sleep(1000); + FreeDataDisconnect(TNC); + + Debugprintf("FreeData Call from %s not in ValidCalls - rejected", fromCall); + return; + } + } + } + + // The TNC responds to any SSID so we can use incomming call as Appl Call + // No we can't - it responds but reports the configured call not the called call + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(toCall, Appl) == 0) + break; + } + + if (App < 32) + { + + memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12); + AppName[12] = 0; + + // if SendTandRtoRelay set and Appl is RMS change to RELAY + + if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 + && (strstr(fromCall, "-T" ) || strstr(fromCall, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (!CheckAppl(TNC, AppName)) + { + Sleep(1000); + FreeDataSendCommand(TNC, "dApplication not Available"); + return; + } + } + + + + ProcessIncommingConnectEx(TNC, fromCall, 0, TRUE, TRUE); + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + // if connect to an application, send command + + if (AppName[0]) + { + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "%s\r", AppName); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + } + } + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, toCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + if (SESS->Frequency == 1500) + { + // try to get from WL2K record + + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, toCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->PacketsSent = 0; + STREAM->Connected = TRUE; + + return; + +} + +VOID FreeDataProcessConnectAck(struct TNCINFO * TNC, char * Call, unsigned char * Msg, int Len) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Modem = TNC->FreeDataInfo; + + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s", STREAM->MyCall, STREAM->RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + STREAM->Connected = TRUE; + STREAM->Connecting = FALSE; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", TNC->FreeDataInfo->toCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + + return; + +} +extern char LOC[7]; +/* +Line 104: if received_json["type"] == 'PING' and received_json["command"] == "PING": +Line 111: if received_json["type"] == 'ARQ' and received_json["command"] == "sendFile": +Line 142: if received_json["type"] == 'ARQ' and received_json["command"] == "sendMessage": +Line 173: if received_json["type"] == 'ARQ' and received_json["command"] == "stopTransmission": +Line 182: if received_json["type"] == 'GET' and received_json["command"] == 'STATION_INFO': +Line 195: if received_json["type"] == 'GET' and received_json["command"] == 'TNC_STATE': +Line 244: if received_json["type"] == 'GET' and received_json["command"] == 'RX_BUFFER': +Line 258: if received_json["type"] == 'GET' and received_json["command"] == 'RX_MSG_BUFFER': +Line 272: if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_BUFFER': +Line 275: if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_MSG_BUFFER': +*/ + +//{\"type\" : \"ARQ\", \"command\" : \"sendMessage\", \"dxcallsign\" : \"G8BPQ\", \"mode\" : \"10\", \"n_frames\" : \"1\", \"data\" : \"Hello Hello\" , \"checksum\" : \"123\", \"timestamp\" : 1642580748576} + + + +static unsigned char BANNED[] = {'"', '=', ':', '{', '}', '[', ']', '/', 13, 0}; // I think only need to escape = ": CR Null + + +static void SendDataMsg(struct TNCINFO * TNC, char * Call, char * Msg, int Len) +{ + // We can't base64 encode chunks. so buffer as original data and encode on send + + SendAsFile(TNC, TNC->FreeDataInfo->farCall, Msg, Len); + WritetoTrace(TNC, Msg, Len); + + return; +} + + + +static int SendAsRaw(struct TNCINFO * TNC, char * Call, char * myCall, char * Msg, int Len) +{ + char Message[16284]; + char * Base64; + + // TNC now only supports send_raw, with base64 encoded data + + char Template[] = "{\"type\" : \"arq\", \"command\" : \"send_raw\", \"uuid\" : \"%s\",\"parameter\":" + "[{\"dxcallsign\" : \"%s\", \"mode\": \"255\", \"n_frames\" : \"1\", \"data\" : \"%s\"}]}\n"; + + + Base64 = byte_base64_encode(Msg, Len); + + Len = sprintf(Message, Template, gen_uuid(), Call, Base64); + + free(Base64); + return send(TNC->TCPDataSock, Message, Len, 0); +} + +void FlushData(struct TNCINFO * TNC) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Info = TNC->FreeDataInfo; + int Len = Info->toSendCount; + + // We need to flag as data (B) then base64 encode it + + memmove(&Info->toSendData[1], Info->toSendData, Len); + Info->toSendData[0] = 'B'; + Len++; + + SendAsRaw(TNC, Info->farCall, Info->ourCall, Info->toSendData, Len); + + Info->toSendCount = 0; + Info->toSendTimeout = 0; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->bytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->bytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + +} + +static int SendAsFile(struct TNCINFO * TNC, char * Call, char * Msg, int Len) +{ + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct FreeDataINFO * Info = TNC->FreeDataInfo; + + // Add to buffer + + if ((Info->toSendCount + Len) > 8192) // Reasonable Limit + { + // Send the buffered bit + + FlushData(TNC); + } + + memcpy(&Info->toSendData[Info->toSendCount], Msg, Len); + Info->toSendCount += Len; + Info->toSendTimeout = 10; // About a second + + STREAM->bytesTXed += Len; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->bytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->bytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + return Len; +} + +static int SendTNCCommand(struct TNCINFO * TNC, char * Type, char * Command) +{ + char Message[256]; + int Len; + + Len = sprintf(Message, "{\"type\" : \"%s\", \"command\" : \"%s\"}\n", Type, Command); + return send(TNC->TCPDataSock, Message, Len, 0); +} + +static void SendPoll(struct TNCINFO * TNC) +{ + return; +} + +static void SendPing(struct TNCINFO * TNC, char * Call) +{ + char Ping[] = "{\"type\" : \"ping\", \"command\" : \"ping\", \"dxcallsign\" : \"%s\", \"timestamp\" : %d}\n"; + char Message[256]; + int Len, ret; + + Len = sprintf(Message, Ping, Call, time(NULL)); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + +static void SendCQ(struct TNCINFO * TNC) +{ + char CQ[] = "{\"type\" : \"broadcast\", \"command\" : \"cqcqcq\"}\n"; + + char Message[256]; + int Len, ret; + + Len = sprintf(Message, "%s", CQ); + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + +static void SendBeacon(struct TNCINFO * TNC, int Interval) +{ + char Template1[] = "{\"type\" : \"broadcast\", \"command\" : \"start_beacon\", \"parameter\" : \"%d\"}\n"; + char Template2[] = "{\"type\" : \"broadcast\", \"command\" : \"stop_beacon\"}\n"; + + char Message[256]; + int Len, ret; + + if (Interval > 0) + Len = sprintf(Message, Template1, Interval); + else + Len = sprintf(Message, "%s", Template2); + + ret = send(TNC->TCPDataSock, (char *)&Message, Len, 0); +} + + +unsigned short int compute_crc(unsigned char *buf,int len); + + +int FreeDataWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->hDevice) + return WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + + return 0; +} + +char * getObjectFromArray(char * Msg) +{ + // This gets the next object from an array ({} = object, [] = array + // We look for the end of the object same number of { and }, teminate after } and return pointer to next object + // So we have terminated Msg, and returned next object in array + + // Only call if Msg is the next object in array + + + char * ptr = Msg; + char c; + + int Open = 0; + int Close = 0; + + while (c = *(ptr++)) + { + if (c == '{') Open ++; else if (c == '}') Close ++; + + if (Open == Close) + { + *(ptr++) = 0; + return ptr; + } + } + return 0; +} +/* + ["DATACHANNEL;RECEIVEDOPENER","ARQ;RECEIVING","ARQ;RECEIVING;SUCCESS"] [{"DXCALLSIGN":"GM8BPQ +{"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642847440,"RXDATA":[{"dt":"f","fn":"config.json", +"ft":"application\/json","d":"data:application\/json;base64, +ewogICAgInRuY19ob3N0IiA6ICIxMjcuMC4wLjEiLAogICAgInRuY19wb3J0IiA6ICIzMDAwIiwKICAgICJkYWVtb25faG9zdCIgOiAiMTI3LjAuMC4xIiwKICAgICJkYWVtb25fcG9ydCIgOiAiMzAwMSIsCiAgICAibXljYWxsIiA6ICJBQTBBQSIsCiAgICAibXlncmlkIiA6ICJBQTExZWEiICAgIAp9" +,"crc":"123123123"}]} +*/ +void ProcessFileObject(struct TNCINFO * TNC, char * This) +{ + char * Call; + char * LOC; + char * FN; + char * Type; + char * ptr, * ptr2; + int Len, NewLen; + + Call = strchr(This, ':'); + Call += 2; + This = strlop(Call, '"'); + + LOC = strchr(This, ':'); + LOC += 2; + This = strlop(LOC, '"'); + + FN = strstr(This, "fn"); + FN += 5; + This = strlop(FN, '"'); + + Type = strstr(This, "base64"); + Type += 7; + This = strlop(Type, '"'); + + // Decode Base64 + + Len = strlen(Type); + + Debugprintf("RX %d %s %d", TNC->Port, FN, Len); + + ptr = ptr2 = Type; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - Type); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + Type[NewLen] = 0; + + Type --; + + Type[0] = 'B'; ; // Base64 Info + + FreeDataProcessTNCMessage(TNC, Call, Type, NewLen + 1); + + +} + +void ProcessMessageObject(struct TNCINFO * TNC, char * This) +{ + // This gets Message from a RX_MSG_BUFFER array element. + + char * Call; + char * LOC; + char * Msg; + int Len; + char * ptr, * ptr2; + + char * ID; + char * TYPE; + char * SEQ; + char * UUID; + char * TEXT; + char * NOIDEA; + char * FORMAT; + char * FILENAME; + int fileLen; + + int n; + + + Call = strstr(This, "dxcallsign"); + Call += 13; + This = strlop(Call, '"'); + + LOC = strchr(This, ':'); + LOC += 2; + This = strlop(LOC, '"'); + + Msg = strstr(This, "\"data\""); + Msg += 8; + This = strlop(Msg, '"'); + + // Decode Base64 + + // FreeData replaces / with \/ so need to undo + + ptr2 = strstr(Msg, "\\/"); + + while (ptr2) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + ptr2 = strstr(ptr2, "\\/"); + } + + Len = strlen(Msg); + + ptr = ptr2 = Msg; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + Len = (int)(ptr2 - Msg); + + if (*(ptr-1) == '=') + Len--; + + if (*(ptr-2) == '=') + Len--; + + Msg[Len] = 0; + +//m�;send_message�;123�;64730c5c-d32c-47b4-9b11-c958fd07a185�;hhhhhhhhhhhhhhhhhh +//�;�;plain/text�; + +//m;send_message;123;64730c5c-d32c-47b4-9b11-c958fd07a185;hhhhhhhhhhhhhhhhhh +//;;plain/text; + + // Message elements seem to be delimited by null ; + // Guessing labels + + ID = Msg; + + if (ID[0] == 'B') + { + // BPQ Message + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + + if (STREAM->Attached) + { + if (STREAM->Connected == 1 && STREAM->Connecting == 0) + { + char * Line = &ID[1]; + Len -= 1; + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Line, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, 256); + Len -= 256; + Line += 256; + STREAM->bytesRXed += 256; + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Line, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, Len); + STREAM->bytesRXed += Len; + + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->bytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->bytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + } + return; + } + + n = strlen(ID) + 2; + Msg += n; + Len -= n; + + if (ID[0] == 'm') + { + // ?? Chat ?? comes from a send raw ?? + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + TYPE = Msg; + n = strlen(TYPE) + 2; + Msg += n; + Len -= n; + + SEQ = Msg; + n = strlen(SEQ) + 2; + Msg += n; + Len -= n; + + UUID = Msg; + n = strlen(UUID) + 2; + Msg += n; + Len -= n; + + TEXT = Msg; + n = strlen(TEXT) + 2; + Msg += n; + Len -= n; + + NOIDEA = Msg; + n = strlen(NOIDEA) + 2; + Msg += n; + Len -= n; + + FORMAT = Msg; + n = strlen(FORMAT) + 2; + Msg += n; + Len -= n; + + // if Atached, send to user + + if (STREAM->Attached) + { + if (STREAM->Connected == 0 && STREAM->Connecting == 0) + { + // Just attached - send as Chat Message + + char Line[560]; + char * rest; + + // Send line by line + + rest = strlop(TEXT, 10); // FreeData chat uses LF + + while (TEXT && TEXT[0]) + { + Len = strlen(TEXT); + if (Len > 512) + TEXT[512] = 0; + + Len = sprintf(Line, "Chat From %-10s%s\r", Call, TEXT); + + while (Len > 256) + { + buffptr = GetBuff(); + buffptr->Len = 256; + memcpy(buffptr->Data, Line, 256); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, 256); + Len -= 256; + TEXT += 256; + STREAM->bytesRXed += 256; + } + + buffptr = GetBuff(); + buffptr->Len = Len; + memcpy(buffptr->Data, Line, Len); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + WritetoTrace(TNC, Line, Len); + STREAM->bytesRXed += Len; + + TEXT = rest; + rest = strlop(TEXT, 10); // FreeData chat ues LF + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->bytesTXed - TNC->FreeDataInfo->toSendCount, STREAM->bytesRXed, TNC->FreeDataInfo->toSendCount); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + } + } + else + { + // Send Not Available Message +//m�;send_message�;123�;64730c5c-d32c-47b4-9b11-c958fd07a185�;hhhhhhhhhhhhhhhhhh +//�;�;plain/text�; + + char reply[512] = "m"; + char * p; + + strcat(TEXT, "\r"); + WritetoTrace(TNC, TEXT, strlen(TEXT)); + + strcpy(&reply[2], ";send_message"); + strcpy(&reply[16], ";123"); + reply[21] = ';'; + strcpy(&reply[22], gen_uuid()); + sprintf(&reply[59], ";Message received but user not on line\n"); + + p = strchr(&reply[59], 0); + + p[1] = ';'; + strcpy(&p[3], ";plain/text"); + p[15] = ';'; + + Len = &p[16] - reply; + + SendAsRaw(TNC, Call, TNC->FreeDataInfo->ourCall, reply, Len); + } + return; + + } + else if (ID[0] == 'f') + { + // File Tranfer + + char Filename[256]; + FILE * fp1; + char Text[64]; + int textLen; + + + FILENAME = Msg; + n = strlen(FILENAME) + 2; + Msg += n; + Len -= n; + + TYPE = Msg; + n = strlen(TYPE) + 2; + Msg += n; + Len -= n; + + SEQ = Msg; // ?? Maybe = 123123123 + n = strlen(SEQ) + 2; + Msg += n; + Len -= n; + + TEXT = Msg; // The file + fileLen = Len; + + if (TNC->FreeDataInfo->RXDir == NULL) + { + Debugprintf("FreeDATA RXDIRECTORY not set - file transfer ignored"); + return; + } + + sprintf(Filename, "%s\\%s", TNC->FreeDataInfo->RXDir, FILENAME); + + fp1 = fopen(Filename, "wb"); + + if (fp1) + { + fwrite(TEXT, 1, fileLen, fp1); + fclose(fp1); + textLen = sprintf(Text, "File %s received from %s \r", FILENAME, Call); + WritetoTrace(TNC, Text, textLen); + } + else + Debugprintf("FreeDATA - File %s create failed %s", Filename); + + return; + } + else if (ID[0] == 'm') + { + + } + + + + + + + + + + +// FreeDataProcessTNCMessage(TNC, Call, Msg, strlen(Msg)); +} + +void processJSONINFO(struct TNCINFO * TNC, char * Info, char * Call, double snr) +{ + char * LOC = ""; + char * ptr, * Context; + + // Info is an array. Normally only one element, but should check + + ptr = strtok_s(&Info[1], ",]", &Context); + + while (ptr && ptr[1]) + { + if (strstr(ptr, "BEACON;RECEIVING")) + { + char CQ[64]; + int Len; + + Len = sprintf(CQ, "Beacon received from %s SNR %3.1f\r", Call, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (Call) + UpdateMH(TNC, Call, '!', 'I'); + } + if (strstr(ptr, "PING;RECEIVING")) + { + // Add to MH + + if (Call) + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "CQ;RECEIVING")) + { + char CQ[64]; + int Len; + + Len = sprintf(CQ, "CQ received from %s SNR %3.1f\r", Call, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "PING;RECEIVEDACK")) + { + char Msg[128]; + int Len; + + Len = sprintf(Msg, "Ping Response from %s SNR %3.1f\r", Call, snr); + FreeDataProcessTNCMessage(TNC, Call, Msg, Len); + + // Add to MH + + UpdateMH(TNC, Call, '!', 'I'); + } + else if (strstr(ptr, "TRANSMITTING;FAILED")) + { + // Failed to send a message - if it was a connect request tell appl + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + PMSGWITHLEN buffptr; + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + ptr = strtok_s(NULL, ",]", &Context); + } +} + + + + + + +char * getJSONValue(char * Msg, char * Key) +{ + char * ptr, *ptr2, *value = 0; + int vallen, keylen = strlen(Key); + char c; + + // We Null Terminate the value, so we must look for keys in reverse order + + ptr = strstr(Msg, Key); + + if (ptr) + { + ptr += (keylen + 1); + + if (*(ptr) == '[') + { + // Array + + int Open = 0; + int Close = 0; + + ptr2 = ptr; + + while (c = *(ptr++)) + { + if (c == '[') + Open ++; + else if (c == ']') + Close ++; + + if (Open == Close) + { + vallen = ptr - ptr2; + value = ptr2; + value[vallen] = 0; + return value; + } + } + } + else if (*(ptr) == '\"') + { + // String + + ptr2 = ptr; + ptr = strchr(ptr + 1, '\"'); + if (ptr) + { + ptr++; + vallen = ptr - ptr2; + value = ptr2; + value[vallen] = 0; + } + } + } + return value; +} + + +char stopTNC[] = "{\"type\" : \"SET\", \"command\": \"STOPTNC\" , \"parameter\": \"---\" }\r"; + + +void StopTNC(struct TNCINFO * TNC) +{ + if (TNC->TCPDataSock) + closesocket(TNC->TCPDataSock); +} + + +void ProcessTNCJSON(struct TNCINFO * TNC, char * Msg, int Len) +{ + char * ptr; + + if (memcmp(Msg, "{\"ptt\":", 7) == 0) + { + if (strstr(Msg, "True")) + { +// TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + } + else + { + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + } + return; + } + + if (memcmp(Msg, "{\"command_response\"", 19) == 0) + { + Debugprintf("%d %s", TNC->Port, Msg); + return; + } + + if (memcmp(Msg, "{\"freedata\"", 10) == 0) + { + // {"freedata":"tnc-message","arq":"session","status":"connected","mycallsign":"G8BPQ-10","dxcallsign":"G8BPQ-0"} + // {"freedata":"tnc-message","arq":"session","status":"connected","heartbeat":"transmitting","mycallsign":"G8BPQ-10","dxcallsign":"G8BPQ-0"} + + char myCall[12] = ""; + char farCall[12] = ""; + char * ptr2; + + ptr = strstr(Msg, "\"tnc-message\",\"arq\":\"session\""); + + if (ptr) + { + ptr = strstr(ptr, "status"); + + if (ptr) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + ptr += 9; + + ptr2 = strstr(ptr, "mycallsign"); + + if (ptr2) + { + memcpy(myCall, &ptr2[13], 10); + strlop(myCall, '"'); + } + + ptr2 = strstr(ptr, "dxcallsign"); + + if (ptr2) + { + memcpy(farCall, &ptr2[13], 10); + strlop(farCall, '"'); + } + + if (memcmp(ptr, "disconnected", 10) == 0) + { + if (TNC->FreeDataInfo->arqstate != 1) + { + TNC->FreeDataInfo->arqstate = 1; + Debugprintf("%d arq_session_state %s", TNC->Port, "disconnected"); + } + + // if connected this is a new disconnect + + if (STREAM->Connected) + { + + hookL4SessionDeleted(TNC, STREAM); + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + } + else if (memcmp(ptr, "connecting", 10) == 0) + { + if (TNC->FreeDataInfo->arqstate != 2) + { + TNC->FreeDataInfo->arqstate = 2; + Debugprintf("%d arq_session_state %s", TNC->Port, "connecting"); + } + } + else if (memcmp(ptr, "connected", 9) == 0) + { + // if connection is idle this is an incoming connect + + if (TNC->FreeDataInfo->arqstate != 3) + { + TNC->FreeDataInfo->arqstate = 3; + Debugprintf("%d arq_session_state %s", TNC->Port, "connected"); + } + + if (STREAM->Connecting == FALSE && STREAM->Connected == FALSE) + { + FreeDataProcessNewConnect(TNC, farCall, myCall); + } + + // if connecting it is a connect ack + + else if (STREAM->Connecting) + { + FreeDataProcessConnectAck(TNC, farCall, Msg, Len); + } + } + + else if (memcmp(ptr, "close", 5) == 0) + { + if (TNC->FreeDataInfo->arqstate != 4) + { + TNC->FreeDataInfo->arqstate = 4; + Debugprintf("%d arq_session_state %s", TNC->Port, "Closed"); + } + } + else if (memcmp(ptr, "failed", 5) == 0) + { + PMSGWITHLEN buffptr; + + if (TNC->FreeDataInfo->arqstate != 5) + { + TNC->FreeDataInfo->arqstate = 5; + Debugprintf("%d arq_session_state %s", TNC->Port, "failed"); + } + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + } + + Debugprintf("%d %s", TNC->Port, Msg); + return; + } + +} + + if (memcmp(Msg, "{\"command\":\"tnc_state\"", 22) == 0) + { +/* +{"command":"tnc_state","ptt_state":"False","tnc_state":"IDLE","arq_state":"False","arq_session":"False", +"arq_session_state":"disconnected","audio_rms":"0","snr":"0","frequency":"None","speed_level":"1", +"mode":"None","bandwidth":"None","fft":"[0]","channel_busy":"False","scatter":[],"rx_buffer_length":"0", +"rx_msg_buffer_length":"0","arq_bytes_per_minute":"0","arq_bytes_per_minute_burst":"0","arq_compression_factor":"0", +"arq_transmission_percent":"0","total_bytes":"0","beacon_state":"False", +"stations":[],"mycallsign":"GM8BPQ-6","dxcallsign":"AA0AA","dxgrid":""} +*/ + char * LOC = 0; + char * Stations; + char * myCall = 0; + char * farCall = 0; + double snr; + int arqstate = 0; + int rx_buffer_length = 0; + int rx_msg_buffer_length = 0; + + Msg += 23; + + ptr = strstr(Msg, "tnc_state"); + + if (ptr) + { + if (ptr[12] == 'B' && TNC->Busy == FALSE) + { + TNC->Busy = TRUE; + strcpy(TNC->WEB_CHANSTATE, "Busy"); + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + else if (ptr[12] == 'I' && TNC->Busy) + { + TNC->Busy = FALSE; + strcpy(TNC->WEB_CHANSTATE, "Idle"); + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + } + + ptr = strstr(Msg, "rx_buffer_length"); + + if (ptr) + rx_buffer_length = atoi(&ptr[19]); + + ptr = strstr(Msg, "rx_msg_buffer_length"); + + if (ptr) + rx_msg_buffer_length = atoi(&ptr[23]); + + ptr = strstr(Msg, "snr"); + + if (ptr) + snr = atof(ptr + 6); + + Stations = getJSONValue(Msg, "\"stations\""); + + if (Stations) + { + ptr = Stations + strlen(Stations) + 1; + LOC = getJSONValue(ptr, "\"dxgrid\""); + farCall = getJSONValue(ptr, "\"dxcallsign\""); + myCall = getJSONValue(ptr, "\"mycallsign\""); + + if (myCall && farCall) + { + myCall++; + strlop(myCall, '"'); + farCall++; + strlop(farCall, '"'); + } + } + + // Look for changes in arq_session_state + + ptr = strstr(Msg, "\"arq_session_state\""); + + if (ptr) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + ptr += 21; + + if (memcmp(ptr, "disconnected", 10) == 0) + { + if (TNC->FreeDataInfo->arqstate != 1) + { + TNC->FreeDataInfo->arqstate = 1; + Debugprintf("%d arq_session_state %s", TNC->Port, "disconnected"); + } + + // if connected this is a new disconnect + + if (STREAM->Connected) + { + // Create a traffic record + + hookL4SessionDeleted(TNC, STREAM); + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + } + else if (memcmp(ptr, "connecting", 10) == 0) + { + if (TNC->FreeDataInfo->arqstate != 2) + { + TNC->FreeDataInfo->arqstate = 2; + Debugprintf("%d arq_session_state %s", TNC->Port, "connecting"); + } + } + else if (memcmp(ptr, "connected", 9) == 0) + { + // if connection is idle this is an incoming connect + + if (TNC->FreeDataInfo->arqstate != 3) + { + TNC->FreeDataInfo->arqstate = 3; + Debugprintf("%d arq_session_state %s", TNC->Port, "connected"); + } + + if (STREAM->Connecting == FALSE && STREAM->Connected == FALSE) + { + FreeDataProcessNewConnect(TNC, farCall, myCall); + } + + // if connecting it is a connect ack + + else if (STREAM->Connecting) + { + FreeDataProcessConnectAck(TNC, farCall, Msg, Len); + } + } + + else if (memcmp(ptr, "disconnecting", 12) == 0) + { + if (TNC->FreeDataInfo->arqstate != 4) + { + TNC->FreeDataInfo->arqstate = 4; + Debugprintf("%d arq_session_state %s", TNC->Port, "disconnecting"); + } + } + else if (memcmp(ptr, "failed", 5) == 0) + { + PMSGWITHLEN buffptr; + + if (TNC->FreeDataInfo->arqstate != 5) + { + TNC->FreeDataInfo->arqstate = 5; + Debugprintf("%d arq_session_state %s", TNC->Port, "failed"); + } + + if (STREAM->Connecting) + { + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + STREAM->Connecting = FALSE; + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(buffptr->Data, "*** Connect Failed\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + STREAM->FramesQueued++; + } + } + } + + if (rx_buffer_length || rx_msg_buffer_length) + FreeGetData(TNC); + + ptr = getJSONValue(Msg, "\"info\""); + + if (ptr == NULL) + return; + + if (strcmp(ptr, "[]") != 0) + { + + processJSONINFO(TNC, ptr, farCall, snr); + Debugprintf("%d %s %s", TNC->Port, ptr, farCall); + } + + return; + } + + if (memcmp(Msg, "{\"freedata\":\"tnc-message\"", 25) == 0) + { + char * mycall = strstr(Msg, "mycall"); + char * dxcall = strstr(Msg, "dxcall"); + char * dxgrid = strstr(Msg, "dxgrid"); + char * snrptr = strstr(Msg, "snr"); + float snr = 0; + char CQ[64]; + int Len; + + Msg += 26; + + if (mycall && dxcall && dxgrid) + { + mycall += 13; + strlop(mycall, '"'); + + dxcall += 13; + strlop(dxcall, '"'); + + dxgrid += 9; + strlop(dxgrid, '"'); + } + + + if (dxcall && strstr(dxcall, "-0")) + strlop(dxcall, '-'); + + if (snrptr) + snr = atof(&snrptr[6]); + + if (memcmp(Msg, "\"beacon\":\"received\"", 18) == 0) + { + if (mycall && dxcall && dxgrid) + { + Len = sprintf(CQ, "Beacon received from %s SNR %3.1f", dxcall, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (dxcall) + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + if (memcmp(Msg, "\"cq\":\"received\"", 14) == 0) + { + if (mycall && dxcall && dxgrid) + { + Len = sprintf(CQ, "CQ received from %s", dxcall); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (dxcall) + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + if (memcmp(Msg, "\"ping\":\"received\"", 16) == 0) + { + if (mycall && dxcall && dxgrid) + { + Len = sprintf(CQ, "PING received from %s SNR %3.1f", dxcall, snr); + WritetoTrace(TNC, CQ, Len); + + // Add to MH + + if (dxcall) + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + if (memcmp(Msg, "\"ping\":\"acknowledge\"", 16) == 0) + { + if (mycall && dxcall && dxgrid) + { + char Msg[128]; + + Len = sprintf(Msg, "Ping Response from %s SNR %3.1f\r", dxcall, snr); + FreeDataProcessTNCMessage(TNC, dxcall, Msg, Len); + + UpdateMH(TNC, dxcall, '!', 'I'); + + return; + } + } + + + + + + + Debugprintf("%d %s", TNC->Port, Msg); + return; + } + + if (memcmp(Msg, "{\"command\":\"rx_buffer\"", 22) == 0) + { + char * Next, * This; + + // Delete from TNC + + SendTNCCommand(TNC, "set", "del_rx_buffer"); Msg += 22; + + ptr = getJSONValue(Msg, "\"eof\""); + ptr = getJSONValue(Msg, "\"data-array\""); + + This = ptr; + + if (This[1] == '{') // Array of objects + { + This++; + do + { + Next = getObjectFromArray(This); + ProcessMessageObject(TNC, This); + This = Next; + + } while (Next && Next[0] == '{'); + } + + return; + } + + Debugprintf("%d %s", TNC->Port, Msg); + + +// {"COMMAND":"RX_BUFFER","DATA-ARRAY":[],"EOF":"EOF"} +/* {"COMMAND":"RX_BUFFER","DATA-ARRAY":[{"DXCALLSIGN":"GM8BPQ","DXGRID":"","TIMESTAMP":1642579504, +"RXDATA":[{"dt":"f","fn":"main.js","ft":"text\/javascript" +,"d":"data:text\/javascript;base64,Y29uc3Qge.....9KTsK","crc":"123123123"}]}],"EOF":"EOF"} + + + +{"arq":"received","uuid":"a1346319-6eb0-42aa-b5a0-c9493c8ccdca","timestamp":1645812393,"dxcallsign":"G8BPQ-2","dxgrid":"","data":"QyBHOEJQUS0yIEc4QlBRLTIgRzhCUFEtMiA="} +{"ptt":"True"} + + +*/ + if (memcmp(Msg, "{\"arq\":\"received\"", 17) == 0) + { + int NewLen; + char * ptr, *ptr2, *Type; + char * Call = 0; + char * myCall = 0; + + Msg += 17; + + ptr = getJSONValue(Msg, "\"data\""); + Type = ++ptr; + + // Decode Base64 + + // FreeData replaces / with \/ so need to undo + + ptr2 = strstr(Type, "\\/"); + + while (ptr2) + { + memmove(ptr2, ptr2 + 1, strlen(ptr2)); + ptr2 = strstr(ptr2, "\\/"); + } + + Len = strlen(Type) - 1; + + // Debugprintf("RX %d %s %d", TNC->Port, FN, Len); + + ptr = ptr2 = Type; + + while (Len > 0) + { + xdecodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - Type); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + Type[NewLen] = 0; + + myCall = getJSONValue(Msg, "\"mycallsign\""); + Call = getJSONValue(Msg, "\"dxcallsign\""); + + if (Call) + { + Call++; + strlop(Call, '"'); + } + + if (myCall) + { + myCall++; + strlop(myCall, '"'); + } + + + FreeDataProcessTNCMessage(TNC, Call, Type, NewLen); + + return; + } + + if (memcmp(Msg, "{\"COMMAND\":\"RX_MSG_BUFFER\"", 26) == 0) + { + char * Next, * This; + + Msg += 26; + ptr = getJSONValue(Msg, "\"EOF\""); + ptr = getJSONValue(Msg, "\"DATA-ARRAY\""); + + This = ptr; + + if (This[1] == '{') // Array of objects + { + This++; + do { + Next = getObjectFromArray(This); + ProcessMessageObject(TNC, This); + This = Next; + } while (Next && Next[0] == '{'); + + // Delete from TNC + + SendTNCCommand(TNC, "SET", "DEL_RX_MSG_BUFFER"); + } + + + return; + } +} + +int FreeDataConnect(struct TNCINFO * TNC, char * Call) +{ + char Connect[] = "{\"type\" : \"arq\", \"command\": \"connect\" , \"dxcallsign\": \"%s\", \"attempts\": \"%d\"}\n"; + char Msg[128]; + int Len; + + Len = sprintf(Msg, Connect, Call, TNC->MaxConReq); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + +int FreeDataDisconnect(struct TNCINFO * TNC) +{ + char Disconnect[] = "{\"type\" : \"arq\", \"command\": \"disconnect\"}\n"; + char Msg[128]; + int Len; + +// return FreeDataSendCommand(TNC, "D"); + + Len = sprintf(Msg, "%s", Disconnect); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + + +int FreeGetData(struct TNCINFO * TNC) +{ + char GetData[] = "{\"type\" : \"get\", \"command\": \"rx_buffer\"}\n"; + char Msg[128]; + int Len; + + Len = sprintf(Msg, "%s", GetData); + + return send(TNC->TCPDataSock, Msg, Len, 0); +} + +int FreeDataSendCommand(struct TNCINFO * TNC, char * Msg) +{ + // Commands are simulated as Messages to the remote BPQ. The TNC itself does not handle any commands + + // First Byte of MSG is a Type - Command or Data. MSG has a limited character set Use =xx for Now. + + // Current Types - C = Connect, D = Disconnect, I = info + + SendAsRaw(TNC, TNC->FreeDataInfo->farCall, TNC->FreeDataInfo->ourCall, Msg, strlen(Msg)); + return 0; +} + +void FreeDataProcessTNCMsg(struct TNCINFO * TNC) +{ + int DataInputLen, MsgLen; + char * ptr, * endptr; + int maxlen; + + // May get message split over packets or multiple messages per packet + + // A complete file transfer arrives as one message, so can bw very long + + + if (TNC->DataInputLen > MAXRXSIZE) // Shouldnt have packets longer than this + TNC->DataInputLen=0; + + maxlen = MAXRXSIZE - TNC->DataInputLen; + + if (maxlen >1400) + maxlen = 1400; + + DataInputLen = recv(TNC->TCPDataSock, &TNC->ARDOPDataBuffer[TNC->DataInputLen], maxlen, 0); + + if (DataInputLen == 0 || DataInputLen == SOCKET_ERROR) + { + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + + TNC->TNCCONNECTED = FALSE; + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + return; + } + + TNC->DataInputLen += DataInputLen; + + TNC->ARDOPDataBuffer[TNC->DataInputLen] = 0; // So we can use string functions + + // Message should be json. We know the format, so don't need a general parser, but need to know if complete. + // I think counting { and } and stopping if equal should work; + +// Debugprintf(TNC->ARDOPDataBuffer); + + //I think now messages end with LF + +loop: + + endptr = strchr(TNC->ARDOPDataBuffer, 10); + + if (endptr == 0) + return; + + *(endptr) = 0; + + if (TNC->ARDOPDataBuffer[0] != '{') + { + TNC->DataInputLen = 0; + return; + } + + ptr = &TNC->ARDOPDataBuffer[0]; + + MsgLen = endptr - ptr; + + ProcessTNCJSON(TNC, ptr, MsgLen); + + // MsgLen doesnt include lf + + MsgLen++; + + if (TNC->DataInputLen == MsgLen) + { + TNC->DataInputLen = 0; + return; + } + + // More in buffer + + ptr += MsgLen; + TNC->DataInputLen -= MsgLen; + + memmove(TNC->ARDOPDataBuffer, ptr, TNC->DataInputLen + 1); + + goto loop; + + // Message Incomplete - wait for rest; +} + + + +VOID FreeDataThread(void * portptr); + +int ConnecttoFreeData(int port) +{ + _beginthread(FreeDataThread, 0, (void *)(size_t)port); + + return 0; +} + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + + +VOID FreeDataThread(void * portptr) +{ + // Messages are JSON encapulated + // Now We run tnc directly so don't open daemon socket + // Looks for data on socket(s) + + int port = (int)(size_t)portptr; + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + char Params[512]; + + if (TNC->HostName == NULL) + return; + + buildParamString(TNC, Params); + + TNC->BusyFlags = 0; + + TNC->TNCCONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +// printf("Starting FreeDATA Thread\n"); + +// if on Windows and Localhost see if TNC is running + +#ifdef WIN32 + + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->Datadestaddr.sin_port); + + if (TNC->PID == 0) + goto TNCNotRunning; + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + HANDLE hProc; + char ExeName[256] = ""; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + goto TNCRunning; + } + +#endif + +TNCNotRunning: + + // Not running or can't check, restart if we have a path + + if (TNC->ProgramPath) + { + Consoleprintf("Trying to (re)start TNC %s", TNC->ProgramPath); + + if (RestartTNC(TNC)) + CountRestarts(TNC); + + Sleep(TNC->AutoStartDelay); + +#ifdef LINBPQ + Sleep(5000); +#endif + + } + +TNCRunning: + + if (TNC->Alerted == FALSE) + { + sprintf(TNC->WEB_COMMSSTATE, "Connecting to TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->Datadestaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->TNCCONNECTING = FALSE; + sprintf(Msg, "Resolve Failed for FreeData Host - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + return; // Resolve failed + } + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FreeData TNC socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->TNCCONNECTING = FALSE; + return; + } + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + // Connect TNC Port + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for FreeData TNC socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPDataSock); + TNC->TCPDataSock = 0; + TNC->TNCCONNECTING = FALSE; + + return; + } + + Sleep(1000); + + TNC->LastFreq = 0; + + TNC->TNCCONNECTING = FALSE; + TNC->TNCCONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + TNC->Alerted = FALSE; + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + sprintf(Msg, "Connected to FreeData TNC Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + while (TNC->TNCCONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + if (TNC->TNCCONNECTED) + FD_SET(TNC->TCPDataSock,&readfs); + + if (TNC->TNCCONNECTING || TNC->TNCCONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + + timeout.tv_sec = 300; + timeout.tv_usec = 0; + + ret = select((int)TNC->TCPDataSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("FreeData Select failed %d ", WSAGetLastError()); + goto Lost; + } + + // If nothing doing send get rx_buffer as link validation poll + + if (ret == 0) + { + char GetData[] = "{\"type\" : \"get\", \"command\": \"rx_buffer\"}\n"; + int Len; + + Len = send(TNC->TCPDataSock, GetData, strlen(GetData), 0); + + if (Len != strlen(GetData)) + goto closeThread; + } + else + { + // See what happened + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + FreeDataProcessTNCMsg(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { +Lost: + sprintf(Msg, "FreeData TNC Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->TNCCONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPDataSock); + TNC->TCPDataSock = 0; + } + + continue; + } + } + +closeThread: + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + sprintf(Msg, "FreeData Thread Terminated Port %d\r\n", TNC->Port); + TNC->lasttime = time(NULL); // Prevent immediate restart + + WritetoConsole(Msg); +} + +void ConnectTNCPort(struct TNCINFO * TNC) +{ + char Msg[255]; + int err; + int bcopt = TRUE; + + TNC->TNCCONNECTING = TRUE; + + TNC->TCPDataSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + sprintf(Msg, "Socket Failed for FreeData TNC socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->TNCCONNECTING = FALSE; + return; + } + + setsockopt(TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // Connected successful + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FreeData TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->TNCCONNECTING = FALSE; + TNC->TNCCONNECTED = TRUE; + TNC->Alerted = FALSE; + return; + } + + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + sprintf(Msg, "Connect Failed for FreeData TNC socket - error code = %d Port %d\n", + err, htons(TNC->Datadestaddr.sin_port)); + + WritetoConsole(Msg); + TNC->Alerted = TRUE; + TNC->TNCCONNECTING = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + TNC->TNCCONNECTING = FALSE; + return; +} + +void buildParamString(struct TNCINFO * TNC, char * line) +{ + // choices=[, "direct", "rigctl", "rigctld"], + + struct FreeDataINFO * FDI = TNC->FreeDataInfo; + int capindex = -1, playindex = -1; + int i; + + // Python adds sound mapper on front and counts Playback after Capture + + for (i = 0; i < CaptureCount; i++) + { + if (strstr(&CaptureNames[i][0], TNC->FreeDataInfo->Capture)) + { + capindex = i + 1; + break; + } + } + + for (i = 0; i < PlaybackCount; i++) + { + if (strstr(&PlaybackNames[i][0], TNC->FreeDataInfo->Playback)) + { + playindex = i + CaptureCount + 2; + break; + } + } + + sprintf(line, + "--mycall %s --ssid %s --mygrid %s --rx %d --tx %d --port %d --radiocontrol %s " + "--tuning_range_fmin %3.1f --tuning_range_fmax %3.1f --tx-audio-level %d", + + FDI->ourCall, FDI->SSIDList, LOC, capindex, playindex, TNC->TCPPort, FDI->hamlibHost ? "rigctld" : "disabled", + FDI->TuningRange * -1.0, FDI->TuningRange * 1.0, FDI->TXLevel); + + if (FDI->hamlibHost) + sprintf(line, "%s --rigctld_ip %s --rigctld_port %d", line, FDI->hamlibHost, FDI->hamlibPort); + + if (FDI->LimitBandWidth) + strcat(line, " --500hz"); + + if (FDI->Explorer) + strcat(line, " --explorer"); + + + // Add these to the end if needed + // --scatter + // --fft + // --fsk + // --qrv (respond to cq) + +} + +// We need a local restart tnc as we need to add params and start a python progrm on Linux + +BOOL KillOldTNC(char * Path); + +static BOOL RestartTNC(struct TNCINFO * TNC) +{ + if (TNC->ProgramPath == NULL || TNC->DontRestart) + return 0; + + if (_memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) + { + int n; + + // Try to start TNC on a remote host + + SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); + struct sockaddr_in destaddr; + + Debugprintf("trying to restart TNC %s", TNC->ProgramPath); + + if (sock == INVALID_SOCKET) + return 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_port = htons(8500); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + return 0; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + n = sendto(sock, TNC->ProgramPath, (int)strlen(TNC->ProgramPath), 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); + + Debugprintf("Restart TNC - sendto returned %d", n); + + Sleep(100); + closesocket(sock); + + return 1; // Cant tell if it worked, but assume ok + } + + // Not Remote + + // Add parameters to command string + +#ifndef WIN32 + { + char * arg_list[64]; + char rxVal[16]; + char txVal[16]; + char tunePlus[16]; + char tuneMinus[16]; + char portVal[16]; + char txLevelVal[16]; + char RigPort[16]; + int n = 0; + int i = 0; + pid_t child_pid; + + struct FreeDataINFO * FDI = TNC->FreeDataInfo; + + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + arg_list[n++] = "python3"; + arg_list[n++] = TNC->ProgramPath; + arg_list[n++] = "--mycall"; + arg_list[n++] = FDI->ourCall; + arg_list[n++] = "--ssid"; + + while(TNC->FreeDataInfo->SSIDS[i]) + arg_list[n++] = TNC->FreeDataInfo->SSIDS[i++]; + + arg_list[n++] = "--mygrid"; + arg_list[n++] = LOC; + arg_list[n++] = "--rx"; + sprintf(rxVal, "%s", TNC->FreeDataInfo->Capture); + arg_list[n++] = rxVal; + arg_list[n++] = "--tx"; + sprintf(txVal, "%s", TNC->FreeDataInfo->Playback); + arg_list[n++] = txVal; + arg_list[n++] = "--port"; + sprintf(portVal, "%d", TNC->TCPPort); + arg_list[n++] = portVal; + arg_list[n++] = "--radiocontrol"; + arg_list[n++] = FDI->hamlibHost ? "rigctld" : "disabled"; + arg_list[n++] = "--tuning_range_fmin"; + sprintf(tuneMinus, "%3.1f", FDI->TuningRange * -1.0); + arg_list[n++] = tuneMinus; + arg_list[n++] = "--tuning_range_fmax"; + sprintf(tunePlus, "%3.1f", FDI->TuningRange * 1.0); + arg_list[n++] = tunePlus; + arg_list[n++] = "--tx-audio-level"; + sprintf(txLevelVal, "%d", FDI->TXLevel); + arg_list[n++] = txLevelVal; + + if (FDI->hamlibHost) + { + arg_list[n++] = "--rigctld_ip"; + arg_list[n++] = FDI->hamlibHost; + arg_list[n++] = "--rigctld_port"; + sprintf(RigPort, "%d", FDI->hamlibPort); + arg_list[n++] = RigPort; + } + + if (FDI->LimitBandWidth) + arg_list[n++] = "--500hz"; + + if (FDI->Explorer) + arg_list[n++] = "--explorer"; + + arg_list[n++] = 0; + + n = 0; + + // Fork and Exec TNC + + printf("Trying to start %s\n", TNC->ProgramPath); + + /* Duplicate this process. */ + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("StartTNC fork() Failed\n"); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + /* The execvp function returns only if an error occurs. */ + + printf ("Failed to start TNC\n"); + exit(0); // Kill the new process + } + + TNC->PID = child_pid; + printf("Started TNC, Process ID = %d\n", TNC->PID); + + return TRUE; + } +#else + + { + int n = 0; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char workingDirectory[256]; + char commandLine[512]; + char Params[512]; + + int i = strlen(TNC->ProgramPath); + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + Debugprintf("RestartTNC Called for %s", TNC->ProgramPath); + + + strcpy(workingDirectory, TNC->ProgramPath); + + while (i--) + { + if (workingDirectory[i] == '\\' || workingDirectory[i] == '/') + { + workingDirectory[i] = 0; + break; + } + } + + buildParamString(TNC, Params); + + if (TNC->PID) + { + KillTNC(TNC); + Sleep(100); + } + + sprintf(commandLine, "\"%s\" %s", TNC->ProgramPath, Params); + + if (CreateProcess(NULL, commandLine, NULL, NULL, FALSE,0, NULL, workingDirectory, &SInfo, &PInfo)) + { + Debugprintf("Restart TNC OK"); + TNC->PID = PInfo.dwProcessId; + return TRUE; + } + else + { + Debugprintf("Restart TNC Failed %d ", GetLastError()); + return FALSE; + } + } +#endif + return 0; +} diff --git a/.svn/pristine/59/598d30ceb6662c51d5cc27b12a29e271c9436d2a.svn-base b/.svn/pristine/59/598d30ceb6662c51d5cc27b12a29e271c9436d2a.svn-base new file mode 100644 index 0000000..a7de9eb --- /dev/null +++ b/.svn/pristine/59/598d30ceb6662c51d5cc27b12a29e271c9436d2a.svn-base @@ -0,0 +1,9222 @@ +/* +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 +*/ + +// Module to implement APRS "New Paradigm" Digipeater and APRS-IS Gateway + +// First Version, November 2011 + +#pragma data_seg("_BPQDATA") +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include "cheaders.h" +#include "bpq32.h" +#include +#include "kernelresource.h" + +#include "tncinfo.h" + +#include "bpqaprs.h" + +#ifndef WIN32 + +#include +#include +#include + +int sfd; +struct sockaddr_un my_addr, peer_addr; +socklen_t peer_addr_size; + + +#endif + + +#define MAXAGE 3600 * 12 // 12 Hours +#define MAXCALLS 20 // Max Flood, Trace and Digi +#define GATETIMELIMIT 40 * 60 // Don't gate to RF if station not heard for this time (40 mins) + +static BOOL APIENTRY GETSENDNETFRAMEADDR(); +static VOID DoSecTimer(); +static VOID DoMinTimer(); +static int APRSProcessLine(char * buf); +static BOOL APRSReadConfigFile(); +VOID APRSISThread(void * Report); +VOID __cdecl Debugprintf(const char * format, ...); +VOID __cdecl Consoleprintf(const char * format, ...); +BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port); +VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); +int APRSDecodeFrame(char * msg, char * buffer, time_t Stamp, uint64_t Mask); // Unsemaphored DecodeFrame +APRSHEARDRECORD * UpdateHeard(UCHAR * Call, int Port); +BOOL CheckforDups(char * Call, char * Msg, int Len); +VOID ProcessQuery(char * Query); +VOID ProcessSpecificQuery(char * Query, int Port, char * Origin, char * DestPlusDigis); +VOID CheckandDigi(DIGIMESSAGE * Msg, int Port, int FirstUnused, int Digis, int Len); +VOID SendBeacon(int toPort, char * Msg, BOOL SendISStatus, BOOL SendSOGCOG); +Dll BOOL APIENTRY PutAPRSMessage(char * Frame, int Len); +VOID ProcessAPRSISMsg(char * APRSMsg); +static VOID SendtoDigiPorts(PDIGIMESSAGE Block, DWORD Len, UCHAR Port); +APRSHEARDRECORD * FindStationInMH(char * call); +BOOL OpenGPSPort(); +void PollGPSIn(); +int CountLocalStations(); +BOOL SendAPPLAPRSMessage(char * Frame); +VOID SendAPRSMessage(char * Message, int toPort); +static VOID TCPConnect(void * unuxed); +struct STATIONRECORD * DecodeAPRSISMsg(char * msg); +struct STATIONRECORD * ProcessRFFrame(char * buffer, int len, int * ourMessage); +VOID APRSSecTimer(); +double myDistance(double laa, double loa, BOOL KM); +struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFound); +int DecodeAPRSPayload(char * Payload, struct STATIONRECORD * Station); +BOOL KillOldTNC(char * Path); + +BOOL ToLOC(double Lat, double Lon , char * Locator); +BOOL InternalSendAPRSMessage(char * Text, char * Call); +void UndoTransparency(char * input); +char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...); +char * GetStandardPage(char * FN, int * Len); +VOID WriteMiniDump(); +BOOL ProcessConfig(); +int ProcessAISMessage(char * msg, int len); +int read_png(unsigned char *bytes); +VOID sendandcheck(SOCKET sock, const char * Buffer, int Len); +void SaveAPRSMessage(struct APRSMESSAGE * ptr); +void ClearSavedMessages(); +void GetSavedAPRSMessages(); +static VOID GPSDConnect(void * unused); +int CanPortDigi(int Port); +int FromLOC(char * Locator, double * pLat, double * pLon); + +extern int SemHeldByAPI; +extern int APRSMONDECODE(); +extern struct ConsoleInfo MonWindow; +extern char VersionString[]; + +BOOL SaveAPRSMsgs = 0; + +BOOL LogAPRSIS = FALSE; + +// All data should be initialised to force into shared segment + +static char ConfigClassName[]="CONFIG"; + +extern BPQVECSTRUC * APRSMONVECPTR; + +extern int MONDECODE(); +extern VOID * zalloc(int len); +extern BOOL StartMinimized; + +extern char TextVerstring[]; + +extern HWND hConsWnd; +extern HKEY REGTREE; + +extern char LOCATOR[80]; +extern char LOC[7]; + +static int SecTimer = 10; +static int MinTimer = 60; + +BOOL APRSApplConnected = FALSE; +BOOL APRSWeb = FALSE; + +void * APPL_Q = 0; // Queue of frames for APRS Appl +void * APPLTX_Q = 0; // Queue of frames from APRS Appl +uint64_t APRSPortMask = 0; + +char APRSCall[10] = ""; +char APRSDest[10] = "APBPQ1"; + +char WXCall[10]; + +UCHAR AXCall[7] = ""; + +char CallPadded[10] = " "; + +char GPSPort[80] = ""; +int GPSSpeed = 0; +char GPSRelay[80] = ""; + +BOOL GateLocal = FALSE; +double GateLocalDistance = 0.0; + +int MaxDigisforIS = 7; // Dont send to IS if more digis uued to reach us + +char WXFileName[MAX_PATH]; +char WXComment[80]; +BOOL SendWX = FALSE; +int WXInterval = 30; +int WXCounter = 29 * 60; + +char APRSCall[10]; +char LoppedAPRSCall[10]; + +BOOL WXPort[MaxBPQPortNo + 1]; // Ports to send WX to + +BOOL GPSOK = 0; + +char LAT[] = "0000.00N"; // in standard APRS Format +char LON[] = "00000.00W"; //in standard APRS Format + +char HostName[80]; // for BlueNMEA +int HostPort = 4352; + +char GPSDHost[80]; +int GPSDPort = 2947; + + +extern int ADSBPort; +extern char ADSBHost[]; + +BOOL BlueNMEAOK = FALSE; +int BlueNMEATimer = 0; + +BOOL GPSDOK = FALSE; +int GPSDTimer = 0; + + +BOOL GPSSetsLocator = 0; // Update Map Location from GPS + +double SOG, COG; // From GPS + +double Lat = 0.0; +double Lon = 0.0; + +BOOL PosnSet = FALSE; +/* +The null position should be include the \. symbol (unknown/indeterminate +position). For example, a Position Report for a station with unknown position +will contain the coordinates …0000.00N\00000.00W.… +*/ +char * FloodCalls = 0; // Calls to relay using N-n without tracing +char * TraceCalls = 0; // Calls to relay using N-n with tracing +char * DigiCalls = 0; // Calls for normal relaying + +UCHAR FloodAX[MAXCALLS][7] = {0}; +UCHAR TraceAX[MAXCALLS][7] = {0}; +UCHAR DigiAX[MAXCALLS][7] = {0}; + +int FloodLen[MAXCALLS]; +int TraceLen[MAXCALLS]; +int DigiLen[MAXCALLS]; + +int ISPort = 0; +char ISHost[256] = ""; +int ISPasscode = 0; +char NodeFilter[1000] = "m/50"; // Filter when the isn't an application +char ISFilter[1000] = "m/50"; // Current Filter +char APPLFilter[1000] = ""; // Filter when an Applcation is running + +extern BOOL IGateEnabled; + +char StatusMsg[256] = ""; // Must be in shared segment +int StatusMsgLen = 0; + +char * BeaconPath[65] = {0}; + +char CrossPortMap[65][65] = {0}; +char APRSBridgeMap[65][65] = {0}; + +UCHAR BeaconHeader[65][10][7] = {""}; // Dest, Source and up to 8 digis +int BeaconHddrLen[65] = {0}; // Actual Length used + +UCHAR GatedHeader[65][10][7] = {""}; // Dest, Source and up to 8 digis for messages gated from IS +int GatedHddrLen[65] = {0}; // Actual Length used + + +char CFGSYMBOL = 'a'; +char CFGSYMSET = 'B'; + +char SYMBOL = '='; // Unknown Locaton +char SYMSET = '/'; + +char * PHG = 0; // Optional PHG (Power-Height-Gain) string for beacon + +BOOL TraceDigi = FALSE; // Add Trace to packets relayed on Digi Calls +BOOL SATGate = FALSE; // Delay Gating to IS directly heard packets +BOOL RXOnly = FALSE; // Run as RX only IGATE, ie don't gate anything to RF + +BOOL DefaultLocalTime = FALSE; +BOOL DefaultDistKM = FALSE; + +int multiple = 0; // Allows multiple copies of LinBPQ/APRS on one machine + +extern BOOL needAIS; + +extern unsigned long long IconData[]; // Symbols as a png image. + +typedef struct _ISDELAY +{ + struct _ISDELAY * Next; + char * ISMSG; + time_t SendTIme; +} ISDELAY; + +ISDELAY * SatISQueue = NULL; + +int MaxTraceHops = 2; +int MaxFloodHops = 2; + +int BeaconInterval = 0; +int MobileBeaconInterval = 0; +time_t LastMobileBeacon = 0; +int BeaconCounter = 0; +int IStatusCounter = 3600; // Used to send ?ISTATUS? Responses +//int StatusCounter = 0; // Used to send Status Messages + +char RunProgram[128] = ""; // Program to start + +BOOL APRSISOpen = FALSE; +BOOL BeacontoIS = TRUE; + +int ISDelayTimer = 0; // Time before trying to reopen APRS-IS link + +char APRSDESTS[][7] = {"AIR*", "ALL*", "AP*", "BEACON", "CQ*", "GPS*", "DF*", "DGPS*", "DRILL*", + "DX*", "ID*", "JAVA*", "MAIL*", "MICE*", "QST*", "QTH*", "RTCM*", "SKY*", + "SPACE*", "SPC*", "SYM*", "TEL*", "TEST*", "TLM*", "WX*", "ZIP"}; + +UCHAR AXDESTS[30][7] = {""}; +int AXDESTLEN[30] = {0}; + +UCHAR axTCPIP[7]; +UCHAR axRFONLY[7]; +UCHAR axNOGATE[7]; + +int MessageCount = 0; + +struct PortInfo +{ + int Index; + int ComPort; + char PortType[2]; + BOOL NewVCOM; // Using User Mode Virtual COM Driver + int ReopenTimer; // Retry if open failed delay + int RTS; + int CTS; + int DCD; + int DTR; + int DSR; + char Params[20]; // Init Params (eg 9600,n,8) + char PortLabel[20]; + HANDLE hDevice; + BOOL Created; + BOOL PortEnabled; + int FLOWCTRL; + int gpsinptr; +#ifdef WIN32 + OVERLAPPED Overlapped; + OVERLAPPED OverlappedRead; +#endif + char GPSinMsg[160]; + int GPSTypeFlag; // GPS Source flags + BOOL RMCOnly; // Only send RMC msgs to this port +}; + + + +struct PortInfo InPorts[1] = {0}; + +// Heard Station info + +#define MAXHEARD 1000 + +int HEARDENTRIES = 0; +int MAXHEARDENTRIES = 0; +int MHLEN = sizeof(APRSHEARDRECORD); + +// Area is allocated as needed + +APRSHEARDRECORD MHTABLE[MAXHEARD] = {0}; + +APRSHEARDRECORD * MHDATA = &MHTABLE[0]; + +static SOCKET sock = 0; + +//Duplicate suppression Code + +#define MAXDUPS 100 // Number to keep +#define DUPSECONDS 28 // Time to Keep + +struct DUPINFO +{ + time_t DupTime; + int DupLen; + char DupUser[8]; // Call in ax.35 format + char DupText[100]; +}; + +struct DUPINFO DupInfo[MAXDUPS]; + +struct OBJECT +{ + struct OBJECT * Next; + UCHAR Path[10][7]; // Dest, Source and up to 8 digis + int PathLen; // Actual Length used + char Message[81]; + char PortMap[MaxBPQPortNo + 1]; + int Interval; + int Timer; +}; + +struct OBJECT * ObjectList; // List of objects to send; + +int ObjectCount = 0; + +#include + +#define M_PI 3.14159265358979323846 + +int RetryCount = 4; +int RetryTimer = 45; +int ExpireTime = 120; +int TrackExpireTime = 1440; +BOOL SuppressNullPosn = FALSE; +BOOL DefaultNoTracks = FALSE; + +int MaxStations = 1000; + +int SharedMemorySize = 0; + + +RECT Rect, MsgRect, StnRect; + +char Key[80]; + +// function prototypes + +VOID RefreshMessages(); + +// a few global variables + +char APRSDir[MAX_PATH] = "BPQAPRS"; +char DF[MAX_PATH]; + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +int StationCount = 0; + +UCHAR NextSeq = 1; + +// Stationrecords are stored in a shared memory segment. based at APRSStationMemory (normally 0x43000000) + +// A pointer to the first is placed at the start of this + +struct STATIONRECORD ** StationRecords = NULL; +struct STATIONRECORD * StationRecordPool = NULL; +struct APRSMESSAGE * MessageRecordPool = NULL; + +struct SharedMem * SMEM; + +UCHAR * Shared; +UCHAR * StnRecordBase; + +VOID SendObject(struct OBJECT * Object); +VOID MonitorAPRSIS(char * Msg, int MsgLen, BOOL TX); + +#ifndef WIN32 +#define WSAEWOULDBLOCK 11 +#endif + +HANDLE hMapFile; + +// Logging + +static int LogAge = 14; + +#ifdef WIN32 + +int DeleteAPRSLogFiles() +{ + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + char File[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + DWORD dwError=0; + LARGE_INTEGER ft; + time_t now = time(NULL); + int Age; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetLogDirectory()); + strcat(szDir, "/logs/APRS*.log"); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + return dwError; + + // Walk directory + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugString(ffd.cFileName); + } + else + { + ft.HighPart = ffd.ftCreationTime.dwHighDateTime; + ft.LowPart = ffd.ftCreationTime.dwLowDateTime; + + ft.QuadPart -= 116444736000000000; + ft.QuadPart /= 10000000; + + Age = (int)((now - ft.LowPart) / 86400); + + if (Age > LogAge) + { + sprintf(File, "%s/logs/%s%c", GetLogDirectory(), ffd.cFileName, 0); + Debugprintf("Deleting %s", File); + DeleteFile(File); + } + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + return dwError; +} + +#else + +#include + +int APRSFilter(const struct dirent * dir) +{ + return (memcmp(dir->d_name, "APRS", 4) == 0 && strstr(dir->d_name, ".log")); +} + +int DeleteAPRSLogFiles() +{ + struct dirent **namelist; + int n; + struct stat STAT; + time_t now = time(NULL); + int Age = 0, res; + char FN[256]; + + n = scandir("logs", &namelist, APRSFilter, alphasort); + + if (n < 0) + perror("scandir"); + else + { + while(n--) + { + sprintf(FN, "logs/%s", namelist[n]->d_name); + if (stat(FN, &STAT) == 0) + { + Age = (now - STAT.st_mtime) / 86400; + + if (Age > LogAge) + { + Debugprintf("Deleting %s\n", FN); + unlink(FN); + } + } + free(namelist[n]); + } + free(namelist); + } + return 0; +} +#endif + +int APRSWriteLog(char * msg) +{ + FILE *file; + UCHAR Value[MAX_PATH]; + time_t T; + struct tm * tm; + + if (LogAPRSIS == 0) + return 0; + + if (strchr(msg, '\n') == 0) + strcat(msg, "\r\n"); + + T = time(NULL); + tm = gmtime(&T); + + if (GetLogDirectory()[0] == 0) + { + strcpy(Value, "logs/APRS_"); + } + else + { + strcpy(Value, GetLogDirectory()); + strcat(Value, "/"); + strcat(Value, "logs/APRS_"); + } + + sprintf(Value, "%s%02d%02d%02d.log", Value, + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + + if ((file = fopen(Value, "ab")) == NULL) + return FALSE; + + fputs(msg, file); + fclose(file); + return 0; +} + + +int ISSend(SOCKET sock, char * Msg, int Len, int flags) +{ + int Loops = 0; + int Sent; + + MonitorAPRSIS(Msg, Len, TRUE); + + Sent = send(sock, Msg, Len, flags); + + while (Sent != Len && Loops++ < 300) // 10 secs max + { + if ((Sent == SOCKET_ERROR) && (WSAGetLastError() != WSAEWOULDBLOCK)) + return SOCKET_ERROR; + + if (Sent > 0) // something sent + { + Len -= Sent; + memmove(Msg, &Msg[Sent], Len); + } + + Sleep(30); + Sent = send(sock, Msg, Len, flags); + } + + return Sent; +} + +void * endofStations; + +Dll BOOL APIENTRY Init_APRS() +{ + int i; + char * DCall; + +#ifdef WIN32 + HKEY hKey=0; + int retCode, Vallen, Type; +#else + int fd; + char RX_SOCK_PATH[] = "BPQAPRSrxsock"; + char TX_SOCK_PATH[] = "BPQAPRStxsock"; + char SharedName[256]; + char * ptr1; +#endif + struct STATIONRECORD * Stn1, * Stn2; + struct APRSMESSAGE * Msg1, * Msg2; + + // Clear tables in case a restart + + StationRecords = NULL; + + StationCount = 0; + HEARDENTRIES = 0; + MAXHEARDENTRIES = 0; + MobileBeaconInterval = 0; + BeaconInterval = 0; + + DeleteAPRSLogFiles(); + + memset(MHTABLE, 0, sizeof(MHTABLE)); + + ConvToAX25(MYNODECALL, MYCALL); + + ConvToAX25("TCPIP", axTCPIP); + ConvToAX25("RFONLY", axRFONLY); + ConvToAX25("NOGATE", axNOGATE); + + memset(&FloodAX[0][0], 0, sizeof(FloodAX)); + memset(&TraceAX[0][0], 0, sizeof(TraceAX)); + memset(&DigiAX[0][0], 0, sizeof(DigiAX)); + + APRSPortMask = 0; + + memset(BeaconPath, sizeof(BeaconPath), 0); + + memset(&CrossPortMap[0][0], 0, sizeof(CrossPortMap)); + memset(&APRSBridgeMap[0][0], 0, sizeof(APRSBridgeMap)); + + for (i = 1; i <= MaxBPQPortNo; i++) + { + if (CanPortDigi(i)) + CrossPortMap[i][i] = TRUE; // Set Defaults - Same Port + CrossPortMap[i][0] = TRUE; // and APRS-IS + } + + PosnSet = 0; + ObjectList = NULL; + ObjectCount = 0; + + ISPort = ISHost[0] = ISPasscode = 0; + + if (APRSReadConfigFile() == 0) + return FALSE; + + if (APRSCall[0] == 0) + { + strcpy(APRSCall, MYNODECALL); + strlop(APRSCall, ' '); + strcpy(LoppedAPRSCall, APRSCall); + memcpy(CallPadded, APRSCall, (int)strlen(APRSCall)); // Call Padded to 9 chars for APRS Messaging + ConvToAX25(APRSCall, AXCall); + } + + if (WXCall[0] == 0) + strcpy(WXCall, APRSCall); + + // Caluclate size of Shared Segment + + SharedMemorySize = sizeof(struct STATIONRECORD) * (MaxStations + 4) + + sizeof(struct APRSMESSAGE) * (MAXMESSAGES + 4) + 32; // 32 for header + + +#ifndef WIN32 + + // Create a Shared Memory Object + + Shared = NULL; + + // Append last bit of current directory to shared name + + ptr1 = BPQDirectory; + + while (strchr(ptr1, '/')) + { + ptr1 = strchr(ptr1, '/'); + ptr1++; + } + + if (multiple) + sprintf(SharedName, "/BPQAPRSSharedMem%s", ptr1); + else + strcpy(SharedName, "/BPQAPRSSharedMem"); + + printf("Using Shared Memory %s\n", SharedName); + +#ifndef WIN32 + + fd = shm_open(SharedName, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + if (fd == -1) + { + perror("Create Shared Memory"); + printf("Create APRS Shared Memory Failed\n"); + } + else + { + if (ftruncate(fd, SharedMemorySize)) + { + perror("Extend Shared Memory"); + printf("Extend APRS Shared Memory Failed\n"); + } + else + { + // Map shared memory object + + Shared = mmap((void *)APRSSHAREDMEMORYBASE, + SharedMemorySize, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if (Shared == MAP_FAILED) + { + perror("Map Shared Memory"); + printf("Map APRS Shared Memory Failed\n"); + Shared = NULL; + } + + if (Shared != (void *)APRSSHAREDMEMORYBASE) + { + printf("Map APRS Shared Memory Allocated at %x\n", Shared); + Shared = NULL; + } + + } + } + +#endif + + printf("Map APRS Shared Memory Allocated at %p\n", Shared); + + if (Shared == NULL) + { + printf("APRS not using shared memory\n"); + Shared = malloc(SharedMemorySize); + printf("APRS Non-Shared Memory Allocated at %x\n", Shared); + } + +#else + +#ifndef LINBPQ + + retCode = RegOpenKeyEx (REGTREE, + "SOFTWARE\\G8BPQ\\BPQ32", + 0, + KEY_QUERY_VALUE, + &hKey); + + if (retCode == ERROR_SUCCESS) + { + Vallen = 4; + retCode = RegQueryValueEx(hKey, "IGateEnabled", 0, &Type, (UCHAR *)&IGateEnabled, &Vallen); + } + +#endif + + // Create Memory Mapping for Station List + + hMapFile = CreateFileMapping( + INVALID_HANDLE_VALUE, // use paging file + NULL, // default security + PAGE_READWRITE, // read/write access + 0, // maximum object size (high-order DWORD) + SharedMemorySize, // maximum object size (low-order DWORD) + "BPQAPRSStationsMappingObject");// name of mapping object + + if (hMapFile == NULL) + { + Consoleprintf("Could not create file mapping object (%d).\n", GetLastError()); + return 0; + } + + UnmapViewOfFile((void *)APRSSHAREDMEMORYBASE); + + + Shared = (LPTSTR) MapViewOfFileEx(hMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + SharedMemorySize, + (void *)APRSSHAREDMEMORYBASE); + + if (Shared == NULL) + { + Consoleprintf("Could not map view of file (%d).\n", GetLastError()); + CloseHandle(hMapFile); + return 0; + } + +#endif + + // First record has pointer to table + + memset(Shared, 0, SharedMemorySize); + + StnRecordBase = Shared + 32; + SMEM = (struct SharedMem *)Shared; + + SMEM->Version = 1; + SMEM->SharedMemLen = SharedMemorySize; + SMEM->NeedRefresh = TRUE; + SMEM->Arch = sizeof(void *); + SMEM->SubVersion = 1; + + Stn1 = (struct STATIONRECORD *)StnRecordBase; + + StationRecords = (struct STATIONRECORD **)Stn1; + + Stn1++; + + StationRecordPool = Stn1; + + for (i = 1; i < MaxStations; i++) // Already have first + { + Stn2 = Stn1; + Stn2++; + Stn1->Next = Stn2; + + Stn1 = Stn2; + } + + Debugprintf("End of Stations %p", Stn1); + endofStations = Stn1; + + Stn1 += 2; // Try to fix corruption of messages. + + // Build Message Record Pool + + Msg1 = (struct APRSMESSAGE *)Stn1; + + MessageRecordPool = Msg1; + + for (i = 1; i < MAXMESSAGES; i++) // Already have first + { + Msg2 = Msg1; + Msg2++; + Msg1->Next = Msg2; + + Msg1 = Msg2; + } + + if (PosnSet == 0) + { + SYMBOL = '.'; + SYMSET = '\\'; // Undefined Posn Symbol + } + else + { + // Convert posn to floating degrees + + char LatDeg[3], LonDeg[4]; + memcpy(LatDeg, LAT, 2); + LatDeg[2]=0; + Lat=atof(LatDeg) + (atof(LAT+2)/60); + + if (LAT[7] == 'S') Lat=-Lat; + + memcpy(LonDeg, LON, 3); + LonDeg[3]=0; + Lon=atof(LonDeg) + (atof(LON+3)/60); + + if (LON[8]== 'W') Lon=-Lon; + + SYMBOL = CFGSYMBOL; + SYMSET = CFGSYMSET; + } + + // First record has control info for APRS Mapping App + + Stn1 = (struct STATIONRECORD *)StnRecordBase; + memcpy(Stn1->Callsign, APRSCall, 10); + Stn1->Lat = Lat; + Stn1->Lon = Lon; + Stn1->LastPort = MaxStations; + +#ifndef WIN32 + + // Open unix socket for messaging app + + sfd = socket(AF_UNIX, SOCK_DGRAM, 0); + + if (sfd == -1) + { + perror("Socket"); + } + else + { + u_long param=1; + ioctl(sfd, FIONBIO, ¶m); // Set non-blocking + + memset(&my_addr, 0, sizeof(struct sockaddr_un)); + my_addr.sun_family = AF_UNIX; + strncpy(my_addr.sun_path, TX_SOCK_PATH, sizeof(my_addr.sun_path) - 1); + + memset(&peer_addr, 0, sizeof(struct sockaddr_un)); + peer_addr.sun_family = AF_UNIX; + strncpy(peer_addr.sun_path, RX_SOCK_PATH, sizeof(peer_addr.sun_path) - 1); + + unlink(TX_SOCK_PATH); + + if (bind(sfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_un)) == -1) + perror("bind"); + } +#endif + + // Convert Dest ADDRS to AX.25 + + for (i = 0; i < 26; i++) + { + DCall = &APRSDESTS[i][0]; + if (strchr(DCall, '*')) + AXDESTLEN[i] = (int)strlen(DCall) - 1; + else + AXDESTLEN[i] = 6; + + ConvToAX25(DCall, &AXDESTS[i][0]); + } + + // Process any Object Definitions + + // Setup Heard Data Area + + HEARDENTRIES = 0; + MAXHEARDENTRIES = MAXHEARD; + + APRSMONVECPTR->HOSTAPPLFLAGS = 0x80; // Request Monitoring + + if (ISPort && IGateEnabled) + { + _beginthread(APRSISThread, 0, (VOID *) TRUE); + } + + if (GPSPort[0]) + OpenGPSPort(); + + WritetoConsole("APRS Digi/Gateway Enabled\n"); + + APRSWeb = TRUE; + + read_png((unsigned char *)IconData); + + // Reload saved messages + + if (SaveAPRSMsgs) + GetSavedAPRSMessages(); + + // If a Run parameter was supplied, run the program + + if (RunProgram[0] == 0) + return TRUE; + + #ifndef WIN32 + { + char * arg_list[] = {NULL, NULL}; + pid_t child_pid; + + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + // Fork and Exec program + + printf("Trying to start %s\n", RunProgram); + + arg_list[0] = RunProgram; + + // Duplicate this process. + + child_pid = fork (); + + if (child_pid == -1) + { + printf ("APRS fork() Failed\n"); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + // The execvp function returns only if an error occurs. + + printf ("Failed to run %s\n", RunProgram); + exit(0); // Kill the new process + } + } +#else + { + int n = 0; + + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + while (KillOldTNC(RunProgram) && n++ < 100) + { + Sleep(100); + } + + if (!CreateProcess(RunProgram, NULL, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo)) + Debugprintf("Failed to Start %s Error %d ", RunProgram, GetLastError()); + } +#endif + + return TRUE; +} + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +BOOL APRSActive; + +VOID APRSClose() +{ + APRSActive = FALSE; + + if (sock) + { + shutdown(sock, SD_BOTH); + Sleep(50); + + closesocket(sock); + } +#ifdef WIN32 + if (InPorts[0].hDevice) + CloseHandle(InPorts[0].hDevice); +#endif +} + +time_t lastSecTimer = 0; + + +Dll VOID APIENTRY Poll_APRS() +{ + time_t Now = time(NULL); + + if (lastSecTimer != Now) + { + lastSecTimer = Now; + + DoSecTimer(); + + MinTimer--; + + if (MinTimer == 0) + { + MinTimer = 60; + DoMinTimer(); + } + } + + if (SMEM->ClearRX) + { + // Clear Received Messages request from GUI + + struct APRSMESSAGE * ptr = SMEM->Messages; + + // Move Message Queue to Free Queue + + if (ptr) + { + while (ptr->Next) // Find end of chain + { + ptr = ptr->Next; + } + + // ptr is end of chain - chain free pool to it + + ptr->Next = MessageRecordPool; + + MessageRecordPool = SMEM->Messages; + MessageCount = 0; + } + + SMEM->Messages = NULL; + SMEM->ClearRX = 0; + SMEM->NeedRefresh = TRUE; + + ClearSavedMessages(); + } + + if (SMEM->ClearTX) + { + // Clear Sent Messages )request from GUI + + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + + // Move Message Queue to Free Queue + + if (ptr) + { + while (ptr->Next) // Find end of chain + { + ptr = ptr->Next; + } + + // ptr is end of chain - chain free pool to it + + ptr->Next = MessageRecordPool; + + MessageRecordPool = SMEM->OutstandingMsgs; + MessageCount = 0; + } + + SMEM->OutstandingMsgs = NULL; + SMEM->ClearTX = 0; + SMEM->NeedRefresh = TRUE; + } +#ifdef LINBPQ +#ifndef WIN32 + { + char Msg[256]; + int numBytes; + + // Look for messages from App + + numBytes = recvfrom(sfd, Msg, 256, 0, NULL, NULL); + + if (numBytes > 0) + { + InternalSendAPRSMessage(&Msg[10], &Msg[0]); + } + } +#endif +#endif + + if (GPSPort[0]) + PollGPSIn(); + + if (APPLTX_Q) + { + PMSGWITHLEN buffptr = Q_REM(&APPLTX_Q); + + InternalSendAPRSMessage(&buffptr->Data[10], &buffptr->Data[0]); + ReleaseBuffer(buffptr); + } + + while (APRSMONVECPTR->HOSTTRACEQ) + { + time_t stamp; + int len; + BOOL MonitorNODES = FALSE; + PMESSAGE monbuff; + UCHAR * monchars; + MESSAGE * Orig; + int Digis = 0; + MESSAGE * AdjBuff; // Adjusted for digis + BOOL FirstUnused = FALSE; + int DigisUsed = 0; // Digis used to reach us + DIGIMESSAGE Msg = {0}; + int Port; + unsigned char buffer[1024]; + char ISMsg[500]; + char * ptr1; + char * Payload; + char * ptr3; + char * ptr4; + BOOL ThirdParty = FALSE; + BOOL NoGate = FALSE; + APRSHEARDRECORD * MH; + char MsgCopy[500]; + int toPort; + struct STATIONRECORD * Station; + int ourMessage = 0; + +#ifdef WIN32 + struct _EXCEPTION_POINTERS exinfo; + char EXCEPTMSG[80] = ""; +#endif + monbuff = Q_REM((VOID **)&APRSMONVECPTR->HOSTTRACEQ); + + monchars = (UCHAR *)monbuff; + AdjBuff = Orig = (MESSAGE *)monchars; // Adjusted for digis + + Port = Orig->PORT; + + if (Port & 0x80) // TX + { + ReleaseBuffer(monbuff); + continue; + } + + if ((APRSPortMask & ((uint64_t)1 << (Port - 1))) == 0)// Port in use for APRS? + { + ReleaseBuffer(monbuff); + continue; + } + + stamp = monbuff->Timestamp; + + if ((UCHAR)monchars[4] & 0x80) // TX + { + ReleaseBuffer(monbuff); + continue; + } + + // See if digipeaters present. + + while ((AdjBuff->ORIGIN[6] & 1) == 0 && Digis < 9) + { + UCHAR * temp = (UCHAR *)AdjBuff; + temp += 7; + AdjBuff = (MESSAGE *)temp; + + // If we have already digi'ed it or if we sent it, + // ignore (Dup Check my fail on slow links) + + if (AdjBuff->ORIGIN[6] & 0x80) + { + // Used Digi + + if (memcmp(AdjBuff->ORIGIN, AXCall, 7) == 0) + { + ReleaseBuffer(monbuff); + return; + } + DigisUsed++; + } + + if (memcmp(AdjBuff->ORIGIN, axTCPIP, 6) == 0) + ThirdParty = TRUE; + + Digis ++; + + if (FirstUnused == FALSE && (AdjBuff->ORIGIN[6] & 0x80) == 0) + { + // Unused Digi - see if we should digi it + + FirstUnused = Digis; + // CheckDigi(buff, AdjBuff->ORIGIN); + } + } + + if (Digis > 8) + { + ReleaseBuffer(monbuff); + continue; // Corrupt + } + + if (Digis) + { + if (memcmp(AdjBuff->ORIGIN, axNOGATE, 6) == 0 + || memcmp(AdjBuff->ORIGIN, axRFONLY, 6) == 0 + || DigisUsed > MaxDigisforIS) + + // Too many digis or Last digis is NOGATE or RFONLY - dont send to IS + + NoGate = TRUE; + } + if (AdjBuff->CTL != 3 || AdjBuff->PID != 0xf0) // Only UI + { + ReleaseBuffer(monbuff); + continue; + } + + // Bridge if requested + + for (toPort = 1; toPort <= MaxBPQPortNo; toPort++) + { + if (APRSBridgeMap[Port][toPort]) + { + MESSAGE * Buffer = GetBuff(); + struct PORTCONTROL * PORT; + + if (Buffer) + { + memcpy(Buffer, Orig, Orig->LENGTH); + Buffer->PORT = toPort; + PORT = GetPortTableEntryFromPortNum(toPort); + + if (PORT) + { + if (PORT->SmartIDInterval && PORT->SmartIDNeeded == 0) + { + // Using Smart ID, but none scheduled + + PORT->SmartIDNeeded = time(NULL) + PORT->SmartIDInterval; + } + PUT_ON_PORT_Q(PORT, Buffer); + } + else + ReleaseBuffer(Buffer); + } + } + } + + // Used to check for dups here but according to "Notes to iGate developers" IS should be sent dups, and dup + // check only applied to digi'ing + +// if (SATGate == 0) +// { +// if (CheckforDups(Orig->ORIGIN, AdjBuff->L2DATA, Orig->LENGTH - Digis * 7 - (19 + sizeof(void *))) +// { +// ReleaseBuffer(monbuff); +// continue; +// } +// } + // Decode Frame to TNC2 Monitor Format + + len = APRSDecodeFrame((char *)monchars, buffer, stamp, APRSPortMask); + + if (len == 0) + { + // Couldn't Decode + + ReleaseBuffer(monbuff); + Debugprintf("APRS discarded frame - decode failed\n"); + continue; + } + + buffer[len] = 0; + + memcpy(MsgCopy, buffer, len); + MsgCopy[len] = 0; + + // Do internal Decode + +#ifdef WIN32 + + strcpy(EXCEPTMSG, "ProcessRFFrame"); + + __try + { + + Station = ProcessRFFrame(MsgCopy, len, &ourMessage); + } + #include "StdExcept.c" + + } +#else + Station = ProcessRFFrame(MsgCopy, len, &ourMessage); +#endif + + if (Station == NULL) + { + ReleaseBuffer(monbuff); + continue; + } + + memcpy(MsgCopy, buffer, len); // Process RF Frame may have changed it + MsgCopy[len] = 0; + + buffer[len++] = 10; + buffer[len] = 0; + ptr1 = &buffer[10]; // Skip Timestamp + Payload = strchr(ptr1, ':') + 2; // Start of Payload + ptr3 = strchr(ptr1, ' '); // End of addresses + *ptr3 = 0; + + // We should send path to IS unchanged, so create IS + // message before chopping path. We won't decide if + // we will actually send it to IS till later + + len = sprintf(ISMsg, "%s,qAR,%s:%s", ptr1, APRSCall, Payload); + + + // if digis, remove any unactioned ones + + if (Digis) + { + ptr4 = strchr(ptr1, '*'); // Last Used Digi + + if (ptr4) + { + // We need header up to ptr4 + + *(ptr4) = 0; + } + else + { + // No digis actioned - remove them all + + ptr4 = strchr(ptr1, ','); // End of Dest + if (ptr4) + *ptr4 = 0; + } + } + + ptr4 = strchr(ptr1, '>'); // End of Source + *ptr4++ = 0; + + MH = UpdateHeard(ptr1, Port); + + MH->Station = Station; + + if (ThirdParty) + { +// Debugprintf("Setting Igate Flag - %s", MsgCopy); + MH->IGate = TRUE; // if we've seen msgs to TCPIP, it must be an Igate + } + + if (NoGate || RXOnly) + goto NoIS; + + // I think all PID F0 UI frames go to APRS-IS, + // Except General Queries, Frames Gated from IS to RF, and Messages Addressed to us + + // or should we process Query frames locally ?? + + if (Payload[0] == '}') + goto NoIS; + + if (Payload[0] == '?') + { + // General Query + + ProcessQuery(&Payload[1]); + + // ?? Should we pass addressed Queries to IS ?? + + goto NoIS; + } + + if (Payload[0] == ':' && memcmp(&Payload[1], CallPadded, 9) == 0) + { + // Message for us + + if (Payload[11] == '?') // Only queries - the node doesnt do messaging + ProcessSpecificQuery(&Payload[12], Port, ptr1, ptr4); + + goto NoIS; + } + + if (APRSISOpen && CrossPortMap[Port][0]) // No point if not open + { + // was done above len = sprintf(ISMsg, "%s>%s,qAR,%s:%s", ptr1, ptr4, APRSCall, Payload); + + if (BeacontoIS == 0) + { + // Don't send anything we've received as an echo + + char SaveCall[7]; + memcpy(SaveCall, &monbuff->ORIGIN, 7); + SaveCall[6] &= 0x7e; // Mask End of address bit + + if (memcmp(SaveCall, AXCall, 7) == 0) // We sent it + { + // Should we check for being received via digi? - not for now + + goto NoIS; + } + } + + if (SATGate && (DigisUsed == 0)) + { + // If in Satgate mode delay directly heard to IGate + + ISDELAY * SatISEntry = malloc(sizeof(ISDELAY)); + SatISEntry->Next = NULL; + SatISEntry->ISMSG = _strdup(ISMsg); + SatISEntry->SendTIme = time(NULL) + 10; // Delay 10 seconds + + if (SatISQueue) + SatISEntry->Next = SatISQueue; // Chain + + SatISQueue = SatISEntry; + goto NoIS; + } + + ISSend(sock, ISMsg, len, 0); + + ptr1 = strchr(ISMsg, 13); + if (ptr1) *ptr1 = 0; +// Debugprintf(">%s", ISMsg); + } + + NoIS: + + // We skipped DUP check for SATGate Mode, so apply it here + + // Now we don't dup check earlier so always check here + +// if (SATGate) +// { + if (CheckforDups(Orig->ORIGIN, AdjBuff->L2DATA, Orig->LENGTH - Digis * 7 - (19 + sizeof(void *)))) + { + ReleaseBuffer(monbuff); + continue; + } +// } + + // See if it is an APRS frame + + // If MIC-E, we need to process, whatever the destination + + // Now process any dest + +/* + DEST = &Orig->DEST[0]; + + for (i = 0; i < 26; i++) + { + if (memcmp(DEST, &AXDESTS[i][0], AXDESTLEN[i]) == 0) + goto OK; + } + + switch(AdjBuff->L2DATA[0]) + { + case '`': + case 0x27: // ' + case 0x1c: + case 0x1d: // MIC-E + + break; + // default: + + // Not to an APRS Destination + +// ReleaseBuffer(monbuff); +// continue; + } + +OK: +*/ + + // If there are unused digis, we may need to digi it. + + if (ourMessage) + { + // A message addressed to us, so no point in digi'ing it + + ReleaseBuffer(monbuff); + continue; + } + + if (Digis == 0 || FirstUnused == 0) + { + // No Digis, so finished + + ReleaseBuffer(monbuff); + continue; + } + + if (memcmp(monbuff->ORIGIN, AXCall, 7) == 0) // We sent it + { + ReleaseBuffer(monbuff); + continue; + } + + // Copy frame to a DIGIMessage Struct + + memcpy(&Msg, monbuff, MSGHDDRLEN + 14 + (7 * Digis)); // Header, Dest, Source, Addresses and Digis + + len = Msg.LENGTH - (MSGHDDRLEN + 14) - (7 * Digis); // Payload Length (including CTL and PID + + memcpy(&Msg.CTL, &AdjBuff->CTL, len); + + // Pass to Digi Code + + CheckandDigi(&Msg, Port, FirstUnused, Digis, len); // Digi if necessary + + ReleaseBuffer(monbuff); + } + + return; +} + +VOID CheckandDigi(DIGIMESSAGE * Msg, int Port, int FirstUnused, int Digis, int Len) +{ + UCHAR * Digi = &Msg->DIGIS[--FirstUnused][0]; + UCHAR * Call; + int Index = 0; + int SSID; + + // Check ordinary digi first + + Call = &DigiAX[0][0]; + SSID = Digi[6] & 0x1e; + + while (*Call) + { + if ((memcmp(Digi, Call, 6) == 0) && ((Call[6] & 0x1e) == SSID)) + { + // Trace Call if enabled + + if (TraceDigi) + memcpy(Digi, AXCall, 7); + + // mark as used; + + Digi[6] |= 0x80; // Used bit + + SendtoDigiPorts(Msg, Len, Port); + return; + } + Call += 7; + Index++; + } + + Call = &TraceAX[0][0]; + Index = 0; + + while (*Call) + { + if (memcmp(Digi, Call, TraceLen[Index]) == 0) + { + // if possible move calls along + // insert our call, set used + // decrement ssid, and if zero, mark as used; + + SSID = (Digi[6] & 0x1E) >> 1; + + if (SSID == 0) + return; // Shouldn't have SSID 0 for Rrace/Flood + + if (SSID > MaxTraceHops) + SSID = MaxTraceHops; // Enforce our limit + + SSID--; + + if (SSID ==0) // Finihed with it ? + Digi[6] = (SSID << 1) | 0xe0; // Used and Fixed bits + else + Digi[6] = (SSID << 1) | 0x60; // Fixed bits + + if (Digis < 8) + { + memmove(Digi + 7, Digi, (Digis - FirstUnused) * 7); + } + + memcpy(Digi, AXCall, 7); + Digi[6] |= 0x80; + + SendtoDigiPorts(Msg, Len, Port); + + return; + } + Call += 7; + Index++; + } + + Index = 0; + Call = &FloodAX[0][0]; + + while (*Call) + { + if (memcmp(Digi, Call, FloodLen[Index]) == 0) + { + // decrement ssid, and if zero, mark as used; + + SSID = (Digi[6] & 0x1E) >> 1; + + if (SSID == 0) + return; // Shouldn't have SSID 0 for Trace/Flood + + if (SSID > MaxFloodHops) + SSID = MaxFloodHops; // Enforce our limit + + SSID--; + + if (SSID ==0) // Finihed with it ? + Digi[6] = (SSID << 1) | 0xe0; // Used and Fixed bits + else + Digi[6] = (SSID << 1) | 0x60; // Fixed bits + + SendtoDigiPorts(Msg, Len, Port); + + return; + } + Call += 7; + Index++; + } +} + + + +static VOID SendtoDigiPorts(PDIGIMESSAGE Block, DWORD Len, UCHAR Port) +{ + // Can't use API SENDRAW, as that tries to get the semaphore, which we already have + // Len is the Payload Length (from CTL onwards) + // The message can contain DIGIS - The payload must be copied forwards if there are less than 8 + + // We send to all ports enabled in CrossPortMap + + UCHAR * EndofDigis = &Block->CTL; + int i = 0; + int toPort; + + while (Block->DIGIS[i][0] && i < 8) + { + i++; + } + + EndofDigis = &Block->DIGIS[i][0]; + *(EndofDigis -1) |= 1; // Set End of Address Bit + + if (i != 8) + memmove(EndofDigis, &Block->CTL, Len); + + Len = Len + (i * 7) + 14; // Include Source, Dest and Digis + +// Block->DEST[6] &= 0x7e; // Clear End of Call +// Block->ORIGIN[6] |= 1; // Set End of Call + +// Block->CTL = 3; //UI + + for (toPort = 1; toPort <= MaxBPQPortNo; toPort++) + { + if (CrossPortMap[Port][toPort]) + Send_AX((PMESSAGE)Block, Len, toPort); + } + return; + +} + +VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port) +{ + // Can't use API SENDRAW, as that tries to get the semaphore, which we already have + + // Len is the Payload Length (CTL, PID, Data) + + // The message can contain DIGIS - The payload must be copied forwards if there are less than 8 + + UCHAR * EndofDigis = &Block->CTL; + + int i = 0; + + while (Block->DIGIS[i][0] && i < 8) + { + i++; + } + + EndofDigis = &Block->DIGIS[i][0]; + *(EndofDigis -1) |= 1; // Set End of Address Bit + + if (i != 8) + memmove(EndofDigis, &Block->CTL, Len); // Include PID + + Len = Len + (i * 7) + 14; // Include Source, Dest and Digis + + Send_AX((PMESSAGE)Block, Len, Port); + + return; + +} + +static BOOL APRSReadConfigFile() +{ + char * Config; + char * ptr1, * ptr2; + + char buf[256],errbuf[256]; + + Config = PortConfig[APRSConfigSlot]; // Config from bpq32.cfg + + sprintf(StatusMsg, "BPQ32 Igate V %s", VersionString); // Set Default Status Message + + if (Config) + { + // Using config from bpq32.cfg + + ptr1 = Config; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!APRSProcessLine(buf)) + { + WritetoConsole("APRS Bad config record "); + strcat(errbuf, "\r\n"); + WritetoConsole(errbuf); + } + } + return TRUE; + } + return FALSE; +} + +BOOL ConvertCalls(char * DigiCalls, UCHAR * AX, int * Lens) +{ + int Index = 0; + char * ptr; + char * Context; + UCHAR Work[MAXCALLS][7] = {0}; + int Len[MAXCALLS] = {0}; + + ptr = strtok_s(DigiCalls, ", ", &Context); + + while(ptr) + { + if (Index == MAXCALLS) return FALSE; + + ConvToAX25(ptr, &Work[Index][0]); + Len[Index++] = (int)strlen(ptr); + ptr = strtok_s(NULL, ", ", &Context); + } + + memcpy(AX, Work, sizeof(Work)); + memcpy(Lens, Len, sizeof(Len)); + return TRUE; +} + + + +static int APRSProcessLine(char * buf) +{ + char * ptr, * p_value; + + ptr = strtok(buf, "= \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + +// OBJECT PATH=APRS,WIDE1-1 PORT=1,IS INTERVAL=30 TEXT=;444.80TRF*111111z4807.60N/09610.63Wr%156 R15m + + if (_stricmp(ptr, "OBJECT") == 0) + { + char * p_Path, * p_Port, * p_Text; + int Interval; + struct OBJECT * Object; + int Digi = 2; + char * Context; + int SendTo; + + p_value = strtok(NULL, "="); + if (p_value == NULL) return FALSE; + if (_stricmp(p_value, "PATH")) + return FALSE; + + p_Path = strtok(NULL, "\t\n\r "); + if (p_Path == NULL) return FALSE; + + p_value = strtok(NULL, "="); + if (p_value == NULL) return FALSE; + if (_stricmp(p_value, "PORT")) + return FALSE; + + p_Port = strtok(NULL, "\t\n\r "); + if (p_Port == NULL) return FALSE; + + p_value = strtok(NULL, "="); + if (p_value == NULL) return FALSE; + if (_stricmp(p_value, "INTERVAL")) + return FALSE; + + p_value = strtok(NULL, " \t"); + if (p_value == NULL) return FALSE; + + Interval = atoi(p_value); + + if (Interval == 0) + return FALSE; + + p_value = strtok(NULL, "="); + if (p_value == NULL) return FALSE; + if (_stricmp(p_value, "TEXT")) + return FALSE; + + p_Text = strtok(NULL, "\n\r"); + if (p_Text == NULL) return FALSE; + + Object = zalloc(sizeof(struct OBJECT)); + + if (Object == NULL) + return FALSE; + + Object->Next = ObjectList; + ObjectList = Object; + + if (Interval < 10) + Interval = 10; + + Object->Interval = Interval; + Object->Timer = (ObjectCount++) * 10 + 30; // Spread them out; + + // Convert Path to AX.25 + + ConvToAX25(APRSCall, &Object->Path[1][0]); + + ptr = strtok_s(p_Path, ",\t\n\r", &Context); + + if (_stricmp(ptr, "APRS") == 0) // First is Dest + ConvToAX25(APRSDest, &Object->Path[0][0]); + else if (_stricmp(ptr, "APRS-0") == 0) + ConvToAX25("APRS", &Object->Path[0][0]); + else + ConvToAX25(ptr, &Object->Path[0][0]); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + ConvToAX25(ptr, &Object->Path[Digi++][0]); + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + Object->PathLen = Digi * 7; + + // Process Port List + + ptr = strtok_s(p_Port, ",", &Context); + + while (ptr) + { + SendTo = atoi(ptr); // this gives zero for IS + + if (SendTo > MaxBPQPortNo) + return FALSE; + + Object->PortMap[SendTo] = TRUE; + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + if (strlen(p_Text) > 80) + p_Text[80] = 0; + + strcpy(Object->Message, p_Text); + return TRUE; + } + + if (_stricmp(ptr, "STATUSMSG") == 0) + { + p_value = strtok(NULL, ";\t\n\r"); + memcpy(StatusMsg, p_value, 128); // Just in case too long + StatusMsgLen = (int)strlen(p_value); + return TRUE; + } + + if (_stricmp(ptr, "WXFileName") == 0) + { + p_value = strtok(NULL, ";\t\n\r"); + strcpy(WXFileName, p_value); + SendWX = TRUE; + return TRUE; + } + if (_stricmp(ptr, "WXComment") == 0) + { + p_value = strtok(NULL, ";\t\n\r"); + + if (p_value == NULL) + return TRUE; + + if (strlen(p_value) > 79) + p_value[80] = 0; + + strcpy(WXComment, p_value); + return TRUE; + } + + + if (_stricmp(ptr, "ISFILTER") == 0) + { + p_value = strtok(NULL, ";\t\n\r"); + strcpy(ISFilter, p_value); + strcpy(NodeFilter, ISFilter); + return TRUE; + } + + if (_stricmp(ptr, "ReplaceDigiCalls") == 0) + { + TraceDigi = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "Multiple") == 0) + { + multiple = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "SATGate") == 0) + { + SATGate = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "RXOnly") == 0) + { + RXOnly = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "DISTKM") == 0) + { + DefaultDistKM = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "LOCALTIME") == 0) + { + DefaultLocalTime = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "LOGAPRSIS") == 0) + { + LogAPRSIS = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "SaveAPRSMsgs") == 0) + { + SaveAPRSMsgs = TRUE; + return TRUE; + } + + p_value = strtok(NULL, " \t\n\r"); + + if (p_value == NULL) + return FALSE; + + if (_stricmp(ptr, "APRSCALL") == 0) + { + strcpy(APRSCall, p_value); + strcpy(LoppedAPRSCall, p_value); + memcpy(CallPadded, APRSCall, (int)strlen(APRSCall)); // Call Padded to 9 chars for APRS Messaging + + // Convert to ax.25 + + return ConvToAX25(APRSCall, AXCall); + } + + if (_stricmp(ptr, "WXCALL") == 0) + { + strcpy(WXCall, p_value); + return TRUE; + } + + if (_stricmp(ptr, "APRSPATH") == 0) + { + int Digi = 2; + int Port; + char * Context; + + p_value = strtok_s(p_value, "=\t\n\r", &Context); + + Port = atoi(p_value); + + if (GetPortTableEntryFromPortNum(Port) == NULL) + return FALSE; + + APRSPortMask |= (uint64_t)1 << (Port - 1); + + if (Context == NULL || Context[0] == 0) + return TRUE; // No dest - a receive-only port + + BeaconPath[Port] = _strdup(_strupr(Context)); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + if (ptr == NULL) + return FALSE; + + ConvToAX25(APRSCall, &BeaconHeader[Port][1][0]); + + if (_stricmp(ptr, "APRS") == 0) // First is Dest + ConvToAX25(APRSDest, &BeaconHeader[Port][0][0]); + else if (_stricmp(ptr, "APRS-0") == 0) + ConvToAX25("APRS", &BeaconHeader[Port][0][0]); + else + ConvToAX25(ptr, &BeaconHeader[Port][0][0]); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + ConvToAX25(ptr, &BeaconHeader[Port][Digi++][0]); + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + BeaconHddrLen[Port] = Digi * 7; + + return TRUE; + } + + if (_stricmp(ptr, "GATEDPATH") == 0) + { + int Digi = 2; + int Port; + char * Context; + + p_value = strtok_s(p_value, "=\t\n\r", &Context); + + Port = atoi(p_value); + + if (GetPortTableEntryFromPortNum(Port) == NULL) + return FALSE; + +// APRSPortMask |= 1 << (Port - 1); + + if (Context == NULL || Context[0] == 0) + return TRUE; // No dest - a receive-only port + + BeaconPath[Port] = _strdup(_strupr(Context)); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + if (ptr == NULL) + return FALSE; + + ConvToAX25(APRSCall, &GatedHeader[Port][1][0]); + + if (_stricmp(ptr, "APRS") == 0) // First is Dest + ConvToAX25(APRSDest, &GatedHeader[Port][0][0]); + else if (_stricmp(ptr, "APRS-0") == 0) + ConvToAX25("APRS", &GatedHeader[Port][0][0]); + else + ConvToAX25(ptr, &GatedHeader[Port][0][0]); + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + ConvToAX25(ptr, &GatedHeader[Port][Digi++][0]); + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + GatedHddrLen[Port] = Digi * 7; + + return TRUE; + } + + + if (_stricmp(ptr, "DIGIMAP") == 0) + { + int DigiTo; + int Port; + char * Context; + + p_value = strtok_s(p_value, "=\t\n\r", &Context); + + Port = atoi(p_value); + + if (GetPortTableEntryFromPortNum(Port) == NULL) + return FALSE; + + // Check that port can digi (SCS Pactor can't set digi'd bit in calls) + + if (CanPortDigi(Port) == 0) + return FALSE; + + + CrossPortMap[Port][Port] = FALSE; // Cancel Default mapping + CrossPortMap[Port][0] = FALSE; // Cancel Default APRSIS + + if (Context == NULL || Context[0] == 0) + return TRUE; + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + DigiTo = atoi(ptr); // this gives zero for IS + + if (DigiTo && GetPortTableEntryFromPortNum(DigiTo) == NULL) + return FALSE; + + CrossPortMap[Port][DigiTo] = TRUE; + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + return TRUE; + } + if (_stricmp(ptr, "BRIDGE") == 0) + { + int DigiTo; + int Port; + char * Context; + + p_value = strtok_s(p_value, "=\t\n\r", &Context); + + Port = atoi(p_value); + + if (GetPortTableEntryFromPortNum(Port) == NULL) + return FALSE; + + if (Context == NULL) + return FALSE; + + ptr = strtok_s(NULL, ",\t\n\r", &Context); + + while (ptr) + { + DigiTo = atoi(ptr); // this gives zero for IS + + if (DigiTo > MaxBPQPortNo) + return FALSE; + + APRSBridgeMap[Port][DigiTo] = TRUE; + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + + return TRUE; + } + + + if (_stricmp(ptr, "BeaconInterval") == 0) + { + BeaconInterval = atoi(p_value); + + if (BeaconInterval < 5) + BeaconInterval = 5; + + if (BeaconInterval) + BeaconCounter = 30; // Send first after 30 secs + + return TRUE; + } + + if (_stricmp(ptr, "MobileBeaconInterval") == 0) + { + MobileBeaconInterval = atoi(p_value) * 60; + return TRUE; + } + if (_stricmp(ptr, "MobileBeaconIntervalSecs") == 0) + { + MobileBeaconInterval = atoi(p_value); + if (MobileBeaconInterval < 10) + MobileBeaconInterval = 10; + + return TRUE; + } + + if (_stricmp(ptr, "BeacontoIS") == 0) + { + BeacontoIS = atoi(p_value); + return TRUE; + } + + + if (_stricmp(ptr, "TRACECALLS") == 0) + { + TraceCalls = _strdup(_strupr(p_value)); + ConvertCalls(TraceCalls, &TraceAX[0][0], &TraceLen[0]); + return TRUE; + } + + if (_stricmp(ptr, "FLOODCALLS") == 0) + { + FloodCalls = _strdup(_strupr(p_value)); + ConvertCalls(FloodCalls, &FloodAX[0][0], &FloodLen[0]); + return TRUE; + } + + if (_stricmp(ptr, "DIGICALLS") == 0) + { + char AllCalls[1024]; + + DigiCalls = _strdup(_strupr(p_value)); + strcpy(AllCalls, APRSCall); + strcat(AllCalls, ","); + strcat(AllCalls, DigiCalls); + ConvertCalls(AllCalls, &DigiAX[0][0], &DigiLen[0]); + return TRUE; + } + + if (_stricmp(ptr, "MaxStations") == 0) + { + MaxStations = atoi(p_value); + + if (MaxStations > 10000) + MaxStations = 10000; + + return TRUE; + } + + if (_stricmp(ptr, "MaxAge") == 0) + { + ExpireTime = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "GPSPort") == 0) + { + if (strcmp(p_value, "0") != 0) + strcpy(GPSPort, p_value); + return TRUE; + } + + if (_stricmp(ptr, "GPSSpeed") == 0) + { + GPSSpeed = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "GPSRelay") == 0) + { + if (strlen(p_value) > 79) + return FALSE; + + strcpy(GPSRelay, p_value); + return TRUE; + } + + if (_stricmp(ptr, "BlueNMEA") == 0 || _stricmp(ptr, "TCPHost") == 0 || _stricmp(ptr, "AISHost") == 0) + { + if (strlen(p_value) > 70) + return FALSE; + + strcpy(HostName, p_value); + return TRUE; + } + + if (_stricmp(ptr, "TCPPort") == 0 || _stricmp(ptr, "AISPort") == 0) + { + HostPort = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "GPSDHost") == 0) + { + if (strlen(p_value) > 70) + return FALSE; + + strcpy(GPSDHost, p_value); + return TRUE; + } + + if (_stricmp(ptr, "GPSDPort") == 0) + { + GPSDPort = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "ADSBHost") == 0) + { + if (strlen(p_value) > 70) + return FALSE; + + strcpy(ADSBHost, p_value); + return TRUE; + } + + if (_stricmp(ptr, "ADSBPort") == 0) + { + ADSBPort = atoi(p_value); + return TRUE; + } + + + + if (_stricmp(ptr, "GPSSetsLocator") == 0) + { + GPSSetsLocator = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "LAT") == 0) + { + if (strlen(p_value) != 8) + return FALSE; + + memcpy(LAT, _strupr(p_value), 8); + PosnSet = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "LON") == 0) + { + if (strlen(p_value) != 9) + return FALSE; + + memcpy(LON, _strupr(p_value), 9); + PosnSet = TRUE; + return TRUE; + } + + if (_stricmp(ptr, "SYMBOL") == 0) + { + if (p_value[0] > ' ' && p_value[0] < 0x7f) + CFGSYMBOL = p_value[0]; + + return TRUE; + } + + if (_stricmp(ptr, "SYMSET") == 0) + { + CFGSYMSET = p_value[0]; + return TRUE; + } + + if (_stricmp(ptr, "PHG") == 0) + { + PHG = _strdup(p_value); + return TRUE; + } + + if (_stricmp(ptr, "MaxTraceHops") == 0) + { + MaxTraceHops = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "MaxFloodHops") == 0) + { + MaxFloodHops = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "ISHOST") == 0) + { + strncpy(ISHost, p_value, 250); + return TRUE; + } + + if (_stricmp(ptr, "ISPORT") == 0) + { + ISPort = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "ISPASSCODE") == 0) + { + ISPasscode = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "MaxDigisforIS") == 0) + { + MaxDigisforIS = atoi(p_value); + return TRUE; + } + + if (_stricmp(ptr, "GateLocalDistance") == 0) + { + GateLocalDistance = atoi(p_value); + if (GateLocalDistance > 0.0) + GateLocal = TRUE; + + return TRUE; + } + + if (_stricmp(ptr, "WXInterval") == 0) + { + WXInterval = atoi(p_value); + WXCounter = (WXInterval - 1) * 60; + return TRUE; + } + + if (_stricmp(ptr, "WXPortList") == 0) + { + char ParamCopy[80]; + char * Context; + int Port; + char * ptr; + int index = 0; + + for (index = 0; index < MaxBPQPortNo; index++) + WXPort[index] = FALSE; + + if (strlen(p_value) > 79) + p_value[80] = 0; + + strcpy(ParamCopy, p_value); + + ptr = strtok_s(ParamCopy, " ,\t\n\r", &Context); + + while (ptr) + { + Port = atoi(ptr); // this gives zero for IS + + WXPort[Port] = TRUE; + + ptr = strtok_s(NULL, " ,\t\n\r", &Context); + } + return TRUE; + } + + if (_stricmp(ptr, "Run") == 0) + { + strcpy(RunProgram, p_value); + return TRUE; + } + + // + // Bad line + // + return (FALSE); +} + +VOID SendAPRSMessageEx(char * Message, int toPort, char * FromCall, int Gated); + + +VOID SendAPRSMessage(char * Message, int toPort) +{ + SendAPRSMessageEx(Message, toPort, APRSCall, 0); +} + +// Ex allows setting source call (For WX Messages) + + +VOID SendAPRSMessageEx(char * Message, int toPort, char * FromCall, int Gated) +{ + int Port; + DIGIMESSAGE Msg; + + int Len; + + // toPort = -1 means all tadio ports. 0 = IS + + if (toPort == -1) + { + for (Port = 1; Port <= MaxBPQPortNo; Port++) + { + if (Gated && GatedHddrLen[Port]) + memcpy(Msg.DEST, &GatedHeader[Port][0][0], 10 * 7); + else if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined + memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7); + else + continue; + + Msg.DEST[6] |= 0x80; // set Command Bit + + ConvToAX25(FromCall, Msg.ORIGIN); + Msg.PID = 0xf0; + Msg.CTL = 3; + Len = sprintf(Msg.L2DATA, "%s", Message); + Send_AX_Datagram(&Msg, Len + 2, Port); + } + + return; + } + + if (toPort == 0 && APRSISOpen) + { + char ISMsg[300]; + + Len = sprintf(ISMsg, "%s>%s,TCPIP*:%s\r\n", FromCall, APRSDest, Message); + ISSend(sock, ISMsg, Len, 0); + } + + if (toPort == 0) + return; + + if (Gated && GatedHddrLen[toPort]) + memcpy(Msg.DEST, &GatedHeader[toPort][0][0], 10 * 7); + else if (BeaconHddrLen[toPort]) // Only send to ports with a DEST defined + memcpy(Msg.DEST, &BeaconHeader[toPort][0][0], 10 * 7); + else + return; + + Msg.DEST[6] |= 0x80; // set Command Bit + + ConvToAX25(FromCall, Msg.ORIGIN); + Msg.PID = 0xf0; + Msg.CTL = 3; + Len = sprintf(Msg.L2DATA, "%s", Message); + Send_AX_Datagram(&Msg, Len + 2, toPort); + + return; +} + + +VOID ProcessSpecificQuery(char * Query, int Port, char * Origin, char * DestPlusDigis) +{ + if (_memicmp(Query, "APRSS", 5) == 0) + { + char Message[255]; + + sprintf(Message, ":%-9s:%s", Origin, StatusMsg); + SendAPRSMessage(Message, Port); + + return; + } + + if (_memicmp(Query, "APRST", 5) == 0 || _memicmp(Query, "PING?", 5) == 0) + { + // Trace Route + //:KH2ZV :?APRST :N8UR :KH2Z>APRS,DIGI1,WIDE*: + //:G8BPQ-14 :Path - G8BPQ-14>APU25N + + char Message[255]; + + sprintf(Message, ":%-9s:Path - %s>%s", Origin, Origin, DestPlusDigis); + SendAPRSMessage(Message, Port); + + return; + } +} + +VOID ProcessQuery(char * Query) +{ + if (memcmp(Query, "IGATE?", 6) == 0) + { + IStatusCounter = (rand() & 31) + 5; // 5 - 36 secs delay + return; + } + + if (memcmp(Query, "APRS?", 5) == 0) + { + BeaconCounter = (rand() & 31) + 5; // 5 - 36 secs delay + return; + } +} +Dll VOID APIENTRY APISendBeacon() +{ + BeaconCounter = 2; +} + +typedef struct _BeaconParams +{ + int toPort; + char * BeaconText; + BOOL SendStatus; + BOOL SendSOGCOG; +} Params; + + +Params BeaconParams; + +void SendBeaconThread(void * Params); + +VOID SendBeacon(int toPort, char * BeaconText, BOOL SendStatus, BOOL SendSOGCOG) +{ + // Send to IS if needed then start a thread to send to radio ports + + if (PosnSet == FALSE) + return; + + if (APRSISOpen && toPort == 0 && BeacontoIS) + { + char SOGCOG[10] = ""; + char ISMsg[300]; + int Len; + + Debugprintf("Sending APRS Beacon to APRS-IS"); + + if (SendSOGCOG | (COG != 0.0)) + sprintf(SOGCOG, "%03.0f/%03.0f", COG, SOG); + + if (PHG) // Send PHG instead of SOG COG + Len = sprintf(ISMsg, "%s>%s,TCPIP*:%c%s%c%s%c%s%s\r\n", APRSCall, APRSDest, + (APRSApplConnected) ? '=' : '!', LAT, SYMSET, LON, SYMBOL, PHG, BeaconText); + else + Len = sprintf(ISMsg, "%s>%s,TCPIP*:%c%s%c%s%c%s%s\r\n", APRSCall, APRSDest, + (APRSApplConnected) ? '=' : '!', LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + + ISSend(sock, ISMsg, Len, 0); + Debugprintf(">%s", ISMsg); + } + + BeaconParams.toPort = toPort; + BeaconParams.BeaconText = BeaconText; + BeaconParams.SendStatus = SendStatus; + BeaconParams.SendSOGCOG = SendSOGCOG; + + _beginthread(SendBeaconThread, 0, (VOID *) &BeaconParams); +} + +void SendBeaconThread(void * Param) +{ + // runs as a thread so we can sleep() between calls + + // Params are passed via a param block + + Params * BeaconParams = (Params *)Param; + + int toPort = BeaconParams->toPort; + char * BeaconText = BeaconParams->BeaconText; + BOOL SendStatus = BeaconParams->SendStatus; + BOOL SendSOGCOG = BeaconParams->SendSOGCOG; + + int Port; + DIGIMESSAGE Msg; + int Len; + char SOGCOG[256] = ""; + struct STATIONRECORD * Station; + struct PORTCONTROL * PORT; + + if (PosnSet == FALSE) + return; + + if (PHG) // Send PHG instead of SOG COG + strcpy(SOGCOG, PHG); + else + if (SendSOGCOG | (COG != 0.0)) + sprintf(SOGCOG, "%03.0f/%03.0f", COG, SOG); + + BeaconCounter = BeaconInterval * 60; + + if (ISPort && IGateEnabled) + Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', + LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + else + Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', + LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + + Msg.PID = 0xf0; + Msg.CTL = 3; + + // Add to dup check list, so we won't digi it if we hear it back + // Should we drop it if we've sent it recently ?? + + if (CheckforDups(APRSCall, Msg.L2DATA, Len - (19 + sizeof(void *)))) + return; + + // Add to our station list + + Station = FindStation(APRSCall, TRUE); + + if (Station == NULL) + return; + + + strcpy(Station->Path, "APBPQ1"); + strcpy(Station->LastPacket, Msg.L2DATA); +// Station->LastPort = Port; + + DecodeAPRSPayload(Msg.L2DATA, Station); + Station->TimeLastUpdated = time(NULL); + + if (toPort) + { + if (BeaconHddrLen[toPort] == 0) + return; + + Debugprintf("Sending APRS Beacon to port %d", toPort); + + memcpy(Msg.DEST, &BeaconHeader[toPort][0][0], 10 * 7); // Clear unused digis + Msg.DEST[6] |= 0x80; // set Command Bit + + GetSemaphore(&Semaphore, 12); + Send_AX_Datagram(&Msg, Len + 2, toPort); + FreeSemaphore(&Semaphore); + + return; + } + + for (Port = 1; Port <= MaxBPQPortNo; Port++) // Check all ports + { + if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined + { + Debugprintf("Sending APRS Beacon to port %d", Port); + + if (ISPort && IGateEnabled) + Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', + LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + else + Len = sprintf(Msg.L2DATA, "%c%s%c%s%c%s%s", (APRSApplConnected) ? '=' : '!', + LAT, SYMSET, LON, SYMBOL, SOGCOG, BeaconText); + Msg.PID = 0xf0; + Msg.CTL = 3; + + memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7); + Msg.DEST[6] |= 0x80; // set Command Bit + + GetSemaphore(&Semaphore, 12); + Send_AX_Datagram(&Msg, Len + 2, Port); + FreeSemaphore(&Semaphore); + + // if Port has interlock set pause before next + + PORT = GetPortTableEntryFromPortNum(Port); + + // Just pause for all ports + +// if (PORT && PORT->PORTINTERLOCK) + Sleep(20000); + } + } + return ; +} + +VOID SendObject(struct OBJECT * Object) +{ + int Port; + DIGIMESSAGE Msg; + int Len; + + // Add to dup list in case we get it back + + CheckforDups(APRSCall, Object->Message, (int)strlen(Object->Message)); + + for (Port = 1; Port <= MaxBPQPortNo; Port++) + { + if (Object->PortMap[Port]) + { + Msg.PID = 0xf0; + Msg.CTL = 3; + Len = sprintf(Msg.L2DATA, "%s", Object->Message); + memcpy(Msg.DEST, &Object->Path[0][0], Object->PathLen + 1); + Msg.DEST[6] |= 0x80; // set Command Bit + + Send_AX_Datagram(&Msg, Len + 2, Port); + } + } + + // Also send to APRS-IS if connected + + if (APRSISOpen && Object->PortMap[0]) + { + char ISMsg[300]; + Len = sprintf(ISMsg, "%s>%s,TCPIP*:%s\r\n", APRSCall, APRSDest, Object->Message); + ISSend(sock, ISMsg, Len, 0); + } +} + + +/* +VOID SendStatus(char * StatusText) +{ + int Port; + DIGIMESSAGE Msg; + int Len; + + if (APRSISOpen) + { + Msg.PID = 0xf0; + Msg.CTL = 3; + + Len = sprintf(Msg.L2DATA, ">%s", StatusText); + + for (Port = 1; Port <= NUMBEROFPORTS; Port++) + { + if (BeaconHddrLen[Port]) // Only send to ports with a DEST defined + { + memcpy(Msg.DEST, &BeaconHeader[Port][0][0], 10 * 7); + Send_AX_Datagram(&Msg, Len + 2, Port); + } + } + + Len = sprintf(Msg.L2DATA, "%s>%s,TCPIP*:>%s\r\n", APRSCall, APRSDest, StatusText); + ISSend(sock, Msg.L2DATA, Len, 0); +// Debugprintf(">%s", Msg.L2DATA); + } +} + + +*/ +VOID SendIStatus() +{ + int Port; + DIGIMESSAGE Msg; + int Len; + + IStatusCounter = 3600; // One per hour + + if (APRSISOpen && BeacontoIS && RXOnly == 0) + { + Msg.PID = 0xf0; + Msg.CTL = 3; + + Len = sprintf(Msg.L2DATA, "%s,TCPIP*:%s", Msg.L2DATA); + } + +} + + +VOID DoSecTimer() +{ + struct OBJECT * Object = ObjectList; + + while (Object) + { + Object->Timer--; + + if (Object->Timer == 0) + { + Object->Timer = 60 * Object->Interval; + SendObject(Object); + } + Object = Object->Next; + } + + // Check SatGate Mode delay Q + + if (SatISQueue) + { + time_t NOW = time(NULL); + ISDELAY * SatISEntry = SatISQueue; + ISDELAY * Prev = NULL; + + while (SatISEntry) + { + if (SatISEntry->SendTIme < NOW) + { + // Send it + + ISSend(sock, SatISEntry->ISMSG, (int)strlen(SatISEntry->ISMSG), 0); + free(SatISEntry->ISMSG); + + if (Prev) + Prev->Next = SatISEntry->Next; + else + SatISQueue = SatISEntry->Next; + + free(SatISEntry); + return; // unlinkely to get 2 in sam esecond and doesn;t matter if we delay a bit more + } + + Prev = SatISEntry; + SatISEntry = SatISEntry->Next; + } + } + + if (ISPort && APRSISOpen == 0 && IGateEnabled) + { + ISDelayTimer++; + + if (ISDelayTimer > 60) + { + ISDelayTimer = 0; + _beginthread(APRSISThread, 0, (VOID *) TRUE); + } + } + + if (HostName[0]) + { + if (BlueNMEAOK == 0) + { + BlueNMEATimer++; + if (BlueNMEATimer > 15) + { + BlueNMEATimer = 0; + _beginthread(TCPConnect, 0, 0); + } + } + } + + if (GPSDHost[0]) + { + if (GPSDOK == 0) + { + GPSDTimer++; + if (GPSDTimer > 15) + { + GPSDTimer = 0; + _beginthread(GPSDConnect, 0, 0); + } + } + } + + if (BeaconCounter) + { + BeaconCounter--; + + if (BeaconCounter == 0) + { + BeaconCounter = BeaconInterval * 60; + SendBeacon(0, StatusMsg, TRUE, FALSE); + } + } + + if (IStatusCounter) + { + IStatusCounter--; + + if (IStatusCounter == 0) + { + SendIStatus(); + } + } + + if (GPSOK) + { + GPSOK--; + + if (GPSOK == 0) +#ifdef LINBPQ + Debugprintf("GPS Lost"); +#else + SetDlgItemText(hConsWnd, IDC_GPS, "No GPS"); +#endif + } + + APRSSecTimer(); // Code from APRS APPL +} + +int CountPool() +{ + struct STATIONRECORD * ptr = StationRecordPool; + int n = 0; + + while (ptr) + { + n++; + ptr = ptr->Next; + } + return n; +} + +static VOID DoMinTimer() +{ + struct STATIONRECORD * ptr = *StationRecords; + struct STATIONRECORD * last = NULL; + time_t AgeLimit = time(NULL ) - (ExpireTime * 60); + int i = 0; + + // Remove old records + + while (ptr) + { + if (ptr->TimeLastUpdated < AgeLimit) + { + StationCount--; + + if (last) + { + last->Next = ptr->Next; + + // Put on front of free chain + + ptr->Next = StationRecordPool; + StationRecordPool = ptr; + + ptr = last->Next; + } + else + { + // First in list + + *StationRecords = ptr->Next; + + // Put on front of free chain + + ptr->Next = StationRecordPool; + StationRecordPool = ptr; + + if (*StationRecords) + { + ptr = *StationRecords; + } + else + { + ptr = NULL; + } + } + } + else + { + last = ptr; + ptr = ptr->Next; + } + } +} + +char APRSMsg[300]; + +int ISHostIndex = 0; +char RealISHost[256]; + +VOID APRSISThread(void * Report) +{ + // Receive from core server + + char Signon[500]; + unsigned char work[4]; + + struct sockaddr_in sinx; + int addrlen=sizeof(sinx); + struct addrinfo hints, *res = 0, *saveres; + size_t len; + int err; + u_long param=1; + BOOL bcopt=TRUE; + char Buffer[1000]; + int InputLen = 1; // Non-zero + char errmsg[300]; + char * ptr; + size_t inptr = 0; + char APRSinMsg[1000]; + char PortString[20]; + char serv[256]; + + Debugprintf("BPQ32 APRS IS Thread"); +#ifndef LINBPQ + SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Connecting"); +#endif + + if (ISFilter[0]) + sprintf(Signon, "user %s pass %d vers BPQ32 %s filter %s\r\n", + APRSCall, ISPasscode, TextVerstring, ISFilter); + else + sprintf(Signon, "user %s pass %d vers BPQ32 %s\r\n", + APRSCall, ISPasscode, TextVerstring); + + + sprintf(PortString, "%d", ISPort); + + // get host info, make socket, and connect it + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever + hints.ai_socktype = SOCK_STREAM; + getaddrinfo(ISHost, PortString, &hints, &res); + + InputLen = sprintf(errmsg, "Connecting to APRS Host %s\r\n", ISHost); + MonitorAPRSIS(errmsg, InputLen, FALSE); + + if (!res) + { + err = WSAGetLastError(); + InputLen = sprintf(errmsg, "APRS IS Resolve %s Failed Error %d\r\n", ISHost, err); + MonitorAPRSIS(errmsg, InputLen, FALSE); + + return; // Resolve failed + + } + + // Step thorough the list of hosts + + saveres = res; // Save for free + + if (res->ai_next) // More than one + { + int n = ISHostIndex; + + while (n && res->ai_next) + { + res = res->ai_next; + n--; + } + + if (n) + { + // We have run off the end of the list + + ISHostIndex = 0; // Back to start + res = saveres; + } + else + ISHostIndex++; + + } + + getnameinfo(res->ai_addr, (int)res->ai_addrlen, RealISHost, 256, serv, 256, 0); + + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + + if (sock == INVALID_SOCKET) + return; + + setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4); + + memcpy(work, res->ai_addr->sa_data, 4); + + Debugprintf("Trying APRSIS Host %d.%d.%d.%d (%d) %s", work[0], work[1], work[2], work[3], ISHostIndex, RealISHost); + + if (connect(sock, res->ai_addr, (int)res->ai_addrlen)) + { + err=WSAGetLastError(); + + // + // Connect failed + // + +#ifndef LINBPQ + MySetWindowText(GetDlgItem(hConsWnd, IGATESTATE), "IGate State: Connect Failed"); +#else + printf("APRS Igate connect failed\n"); +#endif + err=WSAGetLastError(); + InputLen = sprintf(errmsg, "Connect Failed %s af %d Error %d \r\n", RealISHost, res->ai_family, err); + MonitorAPRSIS(errmsg, InputLen, FALSE); + + freeaddrinfo(res); + return; + } + + freeaddrinfo(saveres); + + APRSISOpen = TRUE; + +#ifndef LINBPQ + MySetWindowText(GetDlgItem(hConsWnd, IGATESTATE), "IGate State: Connected"); +#endif + + InputLen=recv(sock, Buffer, 500, 0); + + if (InputLen > 0) + { + Buffer[InputLen] = 0; + Debugprintf(Buffer); + MonitorAPRSIS(Buffer, InputLen, FALSE); + } + + ISSend(sock, Signon, (int)strlen(Signon), 0); +/* + InputLen=recv(sock, Buffer, 500, 0); + + if (InputLen > 0) + { + Buffer[InputLen] = 0; + Debugprintf(Buffer); + MonitorAPRSIS(Buffer, InputLen, FALSE); + } + + InputLen=recv(sock, Buffer, 500, 0); + + if (InputLen > 0) + { + Buffer[InputLen] = 0; + Debugprintf(Buffer); + MonitorAPRSIS(Buffer, InputLen, FALSE); + } +*/ + while (InputLen > 0 && IGateEnabled) + { + InputLen = recv(sock, &APRSinMsg[inptr], (int)(500 - inptr), 0); + + if (InputLen > 0) + { + inptr += InputLen; + + ptr = memchr(APRSinMsg, 0x0a, inptr); + + while (ptr != NULL) + { + ptr++; // include lf + len = ptr-(char *)APRSinMsg; + + inptr -= len; // bytes left + + // UIView server has a null before crlf + + if (*(ptr - 3) == 0) + { + *(ptr - 3) = 13; + *(ptr - 2) = 10; + *(ptr - 1) = 0; + + len --; + } + + if (len > 10 && len < 300) // Ignore if way too long or too short + { + memcpy(&APRSMsg, APRSinMsg, len); + MonitorAPRSIS(APRSMsg, (int)len, FALSE); + if (APRSMsg[len - 2] == 13) + APRSMsg[len - 2] = 0; + else + APRSMsg[len - 1] = 0; + +// Debugprintf("%s", APRSMsg); + + ProcessAPRSISMsg(APRSMsg); + } + + if (inptr > 0) + { + memmove(APRSinMsg, ptr, inptr); + ptr = memchr(APRSinMsg, 0x0a, inptr); + } + else + ptr = 0; + + if (inptr < 0) + break; + } + } + } + + closesocket(sock); + + APRSISOpen = FALSE; + + Debugprintf("BPQ32 APRS IS Thread Exited"); + +#ifndef LINBPQ + if (IGateEnabled) + SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Disconnected"); + else + SetDlgItemText(hConsWnd, IGATESTATE, "IGate State: Disabled"); +#endif + ISDelayTimer = 30; // Retry pretty quickly + return; +} + +VOID ProcessAPRSISMsg(char * APRSMsg) +{ + char * Payload; + char * Source; + char * Dest; + char IGateCall[10] = " "; + char * ptr; + char Message[255]; + PAPRSHEARDRECORD MH; + time_t NOW = time(NULL); + char ISCopy[1024]; + struct STATIONRECORD * Station = NULL; +#ifdef WIN32 + struct _EXCEPTION_POINTERS exinfo; + char EXCEPTMSG[80] = ""; +#endif + + if (APRSMsg[0] == '#') // Comment + return; + + // if APRS Appl is atttached, queue message to it + + strcpy(ISCopy, APRSMsg); + + GetSemaphore(&Semaphore, 12); + +#ifdef WIN32 + + strcpy(EXCEPTMSG, "ProcessAPRSISMsg"); + + __try + { + + Station = DecodeAPRSISMsg(ISCopy); + + } + #include "StdExcept.c" + Debugprintf(APRSMsg); + } +#else + Station = DecodeAPRSISMsg(ISCopy); +#endif + + FreeSemaphore(&Semaphore); + +//}WB4APR-14>APRS,RELAY,TCPIP,G9RXG*::G3NRWVVVV:Hi Ian{001 +//KE7XO-2>hg,TCPIP*,qAC,T2USASW::G8BPQ-14 :Path - G8BPQ-14>APU25N +//IGATECALL>APRS,GATEPATH}FROMCALL>TOCALL,TCPIP,IGATECALL*:original packet data + + Payload = strchr(APRSMsg, ':'); + + // Get call of originating Igate + + ptr = Payload; + + if (Payload == NULL) + return; + + *(Payload++) = 0; + + while (ptr[0] != ',') + ptr--; + + ptr++; + + if (strlen(ptr) > 9) + return; + + memcpy(IGateCall, ptr, (int)strlen(ptr)); + + if (strstr(APRSMsg, ",qAS,") == 0) // Findu generates invalid q construct + { + MH = FindStationInMH(IGateCall); + if (MH) + { +// Debugprintf("Setting Igate Flag - %s:%s", APRSMsg, Payload); + MH->IGate = TRUE; // If we have seen this station on RF, set it as an Igate + } + } + Source = APRSMsg; + Dest = strchr(APRSMsg, '>'); + + if (Dest == NULL) + return; + + *(Dest++) = 0; // Termainate Source + ptr = strchr(Dest, ','); + + if (ptr) + *ptr = 0; + + MH = UpdateHeard(Source, 0); + + MH->Station = Station; + + // See if we should gate to RF. + + // Have we heard dest recently? (use the message dest (not ax.25 dest) - does this mean we only gate Messages? + // Not if it is an Igate (it will get a copy direct) + // Have we recently sent a message from this call - if so, we gate the next Position + +/* + + From http://www.aprs-is.net/IGateDetails.aspx + + Gate message packets and associated posits to RF if all of the following are true: + + the receiving station has been heard within range within a predefined time period (range defined + as digi hops, distance, or both). + + the sending station has not been heard via RF within a predefined time period (packets gated + from the Internet by other stations are excluded from this test). + + the sending station does not have TCPXX, NOGATE, or RFONLY in the header. + + the receiving station has not been heard via the Internet within a predefined time period. + + A station is said to be heard via the Internet if packets from the station contain TCPIP* or + TCPXX* in the header or if gated (3rd-party) packets are seen on RF gated by the station + and containing TCPIP or TCPXX in the 3rd-party header (in other words, the station is seen on RF + as being an IGate). + +*/ + + if (Payload[0] == ':') // Message + { + char MsgDest[10]; + APRSHEARDRECORD * STN; + + if (strlen(Payload) > 100) // I don't think any valid APRS msgs are more than this + return; + + memcpy(MsgDest, &Payload[1], 9); + MsgDest[9] = 0; + + if (strcmp(MsgDest, CallPadded) == 0) // to us + return; + + // Check that the sending station has not been heard via RF recently + + if (MH->rfPort && (NOW - MH->MHTIME) < GATETIMELIMIT) + return; + + STN = FindStationInMH(MsgDest); + + // Shouldn't we check DUP list, in case we have digi'ed this message directly? + + if (CheckforDups(Source, Payload, (int)strlen(Payload))) + return; + + // has the receiving station has been heard on RF and is not an IGate + + if (STN && STN->rfPort && !STN->IGate && (NOW - STN->MHTIME) < GATETIMELIMIT) + { + sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload); + + GetSemaphore(&Semaphore, 12); + SendAPRSMessageEx(Message, STN->rfPort, APRSCall, 1); // Set gated to IS flag + FreeSemaphore(&Semaphore); + + MessageCount++; + MH->LASTMSG = NOW; + + return; + } + } + + // Not a message. If it is a position report gate if have sent a message recently + + if (Payload[0] == '!' || Payload[0] == '/' || Payload[0] == '=' || Payload[0] == '@') // Posn Reports + { + if ((NOW - MH->LASTMSG) < 900 && MH->rfPort) + { + sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload); + + GetSemaphore(&Semaphore, 12); + SendAPRSMessageEx(Message, MH->rfPort, APRSCall, 1); // Set gated to IS flag + FreeSemaphore(&Semaphore); + + return; + } + } + + // If Gate Local to RF is defined, and station is in range, Gate it + + if (GateLocal && Station) + { + if (Station->Object) + Station = Station->Object; // If Object Report, base distance on Object, not station + + if (Station->Lat != 0.0 && Station->Lon != 0.0 && myDistance(Station->Lat, Station->Lon, 0) < GateLocalDistance) + { + sprintf(Message, "}%s>%s,TCPIP,%s*:%s", Source, Dest, APRSCall, Payload); + GetSemaphore(&Semaphore, 12); + SendAPRSMessage(Message, -1); // Send to all APRS Ports + FreeSemaphore(&Semaphore); + + return; + } + } +} + +APRSHEARDRECORD * FindStationInMH(char * Call) +{ + APRSHEARDRECORD * MH = MHDATA; + int i; + + // We keep call in ascii format, as that is what we get from APRS-IS, and we have it in that form + + for (i = 0; i < HEARDENTRIES; i++) + { + if (memcmp(Call, MH->MHCALL, 9) == 0) + return MH; + + MH++; + } + + return NULL; +} + +APRSHEARDRECORD * UpdateHeard(UCHAR * Call, int Port) +{ + APRSHEARDRECORD * MH = MHDATA; + APRSHEARDRECORD * MHBASE = MH; + int i; + time_t NOW = time(NULL); + time_t OLDEST = NOW - MAXAGE; + char CallPadded[10] = " "; + BOOL SaveIGate = FALSE; + time_t SaveLastMsg = 0; + int SaveheardViaIS = 0; + + // We keep call in ascii format, space padded, as that is what we get from APRS-IS, and we have it in that form + + // Make Sure Space Padded + + memcpy(CallPadded, Call, (int)strlen(Call)); + + for (i = 0; i < MAXHEARDENTRIES; i++) + { + if (memcmp(CallPadded, MH->MHCALL, 10) == 0) + { + // if from APRS-IS, only update if record hasn't been heard via RF + + if (Port == 0) + MH->heardViaIS = 1; // Flag heard via IS + + if (Port == 0 && MH->rfPort) + return MH; // Don't update RF with IS + + if (Port == MH->rfPort) + { + SaveIGate = MH->IGate; + SaveLastMsg = MH->LASTMSG; + SaveheardViaIS = MH->heardViaIS; + goto DoMove; + } + } + + if (MH->MHCALL[0] == 0 || MH->MHTIME < OLDEST) // Spare entry + goto DoMove; + + MH++; + } + + // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP + + i = MAXHEARDENTRIES - 1; + + // Move others down and add at front +DoMove: + if (i != 0) // First + memmove(MHBASE + 1, MHBASE, i * sizeof(APRSHEARDRECORD)); + + if (i >= HEARDENTRIES) + { + char Status[80]; + + HEARDENTRIES = i + 1; + + sprintf(Status, "IGATE Stats: Msgs %d Local Stns %d", MessageCount , CountLocalStations()); +#ifndef LINBPQ + SetDlgItemText(hConsWnd, IGATESTATS, Status); +#endif + } + + memcpy (MHBASE->MHCALL, CallPadded, 10); + MHBASE->rfPort = Port; + MHBASE->MHTIME = NOW; + MHBASE->IGate = SaveIGate; + MHBASE->LASTMSG = SaveLastMsg; + MHBASE->heardViaIS = SaveheardViaIS; + + return MHBASE; +} + +int CountLocalStations() +{ + APRSHEARDRECORD * MH = MHDATA; + int i, n = 0; + + for (i = 0; i < HEARDENTRIES; i++) + { + if (MH->rfPort) // DOn't count IS Stations + n++; + + MH++; + } + return n; +} + + +BOOL CheckforDups(char * Call, char * Msg, int Len) +{ + // Primitive duplicate suppression - see if same call and text reeived in last few seconds + + time_t Now = time(NULL); + time_t DupCheck = Now - DUPSECONDS; + int i, saveindex = -1; + char * ptr1; + + if (Len < 1) + return TRUE; + + for (i = 0; i < MAXDUPS; i++) + { + if (DupInfo[i].DupTime < DupCheck) + { + // too old - use first if we need to save it + + if (saveindex == -1) + { + saveindex = i; + } + + if (DupInfo[i].DupTime == 0) // Off end of used area + break; + + continue; + } + + if ((Len == DupInfo[i].DupLen || (DupInfo[i].DupLen == 99 && Len > 99)) && memcmp(Call, DupInfo[i].DupUser, 7) == 0 && (memcmp(Msg, DupInfo[i].DupText, DupInfo[i].DupLen) == 0)) + { + // Duplicate, so discard + + Msg[Len] = 0; + ptr1 = strchr(Msg, 13); + if (ptr1) + *ptr1 = 0; + +// Debugprintf("Duplicate Message suppressed %s", Msg); + return TRUE; // Duplicate + } + } + + // Not in list + + if (saveindex == -1) // List is full + saveindex = MAXDUPS - 1; // Stick on end + + DupInfo[saveindex].DupTime = Now; + memcpy(DupInfo[saveindex].DupUser, Call, 7); + + if (Len > 99) Len = 99; + + DupInfo[saveindex].DupLen = Len; + memcpy(DupInfo[saveindex].DupText, Msg, Len); + + return FALSE; +} + +char * FormatAPRSMH(APRSHEARDRECORD * MH) + { + // Called from CMD.ASM + + struct tm * TM; + static char MHLine[50]; + time_t szClock = MH->MHTIME; + + szClock = (time(NULL) - szClock); + TM = gmtime(&szClock); + + sprintf(MHLine, "%-10s %d %.2d:%.2d:%.2d:%.2d %s\r", + MH->MHCALL, MH->rfPort, TM->tm_yday, TM->tm_hour, TM->tm_min, TM->tm_sec, (MH->IGate) ? "IGATE" : ""); + + return MHLine; + } + +// GPS Handling Code + +void SelectSource(BOOL Recovering); +void DecodeRMC(char * msg, size_t len); + +void PollGPSIn(); + + +UINT GPSType = 0xffff; // Source of Postion info - 1 = Phillips 2 = AIT1000. ffff = not posn message + +int RecoveryTimer; // Serial Port recovery + +double PI = 3.1415926535; +double P2 = 3.1415926535 / 180; + +double Latitude, Longtitude, SOG, COG, LatIncrement, LongIncrement; +double LastSOG = -1.0; + +BOOL Check0183CheckSum(char * msg, size_t len) +{ + BOOL retcode=TRUE; + char * ptr; + UCHAR sum,xsum1,xsum2; + + sum=0; + ptr=++msg; // Skip $ + +loop: + + if (*(ptr)=='*') goto eom; + + sum ^=*(ptr++); + + len--; + + if (len > 0) goto loop; + + return TRUE; // No Checksum + +eom: + _strupr(ptr); + + xsum1=*(++ptr); + xsum1-=0x30; + if (xsum1 > 9) xsum1-=7; + + xsum2=*(++ptr); + xsum2-=0x30; + if (xsum2 > 9) xsum2-=7; + + xsum1=xsum1<<4; + xsum1+=xsum2; + + return (xsum1==sum); +} + +BOOL OpenGPSPort() +{ + struct PortInfo * portptr = &InPorts[0]; + + // open COMM device + + if (strlen(GPSPort) < 4) + { + int port = atoi(GPSPort); +#ifdef WIN32 + sprintf(GPSPort, "COM%d", port); +#else + sprintf(GPSPort, "com%d", port); +#endif + } + + portptr->hDevice = OpenCOMPort(GPSPort, GPSSpeed, TRUE, TRUE, FALSE, 0); + + if (portptr->hDevice == 0) + { + return FALSE; + } + + return TRUE; +} + +void PollGPSIn() +{ + size_t len; + char GPSMsg[2000] = "$GPRMC,061213.000,A,5151.5021,N,00056.8388,E,0.15,324.11,190414,,,A*6F"; + char * ptr; + struct PortInfo * portptr; + + portptr = &InPorts[0]; + + if (!portptr->hDevice) + return; + + getgpsin: + +// Comm Error - probably lost USB Port. Try closing and reopening after a delay + +// if (RecoveryTimer == 0) +// { +// RecoveryTimer = 100; // 10 Secs +// return; +// } +// } + + if (portptr->gpsinptr == 160) + portptr->gpsinptr = 0; + + len = ReadCOMBlock(portptr->hDevice, &portptr->GPSinMsg[portptr->gpsinptr], + 160 - portptr->gpsinptr); + + if (len > 0) + { + portptr->gpsinptr += (int)len; + + ptr = memchr(portptr->GPSinMsg, 0x0a, portptr->gpsinptr); + + while (ptr != NULL) + { + ptr++; // include lf + len=ptr-(char *)&portptr->GPSinMsg; + memcpy(&GPSMsg,portptr->GPSinMsg,len); + + GPSMsg[len] = 0; + + if (Check0183CheckSum(GPSMsg, len)) + if (memcmp(&GPSMsg[3], "RMC", 3) == 0) + DecodeRMC(GPSMsg, len); + + portptr->gpsinptr -= (int)len; // bytes left + + if (portptr->gpsinptr > 0 && *ptr == 0) + { + *ptr++; + portptr->gpsinptr--; + } + + if (portptr->gpsinptr > 0) + { + memmove(portptr->GPSinMsg,ptr, portptr->gpsinptr); + ptr = memchr(portptr->GPSinMsg, 0x0a, portptr->gpsinptr); + } + else + ptr=0; + } + + goto getgpsin; + } + return; +} + + +void ClosePorts() +{ + if (InPorts[0].hDevice) + { + CloseCOMPort(InPorts[0].hDevice); + InPorts[0].hDevice=0; + } + + return; +} + +void DecodeRMC(char * msg, size_t len) +{ + char * ptr1; + char * ptr2; + char TimHH[3], TimMM[3], TimSS[3]; + char OurSog[5], OurCog[4]; + char LatDeg[3], LonDeg[4]; + char NewLat[10] = "", NewLon[10] = ""; + struct STATIONRECORD * Stn1; + + char Day[3]; + + ptr1 = &msg[7]; + + len-=7; + + ptr2=(char *)memchr(ptr1,',',15); + + if (ptr2 == 0) return; // Duff + + *(ptr2++)=0; + + memcpy(TimHH,ptr1,2); + memcpy(TimMM,ptr1+2,2); + memcpy(TimSS,ptr1+4,2); + TimHH[2]=0; + TimMM[2]=0; + TimSS[2]=0; + + ptr1=ptr2; + + if (*(ptr1) != 'A') // ' Data Not Valid + { +#ifndef LINBPQ + SetDlgItemText(hConsWnd, IDC_GPS, "No GPS Fix"); +#endif + return; + } + + ptr1+=2; + + ptr2=(char *)memchr(ptr1,',',15); + + if (ptr2 == 0) return; // Duff + + *(ptr2++)=0; + + memcpy(NewLat, ptr1, 7); + memcpy(LatDeg, ptr1, 2); + LatDeg[2]=0; + Lat=atof(LatDeg) + (atof(ptr1+2)/60); + + if (*(ptr1+7) > '4') if (NewLat[6] < '9') NewLat[6]++; + + ptr1=ptr2; + + NewLat[7] = (*ptr1); + if ((*ptr1) == 'S') Lat=-Lat; + + ptr1+=2; + + ptr2=(char *)memchr(ptr1,',',15); + + if (ptr2 == 0) return; // Duff + *(ptr2++)=0; + + memcpy(NewLon, ptr1, 8); + + memcpy(LonDeg,ptr1,3); + LonDeg[3]=0; + Lon=atof(LonDeg) + (atof(ptr1+3)/60); + + if (*(ptr1+8) > '4') if (NewLon[7] < '9') NewLon[7]++; + + ptr1=ptr2; + + NewLon[8] = (*ptr1); + if ((*ptr1) == 'W') Lon=-Lon; + + // Now have a valid posn, so stop sending Undefined LOC Sysbol + + SYMBOL = CFGSYMBOL; + SYMSET = CFGSYMSET; + + PosnSet = TRUE; + + Stn1 = (struct STATIONRECORD *)StnRecordBase; // Pass to App + Stn1->Lat = Lat; + Stn1->Lon = Lon; + + if (GPSOK == 0) + { +#ifdef LINBPQ + Debugprintf("GPS OK"); + printf("GPS OK\n"); +#else + SetDlgItemText(hConsWnd, IDC_GPS, "GPS OK"); +#endif + } + + GPSOK = 30; + + ptr1+=2; + + ptr2 = (char *)memchr(ptr1,',',30); + + if (ptr2 == 0) return; // Duff + + *(ptr2++)=0; + + memcpy(OurSog, ptr1, 4); + OurSog[4] = 0; + + ptr1=ptr2; + + ptr2 = (char *)memchr(ptr1,',',15); + + if (ptr2 == 0) return; // Duff + + *(ptr2++)=0; + + memcpy(OurCog, ptr1, 3); + OurCog[3] = 0; + + memcpy(Day,ptr2,2); + Day[2]=0; + + SOG = atof(OurSog); + COG = atof(OurCog); + + if (strcmp(NewLat, LAT) || strcmp(NewLon, LON)) + { + if (MobileBeaconInterval) + { + time_t NOW = time(NULL); + + if ((NOW - LastMobileBeacon) > MobileBeaconInterval) + { + LastMobileBeacon = NOW; + SendBeacon(0, StatusMsg, FALSE, TRUE); + } + } + if (GPSSetsLocator) + { + ToLOC(Lat, Lon, LOC); + sprintf(LOCATOR, "%f:%f", Lat, Lon); + } + } + + strcpy(LAT, NewLat); + strcpy(LON, NewLon); +} + +Dll VOID APIENTRY APRSConnect(char * Call, char * Filter) +{ + // Request APRS Data from Switch (called by APRS Applications) + + APRSApplConnected = TRUE; + APRSWeb = TRUE; + + strcpy(APPLFilter, Filter); + + if (APPLFilter[0]) + { + // This is called in APPL context so must queue the message + + char Msg[2000]; + PMSGWITHLEN buffptr; + + sprintf(Msg, "filter %s", Filter); + + if (strlen(Msg) > 240) + Msg[240] = 0; + + + GetSemaphore(&Semaphore, 11); + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + strcpy(&buffptr->Data[0], "SERVER"); + strcpy(&buffptr->Data[10], Msg); + C_Q_ADD(&APPLTX_Q, buffptr); + } + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + strcpy(&buffptr->Data[0], "SERVER"); + strcpy(&buffptr->Data[10], "filter?"); + C_Q_ADD(&APPLTX_Q, buffptr); + } + FreeSemaphore(&Semaphore); + } + strcpy(Call, CallPadded); +} + +Dll VOID APIENTRY APRSDisconnect() +{ + // Stop requesting APRS Data from Switch (called by APRS Applications) + + char Msg[2000]; + PMSGWITHLEN buffptr; + + strcpy(ISFilter, NodeFilter); + sprintf(Msg, "filter %s", NodeFilter); + + APRSApplConnected = FALSE; + APRSWeb = FALSE; + + GetSemaphore(&Semaphore, 11); + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + strcpy(&buffptr->Data[0], "SERVER"); + strcpy(&buffptr->Data[10], Msg); + C_Q_ADD(&APPLTX_Q, buffptr); + } + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + strcpy(&buffptr->Data[0], "SERVER"); + strcpy(&buffptr->Data[10], "filter?"); + C_Q_ADD(&APPLTX_Q, buffptr); + } + + while (APPL_Q) + { + buffptr = Q_REM(&APPL_Q); + ReleaseBuffer(buffptr); + } + + FreeSemaphore(&Semaphore); +} + + +Dll char * APIENTRY APRSGetStatusMsgPtr() +{ + return StatusMsg; +} + + + +Dll BOOL APIENTRY GetAPRSFrame(char * Frame, char * Call) +{ + // Request APRS Data from Switch (called by APRS Applications) + + void ** buffptr; +#ifdef bpq32 + struct _EXCEPTION_POINTERS exinfo; +#endif + + GetSemaphore(&Semaphore, 10); + { + if (APPL_Q) + { + buffptr = Q_REM(&APPL_Q); + + memcpy(Call, (char *)&buffptr[2], 12); + strcpy(Frame, (char *)&buffptr[5]); + + ReleaseBuffer(buffptr); + FreeSemaphore(&Semaphore); + return TRUE; + } + } + + FreeSemaphore(&Semaphore); + + return FALSE; +} + +Dll BOOL APIENTRY PutAPRSFrame(char * Frame, int Len, int Port) +{ + // Called from BPQAPRS App + // Message has to be queued so it can be sent by Timer Process (IS sock is not valid in this context) + + PMSGWITHLEN buffptr; + + GetSemaphore(&Semaphore, 11); + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = ++Len; // Len doesn't include Null + memcpy(&buffptr->Data[0], Frame, Len); + C_Q_ADD(&APPLTX_Q, buffptr); + } + +// buffptr-> = Port; // Pass to SendAPRSMessage(); + + FreeSemaphore(&Semaphore); + + return TRUE; +} + +Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall) +{ + // Called from BPQAPRS App or BPQMail + // Message has to be queued so it can be sent by Timer Process (IS sock is not valid in this context) + + PMSGWITHLEN buffptr; + + if (APRSActive == 0) + return FALSE; + + GetSemaphore(&Semaphore, 11); + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = 0; + memcpy(&buffptr->Data[0], ToCall, 9); + buffptr->Data[9] = 0; + strcpy(&buffptr->Data[10], Text); + C_Q_ADD(&APPLTX_Q, buffptr); + } + + FreeSemaphore(&Semaphore); + + return TRUE; +} + +Dll BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon) +{ + *PLat = Lat; + *PLon = Lon; + + return GPSOK; +} + +Dll BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon) +{ + strcpy(PLat, LAT); + strcpy(PLon, LON); + + return GPSOK; +} + +// Code to support getting GPS from Andriod Device running BlueNMEA + + +#define SD_BOTH 0x02 + +static VOID ProcessReceivedData(SOCKET TCPSock) +{ + char UDPMsg[8192]; + char Buffer[65536]; + + int len = recv(TCPSock, Buffer, 65500, 0); + + char * ptr; + char * Lastptr; + + if (len <= 0) + { + closesocket(TCPSock); + BlueNMEAOK = FALSE; + return; + } + + ptr = Lastptr = Buffer; + Buffer[len] = 0; + + while (len > 0) + { + ptr = strchr(Lastptr, 10); + + if (ptr) + { + size_t Len = ptr - Lastptr -1; + + if (Len > 8100) + return; + + memcpy(UDPMsg, Lastptr, Len); + UDPMsg[Len++] = 13; + UDPMsg[Len++] = 10; + UDPMsg[Len] = 0; + + if (!Check0183CheckSum(UDPMsg, Len)) + { + Debugprintf("Checksum Error %s", UDPMsg); + } + else + { + if (memcmp(&UDPMsg[3], "RMC", 3) == 0) + DecodeRMC(UDPMsg, Len); + + else if (memcmp(UDPMsg, "!AIVDM", 6) == 0) + ProcessAISMessage(UDPMsg, Len); + + } + Lastptr = ptr + 1; + len -= (int)Len; + } + else + return; + } +} + +static VOID TCPConnect(void * unused) +{ + int err, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + struct sockaddr_in destaddr; + SOCKET TCPSock; + + if (HostName[0] == 0) + return; + + destaddr.sin_addr.s_addr = inet_addr(HostName); + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(HostPort); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname(HostName); + + if (!HostEnt) + return; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + TCPSock = socket(AF_INET,SOCK_STREAM,0); + + if (TCPSock == INVALID_SOCKET) + { + return; + } + + setsockopt (TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + BlueNMEAOK = TRUE; // So we don't try to reconnect while waiting + + if (connect(TCPSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) + { + // + // Connected successful + // + + ioctl(TCPSock, FIONBIO, ¶m); + } + else + { + err=WSAGetLastError(); +#ifdef LINBPQ + printf("Connect Failed for AIS socket - error code = %d\n", err); +#else + Debugprintf("Connect Failed for AIS socket - error code = %d", err); +#endif + closesocket(TCPSock); + BlueNMEAOK = FALSE; + + return; + } + + BlueNMEAOK = TRUE; + + while (TRUE) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TCPSock,&readfs); + FD_SET(TCPSock,&errorfs); + + timeout.tv_sec = 900; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TCPSock, &readfs)) + { + ProcessReceivedData(TCPSock); + } + + if (FD_ISSET(TCPSock, &errorfs)) + { +Lost: +#ifdef LINBPQ + printf("AIS Connection lost\n"); +#endif + closesocket(TCPSock); + BlueNMEAOK = FALSE;; + return; + } + } + else + { + // 15 mins without data. Shouldn't happen + + shutdown(TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TCPSock); + BlueNMEAOK = FALSE; + return; + } + } +} + +int GPSDAlerted = 0; + +static VOID GPSDConnect(void * unused) +{ + int err, ret; + u_long param=1; + BOOL bcopt=TRUE; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + struct sockaddr_in destaddr; + SOCKET TCPSock; + + if (GPSDHost[0] == 0) + return; + + destaddr.sin_addr.s_addr = inet_addr(GPSDHost); + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(GPSDPort); + + TCPSock = socket(AF_INET,SOCK_STREAM,0); + + if (TCPSock == INVALID_SOCKET) + { + return; + } + + setsockopt (TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + GPSDOK = TRUE; // So we don't try to reconnect while waiting + + if (connect(TCPSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) + { + // + // Connected successful + // + +#ifdef LINBPQ + printf("GPSD Connected\n"); +#else + Debugprintf("GPSD Connected"); +#endif + GPSDAlerted = 0; + ioctl(TCPSock, FIONBIO, ¶m); + + // Request data + + send(TCPSock, "?WATCH={\"enable\":true,\"nmea\":true}\r\n", 36, 0); + } + else + { + err=WSAGetLastError(); + if (GPSDAlerted == 0) +#ifdef LINBPQ + printf("GPSD Connect Failed - error code = %d\n", err); +#else + Debugprintf("GPSD Connect Failed - error code = %d", err); +#endif + GPSDAlerted = 1; + closesocket(TCPSock); + GPSDOK = FALSE; + + return; + } + + while (TRUE) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TCPSock,&readfs); + FD_SET(TCPSock,&errorfs); + + timeout.tv_sec = 900; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select((int)TCPSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TCPSock, &readfs)) + { + char Buffer[65536]; + int len = recv(TCPSock, Buffer, 65500, 0); + char TCPMsg[8192]; + + char * ptr; + char * Lastptr; + + if (len == 0) + { + closesocket(TCPSock); + GPSDOK = FALSE;; + return; + } + + if (len < 9000) + { + Buffer[len] = 0; + + ptr = Lastptr = Buffer; + Buffer[len] = 0; + + while (len > 0) + { + ptr = strchr(Lastptr, 10); + + if (ptr) + { + size_t Len = ptr - Lastptr -1; + + if (Len > 8100) + return; + + memcpy(TCPMsg, Lastptr, Len); + TCPMsg[Len++] = 13; + TCPMsg[Len++] = 10; + TCPMsg[Len] = 0; + + if (!Check0183CheckSum(TCPMsg, Len)) + { + Debugprintf("Checksum Error %s", TCPMsg); + } + else + { + if (memcmp(&TCPMsg[3], "RMC", 3) == 0) + DecodeRMC(TCPMsg, Len); + } + Lastptr = ptr + 1; + len -= (int)Len; + } + else + return; + } + } + } + + if (FD_ISSET(TCPSock, &errorfs)) + { +Lost: +#ifdef LINBPQ + printf("GPSD Connection lost\n"); +#endif + closesocket(TCPSock); + GPSDOK = FALSE;; + return; + } + } + else + { + // 15 mins without data. Shouldn't happen + + shutdown(TCPSock, SD_BOTH); + Sleep(100); + + closesocket(TCPSock); + GPSDOK = FALSE; + return; + } + } +} + + + + + +// Code Moved from APRS Application + +// +// APRS Mapping and Messaging App for BPQ32 Switch. +// + + +VOID APIENTRY APRSConnect(char * Call, char * Filter); +VOID APIENTRY APRSDisconnect(); +BOOL APIENTRY GetAPRSFrame(char * Frame, char * Call); +BOOL APIENTRY PutAPRSFrame(char * Frame, int Len, int Port); +BOOL APIENTRY PutAPRSMessage(char * Frame, int Len); +BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon); +BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon); +VOID APIENTRY APISendBeacon(); + + +int NewLine(HWND hWnd); +VOID ProcessBuff(HWND hWnd, MESSAGE * buff,int len,int stamp); +int TogglePort(HWND hWnd, int Item, int mask); +VOID SendFrame(UCHAR * buff, int txlen); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +int KissDecode(UCHAR * inbuff, int len); +//void UpdateStation(char * Call, char * Path, char * Comment, double V_Lat, double V_Lon, double V_SOG, double V_COG, int iconRow, int iconCol); +VOID FindStationsByPixel(int MouseX, int MouseY); +void RefreshStation(struct STATIONRECORD * ptr); +void RefreshStationList(); +void RefreshStationMap(); +BOOL DecodeLocationString(UCHAR * Payload, struct STATIONRECORD * Station); +VOID Decode_MIC_E_Packet(char * Payload, struct STATIONRECORD * Station); +BOOL GetLocPixels(double Lat, double Lon, int * X, int * Y); +VOID APRSPoll(); +VOID OSMThread(); +VOID ResolveThread(); +VOID RefreshTile(char * FN, int Zoom, int x, int y); +int ProcessMessage(char * Payload, struct STATIONRECORD * Station); +VOID APRSSecTimer(); +double myBearing(double laa, double loa); + +BOOL CreatePipeThread(); + +VOID SendWeatherBeacon(); +VOID DecodeWXPortList(); + + +VOID DecodeWXReport(struct APRSConnectionInfo * sockptr, char * WX) +{ + UCHAR * ptr = strchr(WX, '_'); + char Type; + int Val; + + if (ptr == 0) + return; + + sockptr->WindDirn = atoi(++ptr); + ptr += 4; + sockptr->WindSpeed = atoi(ptr); + ptr += 3; +WXLoop: + + Type = *(ptr++); + + if (*ptr =='.') // Missing Value + { + while (*ptr == '.') + ptr++; + + goto WXLoop; + } + + Val = atoi(ptr); + + switch (Type) + { + case 'c': // = wind direction (in degrees). + + sockptr->WindDirn = Val; + break; + + case 's': // = sustained one-minute wind speed (in mph). + + sockptr->WindSpeed = Val; + break; + + case 'g': // = gust (peak wind speed in mph in the last 5 minutes). + + sockptr->WindGust = Val; + break; + + case 't': // = temperature (in degrees Fahrenheit). Temperatures below zero are expressed as -01 to -99. + + sockptr->Temp = Val; + break; + + case 'r': // = rainfall (in hundredths of an inch) in the last hour. + + sockptr->RainLastHour = Val; + break; + + case 'p': // = rainfall (in hundredths of an inch) in the last 24 hours. + + sockptr->RainLastDay = Val; + break; + + case 'P': // = rainfall (in hundredths of an inch) since midnight. + + sockptr->RainToday = Val; + break; + + case 'h': // = humidity (in %. 00 = 100%). + + sockptr->Humidity = Val; + break; + + case 'b': // = barometric pressure (in tenths of millibars/tenths of hPascal). + + sockptr->Pressure = Val; + break; + + default: + + return; + } + while(isdigit(*ptr)) + { + ptr++; + } + + if (*ptr != ' ') + goto WXLoop; +} + +static char HeaderTemplate[] = "Accept: */*\r\nHost: %s\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n"; +//char Header[] = "Accept: */*\r\nHost: tile.openstreetmap.org\r\nConnection: close\r\nContent-Length: 0\r\nUser-Agent: BPQ32(G8BPQ)\r\n\r\n"; + +char APRSMsg[300]; + +Dll struct STATIONRECORD * APIENTRY APPLFindStation(char * Call, BOOL AddIfNotFount) +{ + // Called from APRS Appl + + struct STATIONRECORD * Stn; + + GetSemaphore(&Semaphore, 12); + Stn = FindStation(Call, AddIfNotFount) ; + FreeSemaphore(&Semaphore); + + return Stn; +} + +Dll struct APRSMESSAGE * APIENTRY APRSGetMessageBuffer() +{ + struct APRSMESSAGE * ptr = MessageRecordPool; + + if (ptr == NULL) + { + // try getting oldest + + ptr = SMEM->Messages; + + if (ptr) + { + SMEM->Messages = ptr->Next; + memset(ptr, 0, sizeof(struct APRSMESSAGE)); + } + return ptr; + } + + if (ptr) + { + MessageRecordPool = ptr->Next; // Unchain + MessageCount++; + + ptr->Next = NULL; + + memset(ptr, 0, sizeof(struct APRSMESSAGE)); + } + + return ptr; +} + + +struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFount) +{ + int i = 0; + struct STATIONRECORD * find; + struct STATIONRECORD * ptr; + struct STATIONRECORD * last = NULL; + int sum = 0; + + if (APRSActive == 0 || StationRecords == 0) + return FALSE; + + if (strlen(Call) > 9) + { + Debugprintf("APRS Call too long %s", Call); + Call[9] = 0; + } + + find = *StationRecords; + while(find) + { + if (strlen(find->Callsign) > 9) + { + Debugprintf("APRS Call in Station List too long %s", find->Callsign); + find->Callsign[9] = 0; + } + + if (strcmp(find->Callsign, Call) == 0) + return find; + + last = find; + find = find->Next; + i++; + } + + // Not found - add on end + + if (AddIfNotFount) + { + // Get first from station record pool + + ptr = StationRecordPool; + + if (ptr) + { + StationRecordPool = ptr->Next; // Unchain + StationCount++; + } + else + { + // Get First from Stations + + ptr = *StationRecords; + + if (ptr) + *StationRecords = ptr->Next; + } + + if (ptr == NULL) + return NULL; + + memset(ptr, 0, sizeof(struct STATIONRECORD)); + +// EnterCriticalSection(&Crit); + + if (*StationRecords == NULL) + *StationRecords = ptr; + else + last->Next = ptr; + +// LeaveCriticalSection(&Crit); + + // Debugprintf("APRS Add Stn %s Station Count = %d", Call, StationCount); + + strcpy(ptr->Callsign, Call); + ptr->TimeLastUpdated = ptr->TimeAdded = time(NULL); + ptr->Index = i; + ptr->NoTracks = DefaultNoTracks; + + for (i = 0; i < 9; i++) + sum += Call[i]; + + sum %= 20; + + ptr->TrackColour = sum; + ptr->Moved = TRUE; + + return ptr; + } + else + return NULL; +} + +struct STATIONRECORD * ProcessRFFrame(char * Msg, int len, int * ourMessage) +{ + char * Payload; + char * Path = NULL; + char * Comment = NULL; + char * Callsign; + char * ptr; + int Port = 0; + + struct STATIONRECORD * Station = NULL; + + Msg[len - 1] = 0; + +// Debugprintf("RF Frame %s", Msg); + + Msg += 10; // Skip Timestamp + + Payload = strchr(Msg, ':'); // End of Address String + + if (Payload == NULL) + { + Debugprintf("Invalid Msg %s", Msg); + return Station; + } + + ptr = strstr(Msg, "Port="); + + if (ptr) + Port = atoi(&ptr[5]); + + Payload++; + + if (*Payload != 0x0d) + return Station; + + *Payload++ = 0; + + Callsign = Msg; + + Path = strchr(Msg, '>'); + + if (Path == NULL) + { + Debugprintf("Invalid Header %s", Msg); + return Station; + } + + *Path++ = 0; + + ptr = strchr(Path, ' '); + + if (ptr) + *ptr = 0; + + // Look up station - create a new one if not found + + if (strcmp(Callsign, "AIS") == 0) + { + if (needAIS) + { + Payload += 3; + ProcessAISMessage(Payload, strlen(Payload)); + } + else + Debugprintf(Payload); + + return 0; + } + + Station = FindStation(Callsign, TRUE); + + strcpy(Station->Path, Path); + strcpy(Station->LastPacket, Payload); + Station->LastPort = Port; + + *ourMessage = DecodeAPRSPayload(Payload, Station); + Station->TimeLastUpdated = time(NULL); + + return Station; +} + + +/* +2E0AYY>APU25N,TCPIP*,qAC,AHUBSWE2:=5105.18N/00108.19E-Paul in Folkestone Kent {UIV32N} +G0AVP-12>APT310,MB7UC*,WIDE3-2,qAR,G3PWJ:!5047.19N\00108.45Wk074/000/Paul mobile +G0CJM-12>CQ,TCPIP*,qAC,AHUBSWE2:=/3&Rio94sg +M0HFC>APRS,WIDE2-1,qAR,G0MNI:!5342.83N/00013.79W# Humber Fortress ARC Look us up on QRZ +G8WVW-3>APTT4,WIDE1-1,WIDE2-1,qAS,G8WVW:T#063,123,036,000,000,000,00000000 +*/ + + +struct STATIONRECORD * DecodeAPRSISMsg(char * Msg) +{ + char * Payload; + char * Path = NULL; + char * Comment = NULL; + char * Callsign; + struct STATIONRECORD * Station = NULL; + +// Debugprintf(Msg); + + Payload = strchr(Msg, ':'); // End of Address String + + if (Payload == NULL) + { + Debugprintf("Invalid Msg %s", Msg); + return Station; + } + + *Payload++ = 0; + + Callsign = Msg; + + Path = strchr(Msg, '>'); + + if (Path == NULL) + { + Debugprintf("Invalid Msg %s", Msg); + return Station; + } + + *Path++ = 0; + + // Look up station - create a new one if not found + + if (strlen(Callsign) > 11) + { + Debugprintf("Invalid Msg %s", Msg); + return Station; + } + + Station = FindStation(Callsign, TRUE); + + strcpy(Station->Path, Path); + strcpy(Station->LastPacket, Payload); + Station->LastPort = 0; + + DecodeAPRSPayload(Payload, Station); + Station->TimeLastUpdated = time(NULL); + + return Station; +} + +double Cube91 = 91.0 * 91.0 * 91.0; +double Square91 = 91.0 * 91.0; + +BOOL DecodeLocationString(UCHAR * Payload, struct STATIONRECORD * Station) +{ + UCHAR SymChar; + char SymSet; + char NS; + char EW; + double NewLat, NewLon; + char LatDeg[3], LonDeg[4]; + char save; + + // Compressed has first character not a digit (it is symbol table) + + // /YYYYXXXX$csT + + if (Payload[0] == '!') + return FALSE; // Ultimeter 2000 Weather Station + + if (!isdigit(*Payload)) + { + int C, S; + + SymSet = *Payload; + SymChar = Payload[9]; + + NewLat = 90.0 - ((Payload[1] - 33) * Cube91 + (Payload[2] - 33) * Square91 + + (Payload[3] - 33) * 91.0 + (Payload[4] - 33)) / 380926.0; + + Payload += 4; + + NewLon = -180.0 + ((Payload[1] - 33) * Cube91 + (Payload[2] - 33) * Square91 + + (Payload[3] - 33) * 91.0 + (Payload[4] - 33)) / 190463.0; + + C = Payload[6] - 33; + + if (C >= 0 && C < 90 ) + { + S = Payload[7] - 33; + + Station->Course = C * 4; + Station->Speed = (pow(1.08, S) - 1) * 1.15077945; // MPH; + } + + + + } + else + { + // Standard format ddmm.mmN/dddmm.mmE? + + NS = Payload[7] & 0xdf; // Mask Lower Case Bit + EW = Payload[17] & 0xdf; + + SymSet = Payload[8]; + SymChar = Payload[18]; + + if (SymChar == '_') // WX + { + if (strlen(Payload) > 30) + strcpy(Station->LastWXPacket, Payload); + } + + memcpy(LatDeg, Payload,2); + LatDeg[2]=0; + NewLat = atof(LatDeg) + (atof(Payload+2) / 60); + + if (NS == 'S') + NewLat = -NewLat; + else + if (NS != 'N') + return FALSE; + + memcpy(LonDeg,Payload + 9, 3); + + if (SymChar != '_' && Payload[22] == '/') // not if WX + { + Station->Course = atoi(Payload + 19); + Station->Speed = atoi(Payload + 23); + } + + LonDeg[3]=0; + + save = Payload[17]; + Payload[17] = 0; + NewLon = atof(LonDeg) + (atof(Payload+12) / 60); + Payload[17] = save; + + if (EW == 'W') + NewLon = -NewLon; + else + if (EW != 'E') + return FALSE; + } + + Station->Symbol = SymChar; + + if (SymChar > ' ' && SymChar < 0x7f) + SymChar -= '!'; + else + SymChar = 0; + + Station->IconOverlay = 0; + + if ((SymSet >= '0' && SymSet <= '9') || (SymSet >= 'A' && SymSet <= 'Z')) + { + SymChar += 96; + Station->IconOverlay = SymSet; + } + else + if (SymSet == '\\') + SymChar += 96; + + Station->iconRow = SymChar >> 4; + Station->iconCol = SymChar & 15; + + if (NewLat > 90 || NewLat < -90 || NewLon > 180 || NewLon < -180) + return TRUE; + + if (Station->Lat != NewLat || Station->Lon != NewLon) + { + time_t NOW = time(NULL); + time_t Age = NOW - Station->TimeLastTracked; + + if (Age > 15) // Don't update too often + { + // Add to track + + Station->TimeLastTracked = NOW; + +// if (memcmp(Station->Callsign, "ISS ", 4) == 0) +// Debugprintf("%s %s %s ",Station->Callsign, Station->Path, Station->LastPacket); + + Station->LatTrack[Station->Trackptr] = NewLat; + Station->LonTrack[Station->Trackptr] = NewLon; + Station->TrackTime[Station->Trackptr] = NOW; + + Station->Trackptr++; + Station->Moved = TRUE; + + if (Station->Trackptr == TRACKPOINTS) + Station->Trackptr = 0; + } + + Station->Lat = NewLat; + Station->Lon = NewLon; + Station->Approx = 0; + } + + + return TRUE; +} + +int DecodeAPRSPayload(char * Payload, struct STATIONRECORD * Station) +{ + char * TimeStamp; + char * ObjName; + char ObjState; + struct STATIONRECORD * Object; + BOOL Item = FALSE; + char * ptr; + char * Callsign; + char * Path; + char * Msg; + char * context; + struct STATIONRECORD * TPStation; + + Station->Object = NULL; + + if (strcmp(Station->Callsign, "LA1ZDA-2") == 0) + { + int i = 1; + } + switch(*Payload) + { + case '`': + case 0x27: // ' + case 0x1c: + case 0x1d: // MIC-E + + Decode_MIC_E_Packet(Payload, Station); + return 0; + + case '$': // NMEA + Debugprintf(Payload); + break; + + case ')': // Item + +// Debugprintf("%s %s %s", Station->Callsign, Station->Path, Payload); + + Item = TRUE; + ObjName = ptr = Payload + 1; + + while (TRUE) + { + ObjState = *ptr; + if (ObjState == 0) + return 0; // Corrupt + + if (ObjState == '!' || ObjState == '_') // Item Terminator + break; + + ptr++; + } + + *ptr = 0; // Terminate Name + + Object = FindStation(ObjName, TRUE); + Object->ObjState = *ptr++ = ObjState; + + strcpy(Object->Path, Station->Callsign); + strcat(Object->Path, ">"); + if (Object == Station) + { + char Temp[256]; + strcpy(Temp, Station->Path); + strcat(Object->Path, Temp); + Debugprintf("item is station %s", Payload); + } + else + strcat(Object->Path, Station->Path); + + strcpy(Object->LastPacket, Payload); + + if (ObjState != '_') // Deleted Objects may have odd positions + DecodeLocationString(ptr, Object); + + Object->TimeLastUpdated = time(NULL); + Station->Object = Object; + return 0; + + + case ';': // Object + + ObjName = Payload + 1; + ObjState = Payload[10]; // * Live, _Killed + + Payload[10] = 0; + Object = FindStation(ObjName, TRUE); + Object->ObjState = Payload[10] = ObjState; + + strcpy(Object->Path, Station->Callsign); + strcat(Object->Path, ">"); + if (Object == Station) + { + char Temp[256]; + strcpy(Temp, Station->Path); + strcat(Object->Path, Temp); + Debugprintf("Object is station %s", Payload); + } + else + strcat(Object->Path, Station->Path); + + + strcpy(Object->LastPacket, Payload); + + TimeStamp = Payload + 11; + + if (ObjState != '_') // Deleted Objects may have odd positions + DecodeLocationString(Payload + 18, Object); + + Object->TimeLastUpdated = time(NULL); + Object->LastPort = Station->LastPort; + Station->Object = Object; + return 0; + + case '@': + case '/': // Timestamp, No Messaging + + TimeStamp = ++Payload; + Payload += 6; + + case '=': + case '!': + + Payload++; + + DecodeLocationString(Payload, Station); + + return 0; + + case '>': // Status + + strcpy(Station->Status, &Payload[1]); + + case '<': // Capabilities + case '_': // Weather + case 'T': // Telemetry + + break; + + case ':': // Message + + return ProcessMessage(Payload, Station); + + case '}': // Third Party Header + + // Process Payload as a new message + + // }GM7HHB-9>APDR12,TCPIP,MM1AVR*:=5556.62N/00303.55W>204/000/A=000213 http://www.dstartv.com + + Callsign = Msg = &Payload[1]; + Path = strchr(Msg, '>'); + + if (Path == NULL) + return 0; + + *Path++ = 0; + + Payload = strchr(Path, ':'); + + if (Payload == NULL) + return 0; + + *(Payload++) = 0; + + // Check Dup Filter + + if (CheckforDups(Callsign, Payload, (int)strlen(Payload))) + return 0; + + // Look up station - create a new one if not found + + TPStation = FindStation(Callsign, TRUE); + + strcpy(TPStation->Path, Path); + strcpy(TPStation->LastPacket, Payload); + TPStation->LastPort = 0; // Heard on RF, but info is from IS + + DecodeAPRSPayload(Payload, TPStation); + TPStation->TimeLastUpdated = time(NULL); + + return 0; + + default: + + // Non - APRS Message. If Payload contains a valid 6 char locator derive a position from it + + if (Station->Lat != 0.0 || Station->Lon != 0.0) + return 0; // already have position + + ptr = strtok_s(Payload, ",[](){} \n", &context); + + while (ptr && ptr[0]) + { + if (strlen(ptr) == 6) // could be locator + { + double Lat = 0.0, Lon = 0.0; + + if (FromLOC(ptr, &Lat, &Lon)) + { + if (Lat != 0.0 && Lon != 0.0) + { + // Randomise in locator square. + + Lat = Lat + ((rand() / 24.0) / RAND_MAX); + Lon = Lon + ((rand() / 12.0) / RAND_MAX); + Station->Lat = Lat; + Station->Lon = Lon; + Station->Approx = 1; + Debugprintf("%s %s %s", Station->Callsign, Station->Path, Payload); + } + } + } + + ptr = strtok_s(NULL, ",[](){} \n", &context); + } + + return 0; + } + return 0; +} + +// Convert MIC-E Char to Lat Digit (offset by 0x30) +// 0123456789 @ABCDEFGHIJKLMNOPQRSTUVWXYZ +char MicELat[] = "0123456789???????0123456789 ???0123456789 " ; + +char MicECode[]= "0000000000???????111111111110???22222222222" ; + + +VOID Decode_MIC_E_Packet(char * Payload, struct STATIONRECORD * Station) +{ + // Info is encoded in the Dest Addr (in Station->Path) as well as Payload. + // See APRS Spec for full details + + char Lat[10]; // DDMMHH + char LatDeg[3]; + char * ptr; + char c; + int i, n; + int LonDeg, LonMin; + BOOL LonOffset = FALSE; + char NS = 'S'; + char EW = 'E'; + UCHAR SymChar, SymSet; + double NewLat, NewLon; + int SP, DC, SE; // Course/Speed Encoded + int Course, Speed; + + // Make sure packet is long enough to have an valid address + + if (strlen(Payload) < 9) + return; + + ptr = &Station->Path[0]; + + for (i = 0; i < 6; i++) + { + n = (*(ptr++)) - 0x30; + c = MicELat[n]; + + if (c == '?') // Illegal + return; + + if (c == ' ') + c = '0'; // Limited Precision + + Lat[i] = c; + + } + + Lat[6] = 0; + + if (Station->Path[3] > 'O') + NS = 'N'; + + if (Station->Path[5] > 'O') + EW = 'W'; + + if (Station->Path[4] > 'O') + LonOffset = TRUE; + + n = Payload[1] - 28; // Lon Degrees S9PU0T,WIDE1-1,WIDE2-2,qAR,WB9TLH-15:`rB0oII>/]"6W}44 + + if (LonOffset) + n += 100; + + if (n > 179 && n < 190) + n -= 80; + else + if (n > 189 && n < 200) + n -= 190; + + LonDeg = n; + +/* + To decode the longitude degrees value: +1. subtract 28 from the d+28 value to obtain d. +2. if the longitude offset is +100 degrees, add 100 to d. +3. subtract 80 if 180 ˜ d ˜ 189 +(i.e. the longitude is in the range 100–109 degrees). +4. or, subtract 190 if 190 ˜ d ˜ 199. +(i.e. the longitude is in the range 0–9 degrees). +*/ + + n = Payload[2] - 28; // Lon Mins + + if (n > 59) + n -= 60; + + LonMin = n; + + n = Payload[3] - 28; // Lon Mins/100; + +//1. subtract 28 from the m+28 value to obtain m. +//2. subtract 60 if m ™ 60. +//(i.e. the longitude minutes is in the range 0–9). + + + memcpy(LatDeg, Lat, 2); + LatDeg[2]=0; + + NewLat = atof(LatDeg) + (atof(Lat+2) / 6000.0); + + if (NS == 'S') + NewLat = -NewLat; + + NewLon = LonDeg + LonMin / 60.0 + n / 6000.0; + + if (EW == 'W') // West + NewLon = -NewLon; + + + SP = Payload[4] - 28; + DC = Payload[5] - 28; + SE = Payload[6] - 28; // Course 100 and 10 degs + + Speed = DC / 10; // Quotient = Speed Units + Course = DC - (Speed * 10); // Remainder = Course Deg/100 + + Course = SE + (Course * 100); + + Speed += SP * 10; + + if (Speed >= 800) + Speed -= 800; + + if (Course >= 400) + Course -= 400; + + Station->Course = Course; + Station->Speed = Speed * 1.15077945; // MPH + +// Debugprintf("MIC-E Course/Speed %s %d %d", Station->Callsign, Course, Speed); + + SymChar = Payload[7]; // Symbol + SymSet = Payload[8]; // Symbol + + SymChar -= '!'; + + Station->IconOverlay = 0; + + if ((SymSet >= '0' && SymSet <= '9') || (SymSet >= 'A' && SymSet <= 'Z')) + { + SymChar += 96; + Station->IconOverlay = SymSet; + } + else + if (SymSet == '\\') + SymChar += 96; + + Station->iconRow = SymChar >> 4; + Station->iconCol = SymChar & 15; + + if (NewLat > 90 || NewLat < -90 || NewLon > 180 || NewLon < -180) + return; + + if (Station->Lat != NewLat || Station->Lon != NewLon) + { + time_t NOW = time(NULL); + time_t Age = NOW - Station->TimeLastUpdated; + + if (Age > 15) // Don't update too often + { + // Add to track + +// if (memcmp(Station->Callsign, "ISS ", 4) == 0) +// Debugprintf("%s %s %s ",Station->Callsign, Station->Path, Station->LastPacket); + + Station->LatTrack[Station->Trackptr] = NewLat; + Station->LonTrack[Station->Trackptr] = NewLon; + Station->TrackTime[Station->Trackptr] = NOW; + + Station->Trackptr++; + Station->Moved = TRUE; + + if (Station->Trackptr == TRACKPOINTS) + Station->Trackptr = 0; + } + + Station->Lat = NewLat; + Station->Lon = NewLon; + Station->Approx = 0; + } + + return; + +} + +/* + +INT_PTR CALLBACK ChildDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ +// This processes messages from controls on the tab subpages + int Command; + +// int retCode, disp; +// char Key[80]; +// HKEY hKey; +// BOOL OK; +// OPENFILENAME ofn; +// char Digis[100]; + + int Port = PortNum[CurrentPage]; + + switch (message) + { + case WM_NOTIFY: + + switch (((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGE: + OnSelChanged(hDlg); + return TRUE; + // More cases on WM_NOTIFY switch. + case NM_CHAR: + return TRUE; + } + + break; + case WM_INITDIALOG: + OnChildDialogInit( hDlg); + return (INT_PTR)TRUE; + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_COMMAND: + + Command = LOWORD(wParam); + + if (Command == 2002) + return TRUE; + + switch (Command) + { +/* case IDC_FILE: + + memset(&ofn, 0, sizeof (OPENFILENAME)); + ofn.lStructSize = sizeof (OPENFILENAME); + ofn.hwndOwner = hDlg; + ofn.lpstrFile = &FN[Port][0]; + ofn.nMaxFile = 250; + ofn.lpstrTitle = "File to send as beacon"; + ofn.lpstrInitialDir = GetBPQDirectory(); + + if (GetOpenFileName(&ofn)) + SetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0]); + + break; + + + case IDOK: + + GetDlgItemText(hDlg, IDC_UIDEST, &UIDEST[Port][0], 10); + + if (UIDigi[Port]) + { + free(UIDigi[Port]); + UIDigi[Port] = NULL; + } + + if (UIDigiAX[Port]) + { + free(UIDigiAX[Port]); + UIDigiAX[Port] = NULL; + } + + GetDlgItemText(hDlg, IDC_UIDIGIS, Digis, 99); + + UIDigi[Port] = _strdup(Digis); + + GetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0], 255); + GetDlgItemText(hDlg, IDC_MESSAGE, &Message[Port][0], 1000); + + Interval[Port] = GetDlgItemInt(hDlg, IDC_INTERVAL, &OK, FALSE); + + MinCounter[Port] = Interval[Port]; + + SendFromFile[Port] = IsDlgButtonChecked(hDlg, IDC_FROMFILE); + + sprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil\\UIPort%d", PortNum[CurrentPage]); + + retCode = RegCreateKeyEx(REGTREE, + Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "UIDEST", 0, REG_SZ,(BYTE *)&UIDEST[Port][0], strlen(&UIDEST[Port][0])); + retCode = RegSetValueEx(hKey, "FileName", 0, REG_SZ,(BYTE *)&FN[Port][0], strlen(&FN[Port][0])); + retCode = RegSetValueEx(hKey, "Message", 0, REG_SZ,(BYTE *)&Message[Port][0], strlen(&Message[Port][0])); + retCode = RegSetValueEx(hKey, "Interval", 0, REG_DWORD,(BYTE *)&Interval[Port], 4); + retCode = RegSetValueEx(hKey, "SendFromFile", 0, REG_DWORD,(BYTE *)&SendFromFile[Port], 4); + retCode = RegSetValueEx(hKey, "Enabled", 0, REG_DWORD,(BYTE *)&UIEnabled[Port], 4); + retCode = RegSetValueEx(hKey, "Digis",0, REG_SZ, Digis, strlen(Digis)); + + RegCloseKey(hKey); + } + + SetupUI(Port); + + return (INT_PTR)TRUE; + + + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case ID_TEST: + + SendBeacon(Port); + return TRUE; + + + + + } + break; + + } + return (INT_PTR)FALSE; +} + + + + +VOID WINAPI OnTabbedDialogInit(HWND hDlg) +{ + DLGHDR *pHdr = (DLGHDR *) LocalAlloc(LPTR, sizeof(DLGHDR)); + DWORD dwDlgBase = GetDialogBaseUnits(); + int cxMargin = LOWORD(dwDlgBase) / 4; + int cyMargin = HIWORD(dwDlgBase) / 8; + + TC_ITEM tie; + RECT rcTab; + + int i, pos, tab = 0; + INITCOMMONCONTROLSEX init; + + char PortNo[60]; + struct _EXTPORTDATA * PORTVEC; + + hwndDlg = hDlg; // Save Window Handle + + // Save a pointer to the DLGHDR structure. + + SetWindowLong(hwndDlg, GWL_USERDATA, (LONG) pHdr); + + // Create the tab control. + + + init.dwICC = ICC_STANDARD_CLASSES; + init.dwSize=sizeof(init); + i=InitCommonControlsEx(&init); + + pHdr->hwndTab = CreateWindow(WC_TABCONTROL, "", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, + 0, 0, 100, 100, hwndDlg, NULL, hInst, NULL); + + if (pHdr->hwndTab == NULL) { + + // handle error + + } + + // Add a tab for each of the child dialog boxes. + + tie.mask = TCIF_TEXT | TCIF_IMAGE; + + tie.iImage = -1; + + for (i = 1; i <= GetNumberofPorts(); i++) + { + // Only allow UI on ax.25 ports + + PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntry(i); + + if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL + if (PORTVEC->PORTCONTROL.PROTOCOL == 10) // Pactor/WINMOR + continue; + + sprintf(PortNo, "Port %2d", GetPortNumber(i)); + PortNum[tab] = GetPortNumber(i); + + tie.pszText = PortNo; + TabCtrl_InsertItem(pHdr->hwndTab, tab, &tie); + + pHdr->apRes[tab++] = DoLockDlgRes("PORTPAGE"); + + } + + PageCount = tab; + + // Determine the bounding rectangle for all child dialog boxes. + + SetRectEmpty(&rcTab); + + for (i = 0; i < PageCount; i++) + { + if (pHdr->apRes[i]->cx > rcTab.right) + rcTab.right = pHdr->apRes[i]->cx; + + if (pHdr->apRes[i]->cy > rcTab.bottom) + rcTab.bottom = pHdr->apRes[i]->cy; + + } + + MapDialogRect(hwndDlg, &rcTab); + +// rcTab.right = rcTab.right * LOWORD(dwDlgBase) / 4; + +// rcTab.bottom = rcTab.bottom * HIWORD(dwDlgBase) / 8; + + // Calculate how large to make the tab control, so + + // the display area can accomodate all the child dialog boxes. + + TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab); + + OffsetRect(&rcTab, cxMargin - rcTab.left, cyMargin - rcTab.top); + + // Calculate the display rectangle. + + CopyRect(&pHdr->rcDisplay, &rcTab); + + TabCtrl_AdjustRect(pHdr->hwndTab, FALSE, &pHdr->rcDisplay); + + // Set the size and position of the tab control, buttons, + + // and dialog box. + + SetWindowPos(pHdr->hwndTab, NULL, rcTab.left, rcTab.top, rcTab.right - rcTab.left, rcTab.bottom - rcTab.top, SWP_NOZORDER); + + // Move the Buttons to bottom of page + + pos=rcTab.left+cxMargin; + + + // Size the dialog box. + + SetWindowPos(hwndDlg, NULL, 0, 0, rcTab.right + cyMargin + 2 * GetSystemMetrics(SM_CXDLGFRAME), + rcTab.bottom + 2 * cyMargin + 2 * GetSystemMetrics(SM_CYDLGFRAME) + GetSystemMetrics(SM_CYCAPTION), + SWP_NOMOVE | SWP_NOZORDER); + + // Simulate selection of the first item. + + OnSelChanged(hwndDlg); + +} + +// DoLockDlgRes - loads and locks a dialog template resource. + +// Returns a pointer to the locked resource. + +// lpszResName - name of the resource + +DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName) +{ + HRSRC hrsrc = FindResource(NULL, lpszResName, RT_DIALOG); + HGLOBAL hglb = LoadResource(hInst, hrsrc); + + return (DLGTEMPLATE *) LockResource(hglb); +} + +//The following function processes the TCN_SELCHANGE notification message for the main dialog box. The function destroys the dialog box for the outgoing page, if any. Then it uses the CreateDialogIndirect function to create a modeless dialog box for the incoming page. + +// OnSelChanged - processes the TCN_SELCHANGE notification. + +// hwndDlg - handle of the parent dialog box + +VOID WINAPI OnSelChanged(HWND hwndDlg) +{ + char PortDesc[40]; + int Port; + + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndDlg, GWL_USERDATA); + + CurrentPage = TabCtrl_GetCurSel(pHdr->hwndTab); + + // Destroy the current child dialog box, if any. + + if (pHdr->hwndDisplay != NULL) + + DestroyWindow(pHdr->hwndDisplay); + + // Create the new child dialog box. + + pHdr->hwndDisplay = CreateDialogIndirect(hInst, pHdr->apRes[CurrentPage], hwndDlg, ChildDialogProc); + + hwndDisplay = pHdr->hwndDisplay; // Save + + Port = PortNum[CurrentPage]; + // Fill in the controls + + GetPortDescription(PortNum[CurrentPage], PortDesc); + + SetDlgItemText(hwndDisplay, IDC_PORTNAME, PortDesc); + +// CheckDlgButton(hwndDisplay, IDC_FROMFILE, SendFromFile[Port]); + +// SetDlgItemInt(hwndDisplay, IDC_INTERVAL, Interval[Port], FALSE); + + SetDlgItemText(hwndDisplay, IDC_UIDEST, &UIDEST[Port][0]); + SetDlgItemText(hwndDisplay, IDC_UIDIGIS, UIDigi[Port]); + + + +// SetDlgItemText(hwndDisplay, IDC_FILENAME, &FN[Port][0]); +// SetDlgItemText(hwndDisplay, IDC_MESSAGE, &Message[Port][0]); + + ShowWindow(pHdr->hwndDisplay, SW_SHOWNORMAL); + +} + +//The following function processes the WM_INITDIALOG message for each of the child dialog boxes. You cannot specify the position of a dialog box created using the CreateDialogIndirect function. This function uses the SetWindowPos function to position the child dialog within the tab control's display area. + +// OnChildDialogInit - Positions the child dialog box to fall + +// within the display area of the tab control. + +VOID WINAPI OnChildDialogInit(HWND hwndDlg) +{ + HWND hwndParent = GetParent(hwndDlg); + DLGHDR *pHdr = (DLGHDR *) GetWindowLong(hwndParent, GWL_USERDATA); + + SetWindowPos(hwndDlg, HWND_TOP, pHdr->rcDisplay.left, pHdr->rcDisplay.top, 0, 0, SWP_NOSIZE); +} + + +*/ + + +/* +VOID ProcessMessage(char * Payload, struct STATIONRECORD * Station) +{ + char MsgDest[10]; + struct APRSMESSAGE * Message; + struct APRSMESSAGE * ptr = Messages; + char * TextPtr = &Payload[11]; + char * SeqPtr; + int n = 0; + char FromCall[10] = " "; + struct tm * TM; + time_t NOW; + + memcpy(FromCall, Station->Callsign, (int)strlen(Station->Callsign)); + memcpy(MsgDest, &Payload[1], 9); + MsgDest[9] = 0; + + SeqPtr = strchr(TextPtr, '{'); + + if (SeqPtr) + { + *(SeqPtr++) = 0; + if(strlen(SeqPtr) > 6) + SeqPtr[7] = 0; + } + + if (_memicmp(TextPtr, "ack", 3) == 0) + { + // Message Ack. See if for one of our messages + + ptr = OutstandingMsgs; + + if (ptr == 0) + return; + + do + { + if (strcmp(ptr->FromCall, MsgDest) == 0 + && strcmp(ptr->ToCall, FromCall) == 0 + && strcmp(ptr->Seq, &TextPtr[3]) == 0) + { + // Message is acked + + ptr->Retries = 0; + ptr->Acked = TRUE; +// if (hMsgsOut) +// UpdateTXMessageLine(hMsgsOut, n, ptr); + + return; + } + ptr = ptr->Next; + n++; + + } while (ptr); + + return; + } + + Message = malloc(sizeof(struct APRSMESSAGE)); + memset(Message, 0, sizeof(struct APRSMESSAGE)); + strcpy(Message->FromCall, Station->Callsign); + strcpy(Message->ToCall, MsgDest); + + if (SeqPtr) + { + strcpy(Message->Seq, SeqPtr); + + // If a REPLY-ACK Seg, copy to LastRXSeq, and see if it acks a message + + if (SeqPtr[2] == '}') + { + struct APRSMESSAGE * ptr1; + int nn = 0; + + strcpy(Station->LastRXSeq, SeqPtr); + + ptr1 = OutstandingMsgs; + + while (ptr1) + { + if (strcmp(ptr1->FromCall, MsgDest) == 0 + && strcmp(ptr1->ToCall, FromCall) == 0 + && memcmp(&ptr1->Seq, &SeqPtr[3], 2) == 0) + { + // Message is acked + + ptr1->Acked = TRUE; + ptr1->Retries = 0; +// if (hMsgsOut) +// UpdateTXMessageLine(hMsgsOut, nn, ptr); + + break; + } + ptr1 = ptr1->Next; + nn++; + } + } + else + { + // Station is not using reply-ack - set to send simple numeric sequence (workround for bug in APRS Messanger + + Station->SimpleNumericSeq = TRUE; + } + } + + if (strlen(TextPtr) > 100) + TextPtr[100] = 0; + + strcpy(Message->Text, TextPtr); + + NOW = time(NULL); + + if (DefaultLocalTime) + TM = localtime(&NOW); + else + TM = gmtime(&NOW); + + sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + if (_stricmp(MsgDest, APRSCall) == 0 && SeqPtr) // ack it if it has a sequence + { + // For us - send an Ack + + char ack[30]; + + int n = sprintf(ack, ":%-9s:ack%s", Message->FromCall, Message->Seq); + PutAPRSMessage(ack, n); + } + + if (ptr == NULL) + { + Messages = Message; + } + else + { + n++; + while(ptr->Next) + { + ptr = ptr->Next; + n++; + } + ptr->Next = Message; + } + + if (strcmp(MsgDest, APRSCall) == 0) // to me? + { + } +} + +*/ + +VOID APRSSecTimer() +{ + + // Check Message Retries + + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + int n = 0; + + if (SendWX) + SendWeatherBeacon(); + + + while (ptr) + { + if (ptr->Acked == FALSE) + { + if (ptr->Retries) + { + ptr->RetryTimer--; + + if (ptr->RetryTimer == 0) + { + ptr->Retries--; + + if (ptr->Retries) + { + // Send Again + + char Msg[255]; + APRSHEARDRECORD * STN; + + sprintf(Msg, ":%-9s:%s{%s", ptr->ToCall, ptr->Text, ptr->Seq); + + STN = FindStationInMH(ptr->ToCall); + + if (STN) + SendAPRSMessage(Msg, STN->rfPort); + else + { + if (memcmp(ptr->ToCall, "SERVER ", 9)) + SendAPRSMessage(Msg, -1); // All RF ports unless to SERVER + SendAPRSMessage(Msg, 0); // IS + } + ptr->RetryTimer = RetryTimer; + } + } + } + } + + ptr = ptr->Next; + n++; + } +} + +double radians(double Degrees) +{ + return M_PI * Degrees / 180; +} +double degrees(double Radians) +{ + return Radians * 180 / M_PI; +} + +double Distance(double laa, double loa, double lah, double loh, BOOL KM) +{ + +/* + +'Great Circle Calculations. + +'dif = longitute home - longitute away + + +' (this should be within -180 to +180 degrees) +' (Hint: This number should be non-zero, programs should check for +' this and make dif=0.0001 as a minimum) +'lah = latitude of home +'laa = latitude of away + +'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif)) +'distance = dis / 180 * pi * ERAD +'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis))) + +'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians +*/ + + loh = radians(loh); lah = radians(lah); + loa = radians(loa); laa = radians(laa); + + loh = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945; + + if (KM) + loh *= 1.60934; + + return loh; +} + + +double myDistance(double laa, double loa, BOOL KM) +{ + double lah, loh; + + GetAPRSLatLon(&lah, &loh); + + return Distance(laa, loa, lah, loh, KM); +} + +/* + +'Great Circle Calculations. + +'dif = longitute home - longitute away + + +' (this should be within -180 to +180 degrees) +' (Hint: This number should be non-zero, programs should check for +' this and make dif=0.0001 as a minimum) +'lah = latitude of home +'laa = latitude of away + +'dis = ArcCOS(Sin(lah) * Sin(laa) + Cos(lah) * Cos(laa) * Cos(dif)) +'distance = dis / 180 * pi * ERAD +'angle = ArcCOS((Sin(laa) - Sin(lah) * Cos(dis)) / (Cos(lah) * Sin(dis))) + +'p1 = 3.1415926535: P2 = p1 / 180: Rem -- PI, Deg =>= Radians + + + loh = radians(loh); lah = radians(lah); + loa = radians(loa); laa = radians(laa); + + loh = 60*degrees(acos(sin(lah) * sin(laa) + cos(lah) * cos(laa) * cos(loa-loh))) * 1.15077945; + + if (KM) + loh *= 1.60934; + + return loh; +} +*/ + +double Bearing(double lat2, double lon2, double lat1, double lon1) +{ + double dlat, dlon, TC1; + + lat1 = radians(lat1); + lat2 = radians(lat2); + lon1 = radians(lon1); + lon2 = radians(lon2); + + dlat = lat2 - lat1; + dlon = lon2 - lon1; + + if (dlat == 0 || dlon == 0) return 0; + + TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2))); + TC1 = degrees(TC1); + + if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270; + + if (dlat > 0) + { + if (dlon > 0) return -TC1; + if (dlon < 0) return 360 - TC1; + return 0; + } + + if (dlat < 0) + { + if (dlon > 0) return TC1 = 180 - TC1; + if (dlon < 0) return TC1 = 180 - TC1; // 'ok? + return 180; + } + + return 0; +} + + +double myBearing(double lat2, double lon2) +{ + double lat1, lon1; + + GetAPRSLatLon(&lat1, &lon1); + + return Bearing(lat2, lon2, lat1, lon1); +} +/* + + + + + lat1 = radians(lat1); + lat2 = radians(lat2); + lon1 = radians(lon1); + lon2 = radians(lon2); + + dlat = lat2 - lat1; + dlon = lon2 - lon1; + + if (dlat == 0 || dlon == 0) return 0; + + TC1 = atan((sin(lon1 - lon2) * cos(lat2)) / (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon1 - lon2))); + TC1 = degrees(TC1); + + if (fabs(TC1) > 89.5) if (dlon > 0) return 90; else return 270; + + if (dlat > 0) + { + if (dlon > 0) return -TC1; + if (dlon < 0) return 360 - TC1; + return 0; + } + + if (dlat < 0) + { + if (dlon > 0) return TC1 = 180 - TC1; + if (dlon < 0) return TC1 = 180 - TC1; // 'ok? + return 180; + } + + return 0; +} +*/ + +// Weather Data + +static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + +VOID SendWeatherBeacon() +{ + char Msg[256]; + char DD[3]=""; + char HH[3]=""; + char MM[3]=""; + char Lat[10], Lon[10]; + size_t Len; + int index; + char WXMessage[1024]; + char * WXptr; + char * WXend; + time_t WXTime; + time_t now = time(NULL); + FILE * hFile; + struct tm * TM; + struct stat STAT; + + WXCounter++; + + if (WXCounter < WXInterval * 60) + return; + + WXCounter = 0; + +// Debugprintf("BPQAPRS - Trying to open WX file %s", WXFileName); + + if (stat(WXFileName, &STAT)) + { + Debugprintf("APRS WX File %s stat() failed %d", WXFileName, GetLastError()); + return; + } + + WXTime = (now - STAT.st_mtime) /60; // Minutes + + if (WXTime > (3 * WXInterval)) + { + Debugprintf("APRS Send WX File %s too old - %d minutes", WXFileName, WXTime); + return; + } + + hFile = fopen(WXFileName, "rb"); + + if (hFile) + Len = fread(WXMessage, 1, 1024, hFile); + else + { + Debugprintf("APRS WX File %s open() failed %d", WXFileName, GetLastError()); + return; + } + + + if (Len < 30) + { + Debugprintf("BPQAPRS - WX file %s is too short - %d Chars", WXFileName, Len); + fclose(hFile); + return; + } + + WXMessage[Len] = 0; + + // see if wview format + +//04-09-13, 2245 +//TempIn 23 +//TempEx 18 +//WindHi 0 +//WindAv 0 +//WindDr 200 +//BarmPs 30167 +//HumdIn 56 +//HumdEx 100 +//RnFall 0.00 +//DailyRnFall 0.00 + + if (strstr(WXMessage, "TempIn")) + { + int Wind = 0; + int Gust = 0; + int Temp = 0; + int Winddir = 0; + int Humidity = 0; + int Raintoday = 0; + int Rain24hrs = 0; + int Pressure = 0; + + char * ptr; + + ptr = strstr(WXMessage, "TempEx"); + if (ptr) + Temp = (int)(atof(ptr + 7) * 1.8) + 32; + + ptr = strstr(WXMessage, "WindHi"); + if (ptr) + Gust = atoi(ptr + 7); + + ptr = strstr(WXMessage, "WindAv"); + if (ptr) + Wind = atoi(ptr + 7); + + ptr = strstr(WXMessage, "WindDr"); + if (ptr) + Winddir = atoi(ptr + 7); + + ptr = strstr(WXMessage, "BarmPs"); + if (ptr) + Pressure = (int)(atof(ptr + 7) * 0.338638866667); // Inches to 1/10 millbars + + ptr = strstr(WXMessage, "HumdEx"); + if (ptr) + Humidity = atoi(ptr + 7); + + ptr = strstr(WXMessage, "RnFall"); + if (ptr) + Rain24hrs = (int)(atof(ptr + 7) * 100.0); + + ptr = strstr(WXMessage, "DailyRnFall"); + if (ptr) + Raintoday = (int)(atof(ptr + 12) * 100.0); + + if (Humidity > 99) + Humidity = 99; + + sprintf(WXMessage, "%03d/%03dg%03dt%03dr%03dP%03dp%03dh%02db%05d", + Winddir, Wind, Gust, Temp, 0, Raintoday, Rain24hrs, Humidity, Pressure); + + } + + WXptr = strchr(WXMessage, 10); + + if (WXptr) + { + WXend = strchr(++WXptr, 13); + if (WXend == 0) + WXend = strchr(WXptr, 10); + if (WXend) + *WXend = 0; + } + else + WXptr = &WXMessage[0]; + + // Get DDHHMM from Filetime + + TM = gmtime(&STAT.st_mtime); + + sprintf(DD, "%02d", TM->tm_mday); + sprintf(HH, "%02d", TM->tm_hour); + sprintf(MM, "%02d", TM->tm_min); + + GetAPRSLatLonString(Lat, Lon); + + Len = sprintf(Msg, "@%s%s%sz%s/%s_%s%s", DD, HH, MM, Lat, Lon, WXptr, WXComment); + + Debugprintf(Msg); + + for (index = 0; index < MaxBPQPortNo; index++) + { + if (WXPort[index]) + SendAPRSMessageEx(Msg, index, WXCall, FALSE); + } + + fclose(hFile); +} + + +/* +Jan 22 2012 14:10 +123/005g011t031r000P000p000h00b10161 + +/MITWXN Mitchell IN weather Station N9LYA-3 {UIV32} +< previous + +@221452z3844.42N/08628.33W_203/006g007t032r000P000p000h00b10171 +Complete Weather Report Format — with Lat/Long position, no Timestamp +! or = Lat Sym Table ID Long Symbol Code _ Wind Directn/ Speed Weather Data APRS Software WX Unit uuuu + 1 8 1 9 1 7 n 1 2-4 +Examples +!4903.50N/07201.75W_220/004g005t077r000p000P000h50b09900wRSW +!4903.50N/07201.75W_220/004g005t077r000p000P000h50b.....wRSW + +*/ + +// Web Server Code + +// The actual HTTP socket code is in bpq32.dll. Any requests for APRS data are passed in +// using a Named Pipe. The request looks exactly like one from a local socket, and the respone is +// a fully pormatted HTTP packet + + +#define InputBufferLen 1000 + + +#define MaxSessions 100 + + +HANDLE PipeHandle; + +int HTTPPort = 80; +BOOL IPV6 = TRUE; + +#define MAX_PENDING_CONNECTS 5 + +BOOL OpenSockets6(); + +char HTDocs[MAX_PATH] = "HTML"; +char SpecialDocs[MAX_PATH] = "Special Pages"; + +char SymbolText[192][20] = { + +"Police Stn", "No Symbol", "Digi", "Phone", "DX Cluster", "HF Gateway", "Plane sm", "Mob Sat Stn", +"WheelChair", "Snowmobile", "Red Cross", "Boy Scout", "Home", "X", "Red Dot", "Circle (0)", +"Circle (1)", "Circle (2)", "Circle (3)", "Circle (4)", "Circle (5)", "Circle (6)", "Circle (7)", "Circle (8)", +"Circle (9)", "Fire", "Campground", "Motorcycle", "Rail Eng.", "Car", "File svr", "HC Future", + +"Aid Stn", "BBS", "Canoe", "No Symbol", "Eyeball", "Tractor", "Grid Squ.", "Hotel", +"Tcp/ip", "No Symbol", "School", "Usr Log-ON", "MacAPRS", "NTS Stn", "Balloon", "Police", +"TBD", "Rec Veh'le", "Shuttle", "SSTV", "Bus", "ATV", "WX Service", "Helo", +"Yacht", "WinAPRS", "Jogger", "Triangle", "PBBS", "Plane lrge", "WX Station", "Dish Ant.", + +"Ambulance", "Bike", "ICP", "Fire Station", "Horse", "Fire Truck", "Glider", "Hospital", +"IOTA", "Jeep", "Truck", "Laptop", "Mic-E Rptr", "Node", "EOC", "Rover", +"Grid squ.", "Antenna", "Power Boat", "Truck Stop", "Truck 18wh", "Van", "Water Stn", "XAPRS", +"Yagi", "Shelter", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "", "", + +"Emergency", "No Symbol", "No. Digi", "Bank", "No Symbol", "No. Diam'd", "Crash site", "Cloudy", +"MEO", "Snow", "Church", "Girl Scout", "Home (HF)", "UnknownPos", "Destination", "No. Circle", +"No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", "No Symbol", +"Petrol Stn", "Hail", "Park", "Gale Fl", "No Symbol", "No. Car", "Info Kiosk", "Hurricane", + +"No. Box", "Snow blwng", "Coast G'rd", "Drizzle", "Smoke", "Fr'ze Rain", "Snow Shwr", "Haze", +"Rain Shwr", "Lightning", "Kenwood", "Lighthouse", "No Symbol", "Nav Buoy", "Rocket", "Parking ", +"Quake", "Restaurant", "Sat/Pacsat", "T'storm", "Sunny", "VORTAC", "No. WXS", "Pharmacy", +"No Symbol", "No Symbol", "Wall Cloud", "No Symbol", "No Symbol", "No. Plane", "No. WX Stn", "Rain", + +"No. Diamond", "Dust blwng", "No. CivDef", "DX Spot", "Sleet", "Funnel Cld", "Gale", "HAM store", +"No. Blk Box", "WorkZone", "SUV", "Area Locns", "Milepost", "No. Triang", "Circle sm", "Part Cloud", +"No Symbol", "Restrooms", "No. Boat", "Tornado", "No. Truck", "No. Van", "Flooding", "No Symbol", +"Sky Warn", "No Symbol", "Fog", "No Symbol", "No Symbol", "No Symbol", "", ""}; + +// All Calls (8 per line) + +//EI7IG-1 +//G7TKK-1 +//GB7GL-B +//GM1TCN +//GM8BPQ +//GM8BPQ-14 +//LA2VPA-9 +//LA3FIA-10 +//LA6JF-2LD4STM0CHK-7M0OZH-7MB7UFO-1MB7UNMM0DXE-15PA2AYX-9 +//PA3AQW-5PD1CPD5LWD-2PI1ECO + + +char * DoSummaryLine(struct STATIONRECORD * ptr, int n, int Width) +{ + static char Line2[80]; + int x; + char XCall[256]; + char * ptr1 = ptr->Callsign; + char * ptr2 = XCall; + + // Object Names can contain spaces + + while(*ptr1) + { + if (*ptr1 == ' ') + { + memcpy(ptr2, "%20", 3); + ptr2 += 3; + } + else + *(ptr2++) = *ptr1; + + ptr1++; + } + + *ptr2 = 0; + + + // Object Names can contain spaces + + + sprintf(Line2, "%s", + XCall, ptr->Callsign); + + x = ++n/Width; + x = x * Width; + + if (x == n) + strcat(Line2, ""); + + return Line2; +} + +char * DoDetailLine(struct STATIONRECORD * ptr, BOOL LocalTime, BOOL KM) +{ + static char Line[512]; + double Lat = ptr->Lat; + double Lon = ptr->Lon; + char NS='N', EW='E'; + + char LatString[20], LongString[20], DistString[20], BearingString[20]; + int Degrees; + double Minutes; + char Time[80]; + struct tm * TM; + char XCall[256]; + + char * ptr1 = ptr->Callsign; + char * ptr2 = XCall; + + // Object Names can contain spaces + + while(*ptr1) + { + if (*ptr1 == ' ') + { + memcpy(ptr2, "%20", 3); + ptr2 += 3; + } + else + *(ptr2++) = *ptr1; + + ptr1++; + } + + *ptr2 = 0; + + +// if (ptr->ObjState == '_') // Killed Object +// return; + + if (LocalTime) + TM = localtime(&ptr->TimeLastUpdated); + else + TM = gmtime(&ptr->TimeLastUpdated); + + sprintf(Time, "%.2d:%.2d:%.2d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + if (ptr->Lat < 0) + { + NS = 'S'; + Lat=-Lat; + } + if (Lon < 0) + { + EW = 'W'; + Lon=-Lon; + } + +#pragma warning(push) +#pragma warning(disable:4244) + + Degrees = Lat; + Minutes = Lat * 60.0 - (60 * Degrees); + + sprintf(LatString,"%2d°%05.2f'%c", Degrees, Minutes, NS); + + Degrees = Lon; + +#pragma warning(pop) + + Minutes = Lon * 60 - 60 * Degrees; + + sprintf(LongString, "%3d°%05.2f'%c",Degrees, Minutes, EW); + + sprintf(DistString, "%6.1f", myDistance(ptr->Lat, ptr->Lon, KM)); + sprintf(BearingString, "%3.0f", myBearing(ptr->Lat, ptr->Lon)); + + sprintf(Line, " %s%s%s%s %s%s%s%s\r\n", + XCall, ptr->Callsign, + (strchr(ptr->Path, '*'))? "*": "", &SymbolText[ptr->iconRow << 4 | ptr->iconCol][0], LatString, LongString, DistString, BearingString, Time); + + return Line; +} + + +int CompareFN(const void *a, const void *b) +{ + const struct STATIONRECORD * x = a; + const struct STATIONRECORD * y = b; + + x = x->Next; + y = y->Next; + + return strcmp(x->Callsign, y->Callsign); + + /* strcmp functions works exactly as expected from + comparison function */ +} + + + +char * CreateStationList(BOOL RFOnly, BOOL WX, BOOL Mobile, char Objects, int * Count, char * Param, BOOL KM) +{ + char * Line = malloc(100000); + struct STATIONRECORD * ptr = *StationRecords; + int n = 0, i; + struct STATIONRECORD * List[1000]; + int TableWidth = 8; + BOOL LocalTime = DefaultLocalTime; + + if (strstr(Param, "time=local")) + LocalTime = TRUE; + else if (strstr(Param, "time=utc")) + LocalTime = FALSE; + + Line[0] = 0; + + if (Param && Param[0]) + { + char * Key, *Context; + + Key = strtok_s(Param, "=", &Context); + + if (_stricmp(Key, "width") == 0) + TableWidth = atoi(Context); + + if (TableWidth == 0) + TableWidth = 8; + } + + // Build list of calls + + while (ptr) + { + if (ptr->ObjState == Objects && ptr->Lat != 0.0 && ptr->Lon != 0.0) + { + if ((WX && (ptr->LastWXPacket[0] == 0)) || (RFOnly && (ptr->LastPort == 0)) || + (Mobile && ((ptr->Speed < 0.1) || ptr->LastWXPacket[0] != 0))) + { + ptr = ptr->Next; + continue; + } + + List[n++] = ptr; + + if (n > 999) + break; + + } + ptr = ptr->Next; + } + + if (n > 1) + qsort(List, n, sizeof(void *), CompareFN); + + for (i = 0; i < n; i++) + { + if (RFOnly) + strcat(Line, DoDetailLine(List[i], LocalTime, KM)); + else + strcat(Line, DoSummaryLine(List[i], i, TableWidth)); + } + + *Count = n; + + return Line; + +} + +char * APRSLookupKey(struct APRSConnectionInfo * sockptr, char * Key, BOOL KM) +{ + struct STATIONRECORD * stn = sockptr->SelCall; + + if (strcmp(Key, "##MY_CALLSIGN##") == 0) + return _strdup(LoppedAPRSCall); + + if (strcmp(Key, "##CALLSIGN##") == 0) + return _strdup(sockptr->Callsign); + + if (strcmp(Key, "##CALLSIGN_NOSSID##") == 0) + { + char * Call = _strdup(sockptr->Callsign); + char * ptr = strchr(Call, '-'); + if (ptr) + *ptr = 0; + return Call; + } + + if (strcmp(Key, "##MY_WX_CALLSIGN##") == 0) + return _strdup(LoppedAPRSCall); + + if (strcmp(Key, "##MY_BEACON_COMMENT##") == 0) + return _strdup(StatusMsg); + + if (strcmp(Key, "##MY_WX_BEACON_COMMENT##") == 0) + return _strdup(WXComment); + + if (strcmp(Key, "##MILES_KM##") == 0) + if (KM) + return _strdup("KM"); + else + return _strdup("Miles"); + + if (strcmp(Key, "##EXPIRE_TIME##") == 0) + { + char val[80]; + sprintf(val, "%d", ExpireTime); + return _strdup(val); + } + + if (strcmp(Key, "##LOCATION##") == 0) + { + char val[80]; + double Lat = sockptr->SelCall->Lat; + double Lon = sockptr->SelCall->Lon; + char NS='N', EW='E'; + char LatString[20]; + int Degrees; + double Minutes; + + if (Lat < 0) + { + NS = 'S'; + Lat=-Lat; + } + if (Lon < 0) + { + EW = 'W'; + Lon=-Lon; + } + +#pragma warning(push) +#pragma warning(disable:4244) + + Degrees = Lat; + Minutes = Lat * 60.0 - (60 * Degrees); + + sprintf(LatString,"%2d°%05.2f'%c",Degrees, Minutes, NS); + + Degrees = Lon; + +#pragma warning(pop) + + Minutes = Lon * 60 - 60 * Degrees; + + sprintf(val,"%s %3d°%05.2f'%c", LatString, Degrees, Minutes, EW); + + return _strdup(val); + } + + if (strcmp(Key, "##LOCDDMMSS##") == 0) + { + char val[80]; + double Lat = sockptr->SelCall->Lat; + double Lon = sockptr->SelCall->Lon; + char NS='N', EW='E'; + char LatString[20]; + int Degrees; + double Minutes; + + // 48.45.18N, 002.18.37E + + if (Lat < 0) + { + NS = 'S'; + Lat=-Lat; + } + if (Lon < 0) + { + EW = 'W'; + Lon=-Lon; + } + +#pragma warning(push) +#pragma warning(disable:4244) + + Degrees = Lat; + Minutes = Lat * 60.0 - (60 * Degrees); +// IntMins = Minutes; +// Seconds = Minutes * 60.0 - (60 * IntMins); + + sprintf(LatString,"%2d.%05.2f%c",Degrees, Minutes, NS); + + Degrees = Lon; + Minutes = Lon * 60.0 - 60 * Degrees; +// IntMins = Minutes; +// Seconds = Minutes * 60.0 - (60 * IntMins); + +#pragma warning(pop) + + sprintf(val,"%s, %03d.%05.2f%c", LatString, Degrees, Minutes, EW); + + return _strdup(val); + } + + if (strcmp(Key, "##LATLON##") == 0) + { + char val[80]; + double Lat = sockptr->SelCall->Lat; + double Lon = sockptr->SelCall->Lon; + char NS='N', EW='E'; + + // 58.5, -6.2 + + sprintf(val,"%f, %f", Lat, Lon); + return _strdup(val); + } + + if (strcmp(Key, "##STATUS_TEXT##") == 0) + return _strdup(stn->Status); + + if (strcmp(Key, "##LASTPACKET##") == 0) + return _strdup(stn->LastPacket); + + + if (strcmp(Key, "##LAST_HEARD##") == 0) + { + char Time[80]; + struct tm * TM; + time_t Age = time(NULL) - stn->TimeLastUpdated; + + TM = gmtime(&Age); + + sprintf(Time, "%.2d:%.2d:%.2d", TM->tm_hour, TM->tm_min, TM->tm_sec); + + return _strdup(Time); + } + + if (strcmp(Key, "##FRAME_HEADER##") == 0) + return _strdup(stn->Path); + + if (strcmp(Key, "##FRAME_INFO##") == 0) + return _strdup(stn->LastWXPacket); + + if (strcmp(Key, "##BEARING##") == 0) + { + char val[80]; + + sprintf(val, "%03.0f", myBearing(sockptr->SelCall->Lat, sockptr->SelCall->Lon)); + return _strdup(val); + } + + if (strcmp(Key, "##COURSE##") == 0) + { + char val[80]; + + sprintf(val, "%03.0f", stn->Course); + return _strdup(val); + } + + if (strcmp(Key, "##SPEED_MPH##") == 0) + { + char val[80]; + + sprintf(val, "%5.1f", stn->Speed); + return _strdup(val); + } + + if (strcmp(Key, "##DISTANCE##") == 0) + { + char val[80]; + + sprintf(val, "%5.1f", myDistance(sockptr->SelCall->Lat, sockptr->SelCall->Lon, KM)); + return _strdup(val); + } + + + + if (strcmp(Key, "##WIND_DIRECTION##") == 0) + { + char val[80]; + + sprintf(val, "%03d", sockptr->WindDirn); + return _strdup(val); + } + + if (strcmp(Key, "##WIND_SPEED_MPH##") == 0) + { + char val[80]; + + sprintf(val, "%d", sockptr->WindSpeed); + return _strdup(val); + } + + if (strcmp(Key, "##WIND_GUST_MPH##") == 0) + { + char val[80]; + + sprintf(val, "%d", sockptr->WindGust); + return _strdup(val); + } + + if (strcmp(Key, "##TEMPERATURE_F##") == 0) + { + char val[80]; + + sprintf(val, "%d", sockptr->Temp); + return _strdup(val); + } + + if (strcmp(Key, "##HUMIDITY##") == 0) + { + char val[80]; + + sprintf(val, "%d", sockptr->Humidity); + return _strdup(val); + } + + if (strcmp(Key, "##PRESSURE_HPA##") == 0) + { + char val[80]; + + sprintf(val, "%05.1f", sockptr->Pressure /10.0); + return _strdup(val); + } + + if (strcmp(Key, "##RAIN_TODAY_IN##") == 0) + { + char val[80]; + + sprintf(val, "%5.2f", sockptr->RainToday /100.0); + return _strdup(val); + } + + + if (strcmp(Key, "##RAIN_24_IN##") == 0) + { + char val[80]; + + sprintf(val, "%5.2f", sockptr->RainLastDay /100.0); + return _strdup(val); + } + + + if (strcmp(Key, "##RAIN_HOUR_IN##") == 0) + { + char val[80]; + + sprintf(val, "%5.2f", sockptr->RainLastHour /100.0); + return _strdup(val); + } + + if (strcmp(Key, "##MAP_LAT_LON##") == 0) + { + char val[256]; + + sprintf(val, "%f,%f", stn->Lat, stn->Lon); + return _strdup(val); + } + + if (strcmp(Key, "##SYMBOL_DESCRIPTION##") == 0) + return _strdup(&SymbolText[stn->iconRow << 4 | stn->iconCol][0]); + + +/* +##WIND_SPEED_MS## - wind speed metres/sec +##WIND_SPEED_KMH## - wind speed km/hour +##WIND_GUST_MPH## - wind gust miles/hour +##WIND_GUST_MS## - wind gust metres/sec +##WIND_GUST_KMH## - wind gust km/hour +##WIND_CHILL_F## - wind chill F +##WIND_CHILL_C## - wind chill C +##TEMPERATURE_C## - temperature C +##DEWPOINT_F## - dew point temperature F +##DEWPOINT_C## - dew point temperature C +##PRESSURE_IN## - pressure inches of mercury +##PRESSURE_HPA## - pressure hPa (mb) +##RAIN_HOUR_MM## - rain in last hour mm +##RAIN_TODAY_MM## - rain today mm +##RAIN_24_MM## - rain in last 24 hours mm +##FRAME_HEADER## - frame header of the last posit heard from the station +##FRAME_INFO## - information field of the last posit heard from the station +##MAP_LARGE_SCALE##" - URL of a suitable large scale map on www.vicinity.com +##MEDIUM_LARGE_SCALE##" - URL of a suitable medium scale map on www.vicinity.com +##MAP_SMALL_SCALE##" - URL of a suitable small scale map on www.vicinity.com +##MY_LOCATION## - 'Latitude', 'Longitude' in 'Station Setup' +##MY_STATUS_TEXT## - status text configured in 'Status Text' +##MY_SYMBOL_DESCRIPTION## - 'Symbol' that would be shown for our station in 'Station List' +##HIT_COUNTER## - The number of times the page has been accessed +##DOCUMENT_LAST_CHANGED## - The date/time the page was last edited + +##FRAME_HEADER## - frame header of the last posit heard from the station +##FRAME_INFO## - information field of the last posit heard from the station + +*/ + return NULL; +} + +VOID APRSProcessSpecialPage(struct APRSConnectionInfo * sockptr, char * Buffer, int FileSize, char * StationTable, int Count, BOOL WX, BOOL KM) +{ + // replaces ##xxx### constructs with the requested data + + char * NewMessage = malloc(250000); + char * ptr1 = Buffer, * ptr2, * ptr3, * ptr4, * NewPtr = NewMessage; + size_t PrevLen; + size_t BytesLeft = FileSize; + size_t NewFileSize = FileSize; + char * StripPtr = ptr1; + int HeaderLen; + char Header[256]; + + if (WX && sockptr->SelCall && sockptr->SelCall->LastWXPacket) + { + DecodeWXReport(sockptr, sockptr->SelCall->LastWXPacket); + } + + // strip comments blocks + + while (ptr4 = strstr(ptr1, ""); + if (ptr2) + { + PrevLen = (ptr4 - ptr1); + memcpy(StripPtr, ptr1, PrevLen); + StripPtr += PrevLen; + ptr1 = ptr2 + 3; + BytesLeft = FileSize - (ptr1 - Buffer); + } + } + + + memcpy(StripPtr, ptr1, BytesLeft); + StripPtr += BytesLeft; + + BytesLeft = StripPtr - Buffer; + + FileSize = (int)BytesLeft; + NewFileSize = FileSize; + ptr1 = Buffer; + ptr1[FileSize] = 0; + +loop: + ptr2 = strstr(ptr1, "##"); + + if (ptr2) + { + PrevLen = (ptr2 - ptr1); // Bytes before special text + + ptr3 = strstr(ptr2+2, "##"); + + if (ptr3) + { + char Key[80] = ""; + size_t KeyLen; + char * NewText; + size_t NewTextLen; + + ptr3 += 2; + KeyLen = ptr3 - ptr2; + + if (KeyLen < 80) + memcpy(Key, ptr2, KeyLen); + + if (strcmp(Key, "##STATION_TABLE##") == 0) + { + NewText = _strdup(StationTable); + } + else + { + if (strcmp(Key, "##TABLE_COUNT##") == 0) + { + char val[80]; + sprintf(val, "%d", Count); + NewText = _strdup(val); + } + else + NewText = APRSLookupKey(sockptr, Key, KM); + } + + if (NewText) + { + NewTextLen = strlen(NewText); + NewFileSize = NewFileSize + NewTextLen - KeyLen; + // NewMessage = realloc(NewMessage, NewFileSize); + + memcpy(NewPtr, ptr1, PrevLen); + NewPtr += PrevLen; + memcpy(NewPtr, NewText, NewTextLen); + NewPtr += NewTextLen; + + free(NewText); + NewText = NULL; + } + else + { + // Key not found, so just leave + + memcpy(NewPtr, ptr1, PrevLen + KeyLen); + NewPtr += (PrevLen + KeyLen); + } + + ptr1 = ptr3; // Continue scan from here + BytesLeft = Buffer + FileSize - ptr3; + } + else // Unmatched ## + { + memcpy(NewPtr, ptr1, PrevLen + 2); + NewPtr += (PrevLen + 2); + ptr1 = ptr2 + 2; + } + goto loop; + } + + // Copy Rest + + memcpy(NewPtr, ptr1, BytesLeft); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)NewFileSize); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, NewMessage, (int)NewFileSize, 0); + + free (NewMessage); + free(StationTable); + + return; +} + +VOID APRSSendMessageFile(struct APRSConnectionInfo * sockptr, char * FN) +{ + int FileSize = 0; + char * MsgBytes = 0; + char * SaveMsgBytes = 0; + + char MsgFile[MAX_PATH]; + FILE * hFile; + BOOL Special = FALSE; + int HeaderLen; + char Header[256]; + char * Param = 0; + struct stat STAT; + int Sent; + char * ptr; + + if (strchr(FN, '?')) + strtok_s(FN, "?", &Param); + + UndoTransparency(FN); + + if (strstr(FN, "..")) + { + FN[0] = '/'; + FN[1] = 0; + } + + if (strcmp(FN, "/") == 0) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s/index.html", BPQDirectory, APRSDir, SpecialDocs); + else + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s%s", BPQDirectory, APRSDir, SpecialDocs, &FN[5]); + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + { + // Try normal pages + + + if (strcmp(FN, "/") == 0) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s/index.html", BPQDirectory, APRSDir, HTDocs); + else + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s/%s%s", BPQDirectory,APRSDir, HTDocs, &FN[5]); + + // My standard page set is now hard coded + + + + MsgBytes = SaveMsgBytes = GetStandardPage(&FN[6], &FileSize); + + if (MsgBytes) + { + if (FileSize == 0) + FileSize = strlen(MsgBytes); + } + else + { + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + { + HeaderLen = sprintf(Header, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + send(sockptr->sock, Header, HeaderLen, 0); + return; + } + } + } + else + Special = TRUE; + + if (FileSize == 0) // Not from internal template + { + if (stat(MsgFile, &STAT) == 0) + FileSize = STAT.st_size; + + MsgBytes = SaveMsgBytes = malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + } + + // if HTML file, look for ##...## substitutions + + if ((strstr(FN, "htm" ) || strstr(FN, "HTM")) && strstr(MsgBytes, "##" )) + { + // Build Station list, depending on URL + + int Count = 0; + BOOL RFOnly = !(strstr(_strlwr(FN), "rf") == NULL); // Leaves FN in lower case + BOOL WX =!(strstr(FN, "wx") == NULL); + BOOL Mobile = !(strstr(FN, "mobile") == NULL); + char Objects = (strstr(FN, "obj"))? '*' :0; + char * StationList; + BOOL KM = DefaultDistKM; + + if (Param == 0) + Param =""; + else + _strlwr(Param); + + if (strstr(Param, "dist=km")) + KM = TRUE; + else if (strstr(Param, "dist=miles")) + KM = FALSE; + + + StationList = CreateStationList(RFOnly, WX, Mobile, Objects, &Count, Param, KM); + + APRSProcessSpecialPage(sockptr, MsgBytes, FileSize, StationList, Count, WX, KM); + free (MsgBytes); + return; // ProcessSpecial has sent the reply + } + + ptr = FN; + + while (strchr(ptr, '.')) + { + ptr = strchr(ptr, '.'); + ++ptr; + } + + if (_stricmp(ptr, "jpg") == 0 || _stricmp(ptr, "jpeg") == 0 || _stricmp(ptr, "png") == 0 || _stricmp(ptr, "gif") == 0 || _stricmp(ptr, "ico") == 0) + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: image\r\n\r\n", FileSize); + else + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", FileSize); + + send(sockptr->sock, Header, HeaderLen, 0); + + Sent = send(sockptr->sock, MsgBytes, FileSize, 0); +// printf("Send %d %d\n", FileSize, Sent); + + while (Sent < FileSize) + { + FileSize -= Sent; + MsgBytes += Sent; + Sent = send(sockptr->sock, MsgBytes, FileSize, 0); +// printf("Send %d %d\n", FileSize, Sent); + if (Sent == -1) + { + Sleep(10); + Sent = 0; + } + } + + free (SaveMsgBytes); +} + +char WebHeader[] = "" + "" + "APRS Messaging" + "" + "" + "" + "" + "" + "" + "" + "" + "
APRS MapReceived MessagesSent MessagesSend MessageStation PagesReturn to Node Pages
" + "

%s's Messages

" + ""; + +char WebTXHeader[] = "" + "" + "APRS Messaging" + "" + "
FromToSeqTime Message
" + "" + "" + "" + "" + "" + "" + "
APRS MapReceived MessagesSent MessagesSend MessageStation PagesReturn to Node Pages
" + "

Message Sent by %s

" + ""; + +char WebLine[] = ""; + +char WebTXLine[] = "" + ""; + + +char WebTrailer[] = "
ToSeqTimeStatemessage
%s %s %s %s" + "Reply %s
%s %s %s %s %s
"; + +char SendMsgPage[] = "BPQ32 APRS Messaging" + "

APRS Message Input

" + "
" + "" + "" + "
To
Message
" + "

"; + +char APRSIndexPage[] = "BPQ32 Web Server APRS Pages" + "

" + "

BPQ32 APRS Server

" + "" + "" + "" + "" + "" + "" + "" + "
APRS MapReceived MessagesSent MessagesSend MessageStation PagesReturn to Node Pages
%s"; + +extern char Tail[]; + +VOID APRSProcessHTTPMessage(SOCKET sock, char * MsgPtr, BOOL LOCAL, BOOL COOKIE) +{ + int InputLen = 0; + int OutputLen = 0; + char * URL; + char * ptr; + struct APRSConnectionInfo CI; + struct APRSConnectionInfo * sockptr = &CI; + char Key[12] = ""; + char OutBuffer[100000]; + char Header[1024]; + int HeaderLen = 0; + + memset(&CI, 0, sizeof(CI)); + + sockptr->sock = sock; + + if (memcmp(MsgPtr, "POST" , 3) == 0) + { + char * To; + char * Msg = ""; + + URL = &MsgPtr[5]; + + ptr = strstr(URL, "\r\n\r\n"); + + if (ptr) + { + char * param, * context; + + UndoTransparency(ptr); + + param = strtok_s(ptr + 4, "&", &context); + + while (param) + { + char * val = strlop(param, '='); + + if (val) + { + if (_stricmp(param, "call") == 0) + To = _strupr(val); + else if (_stricmp(param, "message") == 0) + Msg = val; + else if (_stricmp(param, "Cancel") == 0) + { + + // Return APRS Index Page + + OutputLen = sprintf(OutBuffer, APRSIndexPage, "



Message Cancelled

"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + } + } + + param = strtok_s(NULL,"&", &context); + } + + strlop(To, ' '); + + if (strlen(To) < 2) + { + OutputLen = sprintf(OutBuffer, SendMsgPage, To); + OutputLen += sprintf(&OutBuffer[OutputLen], "

Invalid Callsign

"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + } + + if (Msg[0] == 0) + { + OutputLen = sprintf(OutBuffer, SendMsgPage, To); + OutputLen += sprintf(&OutBuffer[OutputLen], "

No Message

"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + } + + // Send APRS Messsage + + if (strlen(Msg) > 100) + Msg[100] = 0; + + InternalSendAPRSMessage(Msg, To); + + OutputLen = sprintf(OutBuffer, APRSIndexPage, "



Message Sent

"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + } + } + + URL = &MsgPtr[4]; + + ptr = strstr(URL, " HTTP"); + + if (ptr) + *ptr = 0; + + if (_stricmp(URL, "/APRS") == 0) + { + // Return APRS Index Page + + OutputLen = sprintf(OutBuffer, APRSIndexPage, ""); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + } + + + if (_memicmp(URL, "/aprs/msgs/entermsg", 19) == 0 || _memicmp(URL, "/aprs/entermsg", 14) == 0) + { + char * To = strchr(URL, '='); + + if (LOCAL == FALSE && COOKIE == FALSE) + { + // Send Not Authorized + + OutputLen = sprintf(OutBuffer, APRSIndexPage, "
Not authorized - please return to Node Menu and sign in"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)(OutputLen + strlen(Tail))); + send(sock, Header, HeaderLen, 0); + send(sock, OutBuffer, OutputLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return; + } + + + if (To) + { + To++; + UndoTransparency(To); + strlop(To, '&'); + } + else + To = ""; + + OutputLen = sprintf(OutBuffer, SendMsgPage, To); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + send(sockptr->sock, Header, HeaderLen, 0); + send(sockptr->sock, OutBuffer, OutputLen, 0); + + return; + + } + + else if (_memicmp(URL, "/aprs/msgs", 10) == 0) + { + // Return Messages Received Page + + struct APRSMESSAGE * ptr = SMEM->Messages; + int n = 0; + char BaseCall[10]; + char BaseFrom[10]; + char * MsgCall = LoppedAPRSCall; + BOOL OnlyMine = TRUE; + BOOL AllSSID = TRUE; + BOOL OnlySeq = FALSE; + BOOL ShowBulls = TRUE; + + // Parse parameters + + // ?call=g8bpq&bulls=true&seqonly=true&onlymine=true + + char * params = strchr(URL, '?'); + + if (params) + { + char * param, * context; + + param = strtok_s(++params, "&", &context); + + while (param) + { + char * val = strlop(param, '='); + + if (val) + { + strlop(val, ' '); + if (_stricmp(param, "call") == 0) + MsgCall = _strupr(val); + else if (_stricmp(param, "bulls") == 0) + ShowBulls = !_stricmp(val, "true"); + else if (_stricmp(param, "onlyseq") == 0) + OnlySeq = !_stricmp(val, "true"); + else if (_stricmp(param, "onlymine") == 0) + OnlyMine = !_stricmp(val, "true"); + else if (_stricmp(param, "AllSSID") == 0) + AllSSID = !_stricmp(val, "true"); + } + param = strtok_s(NULL,"&", &context); + } + } + if (AllSSID) + { + memcpy(BaseCall, MsgCall, 10); + strlop(BaseCall, '-'); + } + + OutputLen = sprintf(OutBuffer, WebHeader, MsgCall, MsgCall); + + while (ptr) + { + char ToLopped[11] = ""; + memcpy(ToLopped, ptr->ToCall, 10); + strlop(ToLopped, ' '); + + if (memcmp(ToLopped, "BLN", 3) == 0) + if (ShowBulls == TRUE) + goto wantit; + + if (strcmp(ToLopped, MsgCall) == 0) // to me? + goto wantit; + + if (strcmp(ptr->FromCall, MsgCall) == 0) // to me? + goto wantit; + + if (AllSSID) + { + memcpy(BaseFrom, ToLopped, 10); + strlop(BaseFrom, '-'); + + if (strcmp(BaseFrom, BaseCall) == 0) + goto wantit; + + memcpy(BaseFrom, ptr->FromCall, 10); + strlop(BaseFrom, '-'); + + if (strcmp(BaseFrom, BaseCall) == 0) + goto wantit; + + } + + if (OnlyMine == FALSE) // Want All + if (OnlySeq == FALSE || ptr->Seq[0] != 0) + goto wantit; + + // ignore + + ptr = ptr->Next; + continue; + wantit: + OutputLen += sprintf(&OutBuffer[OutputLen], WebLine, + ptr->FromCall, ptr->ToCall, ptr->Seq, ptr->Time, + ptr->FromCall, ptr->ToCall, ptr->Text); + + ptr = ptr->Next; + + if (OutputLen > 99000) + break; + + } + + OutputLen += sprintf(&OutBuffer[OutputLen], "%s", WebTrailer); + + HeaderLen = sprintf(Header, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, OutBuffer, OutputLen); + + return; + + } + + else if (_memicmp(URL, "/aprs/txmsgs", 12) == 0) + { + // Return Messages Received Page + + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + char * MsgCall = LoppedAPRSCall; + + char Retries[10]; + + + OutputLen = sprintf(OutBuffer, WebTXHeader, MsgCall, MsgCall); + + while (ptr) + { + char ToLopped[11] = ""; + + if (ptr->Acked) + strcpy(Retries, "A"); + else if (ptr->Retries == 0) + strcpy(Retries, "F"); + else + sprintf(Retries, "%d", ptr->Retries); + + memcpy(ToLopped, ptr->ToCall, 10); + strlop(ToLopped, ' '); + + OutputLen += sprintf(&OutBuffer[OutputLen], WebTXLine, + ptr->ToCall, ptr->Seq, ptr->Time, Retries, ptr->Text); + ptr = ptr->Next; + + if (OutputLen > 99000) + break; + + } + + OutputLen += sprintf(&OutBuffer[OutputLen], "%s", WebTrailer); + + HeaderLen = sprintf(Header, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", OutputLen); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, OutBuffer, OutputLen); + + return; + + } + + + if (_memicmp(URL, "/aprs/find.cgi?call=", 20) == 0) + { + // return Station details + + char * Call = &URL[20]; + BOOL RFOnly, WX, Mobile, Object = FALSE; + struct STATIONRECORD * stn; + char * Referrer = strstr(ptr + 1, "Referer:"); + + // Undo any % transparency in call + + char * ptr1 = Call; + char * ptr2 = Key; + char c; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + *(ptr2++) = m * 16 + n; + } + else if (c == '+') + *(ptr2++) = ' '; + else + *(ptr2++) = c; + + c = *(ptr1++); + } + + *(ptr2++) = 0; + + if (Referrer) + { + ptr = strchr(Referrer, 13); + if (ptr) + { + *ptr = 0; + RFOnly = !(strstr(Referrer, "rf") == NULL); + WX = !(strstr(Referrer, "wx") == NULL); + Mobile = !(strstr(Referrer, "mobile") == NULL); + Object = !(strstr(Referrer, "obj") == NULL); + + if (WX) + strcpy(URL, "/aprs/infowx_call.html"); + else if (Mobile) + strcpy(URL, "/aprs/infomobile_call.html"); + else if (Object) + strcpy(URL, "/aprs/infoobj_call.html"); + else + strcpy(URL, "/aprs/info_call.html"); + } + } + + if (Object) + { + // Name is space padded, and could have embedded spaces + + int Keylen = (int)strlen(Key); + + if (Keylen < 9) + memset(&Key[Keylen], 32, 9 - Keylen); + } + + stn = FindStation(Key, FALSE); + + if (stn == NULL) + strcpy(URL, "/aprs/noinfo.html"); + else + sockptr->SelCall = stn; + } + + + strcpy(sockptr->Callsign, Key); + + APRSSendMessageFile(sockptr, URL); + + return; +} + +// Code for handling APRS messages within BPQ32/LinBPQ instead of GUI + + +int ProcessMessage(char * Payload, struct STATIONRECORD * Station) +{ + char MsgDest[10]; + struct APRSMESSAGE * Message; + struct APRSMESSAGE * ptr = SMEM->Messages; + char * TextPtr = &Payload[11]; + char * SeqPtr; + int n = 0; + char FromCall[10] = " "; + struct tm * TM; + time_t NOW; + char noSeq[] = ""; + int ourMessage = 0; + + memcpy(FromCall, Station->Callsign, strlen(Station->Callsign)); + memcpy(MsgDest, &Payload[1], 9); + MsgDest[9] = 0; + + if (strcmp(MsgDest, CallPadded) == 0) // to me? + { + SMEM->NeedRefresh = 255; // Flag to control Msg popup + ourMessage = 1; + } + else + SMEM->NeedRefresh = 1; + + SeqPtr = strchr(TextPtr, '{'); + + if (SeqPtr) + { + *(SeqPtr++) = 0; + if(strlen(SeqPtr) > 6) + SeqPtr[7] = 0; + } + else + SeqPtr = noSeq; + + if (_memicmp(TextPtr, "ack", 3) == 0) + { + // Message Ack. See if for one of our messages + + ptr = SMEM->OutstandingMsgs; + + if (ptr == 0) + return ourMessage; + + do + { + if (strcmp(ptr->FromCall, MsgDest) == 0 + && strcmp(ptr->ToCall, FromCall) == 0 + && strcmp(ptr->Seq, &TextPtr[3]) == 0) + { + // Message is acked + + ptr->Retries = 0; + ptr->Acked = TRUE; + + return ourMessage; + } + ptr = ptr->Next; + n++; + + } while (ptr); + + return ourMessage; + } + + // See if we already have this message + + ptr = SMEM->Messages; + + while(ptr) + { + if (strcmp(ptr->ToCall, MsgDest) == 0 + && strcmp(ptr->FromCall, FromCall) == 0 + && strcmp(ptr->Seq, SeqPtr) == 0 + && strcmp(ptr->Text, TextPtr) == 0) + + // Duplicate + + return ourMessage; + + ptr = ptr->Next; + } + + Message = APRSGetMessageBuffer(); + + if (Message == NULL) + return ourMessage; + + memset(Message, 0, sizeof(struct APRSMESSAGE)); + memset(Message->FromCall, ' ', 9); + memcpy(Message->FromCall, Station->Callsign, strlen(Station->Callsign)); + strcpy(Message->ToCall, MsgDest); + + if (SeqPtr) + { + strcpy(Message->Seq, SeqPtr); + + // If a REPLY-ACK Seg, copy to LastRXSeq, and see if it acks a message + + if (SeqPtr[2] == '}') + { + struct APRSMESSAGE * ptr1; + int nn = 0; + + strcpy(Station->LastRXSeq, SeqPtr); + + ptr1 = SMEM->OutstandingMsgs; + + while (ptr1) + { + if (strcmp(ptr1->FromCall, MsgDest) == 0 + && strcmp(ptr1->ToCall, FromCall) == 0 + && memcmp(&ptr1->Seq, &SeqPtr[3], 2) == 0) + { + // Message is acked + + ptr1->Acked = TRUE; + ptr1->Retries = 0; + + break; + } + ptr1 = ptr1->Next; + nn++; + } + } + else + { + // Station is not using reply-ack - set to send simple numeric sequence (workround for bug in APRS Messanges + + Station->SimpleNumericSeq = TRUE; + } + } + + if (strlen(TextPtr) > 100) + TextPtr[100] = 0; + + strcpy(Message->Text, TextPtr); + + NOW = time(NULL); + + if (DefaultLocalTime) + TM = localtime(&NOW); + else + TM = gmtime(&NOW); + + sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + if (_stricmp(MsgDest, CallPadded) == 0 && SeqPtr) // ack it if it has a sequence + { + // For us - send an Ack + + char ack[30]; + APRSHEARDRECORD * STN; + + sprintf(ack, ":%-9s:ack%s", Message->FromCall, Message->Seq); + + if (memcmp(Message->FromCall, "SERVER ", 9) == 0) + { + SendAPRSMessage(ack, 0); // IS + } + else + { + STN = FindStationInMH(Message->ToCall); + + if (STN) + SendAPRSMessage(ack, STN->rfPort); + else + { + SendAPRSMessage(ack, -1); // All RF ports + SendAPRSMessage(ack, 0); // IS + } + } + } + + if (SaveAPRSMsgs) + SaveAPRSMessage(Message); + + ptr = SMEM->Messages; + + if (ptr == NULL) + { + SMEM->Messages = Message; + } + else + { + n++; + while(ptr->Next) + { + ptr = ptr->Next; + n++; + } + ptr->Next = Message; + } + + return ourMessage; +} + +BOOL InternalSendAPRSMessage(char * Text, char * Call) +{ + char Msg[255]; + size_t len = strlen(Call); + APRSHEARDRECORD * STN; + struct tm * TM; + time_t NOW; + + struct APRSMESSAGE * Message; + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + + Message = APRSGetMessageBuffer(); + + if (Message == NULL) + return FALSE; + + memset(Message, 0, sizeof(struct APRSMESSAGE)); + strcpy(Message->FromCall, CallPadded); + + memset(Message->ToCall, ' ', 9); + memcpy(Message->ToCall, Call, len); + + Message->ToStation = FindStation(Call, TRUE); + + if (Message->ToStation == NULL) + return FALSE; + + SMEM->NeedRefresh = TRUE; + + if (Message->ToStation->LastRXSeq[0]) // Have we received a Reply-Ack message from him? + sprintf(Message->Seq, "%02X}%c%c", ++Message->ToStation->NextSeq, Message->ToStation->LastRXSeq[0], Message->ToStation->LastRXSeq[1]); + else + { + if (Message->ToStation->SimpleNumericSeq) + sprintf(Message->Seq, "%d", ++Message->ToStation->NextSeq); + else + sprintf(Message->Seq, "%02X}", ++Message->ToStation->NextSeq); // Don't know, so assume message-ack capable + } + + if (strlen(Text) > 100) + Text[100] = 0; + + strcpy(Message->Text, Text); + Message->Retries = RetryCount; + Message->RetryTimer = RetryTimer; + + NOW = time(NULL); + + if (DefaultLocalTime) + TM = localtime(&NOW); + else + TM = gmtime(&NOW); + + sprintf(Message->Time, "%.2d:%.2d", TM->tm_hour, TM->tm_min); + + + // Chain to Outstanding Queue + + if (ptr == NULL) + { + SMEM->OutstandingMsgs = Message; + } + else + { + while(ptr->Next) + { + ptr = ptr->Next; + } + ptr->Next = Message; + } + + sprintf(Msg, ":%-9s:%s{%s", Call, Text, Message->Seq); + + if (strcmp(Call, "SERVER") == 0) + { + SendAPRSMessage(Msg, 0); // IS + return TRUE; + } + + STN = FindStationInMH(Message->ToCall); + + if (STN && STN->MHTIME > (time(NULL) - 900)) // Heard in last 15 mins + SendAPRSMessage(Msg, STN->rfPort); + else + { + SendAPRSMessage(Msg, -1); // All RF ports + SendAPRSMessage(Msg, 0); // IS + } + return TRUE; +} + + + + + +extern BOOL APRSReconfigFlag; +extern struct DATAMESSAGE * REPLYBUFFER; +extern char COMMANDBUFFER[81]; +extern char OrigCmdBuffer[81]; + +BOOL isSYSOP(TRANSPORTENTRY * Session, char * Bufferptr); + +VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + // APRS Subcommands. Default for compatibility is APRSMH + + // Others are STATUS ENABLEIGATE DISABLEIGATE RECONFIG + + APRSHEARDRECORD * MH = MHDATA; + int n = MAXHEARDENTRIES; + char * ptr; + char * Pattern, * context; + int Port = -1; + char dummypattern[] =""; + + if (memcmp(CmdTail, "? ", 2) == 0) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "APRS Subcommmands:\r"); + Bufferptr = Cmdprintf(Session, Bufferptr, "STATUS SEND MSGS SENT ENABLEIGATE DISABLEIGATE BEACON RECONFIG\r"); + Bufferptr = Cmdprintf(Session, Bufferptr, "Default is Station list - Params [Port] [Pattern]\r"); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "RECONFIG ", 5) == 0) + { + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + if (!ProcessConfig()) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Configuration File check failed - will continue with old config"); + } + else + { + APRSReconfigFlag=TRUE; + Bufferptr = Cmdprintf(Session, Bufferptr, "Reconfiguration requested\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + } + + if (memcmp(CmdTail, "ENABLEIGATE ", 6) == 0) + { + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + IGateEnabled = TRUE; + Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "DISABLEIGATE ", 6) == 0) + { + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + IGateEnabled = FALSE; + Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Disabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "STATUS ", 7) == 0) + { + if (IGateEnabled == FALSE) + Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Disabled\r"); + else + { + Bufferptr = Cmdprintf(Session, Bufferptr, "IGate Enabled "); + if (APRSISOpen) + Bufferptr = Cmdprintf(Session, Bufferptr, "and connected to %s\r", RealISHost); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "but not connected\r"); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "BEACON ", 7) == 0) + { + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + BeaconCounter = 2; + Bufferptr = Cmdprintf(Session, Bufferptr, "Beacons requested\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "MSGS ", 5) == 0) + { + struct APRSMESSAGE * ptr = SMEM->Messages; + char Addrs[32]; + + Bufferptr = Cmdprintf(Session, Bufferptr, + "\rTime Calls Seq Text\r"); + + while (ptr) + { + char ToLopped[11] = ""; + + memcpy(ToLopped, ptr->ToCall, 10); + strlop(ToLopped, ' '); + + sprintf(Addrs, "%s>%s", ptr->FromCall, ToLopped); + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s %-20s%-5s %s\r", + ptr->Time, Addrs, ptr->Seq, ptr->Text); + + ptr = ptr->Next; + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "SENT ", 5) == 0) + { + struct APRSMESSAGE * ptr = SMEM->OutstandingMsgs; + char Addrs[32]; + + Bufferptr = Cmdprintf(Session, Bufferptr, + "\rTime Calls Seq State Text\r"); + + while (ptr) + { + char ToLopped[11] = ""; + char Retries[10]; + + if (ptr->Acked) + strcpy(Retries, "A"); + else if (ptr->Retries == 0) + strcpy(Retries, "F"); + else + sprintf(Retries, "%d", ptr->Retries); + + + memcpy(ToLopped, ptr->ToCall, 10); + strlop(ToLopped, ' '); + + sprintf(Addrs, "%s>%s", ptr->FromCall, ToLopped); + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s %-20s%-5s %-2s %s\r", + ptr->Time, Addrs, ptr->Seq, Retries, ptr->Text); + + ptr = ptr->Next; + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "SEND ", 5) == 0) + { + // Send Message. Params are Call and Message + + char * Call = strtok_s(&CmdTail[5], " \r", &context); + char * Text = strtok_s(NULL, " \r", &context); + int len = 0; + + if (isSYSOP(Session, Bufferptr) == FALSE) + return; + + if (Call) + len = (int)strlen(Call); + + if (len < 3 || len > 9) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Callsign\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (Text == NULL) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "No Message Text\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + // Replace command tail with original (before conversion to upper case + + Text = Text + (OrigCmdBuffer - COMMANDBUFFER); + + InternalSendAPRSMessage(Text, Call); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // DISPLAY APRS HEARD LIST + + // APRS [Port] [Pattern] + + Pattern = strtok_s(CmdTail, " \r", &context); + + if (Pattern && (int)strlen(Pattern) < 3) + { + // could be port number + + if (isdigit(Pattern[0]) && (Pattern[1] == 0 || isdigit(Pattern[1]))) + { + Port = atoi(Pattern); + Pattern = strtok_s(NULL, " \r", &context); + } + } + + // Param is a pattern to match + + if (Pattern == NULL) + Pattern = dummypattern; + + if (Pattern[0] == ' ') + { + // Prepare Pattern + + char * ptr1 = Pattern + 1; + char * ptr2 = Pattern; + char c; + + do + { + c = *ptr1++; + *(ptr2++) = c; + } + while (c != ' '); + + *(--ptr2) = 0; + } + + strlop(Pattern, ' '); + _strupr(Pattern); + + *(Bufferptr++) = 13; + + while (n--) + { + if (MH->MHCALL[0] == 0) + break; + + if ((Port > -1) && Port != MH->rfPort) + { + MH++; + continue; + } + + ptr = FormatAPRSMH(MH); + + MH++; + + if (ptr) + { + if (Pattern[0] && strstr(ptr, Pattern) == 0) + continue; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", ptr); + } + } + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +int GetPosnFromAPRS(char * Call, double * Lat, double * Lon) +{ + struct STATIONRECORD * Station; + + Station = FindStation(Call, FALSE); + + if (Station) + { + *Lat = Station->Lat; + *Lon = Station->Lon; + + return 1; + } + + return 0; +} + +// Station Name Font + +const unsigned char ASCII[][5] = { +//const u08 ASCII[][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00} // 20 + ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 ! + ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 " + ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 # + ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $ + ,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 % + ,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 & + ,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 ' + ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 ( + ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 ) + ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a * + ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b + + ,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c , + ,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d - + ,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e . + ,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f / + ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0 + ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1 + ,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2 + ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3 + ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4 + ,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5 + ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6 + ,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7 + ,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8 + ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9 + ,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a : + ,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ; + ,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c < + ,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d = + ,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e > + ,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ? + ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @ + ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A + ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B + ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C + ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D + ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E + ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F + ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G + ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H + ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I + ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J + ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K + ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L + ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M + ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N + ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O + ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P + ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q + ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R + ,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S + ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T + ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U + ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V + ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W + ,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X + ,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y + ,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z + ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [ + ,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c + ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ] + ,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^ + ,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _ + ,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 ` + ,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a + ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b + ,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c + ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d + ,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e + ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f + ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g + ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h + ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i + ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j + ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k + ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l + ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m + ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n + ,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o + ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p + ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q + ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r + ,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s + ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t + ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u + ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v + ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w + ,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x + ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y + ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z + ,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b { + ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c | + ,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d } + ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ~ + ,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f DEL +}; + + +// APRS Web Map Code + +// Not sure yet what is best way to do station icons but for now build and cache any needed icons + +extern int IconDataLen; +extern unsigned long long IconData[]; // Symbols as a png image.& + +// IconData is a png image, so needs to be uncompressed to an RGB array + + +// Will key cached icons by IconRow, IconCol, Overlay Char - xxxxA + +int cachedIconCount = 0; + +// We need key, icon data, icon len for each. Maybe a simple linked list - we never remove any + +struct iconCacheEntry +{ + struct iconCacheEntry * Next; + char key[8]; + int pngimagelen; + int pngmalloclen; + unsigned char * pngimage; +}; + +struct iconCacheEntry * iconCache = NULL; + + +// Each icon has to be created as an RGB array, then converted to a png image, as +// Leaflet icons need a png file + +#include "mypng.h" + + +struct png_info_struct * info_ptr = NULL; + +unsigned char * PngEncode (png_byte *pDiData, int iWidth, int iHeight, struct iconCacheEntry * Icon); + +void createIcon(char * Key, int iconRow, int iconCol, char Overlay) +{ + int i, j, index, mask; + int row; + int col; // First row + unsigned char * rgbData = malloc(68 * 22); // 1323 + unsigned char * ptr = rgbData; + png_color colour = {0, 0, 0}; + int Pointer; + char c; + int bit; + struct iconCacheEntry * Icon = zalloc(sizeof(struct iconCacheEntry)); + + strcpy(Icon->key, Key); + + // icon data is in info_ptr->row_pointers (we have 255 of them) + + row = iconRow * 21; + col = iconCol * 21 * 3; + + for (j = 0; j < 22; j++) + { + memcpy(ptr, info_ptr->row_pointers[row + j] + col, 22 * 3); // One scan line + ptr += 68; // Rounded up to mod 4 + } + + + // This code seems to assume an icon is 16 pixels, but image is 22 x 22 ??? + +// j = ptr->iconRow * 21 * 337 * 3 + ptr->iconCol * 21 * 3 + 9 + 337 * 9; +// for (i = 0; i < 16; i++) +// { +// memcpy(nptr, &iconImage[j], 16 * 3); +// nptr += 6144; +// j += 337 * 3; +// } + + + // If an overlay is specified, add it + + if (Overlay) + { + Pointer = 68 * 7 + 7 * 3; // 7th row, 7th col + mask = 1; + + for (index = 0 ; index < 7 ; index++) + { + rgbData[Pointer++] = 255; // Blank line above chars + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + } + + Pointer = 68 * 8 + 7 * 3; // 8th row, 7th col + + for (i = 0; i < 7; i++) + { + rgbData[Pointer++] = 255; // Blank col + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + + for (index = 0 ; index < 5 ; index++) + { + c = ASCII[Overlay - 0x20][index]; // Font data + bit = c & mask; + + if (bit) + { + rgbData[Pointer++] = 0; + rgbData[Pointer++] = 0; + rgbData[Pointer++] = 0; + } + else + { + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + } + } + + rgbData[Pointer++] = 255; // Blank col + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + + mask <<= 1; + Pointer += 47; + } + for (index = 0 ; index < 7 ; index++) + { + rgbData[Pointer++] = 255; // Blank line above chars + rgbData[Pointer++] = 255; + rgbData[Pointer++] = 255; + } + } + + // Encode + + PngEncode(rgbData, 22, 22, Icon); + + if (iconCache) + Icon->Next = iconCache; + + iconCache = Icon; + +} + +int GetAPRSIcon(unsigned char * _REPLYBUFFER, char * NodeURL) +{ + char Key[8] = ""; + struct iconCacheEntry * Icon = iconCache; + + memcpy(Key, &NodeURL[5], 5); + + while (Icon) + { + if (strcmp(Icon->key, Key) == 0) // Have it + { + memcpy(_REPLYBUFFER, Icon->pngimage, Icon->pngimagelen); + return Icon->pngimagelen; + } + Icon = Icon->Next; + } + + return 0; +} + +char * doHTMLTransparency(char * string) +{ + // Make sure string doesn't contain forbidden XML chars (<>"'&) + + char * newstring = malloc(5 * strlen(string) + 1); // If len is zero still need null terminator + + char * in = string; + char * out = newstring; + char c; + + c = *(in++); + + while (c) + { + switch (c) + { + case '<': + + strcpy(out, "<"); + out += 4; + break; + + case '>': + + strcpy(out, ">"); + out += 4; + break; + + case '"': + + strcpy(out, """); + out += 6; + break; + + case '\'': + + strcpy(out, "'"); + out += 6; + break; + + case '&': + + strcpy(out, "&"); + out += 5; + break; + + case ',': + + strcpy(out, ","); + out += 5; + break; + + case '|': + + strcpy(out, "|"); + out += 5; + break; + + default: + + *(out++) = c; + } + c = *(in++); + } + + *(out++) = 0; + return newstring; +} + +int GetAPRSPageInfo(char * Buffer, double N, double S, double W, double E, int aprs, int ais, int adsb) +{ + struct STATIONRECORD * ptr = *StationRecords; + int n = 0, Len = 0; + struct tm * TM; + time_t NOW = time(NULL); + char popup[65536] = ""; + char Msg[2048]; + int LocalTime = 0; + int KM = DefaultDistKM; + char * ptr1; + + while (ptr && aprs && Len < 240000) + { + if (ptr->Lat != 0.0 && ptr->Lon != 0.0) + { + if (ptr->Lat > S && ptr->Lat < N && ptr->Lon > W && ptr->Lon < E) + { + // See if we have the Icon - if not build + + char IconKey[6]; + struct iconCacheEntry * Icon = iconCache; + + sprintf(IconKey, "%02X%02X ", ptr->iconRow, ptr->iconCol); + + if (ptr->IconOverlay) + IconKey[4] = ptr->IconOverlay; + else + IconKey[4] = '@'; + + while (Icon) + { + if (strcmp(Icon->key, IconKey) == 0) // Have it + break; + + Icon = Icon->Next; + } + + if (Icon == NULL) + createIcon(IconKey, ptr->iconRow, ptr->iconCol, ptr->IconOverlay); + + popup[0] = 0; + + if (ptr->Approx) + { + sprintf(Msg, "Approximate Position From Locator
"); + strcat(popup, Msg); + } + ptr1 = doHTMLTransparency(ptr->Path); + sprintf(Msg, "%s
", ptr1); + strcat(popup, Msg); + free(ptr1); + ptr1 = doHTMLTransparency(ptr->LastPacket); + sprintf(Msg, "%s
", ptr1); + strcat(popup, Msg); + free(ptr1); + ptr1 = doHTMLTransparency(ptr->Status); + sprintf(Msg, "%s
", ptr1); + strcat(popup, Msg); + free(ptr1); + if (LocalTime) + TM = localtime(&ptr->TimeLastUpdated); + else + TM = gmtime(&ptr->TimeLastUpdated); + + sprintf(Msg, "Last Heard: %.2d:%.2d:%.2d on Port %d
", + TM->tm_hour, TM->tm_min, TM->tm_sec, ptr->LastPort); + + strcat(popup, Msg); + + sprintf(Msg, "Distance %6.1f Bearing %3.0f Course %1.0f° Speed %3.1f
", + myDistance(ptr->Lat, ptr->Lon, KM), + myBearing(ptr->Lat, ptr->Lon), ptr->Course, ptr->Speed); + strcat(popup, Msg); + + if (ptr->LastWXPacket[0]) + { + //display wx info + + struct APRSConnectionInfo temp; + + memset(&temp, 0, sizeof(temp)); + + DecodeWXReport(&temp, ptr->LastWXPacket); + + sprintf(Msg, "Wind Speed %d MPH
", temp.WindSpeed); + strcat(popup, Msg); + + sprintf(Msg, "Wind Gust %d MPH
", temp.WindGust); + strcat(popup, Msg); + + sprintf(Msg, "Wind Direction %d\xC2\xB0
", temp.WindDirn); + strcat(popup, Msg); + + sprintf(Msg, "Temperature %d\xC2\xB0 F
", temp.Temp); + strcat(popup, Msg); + + sprintf(Msg, "Pressure %05.1f
", temp.Pressure / 10.0); + strcat(popup, Msg); + + sprintf(Msg, "Humidity %d%%
", temp.Humidity); + strcat(popup, Msg); + + sprintf(Msg, "Rainfall Last Hour/Last 24H/Today %5.2f, %5.2f, %5.2f (inches)", + temp.RainLastHour / 100.0, temp.RainLastDay / 100.0, temp.RainToday / 100.0); + + ptr1 = doHTMLTransparency(Msg); + sprintf(Msg, "%s
", ptr1); + strcat(popup, Msg); + free(ptr1); + } + + Len += sprintf(&Buffer[Len],"A,%.4f,%.4f,%s,%s,%s,%d\r\n|", + ptr->Lat, ptr->Lon, ptr->Callsign, popup, IconKey, + NOW - ptr->TimeLastUpdated); + + if (ptr->TrackTime[0] && ptr->TrackTime[1]) // Have trackpoints + { + int n = ptr->Trackptr; + int i; + double lastLat = 0; + + // We read from next track point (oldest) for TRACKPOINT records, ignoring zeros + + Len += sprintf(&Buffer[Len],"T,"); + + for (i = 0; i < TRACKPOINTS; i++) + { + if (ptr->TrackTime[n]) + { + Len += sprintf(&Buffer[Len],"%.4f,%.4f,", ptr->LatTrack[n], ptr->LonTrack[n]); + lastLat = ptr->LatTrack[n]; + } + + n++; + if (n == TRACKPOINTS) + n = 0; + } + if (lastLat != ptr->Lat) + Len += sprintf(&Buffer[Len],"%.4f,%.4f,\r\n|", ptr->Lat, ptr->Lon); //Add current position to end of track + else + Len += sprintf(&Buffer[Len],"\r\n|"); + } + } + } + + ptr = ptr->Next; + } + return Len; +} + + + + + /* The png_jmpbuf() macro, used in error handling, became available in + * libpng version 1.0.6. If you want to be able to run your code with older + * versions of libpng, you must define the macro yourself (but only if it + * is not already defined by libpng!). + */ +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) ((png_ptr)->png_jmpbuf) +#endif +/* Check to see if a file is a PNG file using png_sig_cmp(). png_sig_cmp() + * returns zero if the image is a PNG and nonzero if it isn't a PNG. + * + * The function check_if_png() shown here, but not used, returns nonzero (true) + * if the file can be opened and is a PNG, 0 (false) otherwise. + * + * If this call is successful, and you are going to keep the file open, + * you should call png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); once + * you have created the png_ptr, so that libpng knows your application + * has read that many bytes from the start of the file. Make sure you + * don't call png_set_sig_bytes() with more than 8 bytes read or give it + * an incorrect number of bytes read, or you will either have read too + * many bytes (your fault), or you are telling libpng to read the wrong + * number of magic bytes (also your fault). + * + * Many applications already read the first 2 or 4 bytes from the start + * of the image to determine the file type, so it would be easiest just + * to pass the bytes to png_sig_cmp() or even skip that if you know + * you have a PNG file, and call png_set_sig_bytes(). + */ + +unsigned char * user_io_ptr = 0; + +void __cdecl user_read_fn(png_struct * png, png_bytep Buffer, png_size_t Len) +{ + unsigned char ** ptr = png->io_ptr; + unsigned char * ptr1; + + ptr1 = ptr[0]; + + memcpy(Buffer, ptr1, Len); + ptr[0]+= Len; +} + + +// This is based on example https://www1.udel.edu/CIS/software/dist/libpng-1.2.8/example.c + +// This decodes a png encoded image from memory + +int read_png(unsigned char *bytes) +{ + png_structp png_ptr; + unsigned int sig_read = 0; + + /* Create and initialize the png_struct with the desired error handler + * functions. If you want to use the default stderr and longjump method, + * you can supply NULL for the last three parameters. We also supply the + * the compiler header file version, so that we know if the application + * was compiled with a compatible version of the library. REQUIRED + */ + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (png_ptr == NULL) + { + return (0); + } + /* Allocate/initialize the memory for image information. REQUIRED. */ + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + return (0); + } + /* Set error handling if you are using the setjmp/longjmp method (this is + * the normal method of doing things with libpng). REQUIRED unless you + * set up your own error handlers in the png_create_read_struct() earlier. + */ + + user_io_ptr = (unsigned char *)&IconData; + + png_set_read_fn(png_ptr, (void *)&user_io_ptr,(png_rw_ptr)user_read_fn); + + png_set_sig_bytes(png_ptr, sig_read); + + png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, NULL); + + // Data is in info->row_pointers. Can we use it from there ?? + +// printf("%d %d %d\n", info_ptr->width, info_ptr->height, info_ptr->valid); + + return TRUE; +} + +void Myabort() +{} + +// This is based on pngfile.c + +//------------------------------------- +// PNGFILE.C -- Image File Functions +//------------------------------------- + +// Copyright 2000, Willem van Schaik. For conditions of distribution and +// use, see the copyright/license/disclaimer notice in png.h + +// Encodes pDiData to png format in memory + + + +void my_png_write_data(struct png_struct_def * png_ptr, png_bytep data, png_size_t length) +{ + struct iconCacheEntry * Icon = png_ptr->io_ptr; + + if (Icon->pngimagelen + (int)length > Icon->pngmalloclen) + { + Icon->pngmalloclen += length; + Icon->pngimage = realloc(Icon->pngimage, Icon->pngmalloclen); + } + + memcpy(&Icon->pngimage[Icon->pngimagelen], data, length); + Icon->pngimagelen += length; +} + + // io_ptr = (FILE *)CVT_PTR((png_ptr->io_ptr)); + // Area png_uint_32 check; + + + +static void png_flush(png_structp png_ptr) +{ +} + +unsigned char * PngEncode (png_byte *pDiData, int iWidth, int iHeight, struct iconCacheEntry * Icon) +{ + const int ciBitDepth = 8; + const int ciChannels = 3; + png_structp png_ptr; + png_infop info_ptr = NULL; + png_uint_32 ulRowBytes; + static png_byte **ppbRowPointers = NULL; + int i; + + // Set up image array and pointer. First allocate a buffer as big as the original + // in the unlikely event of it being too small write_data will realloc it + + Icon->pngmalloclen = iWidth * iHeight * 3; + Icon->pngimage = malloc(Icon->pngmalloclen); + Icon->pngimagelen = 0; + + // prepare the standard PNG structures + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!png_ptr) + { + return FALSE; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + return FALSE; + } + + { + // initialize the png structure + + png_set_write_fn(png_ptr, Icon, my_png_write_data, png_flush); + + png_set_IHDR(png_ptr, info_ptr, iWidth, iHeight, ciBitDepth, + PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + + // write the file header information + + png_write_info(png_ptr, info_ptr); + + // row_bytes is the width x number of channels + + ulRowBytes = iWidth * ciChannels; + + // we can allocate memory for an array of row-pointers + + if ((ppbRowPointers = (png_bytepp) malloc(iHeight * sizeof(png_bytep))) == NULL) + Debugprintf( "Visualpng: Out of memory"); + + // set the individual row-pointers to point at the correct offsets + + for (i = 0; i < iHeight; i++) + ppbRowPointers[i] = pDiData + i * (((ulRowBytes + 3) >> 2) << 2); + + // write out the entire image data in one call + + png_write_image (png_ptr, ppbRowPointers); + + // write the additional chunks to the PNG file (not really needed) + + png_write_end(png_ptr, info_ptr); + + // and we're done + + free (ppbRowPointers); + ppbRowPointers = NULL; + + // clean up after the write, and free any memory allocated + + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + + // yepp, done + } + + return Icon->pngimage; +} + +void SaveAPRSMessage(struct APRSMESSAGE * ptr) +{ + // Save messages in case of a restart + + char FN[250]; + FILE *file; + + // Set up filename + + if (BPQDirectory[0] == 0) + { + strcpy(FN,"APRSMsgs.dat"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"APRSMsgs.dat"); + } + + if ((file = fopen(FN, "a")) == NULL) + return ; + + fprintf(file, "%d %s,%s,%s,%s,%s\n", time(NULL), ptr->FromCall, ptr->ToCall, ptr->Seq, ptr->Time, ptr->Text); + + fclose(file); +} + +void ClearSavedMessages() +{ + char FN[250]; + FILE *file; + + // Set up filename + + if (BPQDirectory[0] == 0) + { + strcpy(FN,"APRSMsgs.dat"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"APRSMsgs.dat"); + } + + if ((file = fopen(FN, "w")) == NULL) + return ; + + fclose(file); +} + +void GetSavedAPRSMessages() +{ + // Get Saved messages + + // 1668768157 SERVER ,GM8BPQ-2 ,D7Yx,10:42,filter m/200 active + + char FN[250]; + FILE *file; + struct APRSMESSAGE * Message; + struct APRSMESSAGE * ptr; + char Line[512]; + char * Stamp = 0; + char * From = 0; + char * To = 0; + char * Seq = 0; + char * Time = 0; + char * Text = 0; + + // Set up filename + + if (BPQDirectory[0] == 0) + { + strcpy(FN,"APRSMsgs.dat"); + } + else + { + strcpy(FN,BPQDirectory); + strcat(FN,"/"); + strcat(FN,"APRSMsgs.dat"); + } + + if ((file = fopen(FN, "r")) == NULL) + return ; + + while (fgets(Line, sizeof(Line), file)) + { + Stamp = Line; + From = strlop(Stamp, ' '); + To = strlop(From, ','); + Seq = strlop(To, ','); + Time = strlop(Seq, ','); + Text = strlop(Time, ','); + + if (Stamp && From && To && Seq && Time && Text) + { + Message = APRSGetMessageBuffer(); + + if (Message == NULL) + break; + + memset(Message, 0, sizeof(struct APRSMESSAGE)); + + strcpy(Message->FromCall, From); + strcpy(Message->ToCall, To); + strcpy(Message->Seq, Seq); + strcpy(Message->Time, Time); + strcpy(Message->Text, Text); + + ptr = SMEM->Messages; + + if (ptr == NULL) + { + SMEM->Messages = Message; + } + else + { + while(ptr->Next) + { + ptr = ptr->Next; + } + ptr->Next = Message; + } + + } + } + fclose(file); +} diff --git a/.svn/pristine/5a/5a8db36bb3d447af83958f365655048d0a2d2adb.svn-base b/.svn/pristine/5a/5a8db36bb3d447af83958f365655048d0a2d2adb.svn-base new file mode 100644 index 0000000..3344195 --- /dev/null +++ b/.svn/pristine/5a/5a8db36bb3d447af83958f365655048d0a2d2adb.svn-base @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.svn/pristine/5e/5e5b69861072dddd0ba0a93abb8cfc407fe961b9.svn-base b/.svn/pristine/5e/5e5b69861072dddd0ba0a93abb8cfc407fe961b9.svn-base new file mode 100644 index 0000000..fbf276e --- /dev/null +++ b/.svn/pristine/5e/5e5b69861072dddd0ba0a93abb8cfc407fe961b9.svn-base @@ -0,0 +1,48 @@ +/* +Copyright 2001-2015 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 +*/ + +#include "CHeaders.h" +#include "tncinfo.h" + + + + +//int SENDNODES() {return 0;} +//int BYECMD() {return 0;} +//int CMDR00() {return 0;} + +//int DoNetromConnect() {return 0;} + + +//int CMDC00() {return 0;} + + + +//int CheckReceivedData() {return 0;} +//int GetLastError() {return 0;} + + + +VOID i2c_smbus_write_byte() +{ +} + +VOID i2c_smbus_read_byte() +{ +} diff --git a/.svn/pristine/5e/5edc33bc34a423e4ad51456cb4aebe961ff91f38.svn-base b/.svn/pristine/5e/5edc33bc34a423e4ad51456cb4aebe961ff91f38.svn-base new file mode 100644 index 0000000..0506130 --- /dev/null +++ b/.svn/pristine/5e/5edc33bc34a423e4ad51456cb4aebe961ff91f38.svn-base @@ -0,0 +1,2377 @@ +/* +Copyright 2001-2015 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// Interface to allow G8BPQ switch to use ARDOP Virtual TNC in a form +// of ax.25 + +// Uses a lot of routines in ARDOP.c + +// Includes its own ax.25 L2 stack, as I want to do dynamic packet size and +// maxframe + +// Actually may be better to use existing L2, but modify for dynamic paclen, but we +// need tighter coupling than the KISS driver provides. So this may become a "SuperKISS" TNC +// as a minimum I think this needs channel busy info connected to L2. Not sure yet!! + + +#define _CRT_SECURE_NO_DEPRECATE +#define _USE_32BIT_TIME_T + +#include +#include + + +#include "CHeaders.h" + +#ifdef WIN32 +#include +#endif + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +int (WINAPI FAR *EnumProcessesPtr)(); + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#include "bpq32.h" + +#include "tncinfo.h" + + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +static int Socket_Data(int sock, int error, int eventcode); + +int ARDOPKillTNC(struct TNCINFO * TNC); +int ARDOPRestartTNC(struct TNCINFO * TNC); +int KillPopups(struct TNCINFO * TNC); +VOID MoveWindows(struct TNCINFO * TNC); +int SendReporttoWL2K(struct TNCINFO * TNC); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +BOOL KillOldTNC(char * Path); +int ARDOPSendData(struct TNCINFO * TNC, char * Buff, int Len); +VOID ARDOPSendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue); +VOID SendToTNC(struct TNCINFO * TNC, UCHAR * Encoded, int EncLen); +VOID ARDOPProcessDataPacket(struct TNCINFO * TNC, UCHAR * Type, UCHAR * Data, int Length); + +#ifndef LINBPQ +BOOL CALLBACK EnumARDOPWindowsProc(HWND hwnd, LPARAM lParam); +#endif + +static char ClassName[]="ARDOPSTATUS"; +static char WindowTitle[] = "ARDOP"; +static int RigControlRow = 165; + +#define WINMOR +#define NARROWMODE 21 +#define WIDEMODE 22 + +#ifndef LINBPQ +#include +#endif + +extern char * PortConfig[33]; +extern int SemHeldByAPI; + +static RECT Rect; + +struct TNCINFO * TNCInfo[34]; // Records are Malloc'd + +static int ProcessLine(char * buf, int Port); + +unsigned long _beginthread( void( *start_address )(), unsigned stack_size, int arglist); + +// RIGCONTROL COM60 19200 ICOM IC706 5e 4 14.103/U1w 14.112/u1 18.1/U1n 10.12/l1 + +static ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->WINMORHostName = malloc(strlen(p_ipad)+1); + + if (TNC->WINMORHostName == NULL) return TRUE; + + strcpy(TNC->WINMORHostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "CI-V") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "CAT") == 0) + TNC->PTTMode = PTTCI_V; + else if (_stricmp(ptr, "RTS") == 0) + TNC->PTTMode = PTTRTS; + else if (_stricmp(ptr, "DTR") == 0) + TNC->PTTMode = PTTDTR; + else if (_stricmp(ptr, "DTRRTS") == 0) + TNC->PTTMode = PTTDTR | PTTRTS; + + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); +// if (p_cmd) TNC->ProgramPath = _strdup(_strupr(p_cmd)); + } + } + + TNC->MaxConReq = 10; // Default + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if ((_memicmp(buf, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) + {} // Ignore + else +/* + if (_memicmp(buf, "PATH", 4) == 0) + { + char * Context; + p_cmd = strtok_s(&buf[5], "\n\r", &Context); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + else +*/ + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + TNC->WL2K = DecodeWL2KReportLine(buf); + else + if (_memicmp(buf, "BUSYHOLD", 8) == 0) // Hold Time for Busy Detect + TNC->BusyHold = atoi(&buf[8]); + + else + if (_memicmp(buf, "BUSYWAIT", 8) == 0) // Wait time beofre failing connect if busy + TNC->BusyWait = atoi(&buf[8]); + + else + if (_memicmp(buf, "MAXCONREQ", 9) == 0) // Hold Time for Busy Detect + TNC->MaxConReq = atoi(&buf[9]); + + else + if (_memicmp(buf, "STARTINROBUST", 13) == 0) + TNC->StartInRobust = TRUE; + + else + if (_memicmp(buf, "ROBUST", 6) == 0) + { + if (_memicmp(&buf[7], "TRUE", 4) == 0) + TNC->Robust = TRUE; + + strcat (TNC->InitScript, buf); + } + else + + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + + + +void ARDOPThread(int port); +VOID ARDOPProcessDataSocketData(int port); +int ConnecttoARDOP(); +static VOID ARDOPProcessReceivedData(struct TNCINFO * TNC); +static VOID ARDOPProcessReceivedControl(struct TNCINFO * TNC); +int V4ProcessReceivedData(struct TNCINFO * TNC); +VOID ARDOPReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + + +#define MAXBPQPORTS 32 + +static time_t ltime; + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + +static int ExtProc(int fn, int port,unsigned char * buff) +{ + int datalen; + UINT * buffptr; + char txbuff[500]; + unsigned int bytes,txlen=0; + int Param; + HKEY hKey=0; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + if (TNC->CONNECTED == 0) + { + // clear Q if not connected + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + } + + + switch (fn) + { + case 1: // poll + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[256] = ""; + char Call[12] = " "; + struct _MESSAGE * buffptr; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + if (TNC->CONNECTED == 0) + { + // discard if not connected + + ReleaseBuffer(buffptr); + continue; + } + + datalen = buffptr->LENGTH - 7; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + // Frame has ax.25 format header. Convert to Text + + ConvFromAX25(Buffer + 7, Call); // Origin + strlop(Call, ' '); + strcat(FECMsg, Call); + strcat(FECMsg, ">"); + + ConvFromAX25(Buffer, Call); // Dest + strlop(Call, ' '); + strcat(FECMsg, Call); + + Buffer += 14; // TO Digis + datalen -= 7; + + while ((Buffer[-1] & 1) == 0) + { + Call[0] = ','; + ConvFromAX25(Buffer, &Call[1]); + strlop(&Call[1], ' '); + strcat(FECMsg, Call); + Buffer += 7; // End of addr + datalen -= 7; + } + + strcat(FECMsg, "|"); + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + + } + strcat(FECMsg, Buffer); + + ARDOPSendData(TNC, FECMsg, strlen(FECMsg)); + TNC->FECPending = 1; + + ReleaseBuffer((UINT *)buffptr); + } + + if (TNC->Busy) // Count down to clear + { + if ((TNC->BusyFlags & CDBusy) == 0) // TNC Has reported not busy + { + TNC->Busy--; + if (TNC->Busy == 0) + SetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } + } + + if (TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send + +// ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); // !!!! Temp bug workaround !!!! + + ARDOPSendCommand(TNC, TNC->ConnectCmd, TRUE); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[8], strlen(TNC->ConnectCmd)-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + free(TNC->ConnectCmd); + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + UINT * buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr[1]=39; + memcpy(buffptr+2,"Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + if (TNC->HeartBeat++ > 600 || (TNC->Streams[0].Connected && TNC->HeartBeat > 50)) // Every Minute unless connected + { + TNC->HeartBeat = 0; + + if (TNC->CONNECTED) + { + // Probe link + + if (TNC->Streams[0].Connecting || TNC->Streams[0].Connected) + fn =fn; //ARDOPSendCommand(TNC, "MODE", TRUE); + else + { +// if (time(NULL) - TNC->WinmorRestartCodecTimer > 300) // 5 mins +// { +// ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); +// ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); +// } +// else + ARDOPSendCommand(TNC, "STATE", TRUE); + } + } + } + + if (TNC->FECMode) + { + if (TNC->FECIDTimer++ > 6000) // ID every 10 Mins + { + if (!TNC->Busy) + { + TNC->FECIDTimer = 0; + ARDOPSendCommand(TNC, "SENDID", TRUE); + } + } + } + + // FECPending can be set if not in FEC Mode (eg beacon) + + if (TNC->FECPending) // Check if FEC Send needed + { + if (TNC->Streams[0].BytesOutstanding) //Wait for data to be queued (async data session) + { + if (!TNC->Busy) + { + TNC->FECPending = 0; + ARDOPSendCommand(TNC,"FECSEND TRUE", TRUE); + } + } + } + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); + } + } + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + + if (TNC->WIMMORPID) + { + ARDOPKillTNC(TNC); + ARDOPRestartTNC(TNC); + } + } + } + + if (TNC->TimeSinceLast++ > 800) // Allow 10 secs for Keepalive + { + // Restart TNC + + if (TNC->ProgramPath) + { + if (strstr(TNC->ProgramPath, "WINMOR TNC")) + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); + + ARDOPKillTNC(TNC); + ARDOPRestartTNC(TNC); + + TNC->TimeSinceLast = 0; + } + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + TNC->Streams[0].Attached = TRUE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + // Stop Listening, and set MYCALL to user's call + + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + ARDOPChangeMYC(TNC, TNC->Streams[0].MyCall); + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command(-1, Msg); + + } + + if (TNC->Streams[0].Attached) + CheckForDetach(TNC, 0, &TNC->Streams[0], TidyClose, ForcedClose, CloseComplete); + + if (TNC->Streams[0].ReportDISC) + { + TNC->Streams[0].ReportDISC = FALSE; + buff[4] = 0; + return -1; + } + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + ConnecttoARDOP(port); + TNC->lasttime = ltime; + } + } + + // See if any frames for this port + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + UINT * buffptr = Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + txlen=buffptr[1]; + memcpy(txbuff,buffptr+2,txlen); + bytes = ARDOPSendData(TNC, &txbuff[0], txlen); + STREAM->BytesTXed += bytes; + WritetoTrace(TNC, txbuff, txlen); + ReleaseBuffer(buffptr); + } + + + if (TNC->WINMORtoBPQ_Q != 0) + { + buffptr=Q_REM(&TNC->WINMORtoBPQ_Q); + + datalen=buffptr[1]; + + buff[4] = 0; // Compatibility with Kam Driver + buff[7] = 0xf0; + memcpy(&buff[8],buffptr+2,datalen); // Data goes to +7, but we have an extra byte + datalen+=8; + buff[5]=(datalen & 0xff); + buff[6]=(datalen >> 8); + + ReleaseBuffer(buffptr); + + return (1); + } + + return (0); + + case 2: // send + + if (!TNC->CONNECTED) + { + return 0; // Don't try if not connected + } + + + txlen=(buff[6]<<8) + buff[5] - 7; + + { + char Buffer[300]; + int len; + + // Send FEC Data + + buff[8 + txlen] = 0; + len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, &buff[8]); + + ARDOPSendData(TNC, Buffer, len); + TNC->FECPending = 1; + + return 0; + } + + return (0); + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + return 0; + break; + + case 4: // reinit + + return 0; + + case 5: // Close + + if (TNC->CONNECTED) + { + GetSemaphore(&Semaphore, 52); + ARDOPSendCommand(TNC, "CLOSE", FALSE); + FreeSemaphore(&Semaphore); + Sleep(100); + } + shutdown(TNC->WINMORSock, SD_BOTH); + Sleep(100); + closesocket(TNC->WINMORSock); + return 0; + + case 6: // Scan Stop Interface + + Param = (int)buff; + + if (Param == 1) // Request Permission + { + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + if (!TNC->ConnectPending) + return 0; // OK to Change + +// ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + + return TRUE; + } + + if (Param == 2) // Check Permission + { + if (TNC->ConnectPending) + { + TNC->ConnectPending--; + return -1; // Skip Interval + } + return 1; // OK to change + } + + if (Param == 3) // Release Permission + { +// ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + if (strcmp(Scan->ARDOPMode, TNC->ARDOPCurrentMode) != 0) + { + // Mode changed + + char CMD[32]; + + if (TNC->ARDOPCurrentMode[0] == 0) + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + + strcpy(TNC->ARDOPCurrentMode, Scan->ARDOPMode); + + + if (Scan->ARDOPMode[0] == 'S') // SKIP - Dont Allow Connects + { + if (TNC->ARDOPCurrentMode[0] != 0) + { + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + TNC->ARDOPCurrentMode[0] = 0; + } + + TNC->WL2KMode = 0; + return 0; + } + + if (strchr(Scan->ARDOPMode, 'F')) + sprintf(CMD, "ARQBW %sORCED", Scan->ARDOPMode); + else + sprintf(CMD, "ARQBW %sAX", Scan->ARDOPMode); + + return 0; + } + return 0; + } + return 0; +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "ARDOP Status" + "

ARDOP Status

", + TNC->Port); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +VOID ARDOPSuspendPort(struct TNCINFO * TNC); +VOID ARDOPReleasePort(struct TNCINFO * TNC); + + +UINT ARDOPAXExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + APPLCALLS * APPL; + struct TNCINFO * TNC; + char Aux[100] = "MYAUX "; + char Appl[11]; + char * TempScript; + struct PORTCONTROL * PORT = &PortEntry->PORTCONTROL; + // + // Will be called once for each WINMOR port + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return (int) ExtProc; + } + + PORT->PORTT1 = 15; + PORT->PORTT2 = 3; + PORT->PORTN2 = 5; + PORT->PORTPACLEN = 128; + + TNC->Port = port; + + TNC->ARDOPBuffer = malloc(8192); + TNC->ARDOPDataBuffer = malloc(8192); + + if (TNC->ProgramPath) + TNC->WeStartedTNC = ARDOPRestartTNC(TNC); + + TNC->Hardware = H_AXARDOP; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + TNC->Interlock = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 0; // KISS ??? + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->MAXHOSTMODESESSIONS = 0; + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = ARDOPSuspendPort; + TNC->ReleasePortProc = ARDOPReleasePort; + + TNC->ModemCentre = 1500; // WINMOR is always 1500 Offset + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = malloc(1000); + + strcpy(TempScript, "INITIALIZE\r"); + strcat(TempScript, "VERSION\r"); + strcat(TempScript, "CWID False\r"); + strcat(TempScript, "PROTOCOLMODE ARQ\r"); + strcat(TempScript, "ARQTIMEOUT 90\r"); +// strcat(TempScript, "ROBUST False\r"); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Set MYCALL + +// strcat(TNC->InitScript,"FECRCV True\r"); +// strcat(TNC->InitScript,"AUTOBREAK True\r"); + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); +// strcat(TNC->InitScript,"PROCESSID\r"); +// strcat(TNC->InitScript,"CODEC TRUE\r"); + strcat(TNC->InitScript,"LISTEN FALSE\r"); // Will use TNC in FEC mode only + strcat(TNC->InitScript,"MYCALL\r"); + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->WINMORHostName=malloc(10); + + if (TNC->WINMORHostName != NULL) + strcpy(TNC->WINMORHostName,"127.0.0.1"); + + } + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,40,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill ARDOP TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart ARDOP TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after each Connection"); + + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + MoveWindows(TNC); +#endif + Consoleprintf("ARDOPAX Host %s %d", TNC->WINMORHostName, htons(TNC->destaddr.sin_port)); + + ConnecttoARDOP(port); + + time(&TNC->lasttime); // Get initial time value + + return ((int) ExtProc); +} + +static int ConnecttoARDOP(int port) +{ + _beginthread(ARDOPThread,0,port); + + return 0; +} + +static VOID ARDOPThread(port) +{ + // Opens sockets and looks for data on control and data sockets. + + // Socket may be TCP/IP or Serial + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + struct TNCINFO * TNC = TNCInfo[port]; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + char * ptr1, * ptr2; + UINT * buffptr; + char Cmd[32]; + + if (TNC->WINMORHostName == NULL) + return; + + TNC->BusyFlags = 0; + + TNC->CONNECTING = TRUE; + + Sleep(5000); // Allow init to complete + +// // If we started the TNC make sure it is still running. + +// if (!IsProcess(TNC->WIMMORPID)) +// { +// ARDOPRestartTNC(TNC); +// Sleep(3000); +// } + + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->WINMORHostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->WINMORHostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->WINMORHostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + +// closesocket(TNC->WINMORSock); +// closesocket(TNC->WINMORDataSock); + + TNC->WINMORSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->WINMORSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + TNC->WINMORDataSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->WINMORDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + closesocket(TNC->WINMORSock); + + return; + } + + + setsockopt(TNC->WINMORSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->WINMORDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); +// setsockopt(TNC->WINMORDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + +/* if (bind(TNC->WINMORSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for ARDOP socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->WINMORSock); + TNC->CONNECTING = FALSE; + + return; + } +*/ + if (connect(TNC->WINMORSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for ARDOP socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->WINMORSock); + TNC->WINMORSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + // Connect Data Port + + if (connect(TNC->WINMORDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for ARDOP Data socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->WINMORSock); + closesocket(TNC->WINMORDataSock); + TNC->WINMORSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + +#ifndef LINBPQ +// FreeSemaphore(&Semaphore); + EnumWindows(EnumARDOPWindowsProc, (LPARAM)TNC); +// GetSemaphore(&Semaphore, 52); +#endif + Sleep(1000); + + TNC->LastFreq = 0; // so V4 display will be updated + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + + // Send INIT script + + // ARDOP needs each command in a separate send + + ptr1 = &TNC->InitScript[0]; + + // We should wait for first RDY. Cheat by queueing a null command + + GetSemaphore(&Semaphore, 52); + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + + buffptr = GetBuff(); + buffptr[1] = 0; + C_Q_ADD(&TNC->BPQtoWINMOR_Q, buffptr); + + while (ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, 13); + if (ptr2) + *(ptr2) = 0; + + // if Date or Time command add current time + + if (_memicmp(ptr1, "DATE", 4) == 0) + { + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Cmd,"DATE %02d%02d%02d\r", tm->tm_mday, tm->tm_mon + 1, tm->tm_year - 100); + ptr1 = Cmd; + } + else if (_memicmp(ptr1, "TIME", 4) == 0) + { + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Cmd,"TIME %02d%02d%02d\r", tm->tm_hour, tm->tm_min, tm->tm_sec); + ptr1 = Cmd; + } + + ARDOPSendCommand(TNC, ptr1, TRUE); + + if (ptr2) + *(ptr2++) = 13; // Put CR back for next time + + ptr1 = ptr2; + } + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to ARDOP TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + FreeSemaphore(&Semaphore); + + sprintf(Msg, "Connected to ARDOP TNC Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + while (TNC->CONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TNC->WINMORSock,&readfs); + FD_SET(TNC->WINMORSock,&errorfs); + + if (TNC->CONNECTED) FD_SET(TNC->WINMORDataSock,&readfs); + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->WINMORDataSock,&writefs); // Need notification of busy clearing + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->WINMORDataSock,&errorfs); + + timeout.tv_sec = 600; + timeout.tv_usec = 0; // We should get messages more frequently that this + + ret = select(TNC->WINMORDataSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("ARDOP Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->WINMORSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + ARDOPProcessReceivedControl(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->WINMORDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + ARDOPProcessReceivedData(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->WINMORSock, &errorfs)) + { +Lost: + sprintf(Msg, "ARDOP Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->WINMORDataSock); + TNC->WINMORSock = 0; + return; + } + + if (FD_ISSET(TNC->WINMORDataSock, &errorfs)) + { + sprintf(Msg, "ARDOP Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->WINMORSock); + closesocket(TNC->WINMORDataSock); + TNC->WINMORSock = 0; + return; + } + + + continue; + } + else + { + // 60 secs without data. Shouldn't happen + + sprintf(Msg, "ARDOP No Data Timeout Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + +// sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); +// GetSemaphore(&Semaphore, 52); +// MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); +// FreeSemaphore(&Semaphore); + + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + GetSemaphore(&Semaphore, 52); + ARDOPSendCommand(TNC, "CODEC FALSE", FALSE); + FreeSemaphore(&Semaphore); + + Sleep(100); + shutdown(TNC->WINMORSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->WINMORDataSock); + + Sleep(100); + shutdown(TNC->WINMORDataSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->WINMORDataSock); + + if (TNC->WIMMORPID && TNC->WeStartedTNC) + { + ARDOPKillTNC(TNC); + } + return; + } + } + sprintf(Msg, "ARDOP Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + + +static VOID ARDOPProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + UINT * buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; +// unsigned int CRC; + +// CRC = checkcrc16(&Buffer[2], MsgLen - 2); + + Buffer[MsgLen - 1] = 0; // Remove CR + +// if (CRC == 0) +// { +// Debugprintf("ADDOP CRC Error %s", Buffer); +// return; +// } + + Buffer+=2; // Skip c: + + if (_memicmp(Buffer, "RDY", 3) == 0) + { + // Command ACK. Remove from bufer and send next if any + + UINT * buffptr; + UINT * Q; + + + buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + + // See if another + + // Leave on Queue till acked + + // Q may not be word aligned, so copy as bytes (for ARM5) + + Q = (UINT *)&TNC->BPQtoWINMOR_Q; + + buffptr = (UINT *)Q[0]; + + if (buffptr) + SendToTNC(TNC, (UCHAR *)&buffptr[2], buffptr[1]); + + return; + } + + if (_memicmp(Buffer, "CRCFAULT", 8) == 0) + { + // Command NAK. Resend + + UINT * buffptr; + UINT * Q; + + // Leave on Queue till acked + + // Q may not be word aligned, so copy as bytes (for ARM5) + + Q = (UINT *)&TNC->BPQtoWINMOR_Q; + + buffptr = (UINT *)Q[0]; + + if (buffptr) + SendToTNC(TNC, (UCHAR *)&buffptr[2], buffptr[1]); + + Debugprintf("ARDP CRCFAULT Received"); + return; + } + + if (_memicmp(Buffer, "FAULT failure to Restart Sound card", 20) == 0) + { + Debugprintf(Buffer); + + // Force a restart + + ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); + ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); + } + else + { + TNC->TimeSinceLast = 0; + } + + + if (_memicmp(Buffer, "STATE ", 6) == 0) + { + Debugprintf(Buffer); + + if (_memicmp(&Buffer[6], "OFFLINE", 7) == 0) + { + // Force a restart + + ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); + ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); + } + return; + } + + if (_memicmp(Buffer, "PTT T", 5) == 0) + { + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, TRUE); + + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + if (_memicmp(Buffer, "PTT F", 5) == 0) + { + if (TNC->PTTMode) + Rig_PTT(TNC->RIG, FALSE); + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->BusyHold) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + MySetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + + if (_memicmp(Buffer, "TARGET", 6) == 0) + { + TNC->ConnectPending = 6; // This comes before Pending + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 3); + memcpy(TNC->TargetCall, &Buffer[7], 10); + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + + if (_memicmp(Buffer, "OFFSET", 6) == 0) + { +// WritetoTrace(TNC, Buffer, MsgLen - 5); +// memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "BUFFER", 6) == 0) + { + Debugprintf(Buffer); + + sscanf(&Buffer[7], "%d", &TNC->Streams[0].BytesOutstanding); + + if (TNC->Streams[0].BytesOutstanding == 0) + { + // all sent + + if (TNC->Streams[0].Disconnecting) // Disconnect when all sent + { + if (STREAM->NeedDisc == 0) + STREAM->NeedDisc = 60; // 6 secs + } +// else +// if (TNC->TXRXState == 'S') +// ARDOPSendCommand(TNC,"OVER"); + + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %s", + STREAM->BytesTXed, STREAM->BytesRXed, &Buffer[7]); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + + if (_memicmp(Buffer, "CONNECTED ", 10) == 0) + { + char Call[11]; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + int Speed = 0; + + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 3); + + ARDOPSendCommand(TNC, "RDY", FALSE); + + STREAM->ConnectTime = time(NULL); + STREAM->BytesRXed = STREAM->BytesTXed = STREAM->PacketsSent = 0; + +// if (TNC->StartInRobust) +// ARDOPSendCommand(TNC, "ROBUST TRUE"); + + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + // Get Speed + + ptr = strchr(&Buffer[10], ' '); + if (ptr) + { + Speed = atoi(ptr); + + if (Speed == 200) + TNC->WL2KMode = 40; + else if (Speed == 500) + TNC->WL2KMode = 41; + else if (Speed == 1000) + TNC->WL2KMode = 42; + else if (Speed == 2000) + TNC->WL2KMode = 43; + } + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + TRANSPORTENTRY * SESS; + + // Incomming Connect + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + ProcessIncommingConnectEx(TNC, Call, 0, TRUE, TRUE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + SESS->Frequency = (atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + char Status[32]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command(-1, Status); + Debugprintf("ARDOP Call from %s rejected", Call); + return; + } + } + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr[1] = MsgLen; + memcpy(buffptr+2, Buffer, MsgLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + ARDOPSendData(TNC, Msg, strlen(Msg)); + STREAM->NeedDisc = 100; // 10 secs + } + } + + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + ReplyLen = sprintf(Reply, "*** Connected to %s\r", Call); + + buffptr[1] = ReplyLen; + memcpy(buffptr+2, Reply, ReplyLen); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + if (TNC->RIG && TNC->RIG->Valchar[0]) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, Call, '+', 'O'); + return; + } + } + + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0 + || _memicmp(Buffer, "STATUS CONNECT TO", 17) == 0 + || _memicmp(Buffer, "STATUS ARQ TIMEOUT FROM PROTOCOL STATE", 24) == 0) + { + Debugprintf(Buffer); + + ARDOPSendCommand(TNC, "RDY", FALSE); + TNC->ConnectPending = FALSE; // Cancel Scan Lock + + if (TNC->FECMode) + return; + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "ARDOP} Failure with %s\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); + +// ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); // !!!! Temp bug workaround !!!! + + return; + } + + WritetoTrace(TNC, Buffer, MsgLen - 3); + + // Release Session3 + + if (TNC->Streams[0].Connected) + { + // Create a traffic record + + char logmsg[120]; + time_t Duration; + + Duration = time(NULL) - STREAM->ConnectTime; + + if (Duration == 0) + Duration = 1; + + sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds", + TNC->Port, STREAM->RemoteCall, + STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration), + STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration); + + Debugprintf(logmsg); + } + + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + if (TNC->Streams[0].Disconnecting) // + ARDOPReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + + return; + } + + Debugprintf(Buffer); + + if (_memicmp(Buffer, "STATUS ", 7) == 0) + { + ARDOPSendCommand(TNC, "RDY", FALSE); + return; + } + + if (_memicmp(Buffer, "RADIOMODELS", 11) == 0) + return; + + if (_memicmp(Buffer, "MODE", 4) == 0) + { + // Debugprintf("WINMOR RX: %s", Buffer); + + strcpy(TNC->WEB_MODE, &Buffer[5]); + SetWindowText(TNC->xIDC_MODE, &Buffer[5]); + return; + } + + if (_memicmp(&Buffer[0], "PENDING", 7) == 0) // Save Pending state for scan control + { + ARDOPSendCommand(TNC, "RDY", FALSE); + TNC->ConnectPending = 6; // Time out after 6 Scanintervals + return; + } + + if (_memicmp(&Buffer[0], "CANCELPENDING", 13) == 0 + || _memicmp(&Buffer[0], "REJECTEDB", 9) == 0) //REJECTEDBUSY or REJECTEDBW + { + ARDOPSendCommand(TNC, "RDY", FALSE); + TNC->ConnectPending = FALSE; + return; + } + + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 3); +// return; + } + + if (_memicmp(Buffer, "NEWSTATE", 8) == 0) + { + ARDOPSendCommand(TNC, "RDY", FALSE); + + TNC->WinmorRestartCodecTimer = time(NULL); + + MySetWindowText(TNC->xIDC_PROTOSTATE, &Buffer[9]); + strcpy(TNC->WEB_PROTOSTATE, &Buffer[9]); + + if (_memicmp(&Buffer[9], "DISCONNECTING", 13) == 0) // So we can timout stuck discpending + { + TNC->DiscPending = 600; + return; + } + if (_memicmp(&Buffer[9], "DISCONNECTED", 12) == 0) + { + TNC->DiscPending = FALSE; + TNC->ConnectPending = FALSE; + + if (TNC->RestartAfterFailure) + { + if (TNC->HadConnect) + { + TNC->HadConnect = FALSE; + + if (TNC->WIMMORPID) + { + ARDOPKillTNC(TNC); + ARDOPRestartTNC(TNC); + } + } + } + return; + } + + if (strcmp(&Buffer[9], "ISS") == 0) // Save Pending state for scan control + TNC->TXRXState = 'S'; + else if (strcmp(&Buffer[9], "IRS") == 0) + TNC->TXRXState = 'R'; + + return; + } + + + if (_memicmp(Buffer, "PROCESSID", 9) == 0) + { + HANDLE hProc; + char ExeName[256] = ""; + + TNC->WIMMORPID = atoi(&Buffer[10]); + +#ifndef LINBPQ + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->WIMMORPID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + + // Set Window Title to reflect BPQ Port Description + + EnumWindows(EnumARDOPWindowsProc, (LPARAM)TNC); +#endif + } + + if ((_memicmp(Buffer, "FAULT Not from state FEC", 24) == 0) || (_memicmp(Buffer, "FAULT Blocked by Busy Lock", 24) == 0)) + { + if (TNC->FECMode) + { + Sleep(1000); + +// if (TNC->FEC1600) +// ARDOPSendCommand(TNC,"FECSEND 1600"); +// else +// ARDOPSendCommand(TNC,"FECSEND 500"); + return; + } + } + + if (_memicmp(Buffer, "PLAYBACKDEVICES", 15) == 0) + { + TNC->PlaybackDevices = _strdup(&Buffer[16]); + } + // Others should be responses to commands + + if (_memicmp(Buffer, "BLOCKED", 6) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 3); + return; + } + + if (_memicmp(Buffer, "OVER", 4) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 3); + return; + } + + // Return others to user (if attached but not connected) + + if (TNC->Streams[0].Attached == 0) + return; + + if (TNC->Streams[0].Connected) + return; + + if (MsgLen > 200) + MsgLen = 200; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr[1] = sprintf((UCHAR *)&buffptr[2], "ARDOP} %s\r", Buffer); + + C_Q_ADD(&TNC->WINMORtoBPQ_Q, buffptr); +} + +static VOID ARDOPProcessReceivedData(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; +// char * ptr, * ptr2; +// char Buffer[4096]; + + // shouldn't get several messages per packet, as each should need an ack + // May get message split over packets + + // Both command and data arrive here, which complicated things a bit + + // Commands start with c: and end with CR. + // Data starts with d: and has a length field + // “d:ARQ|FEC|ERR|, 2 byte count (Hex 0001 – FFFF), binary data, +2 Byte CRC” + + // As far as I can see, shortest frame is “c:RDY + 2 byte CRC” = 8 bytes + + if (TNC->DataInputLen > 8000) // Shouldnt have packets longer than this + TNC->DataInputLen=0; + + // I don't think it likely we will get packets this long, but be aware... + + // We can get pretty big ones in the faster + + InputLen=recv(TNC->WINMORDataSock, &TNC->ARDOPDataBuffer[TNC->DataInputLen], 8192 - TNC->DataInputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + +// closesocket(TNC->WINMORSock); + closesocket(TNC->WINMORDataSock); + + TNC->WINMORDataSock = 0; + + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + return; + } + + TNC->DataInputLen += InputLen; + +loop: + + if (TNC->DataInputLen < 8) + return; // Wait for more to arrive (?? timeout??) + + if (TNC->ARDOPDataBuffer[1] = ':') // At least message looks reasonable + { + if (TNC->ARDOPDataBuffer[0] == 'd') + { + // Data = check we have it all + + int DataLen = (TNC->ARDOPDataBuffer[2] << 8) + TNC->ARDOPDataBuffer[3]; // HI First +// unsigned short CRC; + UCHAR DataType[4]; + UCHAR * Data; + + if (TNC->DataInputLen < DataLen + 4) + return; // Wait for more + + MsgLen = DataLen + 4; // d: Len CRC + + // Check CRC + +// CRC = compute_crc(&TNC->ARDOPBuffer[2], DataLen + 4); + +// CRC = checkcrc16(&TNC->ARDOPBuffer[2], DataLen + 4); + +// if (CRC == 0) +// { +// Debugprintf("ADDOP CRC Error %s", &TNC->ARDOPBuffer[2]); +// return; +// } + + + memcpy(DataType, &TNC->ARDOPDataBuffer[4] , 3); + DataType[3] = 0; + Data = &TNC->ARDOPDataBuffer[7]; + DataLen -= 3; + + ARDOPProcessDataPacket(TNC, DataType, Data, DataLen); + +// ARDOPSendCommand(TNC, "RDY", FALSE); + + // See if anything else in buffer + + TNC->DataInputLen -= MsgLen; + + if (TNC->InputLen == 0) + return; + + memmove(TNC->ARDOPDataBuffer, &TNC->ARDOPDataBuffer[MsgLen], TNC->DataInputLen); + goto loop; + } + } + return; +} + + + +static VOID ARDOPProcessReceivedControl(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[4096]; + + // shouldn't get several messages per packet, as each should need an ack + // May get message split over packets + + // Both command and data arrive here, which complicated things a bit + + // Commands start with c: and end with CR. + // Data starts with d: and has a length field + // “d:ARQ|FEC|ERR|, 2 byte count (Hex 0001 – FFFF), binary data, +2 Byte CRC” + + // As far as I can see, shortest frame is “c:RDY + 2 byte CRC” = 8 bytes + + if (TNC->InputLen > 8000) // Shouldnt have packets longer than this + TNC->InputLen=0; + + // I don't think it likely we will get packets this long, but be aware... + + // We can get pretty big ones in the faster + + InputLen=recv(TNC->WINMORSock, &TNC->ARDOPBuffer[TNC->InputLen], 8192 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + // Does this mean closed? + + closesocket(TNC->WINMORSock); + + TNC->WINMORSock = 0; + + TNC->CONNECTED = FALSE; + TNC->Streams[0].ReportDISC = TRUE; + + return; + } + + TNC->InputLen += InputLen; + +loop: + + if (TNC->InputLen < 6) + return; // Wait for more to arrive (?? timeout??) + + if (TNC->ARDOPBuffer[1] = ':') // At least message looks reasonable + { + if (TNC->ARDOPBuffer[0] == 'c') + { + // Command = look for CR + + ptr = memchr(TNC->ARDOPBuffer, '\r', TNC->InputLen); + + if (ptr == 0) // CR in buffer + return; // Wait for it + + ptr2 = &TNC->ARDOPBuffer[TNC->InputLen]; + + if ((ptr2 - ptr) == 1) // CR (no CRC in new version) + { + // Usual Case - single meg in buffer + + ARDOPProcessResponse(TNC, TNC->ARDOPBuffer, TNC->InputLen); + TNC->InputLen=0; + return; + } + else + { + // buffer contains more that 1 message + + // I dont think this should happen, but... + + MsgLen = TNC->InputLen - (ptr2-ptr) + 1; // Include CR + + memcpy(Buffer, TNC->ARDOPBuffer, MsgLen); + + ARDOPProcessResponse(TNC, Buffer, MsgLen); + + if (TNC->InputLen < MsgLen) + { + TNC->InputLen = 0; + return; + } + memmove(TNC->ARDOPBuffer, ptr + 1, TNC->InputLen-MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + } + } + return; +} + + + +static VOID ARDOPProcessDataPacket(struct TNCINFO * TNC, UCHAR * Type, UCHAR * Data, int Length) +{ + // Info on Data Socket - just packetize and send on + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int PacLen = 236; + UINT * buffptr; + + TNC->TimeSinceLast = 0; + + if (strcmp(Type, "IDF") == 0) + { + // Place ID frames in Monitor Window and MH + + char Call[20]; + + Data[Length] = 0; + WritetoTrace(TNC, Data, Length); + + if (memcmp(Data, "ID:", 3) == 0) // These seem to be transmitted ID's + { + memcpy(Call, &Data[3], 20); + strlop(Call, ':'); + UpdateMH(TNC, Call, '!', 'I'); + } + return; + } + + STREAM->BytesRXed += Length; + + Data[Length] = 0; + Debugprintf("ARDOP: RXD %d bytes", Length); + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->BytesTXed, STREAM->BytesRXed,STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + + if (TNC->FECMode) + { + Length = strlen(Data); + if (Data[Length - 1] == 10) + Data[Length - 1] = 13; + + } + + if (strcmp(Type, "FEC") == 0) + { + // Make sure it at least looks like an ax.25 format message + + + char * ptr1 = Data; + char * ptr2 = strchr(ptr1, '>'); + int Len = 80; + + if (ptr2 && (ptr2 - ptr1) < 10) + { + // Could be APRS + + if (memcmp(ptr2 + 1, "AP", 2) == 0) + { + // assume it is + + char * ptr3 = strchr(ptr2, '|'); + struct _MESSAGE * buffptr = GetBuff(); + + if (ptr3 == 0) + return; + + *(ptr3++) = 0; // Terminate TO call + + Len = strlen(ptr3); + + // Convert to ax.25 format + + if (buffptr == 0) + return; // No buffers, so ignore + + buffptr->PORT = TNC->Port; + + ConvToAX25(ptr1, buffptr->ORIGIN); + ConvToAX25(ptr2 + 1, buffptr->DEST); + buffptr->ORIGIN[6] |= 1; // Set end of address + buffptr->CTL = 3; + buffptr->PID = 0xF0; + memcpy(buffptr->L2DATA, ptr3, Len); + buffptr->LENGTH = 23 + Len; + time(&buffptr->Timestamp); + + BPQTRACE((MESSAGE *)buffptr, TRUE); + + return; + + } + } + + // FEC but not AX.25 APRS. Discard if connected + + return; + } + + // Discard ARQ frames + + return; +} + +static VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[0].BytesOutstanding == 0) + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); +} + +static VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + ARDOPSendCommand(TNC, "ABORT", TRUE); +} + +static VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + ARDOPReleaseTNC(TNC); + + if (TNC->FECMode) + { + TNC->FECMode = FALSE; + ARDOPSendCommand(TNC, "SENDID", TRUE); + } +} + diff --git a/.svn/pristine/61/618003aca71cf31e07e2ceca7a24d40506cb14d2.svn-base b/.svn/pristine/61/618003aca71cf31e07e2ceca7a24d40506cb14d2.svn-base new file mode 100644 index 0000000..ac753d9 --- /dev/null +++ b/.svn/pristine/61/618003aca71cf31e07e2ceca7a24d40506cb14d2.svn-base @@ -0,0 +1,24 @@ +#include +#include + +/* + * TST_PG.C + * + * Little test program of "PG" command for FBB BBS software. + * + * (C) F6FBB 1991. + * + * FBB software 5.14 and up. + * + * + * This program echoes to the user what he types + * or executes a BBS command preceded by "CMD" + * until "BYE" is received + */ + + +main(int argc, char **argv) +{ + Sleep(10000); + return 0; +} \ No newline at end of file diff --git a/.svn/pristine/61/61d8101d29fd0ceb285dc7f74ac10e124d924e7a.svn-base b/.svn/pristine/61/61d8101d29fd0ceb285dc7f74ac10e124d924e7a.svn-base new file mode 100644 index 0000000..d563411 --- /dev/null +++ b/.svn/pristine/61/61d8101d29fd0ceb285dc7f74ac10e124d924e7a.svn-base @@ -0,0 +1,406 @@ +/* +Copyright 2001-2015 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 +*/ + +// +// Implements the DOS register based API. + +// Called via an assmbler glue that puts registers into C variables. + +#define _CRT_SECURE_NO_DEPRECATE + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" +#include + +#include "compatbits.h" + +#include "cheaders.h" + +extern QCOUNT; +extern BPQVECSTRUC BPQHOSTVECTOR[]; +extern int MAJORVERSION; +extern int MINORVERSION; +extern char pgm[256]; // Uninitialised so per process + +VOID PostDataAvailable(TRANSPORTENTRY * Session); +DllExport int APIENTRY SendMsg(int stream, char * msg, int len); +DllExport int APIENTRY AllocateStream(int stream); +DllExport int APIENTRY SendRaw(int port, char * msg, int len); +DllExport time_t APIENTRY GetRaw(int stream, char * msg, int * len, int * count); +VOID SENDNODESMSG(); + +int BTLENGTH; +char BTEXTFLD[256]; +int REALTIMETICKS; + +VOID CHOSTAPI(ULONG * pEAX, ULONG * pEBX, ULONG * pECX, ULONG * pEDX, VOID ** pESI, VOID ** pEDI) +{ + ULONG EAX = *pEAX; + ULONG EBX = *pEBX; + ULONG ECX = *pECX; + ULONG EDX = *pEDX; + VOID * ESI = *pESI; + VOID * EDI = *pEDI; + + int Command; + int Stream; + int n; + int Temp; + PBPQVECSTRUC HostVec; + TRANSPORTENTRY * Session; + +/* +; COMMANDS SUPPORTED ARE +; +; AH = 0 Get node/switch version number and description. On return +; AH = major version number and AL = minor version number, +; and user's buffer pointed to by ES:ESI is set to the text +; string normally output by the USERS command, eg: +; "G8BPQ Packet Switch Version 4.01 Dev". CX is set to the +; length of the text string. +; +; +; AH = 1 Set application mask to value in DL (or even DX if 16 +; applications are ever to be supported). +; +; Set application flag(s) to value in CL (or CX). +; whether user gets connected/disconnected messages issued +; by the node etc. +; +; +; AH = 2 Send frame in ES:ESI (length CX) +; +; +; AH = 3 Receive frame into buffer at ES:ESI, length of frame returned +; in CX. BX returns the number of outstanding frames still to +; be received (ie. after this one) or zero if no more frames +; (ie. this is last one). +; +; +; +; AH = 4 Get stream status. Returns: +; +; CX = 0 if stream disconnected or CX = 1 if stream connected +; DX = 0 if no change of state since last read, or DX = 1 if +; the connected/disconnected state has changed since +; last read (ie. delta-stream status). +; +; +; +; AH = 6 Session control. +; +; CX = 0 Conneect - _APPLMASK in DL +; CX = 1 connect +; CX = 2 disconnect +; CX = 3 return user to node +; +; +; AH = 7 Get buffer counts for stream. Returns: +; +; AX = number of status change messages to be received +; BX = number of frames queued for receive +; CX = number of un-acked frames to be sent +; DX = number of buffers left in node +; SI = number of trace frames queued for receive +; +;AH = 8 Port control/information. Called with a stream number +; in AL returns: +; +; AL = Radio port on which channel is connected (or zero) +; AH = SESSION TYPE BITS +; BX = L2 paclen for the radio port +; CX = L2 maxframe for the radio port +; DX = L4 window size (if L4 circuit, or zero) +; ES:EDI = CALLSIGN + +;AH = 9 Fetch node/application callsign & alias. AL = application +; number: +; +; 0 = node +; 1 = BBS +; 2 = HOST +; 3 = SYSOP etc. etc. +; +; Returns string with alias & callsign or application name in +; user's buffer pointed to by ES:ESI length CX. For example: +; +; "WORCS:G8TIC" or "TICPMS:G8TIC-10". +; +; +; AH = 10 Unproto transmit frame. Data pointed to by ES:ESI, of +; length CX, is transmitted as a HDLC frame on the radio +; port (not stream) in AL. +; +; +; AH = 11 Get Trace (RAW Data) Frame into ES:EDI, +; Length to CX, Timestamp to AX +; +; +; AH = 12 Update Switch. At the moment only Beacon Text may be updated +; DX = Function +; 1=update BT. ES:ESI, Len CX = Text +; 2=kick off nodes broadcast +; +; AH = 14 Internal Interface for IP Router +; +; Send frame - to NETROM L3 if DL=0 +; to L2 Session if DL<>0 +; +; +; AH = 15 Get interval timer +; + +*/ + + Command = (EAX & 0xFFFF) >> 8; + + Stream = (EAX & 0xFF); + n = Stream - 1; // API Numbers Streams 1-64 + + if (n < 0 || n > 63) + n = 64; + + HostVec = &BPQHOSTVECTOR[n]; + Session = HostVec->HOSTSESSION; + + switch (Command) + { + case 0: // Check Loaded/Get Version + + EAX = ('P' << 8) | 'B'; + EBX = ('Q' << 8) | ' '; + + EDX = (MAJORVERSION << 8) | MINORVERSION; + break; + + case 1: // Set Appl mAsk + + HostVec->HOSTAPPLMASK = EDX; // APPL MASK + HostVec->HOSTAPPLFLAGS = (UCHAR)ECX; // APPL FLAGS + + // If either is non-zero, set allocated and Process. This gets round problem with + // stations that don't call allocate stream + + if (ECX || EBX) + { + HostVec->HOSTFLAGS |= 0x80; // SET ALLOCATED BIT + HostVec->STREAMOWNER = GetCurrentProcessId(); + + // Set Program Name + + memcpy(&HostVec->PgmName, pgm, 31); + } + break; + + case 2: // Send Frame + + // ES:ESI = MESSAGE, CX = LENGTH, BX = VECTOR + + EAX = SendMsg(Stream, ESI, ECX); + break; + + case 3: + + // AH = 3 Receive frame into buffer at ES:EDI, length of frame returned + // in CX. BX returns the number of outstanding frames still to + // be received (ie. after this one) or zero if no more frames + // (ie. this is last one). + + EAX = GetMsg(Stream, EDI, &ECX, &EBX); + break; + + case 4: + + // AH = 4 Get stream status. Returns: + // CX = 0 if stream disconnected or CX = 1 if stream connected + // DX = 0 if no change of state since last read, or DX = 1 if + // the connected/disconnected state has changed since + // last read (ie. delta-stream status). + + ECX = EDX = 0; + + if (HostVec->HOSTFLAGS & 3) //STATE CHANGE BITS + EDX = 1; + + if (Session) + ECX = 1; + + break; + + case 5: + + // AH = 5 Ack stream status change + + HostVec->HOSTFLAGS &= 0xFC; // Clear Chnage Bits + break; + + case 6: + + // AH = 6 Session control. + + // CX = 0 Conneect - APPLMASK in DL + // CX = 1 connect + // CX = 2 disconnect + // CX = 3 return user to node + + SessionControl(Stream, ECX, EDX); + break; + + case 7: + + // AH = 7 Get buffer counts for stream. Returns: + + // AX = number of status change messages to be received + // BX = number of frames queued for receive + // CX = number of un-acked frames to be sent + // DX = number of buffers left in node + // SI = number of trace frames queued for receive + + + ECX = 0; // unacked frames + EDX = QCOUNT; + + ESI = (void *)MONCount(Stream); + EBX = RXCount(Stream); + ECX = TXCount(Stream); + + EAX = 0; // Is this right ??? + + break; + + case 8: + + // AH = 8 Port control/information. Called with a stream number + // in AL returns: + // + // AL = Radio port on which channel is connected (or zero) + // AH = SESSION TYPE BITS + // BX = L2 paclen for the radio port + // CX = L2 maxframe for the radio port + // DX = L4 window size (if L4 circuit, or zero) + // ES:EDI = CALLSIGN + + + GetConnectionInfo(Stream, EDI, &EAX, &Temp, &EBX, &ECX, &EDX); // Return the Secure Session Flag rather than not connected + EAX |= Temp <<8; + + break; + + + case 9: + + // Not Implemented + + break; + + case 10: + + // AH = 10 Unproto transmit frame. Data pointed to by ES:ESI, of + // length CX, is transmitted as a HDLC frame on the radio + // port (not stream) in AL. + + EAX = SendRaw(EAX, ESI, ECX); + return; + + case 11: + + // AH = 11 Get Trace (RAW Data) Frame into ES:EDI, + // Length to CX, Timestamp to AX + + EAX = GetRaw(Stream, EDI, &ECX, &EBX); + break; + + case 12: + + // Update Switch + + if (EDX == 2) + { + SENDNODESMSG(); + break; + } + if (EDX == 2) + { + // UPDATE BT + + BTLENGTH = ECX; + memcpy(BTEXTFLD, ESI, ECX + 7); + } + + break; + + case 13: + + // BPQALLOC + + // AL = 0 = Find Free + // AL != 0 Alloc or Release + + if (EAX == 0) + { + EAX = FindFreeStream(); + break; + } + + if (ECX == 1) // Allocate + { + EAX = AllocateStream(Stream); + break; + } + + DeallocateStream(Stream); + break; + + case 14: + + // AH = 14 Internal Interface for IP Router + + // Send frame - to NETROM L3 if DL=0 + // to L2 Session if DL<>0 + + break; // Shouldn't be needed + + case 15: + + // GETTIME + + EAX = REALTIMETICKS; + EBX = 0; + +#ifdef EXCLUDEBITS + + EBX = (ULONG)ExcludeList; + +#endif + break; + + } + + *pEAX = EAX; + *pEBX = EBX; + *pECX = ECX; + *pEDX = EDX; + *pESI = ESI; + *pEDI = EDI; + + return; +} + diff --git a/.svn/pristine/61/61ef2647fd9329d88b41f4a233ee9e9dddfca8ea.svn-base b/.svn/pristine/61/61ef2647fd9329d88b41f4a233ee9e9dddfca8ea.svn-base new file mode 100644 index 0000000..b1dde92 --- /dev/null +++ b/.svn/pristine/61/61ef2647fd9329d88b41f4a233ee9e9dddfca8ea.svn-base @@ -0,0 +1,63 @@ + + +#define KISSMAXBLOCK 512 + +// KISS over TCP Slave now allows multiple connections +// so need a struct to keep track of them + +typedef struct _KISSTCPSess +{ + struct _KISSTCPSesssion * Next; + SOCKET Socket; + UCHAR RXBuffer[KISSMAXBLOCK]; + int RXLen; + + time_t Timeout; + +} KISSTCPSess; + +typedef struct tagASYINFO +{ + HANDLE idComDev ; + BYTE bPort; + DWORD dwBaudRate ; + SOCKET sock; // for KISS over UDP/TCP + BOOL Connecting; // Kiss over TCP + BOOL Connected; // Kiss over TCP + BOOL Listening; // Kiss over TCP + BOOL Alerted; // Connect Fail Reported + + struct sockaddr_in destaddr; + struct PORTCONTROL * Portvector; + UCHAR RXMSG[512]; // Msg being built + UCHAR RXBUFFER[KISSMAXBLOCK]; // Raw chars from Comms + int RXBCOUNT; // chars in RXBUFFER + UCHAR * RXBPTR; // get pointer for RXBUFFER (put ptr is RXBCOUNT) + UCHAR * RXMPTR; // put pointer for RXMSG + BOOL MSGREADY; // Complete msg in RXMSG + BOOL ESCFLAG; // FESC/DLE received + BOOL NEEDCRC; // ETX received, waiting for CRC (NETROM) + int ReopenTimer; // for failed com ports + + // We now allow multiple connections to KISS Slave + + struct _KISSTCPSess * slaveSessions; + +} ASYINFO, *NPASYINFO ; + +extern NPASYINFO KISSInfo[MAXBPQPORTS]; + +#define _fmemset memset +#define _fmemmove memmove + +// function prototypes (private) + +NPASYINFO CreateKISSINFO( int port, int speed ); + + +BOOL DestroyKISSINFO(NPASYINFO npKISSINFO) ; +int ReadCommBlock(NPASYINFO npKISSINFO, char * lpszBlock, int nMaxLength); +static BOOL WriteCommBlock(NPASYINFO npKISSINFO, char * lpByte, DWORD dwBytesToWrite); +HANDLE OpenConnection(struct PORTCONTROL * PortVector); +BOOL SetupConnection(NPASYINFO npKISSINFO); +BOOL CloseConnection(NPASYINFO npKISSINFO); diff --git a/.svn/pristine/63/6398d36875f703bdfb3fadbef68c402f2992afc3.svn-base b/.svn/pristine/63/6398d36875f703bdfb3fadbef68c402f2992afc3.svn-base new file mode 100644 index 0000000..ed8153e --- /dev/null +++ b/.svn/pristine/63/6398d36875f703bdfb3fadbef68c402f2992afc3.svn-base @@ -0,0 +1,15934 @@ +/* +Copyright 2001-2018 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 +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// Utility Routines + +#include "bpqmail.h" +#ifdef WIN32 +#include "Winspool.h" +#else +#include +#endif + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); + +BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen); + +BOOL Bells; +BOOL FlashOnBell; // Flash instead of Beep +BOOL StripLF; + +BOOL WarnWrap; +BOOL FlashOnConnect; +BOOL WrapInput; +BOOL CloseWindowOnBye; + +RECT ConsoleRect; + +BOOL OpenConsole; +BOOL OpenMon; + +int reportMailEvents = 0; + +FBBFilter * Filters = NULL; + +extern struct ConsoleInfo BBSConsole; + +extern char LOC[7]; + +extern BOOL MQTT; + +//#define BBSIDLETIME 120 +//#define USERIDLETIME 300 + + +#define BBSIDLETIME 900 +#define USERIDLETIME 900 + +#ifdef LINBPQ +extern BPQVECSTRUC ** BPQHOSTVECPTR; +UCHAR * GetLogDirectory(); +DllExport int APIENTRY SessionStateNoAck(int stream, int * state); +int RefreshWebMailIndex(); +#else +__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; +typedef char * (WINAPI FAR *FARPROCZ)(); +typedef int (WINAPI FAR *FARPROCX)(); +FARPROCZ pGetLOC; +FARPROCX pRefreshWebMailIndex; + +#endif + +Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall); +VOID APIENTRY md5 (char *arg, unsigned char * checksum); +int APIENTRY GetRaw(int stream, char * msg, int * len, int * count); +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); +void FreeSemaphore(struct SEM * Semaphore); +int EncryptPass(char * Pass, char * Encrypt); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); +void DeletetoRecycle(char * FN); +VOID DoImportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID DoExportCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +VOID TidyPrompts(); +char * ReadMessageFileEx(struct MsgInfo * MsgRec); +char * APIENTRY GetBPQDirectory(); +BOOL SendARQMail(CIRCUIT * conn); +int APIENTRY ChangeSessionIdletime(int Stream, int idletime); +int APIENTRY GetApplNum(int Stream); +VOID FormatTime(char * Time, time_t cTime); +BOOL CheckifPacket(char * Via); +char * APIENTRY GetVersionString(); +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename); +void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); +int GetCMSHash(char * Challenge, char * Password); +BOOL SendAMPRSMTP(CIRCUIT * conn); +VOID ProcessMCASTLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen); +VOID MCastTimer(); +VOID MCastConTimer(ConnectionInfo * conn); +int FindFreeBBSNumber(); +VOID DoSetMsgNo(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +BOOL ProcessYAPPMessage(CIRCUIT * conn); +void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename); +void YAPPSendData(ConnectionInfo * conn); +VOID CheckBBSNumber(int i); +struct UserInfo * FindAMPR(); +VOID SaveInt64Value(config_setting_t * group, char * name, long long value); +VOID SaveIntValue(config_setting_t * group, char * name, int value); +VOID SaveStringValue(config_setting_t * group, char * name, char * value); +char *stristr (char *ch1, char *ch2); +BOOL CheckforMessagetoServer(struct MsgInfo * Msg); +void DoHousekeepingCmd(CIRCUIT * conn, struct UserInfo * user, char * Arg1, char * Context); +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled); +void ListCategories(ConnectionInfo * conn); +void RebuildNNTPList(); +long long GetInt64Value(config_setting_t * group, char * name); +void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len); +int ReformatSyncMessage(CIRCUIT * conn); +char * initMultipartUnpack(char ** Input); +char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg); +int decode_quoted_printable(char *ptr, int len); +void decodeblock( unsigned char in[4], unsigned char out[3]); +int encode_quoted_printable(char *s, char * out, int Len); +int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress); +int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall); +void SendMessageReadEvent(char * call, struct MsgInfo * Msg); +void SendNewMessageEvent(char * call, struct MsgInfo * Msg); +void MQTTMessageEvent(struct MsgInfo * message); + +config_t cfg; +config_setting_t * group; + +extern ULONG BBSApplMask; + +//static int SEMCLASHES = 0; + +char SecureMsg[80] = ""; // CMS Secure Signon Response + +int NumberofStreams; + +extern char VersionStringWithBuild[50]; + +#define MaxSockets 64 + +extern struct SEM OutputSEM; + +extern ConnectionInfo Connections[MaxSockets+1]; + +extern struct UserInfo ** UserRecPtr; +extern int NumberofUsers; + +extern struct UserInfo * BBSChain; // Chain of users that are BBSes + +extern struct MsgInfo ** MsgHddrPtr; +extern int NumberofMessages; + +extern int FirstMessageIndextoForward; // Lowest Message wirh a forward bit set - limits search + +extern char UserDatabaseName[MAX_PATH]; +extern char UserDatabasePath[MAX_PATH]; + +extern char MsgDatabasePath[MAX_PATH]; +extern char MsgDatabaseName[MAX_PATH]; + +extern char BIDDatabasePath[MAX_PATH]; +extern char BIDDatabaseName[MAX_PATH]; + +extern char WPDatabasePath[MAX_PATH]; +extern char WPDatabaseName[MAX_PATH]; + +extern char BadWordsPath[MAX_PATH]; +extern char BadWordsName[MAX_PATH]; + +extern char BaseDir[MAX_PATH]; +extern char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% +extern char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat + + +extern char MailDir[MAX_PATH]; + +extern BIDRec ** BIDRecPtr; +extern int NumberofBIDs; + +extern BIDRec ** TempBIDRecPtr; +extern int NumberofTempBIDs; + +extern WPRec ** WPRecPtr; +extern int NumberofWPrecs; + +extern char ** BadWords; +extern int NumberofBadWords; +extern char * BadFile; + +extern int LatestMsg; +extern struct SEM MsgNoSemaphore; // For locking updates to LatestMsg +extern int HighestBBSNumber; + +extern int MaxMsgno; +extern int BidLifetime; +extern int MaxAge; +extern int MaintInterval; +extern int MaintTime; + +extern int ProgramErrors; + +extern BOOL MonBBS; +extern BOOL MonCHAT; +extern BOOL MonTCP; + +BOOL SendNewUserMessage = TRUE; +BOOL AllowAnon = FALSE; +BOOL UserCantKillT = FALSE; + +typedef int (WINAPI FAR *FARPROCX)(); +FARPROCX pRunEventProgram; +FARPROCX pGetPortFrequency; + +int RunEventProgram(char * Program, char * Param); + + +extern BOOL EventsEnabled; + +#define BPQHOSTSTREAMS 64 + +// Although externally streams are numbered 1 to 64, internally offsets are 0 - 63 + +extern BPQVECSTRUC BPQHOSTVECTOR[BPQHOSTSTREAMS + 5]; + +/* --- G7TAJ PG Server--- */ + +void run_pg( CIRCUIT * conn, struct UserInfo * user ); +void startrun_pgThread( RUNPGARGS_PTR Args ); + +char * SERVERLIST[256][3]; + +int NUM_SERVERS = 0; + +/*------- G7TAJ END ----------*/ + + +#ifdef LINBPQ +extern BPQVECSTRUC ** BPQHOSTVECPTR; +extern char WL2KModes [54][18]; + + +#else + +__declspec(dllimport) BPQVECSTRUC ** BPQHOSTVECPTR; + + +char WL2KModes [54][18] = { + "Packet 1200", "Packet 2400", "Packet 4800", "Packet 9600", "Packet 19200", "Packet 38400", "High Speed Packet", "", "", "", "", + "", "Pactor 1", "", "", "Pactor 2", "", "Pactor 3", "", "", "Pactor 4", // 10 - 20 + "Winmor 500", "Winmor 1600", "", "", "", "", "", "", "", // 21 - 29 + "Robust Packet", "", "", "", "", "", "", "", "", "", // 30 - 39 + "ARDOP 200", "ARDOP 500", "ARDOP 1000", "ARDOP 2000", "ARDOP 2000 FM", "", "", "", "", "", // 40 - 49 + "VARA", "VARA FM", "VARA FM WIDE", "VARA 500"}; +#endif + + + + + +FILE * LogHandle[4] = {NULL, NULL, NULL, NULL}; + +time_t LastLogTime[4] = {0, 0, 0, 0}; + +char FilesNames[4][100] = {"", "", "", ""}; + +char * Logs[4] = {"BBS", "CHAT", "TCP", "DEBUG"}; + +extern struct SEM ConfigSEM; + + +BOOL OpenLogfile(int Flags) +{ + UCHAR FN[MAX_PATH]; + time_t LT; + struct tm * tm; + + LT = time(NULL); + tm = gmtime(<); + + sprintf(FN,"%s/logs/log_%02d%02d%02d_%s.txt", GetLogDirectory(), tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]); + + LogHandle[Flags] = fopen(FN, "ab"); + +#ifndef WIN32 + + if (strcmp(FN, &FilesNames[Flags][0])) + { + UCHAR SYMLINK[MAX_PATH]; + + sprintf(SYMLINK,"%s/logLatest_%s.txt", GetBPQDirectory(), Logs[Flags]); + unlink(SYMLINK); + strcpy(&FilesNames[Flags][0], FN); + symlink(FN, SYMLINK); + } + +#endif + + return (LogHandle[Flags] != NULL); +} + +typedef int (WINAPI FAR *FARPROCX)(); + +extern FARPROCX pDllBPQTRACE; + +struct SEM LogSEM = {0, 0}; + +void WriteLogLine(CIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags) +{ + char CRLF[2] = {0x0d,0x0a}; + struct tm * tm; + char Stamp[20]; + time_t LT; +// struct _EXCEPTION_POINTERS exinfo; + + // Write to Node BPQTRACE system + + if ((Flags == LOG_BBS || Flags == LOG_DEBUG_X) && MsgLen < 250) + { + MESSAGE Monframe; + memset(&Monframe, 0, sizeof(Monframe)); + + Monframe.PORT = 64; + Monframe.LENGTH = 12 + MsgLen; + Monframe.DEST[0] = 1; // Plain Text Monitor + + memcpy(&Monframe.DEST[1], Msg, MsgLen); + Monframe.DEST[1 + MsgLen] = 0; + + time(&Monframe.Timestamp); +#ifdef LINBPQ + GetSemaphore(&Semaphore, 88); + BPQTRACE(&Monframe, FALSE); + FreeSemaphore(&Semaphore); +#else + if (pDllBPQTRACE) + pDllBPQTRACE(&Monframe, FALSE); +#endif + } +#ifndef LINBPQ + __try + { +#endif + + + +#ifndef LINBPQ + + if (hMonitor) + { + if (Flags == LOG_TCP && MonTCP) + { + WritetoMonitorWindow((char *)&Flag, 1); + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_CHAT && MonCHAT) + { + WritetoMonitorWindow((char *)&Flag, 1); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + WritetoMonitorWindow(call, 10); + } + else + WritetoMonitorWindow(" ", 10); + + WritetoMonitorWindow(Msg, MsgLen); + if (Msg[MsgLen-1] != '\r') + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_BBS && MonBBS) + { + WritetoMonitorWindow((char *)&Flag, 1); + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + WritetoMonitorWindow(call, 10); + } + else + WritetoMonitorWindow(" ", 10); + + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + else if (Flags == LOG_DEBUG_X) + { + WritetoMonitorWindow((char *)&Flag, 1); + WritetoMonitorWindow(Msg, MsgLen); + WritetoMonitorWindow(CRLF , 1); + } + } +#endif + + if (Flags == LOG_TCP && !LogTCP) + return; + if (Flags == LOG_BBS && !LogBBS) + return; + if (Flags == LOG_CHAT && !LogCHAT) + return; + + GetSemaphore(&LogSEM, 0); + + if (LogHandle[Flags] == NULL) + OpenLogfile(Flags); + + if (LogHandle[Flags] == NULL) + { + FreeSemaphore(&LogSEM); + return; + } + LT = time(NULL); + tm = gmtime(<); + + sprintf(Stamp,"%02d%02d%02d %02d:%02d:%02d %c", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, Flag); + + fwrite(Stamp, 1, strlen(Stamp), LogHandle[Flags]); + + if (conn && conn->Callsign[0]) + { + char call[20]; + sprintf(call, "%s ", conn->Callsign); + fwrite(call, 1, 10, LogHandle[Flags]); + } + else + fwrite(" ", 1, 10, LogHandle[Flags]); + + fwrite(Msg, 1, MsgLen, LogHandle[Flags]); + + if (Flags == LOG_CHAT && Msg[MsgLen-1] == '\r') + fwrite(&CRLF[1], 1, 1, LogHandle[Flags]); + else + fwrite(CRLF, 1, 2, LogHandle[Flags]); + + // Don't close/reopen logs every time + +// if ((LT - LastLogTime[Flags]) > 60) + { + LastLogTime[Flags] = LT; + fclose(LogHandle[Flags]); + LogHandle[Flags] = NULL; + } + FreeSemaphore(&LogSEM); + +#ifndef LINBPQ + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +#endif +} + +int CriticalErrorHandler(char * error) +{ + Debugprintf("Critical Error %s", error); + ProgramErrors = 25; + CheckProgramErrors(); // Force close + return 0; +} + +BOOL CheckForTooManyErrors(ConnectionInfo * conn) +{ + conn->ErrorCount++; + + if (conn->ErrorCount > 4) + { + BBSputs(conn, "Too many errors - closing\r"); + conn->CloseAfterFlush = 20; + return TRUE; + } + return FALSE; +} + + + + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[16384]; + va_list(arglist); + int Len; + + va_start(arglist, format); + Len = vsprintf(Mess, format, arglist); +#ifndef LINBPQ + WriteLogLine(NULL, '!',Mess, Len, LOG_DEBUG_X); +#endif + // #ifdef _DEBUG + strcat(Mess, "\r\n"); + OutputDebugString(Mess); + +// #endif + return; +} + +VOID __cdecl Logprintf(int LogMode, CIRCUIT * conn, int InOut, const char * format, ...) +{ + char Mess[1000]; + va_list(arglist);int Len; + + va_start(arglist, format); + Len = vsprintf(Mess, format, arglist); + WriteLogLine(conn, InOut, Mess, Len, LogMode); + + return; +} + +struct MsgInfo * GetMsgFromNumber(int msgno) +{ + if (msgno < 1 || msgno > 999999) + return NULL; + + return MsgnotoMsg[msgno]; +} + +struct UserInfo * AllocateUserRecord(char * Call) +{ + struct UserInfo * User = zalloc(sizeof (struct UserInfo)); + + strcpy(User->Call, Call); + User->Length = sizeof (struct UserInfo); + + GetSemaphore(&AllocSemaphore, 0); + + UserRecPtr=realloc(UserRecPtr,(++NumberofUsers+1) * sizeof(void *)); + UserRecPtr[NumberofUsers]= User; + + FreeSemaphore(&AllocSemaphore); + + return User; +} + +struct MsgInfo * AllocateMsgRecord() +{ + struct MsgInfo * Msg = zalloc(sizeof (struct MsgInfo)); + + GetSemaphore(&AllocSemaphore, 0); + + MsgHddrPtr=realloc(MsgHddrPtr,(++NumberofMessages+1) * sizeof(void *)); + MsgHddrPtr[NumberofMessages] = Msg; + + FreeSemaphore(&AllocSemaphore); + + return Msg; +} + +BIDRec * AllocateBIDRecord() +{ + BIDRec * BID = zalloc(sizeof (BIDRec)); + + GetSemaphore(&AllocSemaphore, 0); + + BIDRecPtr = realloc(BIDRecPtr,(++NumberofBIDs+1) * sizeof(void *)); + BIDRecPtr[NumberofBIDs] = BID; + + FreeSemaphore(&AllocSemaphore); + + return BID; +} + +BIDRec * AllocateTempBIDRecord() +{ + BIDRec * BID = zalloc(sizeof (BIDRec)); + + GetSemaphore(&AllocSemaphore, 0); + + TempBIDRecPtr=realloc(TempBIDRecPtr,(++NumberofTempBIDs+1) * sizeof(void *)); + TempBIDRecPtr[NumberofTempBIDs] = BID; + + FreeSemaphore(&AllocSemaphore); + + return BID; +} + +struct UserInfo * LookupCall(char * Call) +{ + struct UserInfo * ptr = NULL; + int i; + + for (i=1; i <= NumberofUsers; i++) + { + ptr = UserRecPtr[i]; + + if (_stricmp(ptr->Call, Call) == 0) return ptr; + + } + + return NULL; +} + +int GetNetInt(char * Line) +{ + char temp[1024]; + char * ptr = strlop(Line, ','); + int n = atoi(Line); + if (ptr == NULL) + Line[0] = 0; + else + { + strcpy(temp, ptr); + strcpy(Line, temp); + } + return n; +} + +VOID GetUserDatabase() +{ + struct UserInfo UserRec; + + FILE * Handle; + size_t ReadLen; + struct UserInfo * user; + time_t UserLimit = time(NULL) - (UserLifetime * 86400); // Oldest user to keep + int i; + + // See if user config is in main config + + group = config_lookup (&cfg, "BBSUsers"); + + if (group) + { + // We have User config in the main config file. so use that + + int index = 0; + char * stats; + struct MsgStats * Stats; + char * ptr, * ptr2; + + config_setting_t * entry = config_setting_get_elem (group, index++); + + // Initialise a new File + + UserRecPtr = malloc(sizeof(void *)); + UserRecPtr[0] = malloc(sizeof (struct UserInfo)); + memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); + UserRecPtr[0]->Length = sizeof (struct UserInfo); + + NumberofUsers = 0; + + while (entry) + { + char call[16]; + + // entry->name is call, will have * in front if a call stating woth number + + if (entry->name[0] == '*') + strcpy(call, &entry->name[1]); + else + strcpy(call, entry->name); + + user = AllocateUserRecord(call); + + ptr = entry->value.sval; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->Name, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->Address, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->HomeBBS, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->QRA, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->pass, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->ZIP, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) strcpy(user->CMSPass, ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->lastmsg = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->flags = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->PageLen = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->BBSNumber = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->RMSSSIDBits = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->WebSeqNo = atoi(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) user->TimeLastConnected = atol(ptr); + ptr = ptr2; + + ptr2 = strlop(ptr, '^'); + if (ptr) Stats = &user->Total; + stats = ptr; + + if (Stats == NULL) + { + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + + Stats->ConnectsIn = GetNetInt(stats); + Stats->ConnectsOut = GetNetInt(stats); + Stats->MsgsReceived[0] = GetNetInt(stats); + Stats->MsgsReceived[1] = GetNetInt(stats); + Stats->MsgsReceived[2] = GetNetInt(stats); + Stats->MsgsReceived[3] = GetNetInt(stats); + Stats->MsgsSent[0] = GetNetInt(stats); + Stats->MsgsSent[1] = GetNetInt(stats); + Stats->MsgsSent[2] = GetNetInt(stats); + Stats->MsgsSent[3] = GetNetInt(stats); + Stats->MsgsRejectedIn[0] = GetNetInt(stats); + Stats->MsgsRejectedIn[1] = GetNetInt(stats); + Stats->MsgsRejectedIn[2] = GetNetInt(stats); + Stats->MsgsRejectedIn[3] = GetNetInt(stats); + Stats->MsgsRejectedOut[0] = GetNetInt(stats); + Stats->MsgsRejectedOut[1] = GetNetInt(stats); + Stats->MsgsRejectedOut[2] = GetNetInt(stats); + Stats->MsgsRejectedOut[3] = GetNetInt(stats); + Stats->BytesForwardedIn[0] = GetNetInt(stats); + Stats->BytesForwardedIn[1] = GetNetInt(stats); + Stats->BytesForwardedIn[2] = GetNetInt(stats); + Stats->BytesForwardedIn[3] = GetNetInt(stats); + Stats->BytesForwardedOut[0] = GetNetInt(stats); + Stats->BytesForwardedOut[1] = GetNetInt(stats); + Stats->BytesForwardedOut[2] = GetNetInt(stats); + Stats->BytesForwardedOut[3] = GetNetInt(stats); + + Stats = &user->Last; + stats = ptr2; + + if (Stats == NULL) + { + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + + Stats->ConnectsIn = GetNetInt(stats); + Stats->ConnectsOut = GetNetInt(stats); + Stats->MsgsReceived[0] = GetNetInt(stats); + Stats->MsgsReceived[1] = GetNetInt(stats); + Stats->MsgsReceived[2] = GetNetInt(stats); + Stats->MsgsReceived[3] = GetNetInt(stats); + Stats->MsgsSent[0] = GetNetInt(stats); + Stats->MsgsSent[1] = GetNetInt(stats); + Stats->MsgsSent[2] = GetNetInt(stats); + Stats->MsgsSent[3] = GetNetInt(stats); + Stats->MsgsRejectedIn[0] = GetNetInt(stats); + Stats->MsgsRejectedIn[1] = GetNetInt(stats); + Stats->MsgsRejectedIn[2] = GetNetInt(stats); + Stats->MsgsRejectedIn[3] = GetNetInt(stats); + Stats->MsgsRejectedOut[0] = GetNetInt(stats); + Stats->MsgsRejectedOut[1] = GetNetInt(stats); + Stats->MsgsRejectedOut[2] = GetNetInt(stats); + Stats->MsgsRejectedOut[3] = GetNetInt(stats); + Stats->BytesForwardedIn[0] = GetNetInt(stats); + Stats->BytesForwardedIn[1] = GetNetInt(stats); + Stats->BytesForwardedIn[2] = GetNetInt(stats); + Stats->BytesForwardedIn[3] = GetNetInt(stats); + Stats->BytesForwardedOut[0] = GetNetInt(stats); + Stats->BytesForwardedOut[1] = GetNetInt(stats); + Stats->BytesForwardedOut[2] = GetNetInt(stats); + Stats->BytesForwardedOut[3] = GetNetInt(stats); + + + if ((user->flags & F_BBS) == 0) // Not BBS - Check Age + { + if (UserLifetime && user->TimeLastConnected) // Dont delete manually added Users that havent yet connected + { + if (user->TimeLastConnected < UserLimit) + { + // Too Old - ignore + + NumberofUsers--; + free(user); + entry = config_setting_get_elem (group, index++); + continue; + } + } + } + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (user->lastmsg < 0 || user->lastmsg > LatestMsg) + user->lastmsg = LatestMsg; + + + entry = config_setting_get_elem (group, index++); + } + } + else + { + Handle = fopen(UserDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memset(UserRecPtr[0], 0, sizeof (struct UserInfo)); + UserRecPtr[0]->Length = sizeof (struct UserInfo); + + NumberofUsers = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&UserRec, 0, sizeof (struct UserInfo)); + UserRec.Length = sizeof (struct UserInfo); + } + else + { + // See if format has changed + + if (UserRec.Length == 0) + { + // Old format without a Length field + + struct OldUserInfo * OldRec = (struct OldUserInfo *)&UserRec; + int Users = OldRec->ConnectsIn; // User Count in control record + char Backup1[MAX_PATH]; + + // Create a backup in case reversion is needed and Reposition to first User record + + fclose(Handle); + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".oldformat"); + + CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak + + Handle = fopen(UserDatabasePath, "rb"); + + ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); // Skip Control Record + + // Set up control record + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); + UserRecPtr[0]->Length = sizeof (UserRec); + + NumberofUsers = 0; + +OldNext: + + ReadLen = fread(&UserRec, 1, (int)sizeof (struct OldUserInfo), Handle); + + if (ReadLen > 0) + { + if (OldRec->Call[0] < '0') + goto OldNext; // Blank record + + user = AllocateUserRecord(OldRec->Call); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + // Copy info from Old record + + user->lastmsg = OldRec->lastmsg; + user->Total.ConnectsIn = OldRec->ConnectsIn; + user->TimeLastConnected = OldRec->TimeLastConnected; + user->flags = OldRec->flags; + user->PageLen = OldRec->PageLen; + user->BBSNumber = OldRec->BBSNumber; + memcpy(user->Name, OldRec->Name, 18); + memcpy(user->Address, OldRec->Address, 61); + user->Total.MsgsReceived[0] = OldRec->MsgsReceived; + user->Total.MsgsSent[0] = OldRec->MsgsSent; + user->Total.MsgsRejectedIn[0] = OldRec->MsgsRejectedIn; // Messages we reject + user->Total.MsgsRejectedOut[0] = OldRec->MsgsRejectedOut; // Messages Rejectd by other end + user->Total.BytesForwardedIn[0] = OldRec->BytesForwardedIn; + user->Total.BytesForwardedOut[0] = OldRec->BytesForwardedOut; + user->Total.ConnectsOut = OldRec->ConnectsOut; // Forwarding Connects Out + user->RMSSSIDBits = OldRec->RMSSSIDBits; // SSID's to poll in RMS + memcpy(user->HomeBBS, OldRec->HomeBBS, 41); + memcpy(user->QRA, OldRec->QRA, 7); + memcpy(user->pass, OldRec->pass, 13); + memcpy(user->ZIP, OldRec->ZIP, 9); + + // Read any forwarding info, even if not a BBS. + // This allows a BBS to be temporarily set as a + // normal user without loosing forwarding info + + SetupForwardingStruct(user); + + if (user->flags & F_BBS) + { + // Defined as BBS - allocate and initialise forwarding structure + + // Add to BBS Chain; + + user->BBSNext = BBSChain; + BBSChain = user; + + // Save Highest BBS Number + + if (user->BBSNumber > HighestBBSNumber) HighestBBSNumber = user->BBSNumber; + } + goto OldNext; + } + + SortBBSChain(); + fclose(Handle); + + return; + } + } + + // Set up control record + + UserRecPtr=malloc(sizeof(void *)); + UserRecPtr[0]= malloc(sizeof (struct UserInfo)); + memcpy(UserRecPtr[0], &UserRec, sizeof (UserRec)); + UserRecPtr[0]->Length = sizeof (UserRec); + + NumberofUsers = 0; + +Next: + + ReadLen = fread(&UserRec, 1, (int)sizeof (UserRec), Handle); + + if (ReadLen > 0) + { + if (UserRec.Call[0] < '0') + goto Next; // Blank record + + if (UserRec.TimeLastConnected == 0) + UserRec.TimeLastConnected = UserRec.xTimeLastConnected; + + if ((UserRec.flags & F_BBS) == 0) // Not BBS - Check Age + if (UserLifetime) // if limit set + if (UserRec.TimeLastConnected) // Dont delete manually added Users that havent yet connected + if (UserRec.TimeLastConnected < UserLimit) + goto Next; // Too Old - ignore + + user = AllocateUserRecord(UserRec.Call); + memcpy(user, &UserRec, sizeof (UserRec)); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + user->ForwardingInfo = NULL; // In case left behind on crash + user->BBSNext = NULL; + user->POP3Locked = FALSE; + + if (user->lastmsg < 0 || user->lastmsg > LatestMsg) + user->lastmsg = LatestMsg; + + goto Next; + } + fclose(Handle); + } + + // Setting up BBS struct has been moved until all user record + // have been read so we can fix corrupt BBSNUmber + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + // Read any forwarding info, even if not a BBS. + // This allows a BBS to be temporarily set as a + // normal user without loosing forwarding info + + SetupForwardingStruct(user); + + if (user->flags & F_BBS) + { + // Add to BBS Chain; + + if (user->BBSNumber == NBBBS) // Fix corrupt records + { + user->BBSNumber = FindFreeBBSNumber(); + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; // cant really do much else + } + + user->BBSNext = BBSChain; + BBSChain = user; + +// Logprintf(LOG_BBS, NULL, '?', "BBS %s BBSNumber %d", user->Call, user->BBSNumber); + + // Save Highest BBS Number + + if (user->BBSNumber > HighestBBSNumber) + HighestBBSNumber = user->BBSNumber; + } + } + + // Check for dulicate BBS numbers + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_BBS) + { + if (user->BBSNumber == 0) + user->BBSNumber = FindFreeBBSNumber(); + + CheckBBSNumber(user->BBSNumber); + } + } + + SortBBSChain(); +} + +VOID CopyUserDatabase() +{ + return; // User config now in main config file +/* + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + + // Keep 4 Generations + + strcpy(Backup2, UserDatabasePath); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, UserDatabasePath); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, UserDatabasePath); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + CopyFile(UserDatabasePath, Backup1, FALSE); // Copy to .bak +*/ +} + +VOID CopyConfigFile(char * ConfigName) +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + + // Keep 4 Generations + + strcpy(Backup2, ConfigName); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, ConfigName); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, ConfigName); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, ConfigName); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); // Move .bak to .bak.1 + + CopyFile(ConfigName, Backup1, FALSE); // Copy to .bak +} + + + +VOID SaveUserDatabase() +{ + SaveConfig(ConfigName); // User config is now in main config file + GetConfig(ConfigName); + +/* + FILE * Handle; + size_t WriteLen; + int i; + + Handle = fopen(UserDatabasePath, "wb"); + + UserRecPtr[0]->Total.ConnectsIn = NumberofUsers; + + for (i=0; i <= NumberofUsers; i++) + { + WriteLen = fwrite(UserRecPtr[i], 1, (int)sizeof (struct UserInfo), Handle); + } + + fclose(Handle); +*/ + return; +} + +VOID GetMessageDatabase() +{ + struct MsgInfo MsgRec; + FILE * Handle; + size_t ReadLen; + struct MsgInfo * Msg; + char * MsgBytes; + int FileRecsize = sizeof(struct MsgInfo); // May be changed if reformating + BOOL Reformatting = FALSE; + char HEX[3] = ""; + int n; + + // See if Message Database is in main config + + group = config_lookup (&cfg, "MSGS"); + +// group = 0; + + if (group) + { + // We have User config in the main config file. so use that + + int index = 0; + char * ptr, * ptr2; + config_setting_t * entry = config_setting_get_elem (group, index++); + + // Initialise a new File + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); + NumberofMessages = 0; + MsgHddrPtr[0]->status = 2; + + if (entry) + { + // First Record has current message number + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + ptr2 = strlop(ptr2, '|'); + if (ptr2) + LatestMsg = atoi(ptr2); + } + + entry = config_setting_get_elem (group, index++); + + while (entry) + { + // entry->name is MsgNo with 'R' in front + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + + memset(&MsgRec, 0, sizeof(struct MsgInfo)); + + MsgRec.number = atoi(&entry->name[1]); + MsgRec.type = ptr[0]; + + ptr = ptr2; + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + ptr2 = strlop(ptr, '|'); + MsgRec.status = ptr[0]; + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.length = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datereceived = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.bbsfrom, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.via, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.from, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.to, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.bid, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.B2Flags = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datecreated = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.datechanged = atol(ptr); + + ptr = ptr2; + if (ptr) ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + if (ptr[0]) + { + char String[50] = "00000000000000000000"; + String[20] = 0; + memcpy(String, ptr, strlen(ptr)); + for (n = 0; n < NBMASK; n++) + { + memcpy(HEX, &String[n * 2], 2); + MsgRec.fbbs[n] = (UCHAR)strtol(HEX, 0, 16); + } + } + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) + { + entry = config_setting_get_elem (group, index++); + continue; + } + + if (ptr[0]) + { + char String[50] = "00000000000000000000"; + String[20] = 0; + memcpy(String, ptr, strlen(ptr)); + for (n = 0; n < NBMASK; n++) + { + memcpy(HEX, &String[n * 2], 2); + MsgRec.forw[n] = (UCHAR)strtol(HEX, 0, 16); + } + } + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(MsgRec.emailfrom, ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) MsgRec.UTF8 = atoi(ptr); + + ptr = ptr2; + + if (ptr) + { + strcpy(MsgRec.title, ptr); + + MsgBytes = ReadMessageFileEx(&MsgRec); + + if (MsgBytes) + { + free(MsgBytes); + Msg = AllocateMsgRecord(); + memcpy(Msg, &MsgRec, sizeof (MsgRec)); + + MsgnotoMsg[Msg->number] = Msg; + + // Fix Corrupted NTS Messages + + if (Msg->type == 'N') + Msg->type = 'T'; + + // Look for corrupt FROM address (ending in @) + + strlop(Msg->from, '@'); + + BuildNNTPList(Msg); // Build NNTP Groups list + + // If any forward bits are set, increment count on corresponding BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + } + } + } + entry = config_setting_get_elem (group, index++); + } + + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + + return; + } + + Handle = fopen(MsgDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= zalloc(sizeof (MsgRec)); + NumberofMessages = 0; + MsgHddrPtr[0]->status = 2; + + return; + } + + // Get First Record + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&MsgRec, 0, sizeof (MsgRec)); + MsgRec.status = 2; + } + + // Set up control record + + MsgHddrPtr=malloc(sizeof(void *)); + MsgHddrPtr[0]= malloc(sizeof (MsgRec)); + memcpy(MsgHddrPtr[0], &MsgRec, sizeof (MsgRec)); + + LatestMsg=MsgHddrPtr[0]->length; + + NumberofMessages = 0; + + if (MsgRec.status == 1) // Used as file format version + // 0 = original, 1 = Extra email from addr, 2 = More BBS's. + { + char Backup1[MAX_PATH]; + + // Create a backup in case reversion is needed and Reposition to first User record + + fclose(Handle); + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".oldformat"); + + CopyFile(MsgDatabasePath, Backup1, FALSE); // Copy to .oldformat + + Handle = fopen(MsgDatabasePath, "rb"); + + FileRecsize = sizeof(struct OldMsgInfo); + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + MsgHddrPtr[0]->status = 2; + } + +Next: + + ReadLen = fread(&MsgRec, 1, FileRecsize, Handle); + + if (ReadLen > 0) + { + // Validate Header + + if (FileRecsize == sizeof(struct MsgInfo)) + { + if (MsgRec.type == 0 || MsgRec.number == 0) + goto Next; + + MsgBytes = ReadMessageFileEx(&MsgRec); + + if (MsgBytes) + { + // MsgRec.length = strlen(MsgBytes); + free(MsgBytes); + } + else + goto Next; + + Msg = AllocateMsgRecord(); + + memcpy(Msg, &MsgRec, +sizeof (MsgRec)); + } + else + { + // Resizing - record from file is an OldRecInfo + + struct OldMsgInfo * OldMessage = (struct OldMsgInfo *) &MsgRec; + + if (OldMessage->type == 0) + goto Next; + + if (OldMessage->number > 99999 || OldMessage->number < 1) + goto Next; + + Msg = AllocateMsgRecord(); + + + Msg->B2Flags = OldMessage->B2Flags; + memcpy(Msg->bbsfrom, OldMessage->bbsfrom, 7); + memcpy(Msg->bid, OldMessage->bid, 13); + Msg->datechanged = OldMessage->datechanged; + Msg->datecreated = OldMessage->datecreated; + Msg->datereceived = OldMessage->datereceived; + memcpy(Msg->emailfrom, OldMessage->emailfrom, 41); + memcpy(Msg->fbbs , OldMessage->fbbs, 10); + memcpy(Msg->forw , OldMessage->forw, 10); + memcpy(Msg->from, OldMessage->from, 7); + Msg->length = OldMessage->length; + Msg->nntpnum = OldMessage->nntpnum; + Msg->number = OldMessage->number; + Msg->status = OldMessage->status; + memcpy(Msg->title, OldMessage->title, 61); + memcpy(Msg->to, OldMessage->to, 7); + Msg->type = OldMessage->type; + memcpy(Msg->via, OldMessage->via, 41); + } + + MsgnotoMsg[Msg->number] = Msg; + + // Fix Corrupted NTS Messages + + if (Msg->type == 'N') + Msg->type = 'T'; + + // Look for corrupt FROM address (ending in @) + + strlop(Msg->from, '@'); + + // Move Dates if first run with new format + + if (Msg->datecreated == 0) + Msg->datecreated = Msg->xdatecreated; + + if (Msg->datereceived == 0) + Msg->datereceived = Msg->xdatereceived; + + if (Msg->datechanged == 0) + Msg->datechanged = Msg->xdatechanged; + + BuildNNTPList(Msg); // Build NNTP Groups list + + Msg->Locked = 0; // In case left locked + Msg->Defered = 0; // In case left set. + + // If any forward bits are set, increment count on corresponding BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + } + + goto Next; + } + + if (FirstMessageIndextoForward == 0) + FirstMessageIndextoForward = NumberofMessages; // limit search + + fclose(Handle); +} + +VOID CopyMessageDatabase() +{ + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + +// return; + + // Keep 4 Generations + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, MsgDatabasePath); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); //Move .bak to .bak.1 + + strcpy(Backup2, MsgDatabasePath); + strcat(Backup2, ".bak"); + + CopyFile(MsgDatabasePath, Backup2, FALSE); // Copy to .bak + +} + +VOID SaveMessageDatabase() +{ + FILE * Handle; + size_t WriteLen; + int i; + char Key[16]; + struct MsgInfo *Msg; +// char CfgName[MAX_PATH]; + char HEXString1[64]; + char HEXString2[64]; + int n; +// char * CfgBuffer; + char Cfg[1024]; +// int CfgLen = 0; +// FILE * hFile; + +// SaveConfig(ConfigName); // Message Headers now in main config +// return; + +#ifdef LINBPQ + RefreshWebMailIndex(); +#else + if (pRefreshWebMailIndex) + pRefreshWebMailIndex(); +#endif + + Handle = fopen(MsgDatabasePath, "wb"); + + if (Handle == NULL) + { + CriticalErrorHandler("Failed to open message database"); + return; + } + + MsgHddrPtr[0]->status = 2; + MsgHddrPtr[0]->number = NumberofMessages; + MsgHddrPtr[0]->length = LatestMsg; + + for (i=0; i <= NumberofMessages; i++) + { + WriteLen = fwrite(MsgHddrPtr[i], 1, sizeof (struct MsgInfo), Handle); + + if (WriteLen != sizeof(struct MsgInfo)) + { + CriticalErrorHandler("Failed to write message database record"); + return; + } + } + + if (fclose(Handle) != 0) + CriticalErrorHandler("Failed to close message database"); + + for (i = 1; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); + + n = 39; + while (n >=0 && HEXString1[n] == '0') + HEXString1[n--] = 0; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); + + n = 39; + while (n >= 0 && HEXString2[n] == '0') + HEXString2[n--] = 0; + + sprintf(Key, "R%d:\r\n", i); + + n = sprintf(Cfg, "%c|%c|%d|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, + Msg->number, Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], + &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, + &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); + } + + return; +} + +VOID GetBIDDatabase() +{ + BIDRec BIDRec; + FILE * Handle; + size_t ReadLen; + BIDRecP BID; + int index = 0; + char * ptr, * ptr2; + + // If BID info is in main config file, use it + + group = config_lookup (&cfg, "BIDS"); + + if (group) + { + config_setting_t * entry = config_setting_get_elem (group, index++); + + BIDRecPtr=malloc(sizeof(void *)); + BIDRecPtr[0]= malloc(sizeof (BIDRec)); + memset(BIDRecPtr[0], 0, sizeof (BIDRec)); + NumberofBIDs = 0; + + while (entry) + { + // entry->name is Bid with 'R' in front + + ptr = entry->value.sval; + ptr2 = strlop(ptr, '|'); + + if (ptr && ptr2) + { + BID = AllocateBIDRecord(); + strcpy(BID->BID, &entry->name[1]); + BID->mode = atoi(ptr); + BID->u.timestamp = atoi(ptr2); + + if (BID->u.timestamp == 0) + BID->u.timestamp = LOWORD(time(NULL)/86400); + + } + entry = config_setting_get_elem (group, index++); + } + return; + } + + Handle = fopen(BIDDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + BIDRecPtr=malloc(sizeof(void *)); + BIDRecPtr[0]= malloc(sizeof (BIDRec)); + memset(BIDRecPtr[0], 0, sizeof (BIDRec)); + NumberofBIDs = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&BIDRec, 0, sizeof (BIDRec)); + } + + // Set up control record + + BIDRecPtr = malloc(sizeof(void *)); + BIDRecPtr[0] = malloc(sizeof (BIDRec)); + memcpy(BIDRecPtr[0], &BIDRec, sizeof (BIDRec)); + + NumberofBIDs = 0; + +Next: + + ReadLen = fread(&BIDRec, 1, sizeof (BIDRec), Handle); + + if (ReadLen > 0) + { + BID = AllocateBIDRecord(); + memcpy(BID, &BIDRec, sizeof (BIDRec)); + + if (BID->u.timestamp == 0) + BID->u.timestamp = LOWORD(time(NULL)/86400); + + goto Next; + } + + fclose(Handle); +} + +VOID CopyBIDDatabase() +{ + char Backup[MAX_PATH]; + +// return; + + + strcpy(Backup, BIDDatabasePath); + strcat(Backup, ".bak"); + + CopyFile(BIDDatabasePath, Backup, FALSE); +} + +VOID SaveBIDDatabase() +{ + FILE * Handle; + size_t WriteLen; + int i; + +// return; // Bids are now in main config and are saved when message is saved + + Handle = fopen(BIDDatabasePath, "wb"); + + BIDRecPtr[0]->u.msgno = NumberofBIDs; // First Record has file size + + for (i=0; i <= NumberofBIDs; i++) + { + WriteLen = fwrite(BIDRecPtr[i], 1, sizeof (BIDRec), Handle); + } + + fclose(Handle); + + return; +} + +BIDRec * LookupBID(char * BID) +{ + BIDRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofBIDs; i++) + { + ptr = BIDRecPtr[i]; + + if (_stricmp(ptr->BID, BID) == 0) + return ptr; + } + + return NULL; +} + +BIDRec * LookupTempBID(char * BID) +{ + BIDRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofTempBIDs; i++) + { + ptr = TempBIDRecPtr[i]; + + if (_stricmp(ptr->BID, BID) == 0) return ptr; + } + + return NULL; +} + +VOID RemoveTempBIDS(CIRCUIT * conn) +{ + // Remove any Temp BID records for conn. Called when connection closes - Msgs will be complete or failed + + if (NumberofTempBIDs == 0) + return; + else + { + BIDRec * ptr = NULL; + BIDRec ** NewTempBIDRecPtr = zalloc((NumberofTempBIDs+1) * sizeof(void *)); + int i = 0, n; + + GetSemaphore(&AllocSemaphore, 0); + + for (n = 1; n <= NumberofTempBIDs; n++) + { + ptr = TempBIDRecPtr[n]; + + if (ptr) + { + if (ptr->u.conn == conn) + // Remove this entry + free(ptr); + else + NewTempBIDRecPtr[++i] = ptr; + } + } + + NumberofTempBIDs = i; + + free(TempBIDRecPtr); + + TempBIDRecPtr = NewTempBIDRecPtr; + FreeSemaphore(&AllocSemaphore); + } + +} + +VOID GetBadWordFile() +{ + FILE * Handle; + DWORD FileSize; + char * ptr1, * ptr2; + struct stat STAT; + + if (stat(BadWordsPath, &STAT) == -1) + return; + + FileSize = STAT.st_size; + + Handle = fopen(BadWordsPath, "rb"); + + if (Handle == NULL) + return; + + // Release old info in case a re-read + + if (BadWords) free(BadWords); + if (BadFile) free(BadFile); + + BadWords = NULL; + BadFile = NULL; + NumberofBadWords = 0; + + BadFile = malloc(FileSize+1); + + fread(BadFile, 1, FileSize, Handle); + + fclose(Handle); + + BadFile[FileSize]=0; + + _strlwr(BadFile); // Compares are case-insensitive + + ptr1 = BadFile; + + while (ptr1) + { + if (*ptr1 == '\n') ptr1++; + + ptr2 = strtok_s(NULL, "\r\n", &ptr1); + if (ptr2) + { + if (*ptr2 != '#') + { + BadWords = realloc(BadWords,(++NumberofBadWords+1) * sizeof(void *)); + BadWords[NumberofBadWords] = ptr2; + } + } + else + break; + } +} + +BOOL CheckBadWord(char * Word, char * Msg) +{ + char * ptr1 = Msg, * ptr2; + size_t len = strlen(Word); + + while (*ptr1) // Stop at end + { + ptr2 = strstr(ptr1, Word); + + if (ptr2 == NULL) + return FALSE; // OK + + // Only bad if it ia not part of a longer word + + if ((ptr2 == Msg) || !(isalpha(*(ptr2 - 1)))) // No alpha before + if (!(isalpha(*(ptr2 + len)))) // No alpha after + return TRUE; // Bad word + + // Keep searching + + ptr1 = ptr2 + len; + } + + return FALSE; // OK +} + +BOOL CheckBadWords(char * Msg) +{ + char * dupMsg = _strlwr(_strdup(Msg)); + int i; + + for (i = 1; i <= NumberofBadWords; i++) + { + if (CheckBadWord(BadWords[i], dupMsg)) + { + free(dupMsg); + return TRUE; // Bad + } + } + + free(dupMsg); + return FALSE; // OK + +} + +VOID SendWelcomeMsg(int Stream, ConnectionInfo * conn, struct UserInfo * user) +{ + if (user->flags & F_Expert) + ExpandAndSendMessage(conn, ExpertWelcomeMsg, LOG_BBS); + else if (conn->NewUser) + ExpandAndSendMessage(conn, NewWelcomeMsg, LOG_BBS); + else + ExpandAndSendMessage(conn, WelcomeMsg, LOG_BBS); + + if (user->HomeBBS[0] == 0 && !DontNeedHomeBBS) + BBSputs(conn, "Please enter your Home BBS using the Home command.\rYou may also enter your QTH and ZIP/Postcode using qth and zip commands.\r"); + +// if (user->flags & F_Temp_B2_BBS) +// nodeprintf(conn, "%s CMS >\r", BBSName); +// else + SendPrompt(conn, user); +} + +VOID SendPrompt(ConnectionInfo * conn, struct UserInfo * user) +{ + if (user->Temp->ListSuspended) + return; // Dont send prompt if pausing a listing + + if (user->flags & F_Expert) + ExpandAndSendMessage(conn, ExpertPrompt, LOG_BBS); + else if (conn->NewUser) + ExpandAndSendMessage(conn, NewPrompt, LOG_BBS); + else + ExpandAndSendMessage(conn, Prompt, LOG_BBS); + +// if (user->flags & F_Expert) +// nodeprintf(conn, "%s\r", ExpertPrompt); +// else if (conn->NewUser) +// nodeprintf(conn, "%s\r", NewPrompt); +// else +// nodeprintf(conn, "%s\r", Prompt); +} + + + +VOID * _zalloc(size_t len) +{ + // ?? malloc and clear + + void * ptr; + + ptr=malloc(len); + memset(ptr, 0, len); + + return ptr; +} + +BOOL isAMPRMsg(char * Addr) +{ + // See if message is addressed to ampr.org and is either + // for us or we have SendAMPRDirect (ie don't need RMS or SMTP to send it) + + size_t toLen = strlen(Addr); + + if (_memicmp(&Addr[toLen - 8], "ampr.org", 8) == 0) + { + // message is for ampr.org + + char toCall[48]; + char * via; + + strcpy(toCall, _strupr(Addr)); + + via = strlop(toCall, '@'); + + if (_stricmp(via, AMPRDomain) == 0) + { + // message is for us. + + return TRUE; + } + + if (SendAMPRDirect) + { + // We want to send ampr mail direct to host. Queue to BBS AMPR + + if (FindAMPR()) + { + // We have bbs AMPR + + return TRUE; + } + } + } + return FALSE; +} + +struct UserInfo * FindAMPR() +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, "AMPR") == 0) + return bbs; + } + + return NULL; +} + +struct UserInfo * FindRMS() +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, "RMS") == 0) + return bbs; + } + + return NULL; +} + +struct UserInfo * FindBBS(char * Name) +{ + struct UserInfo * bbs; + + for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) + { + if (strcmp(bbs->Call, Name) == 0) + return bbs; + } + + return NULL; +} + +int CountConnectionsOnPort(int CheckPort) +{ + int n, Count = 0; + CIRCUIT * conn; + int port, sesstype, paclen, maxframe, l4window; + char callsign[11]; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active) + { + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + if (port == CheckPort) + Count++; + } + } + + return Count; +} + +/* +REJECT.SYS (\FBB\SYSTEM). + + This file is in SYSTEM-directory. With this file it is possible to reject or +hold certain types or sizes of messages. + +The first letter of each valid line specifies the action : + +R = Reject : The message will not be received. +H = Hold : The message will be received but held until the sysop reviews. +L = Local Hold : Only messages created on this BBS will be held. + + # File for rejecting messages. They are rejected with N-BID: + # + # Type, from, @BBS, to, BID, maximum size: + # + # * and ? can be used as wildcards (as in MS-DOS) + # + R B TOTO ALL TATA * 0 + R B * * VENTE * 0 + R B * VENTE * * 0 + H * P1RAT * * * 0 + L B * * * * 0 + */ + + +BOOL CheckRejFilters(char * From, char * To, char * ATBBS, char * BID, char Type, int Len) +{ + char ** Calls; + FBBFilter * p = Filters; + char ToCopy[256]; + + if (Type == 'B' && FilterWPBulls && _stricmp(To, "WP") == 0) + return TRUE; + + if (RejFrom && From) + { + Calls = RejFrom; + + while(Calls[0]) + { + if (_stricmp(Calls[0], From) == 0) + return TRUE; + + Calls++; + } + } + + if (RejTo && To) + { + Calls = RejTo; + + while(Calls[0]) + { + if (_stricmp(Calls[0], To) == 0) + return TRUE; + + Calls++; + } + } + + if (RejAt && ATBBS) + { + Calls = RejAt; + + while(Calls[0]) + { + if (_stricmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + + if (RejBID && BID) + { + Calls = RejBID; + + while(Calls[0]) + { + if (Calls[0][0] == '*') + { + if (stristr(BID, &Calls[0][1])) + return TRUE; + } + else + { + if (_stricmp(BID, Calls[0]) == 0) + return TRUE; + } + + Calls++; + } + } + + // check fbb reject.sys type filters + + strcpy(ToCopy, To); + _strupr(ToCopy); + + while (p) + { + if (p->Action != 'R') + goto Continue; + + if (p->Type != Type && p->Type != '*') + goto Continue; + + if (wildcardcompare(From, p->From) == 0) + goto Continue; + + if (wildcardcompare(ToCopy, p->TO) == 0) + goto Continue; + + if (ATBBS) + if (wildcardcompare(ATBBS, p->AT) == 0) + goto Continue; + + if (BID) + if (wildcardcompare(BID, p->BID) == 0) + goto Continue; + + if (p->MaxLen && Len < p->MaxLen) + goto Continue; + + return TRUE; // Hold + +Continue: + p = p->Next; + } + + return FALSE; // Ok to accept +} + +BOOL CheckValidCall(char * From) +{ + unsigned int i; + + if (DontCheckFromCall) + return TRUE; + + if (strcmp(From, "SYSOP") == 0 || strcmp(From, "SYSTEM") == 0 || strcmp(From, "SERVIC") == 0 || + strcmp(From, "IMPORT") == 0 || strcmp(From, "SMTP:") == 0 || strcmp(From, "RMS:") == 0) + return TRUE; + + for (i = 1; i < strlen(From); i++) // skip first which may also be digit + { + if (isdigit(From[i])) + { + // Has a digit. Check Last is not digit + + if (isalpha(From[strlen(From) - 1])) + return TRUE; + } + } + + // No digit, return false + + return FALSE; +} + +BOOL wildcardcompare(char * Target, char * Match); + +BOOL CheckHoldFilters(struct MsgInfo * Msg, char * From, char * To, char * ATBBS, char * BID) +{ + char ** Calls; + FBBFilter * p = Filters; + + if (HoldFrom && From) + { + Calls = HoldFrom; + + while(Calls[0]) + { + if (_stricmp(Calls[0], From) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldTo && To) + { + Calls = HoldTo; + + while(Calls[0]) + { + if (_stricmp(Calls[0], To) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldAt && ATBBS) + { + Calls = HoldAt; + + while(Calls[0]) + { + if (_stricmp(Calls[0], ATBBS) == 0) + return TRUE; + + Calls++; + } + } + + if (HoldBID && BID) + { + Calls = HoldBID; + + while(Calls[0]) + { + if (Calls[0][0] == '*') + { + if (stristr(BID, &Calls[0][1])) + return TRUE; + } + else + { + if (_stricmp(BID, Calls[0]) == 0) + return TRUE; + } + + Calls++; + } + } + + // check fbb reject.sys type filters + + while (p) + { + if (p->Action != 'H') + goto Continue; + + if (p->Type != Msg->type && p->Type != '*') + goto Continue; + + if (wildcardcompare(Msg->from, p->From) == 0) + goto Continue; + + if (wildcardcompare(Msg->to, p->TO) == 0) + goto Continue; + + if (wildcardcompare(Msg->via, p->AT) == 0) + goto Continue; + + if (wildcardcompare(Msg->bid, p->BID) == 0) + goto Continue; + + if (p->MaxLen && Msg->length < p->MaxLen) + goto Continue; + + return TRUE; // Hold + +Continue: + p = p->Next; + } + + return FALSE; // Ok to accept +} + +BOOL CheckifLocalRMSUser(char * FullTo) +{ + struct UserInfo * user = LookupCall(FullTo); + + if (user) + if (user->flags & F_POLLRMS) + return TRUE; + + return FALSE; + +} + + + +int check_fwd_bit(char *mask, int bbsnumber) +{ + if (bbsnumber) + return (mask[(bbsnumber - 1) / 8] & (1 << ((bbsnumber - 1) % 8))); + else + return 0; +} + + +void set_fwd_bit(char *mask, int bbsnumber) +{ + if (bbsnumber) + mask[(bbsnumber - 1) / 8] |= (1 << ((bbsnumber - 1) % 8)); +} + + +void clear_fwd_bit (char *mask, int bbsnumber) +{ + if (bbsnumber) + mask[(bbsnumber - 1) / 8] &= (~(1 << ((bbsnumber - 1) % 8))); +} + +VOID BBSputs(CIRCUIT * conn, char * buf) +{ + // Sends to user and logs + + WriteLogLine(conn, '>',buf, (int)strlen(buf) -1, LOG_BBS); + + QueueMsg(conn, buf, (int)strlen(buf)); +} + +VOID __cdecl nodeprintf(ConnectionInfo * conn, const char * format, ...) +{ + char Mess[1000]; + int len; + va_list(arglist); + + + va_start(arglist, format); + len = vsprintf(Mess, format, arglist); + + QueueMsg(conn, Mess, len); + + WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); + + return; +} + +// nodeprintfEx add a LF if NEEFLF is set + +VOID __cdecl nodeprintfEx(ConnectionInfo * conn, const char * format, ...) +{ + char Mess[1000]; + int len; + va_list(arglist); + + + va_start(arglist, format); + len = vsprintf(Mess, format, arglist); + + QueueMsg(conn, Mess, len); + + WriteLogLine(conn, '>',Mess, len-1, LOG_BBS); + + if (conn->BBSFlags & NEEDLF) + QueueMsg(conn, "\r", 1); + + return; +} + + +int compare( const void *arg1, const void *arg2 ); + +VOID SortBBSChain() +{ + struct UserInfo * user; + struct UserInfo * users[161]; + int i = 0, n; + + // Get array of addresses + + for (user = BBSChain; user; user = user->BBSNext) + { + users[i++] = user; + if (i > 160) break; + } + + qsort((void *)users, i, sizeof(void *), compare ); + + BBSChain = NULL; + + // Rechain (backwards, as entries ate put on front of chain) + + for (n = i-1; n >= 0; n--) + { + users[n]->BBSNext = BBSChain; + BBSChain = users[n]; + } +} + +int compare(const void *arg1, const void *arg2) +{ + // Compare Calls. Fortunately call is at start of stuct + + return _stricmp(*(char**)arg1 , *(char**)arg2); +} + +int CountMessagesTo(struct UserInfo * user, int * Unread) +{ + int i, Msgs = 0; + UCHAR * Call = user->Call; + + *Unread = 0; + + for (i = NumberofMessages; i > 0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (_stricmp(MsgHddrPtr[i]->to, Call) == 0) + { + Msgs++; + if (MsgHddrPtr[i]->status == 'N') + *Unread = *Unread + 1; + } + } + return(Msgs); +} + + + +// Custimised message handling routines. +/* + Variables - a subset of those used by FBB + + $C : Number of the next message. + $I : First name of the connected user. + $L : Number of the latest message. + $N : Number of active messages + $U : Callsign of the connected user. + $W : Inserts a carriage return. + $Z : Last message read by the user (L command). + %X : Number of messages for the user. + %x : Number of new messages for the user. +*/ + +VOID ExpandAndSendMessage(CIRCUIT * conn, char * Msg, int LOG) +{ + char NewMessage[10000]; + char * OldP = Msg; + char * NewP = NewMessage; + char * ptr, * pptr; + size_t len; + char Dollar[] = "$"; + char CR[] = "\r"; + char num[20]; + int Msgs = 0, Unread = 0; + + ptr = strchr(OldP, '$'); + + while (ptr) + { + len = ptr - OldP; // Chars before $ + memcpy(NewP, OldP, len); + NewP += len; + + switch (*++ptr) + { + case 'I': // First name of the connected user. + + pptr = conn->UserPointer->Name; + break; + + case 'L': // Number of the latest message. + + sprintf(num, "%d", LatestMsg); + pptr = num; + break; + + case 'N': // Number of active messages. + + sprintf(num, "%d", NumberofMessages); + pptr = num; + break; + + case 'U': // Callsign of the connected user. + + pptr = conn->UserPointer->Call; + break; + + case 'W': // Inserts a carriage return. + + pptr = CR; + break; + + case 'Z': // Last message read by the user (L command). + + sprintf(num, "%d", conn->UserPointer->lastmsg); + pptr = num; + break; + + case 'X': // Number of messages for the user. + + Msgs = CountMessagesTo(conn->UserPointer, &Unread); + sprintf(num, "%d", Msgs); + pptr = num; + break; + + case 'x': // Number of new messages for the user. + + Msgs = CountMessagesTo(conn->UserPointer, &Unread); + sprintf(num, "%d", Unread); + pptr = num; + break; + + case 'F': // Number of new messages to forward to this BBS. + + Msgs = CountMessagestoForward(conn->UserPointer); + sprintf(num, "%d", Msgs); + pptr = num; + break; + + default: + + pptr = Dollar; // Just Copy $ + } + + len = strlen(pptr); + memcpy(NewP, pptr, len); + NewP += len; + + OldP = ++ptr; + ptr = strchr(OldP, '$'); + } + + strcpy(NewP, OldP); + + len = RemoveLF(NewMessage, (int)strlen(NewMessage)); + + WriteLogLine(conn, '>', NewMessage, (int)len, LOG); + QueueMsg(conn, NewMessage, (int)len); +} + +BOOL isdigits(char * string) +{ + // Returns TRUE id sting is decimal digits + + size_t i, n = strlen(string); + + for (i = 0; i < n; i++) + { + if (isdigit(string[i]) == FALSE) return FALSE; + } + return TRUE; +} + +BOOL wildcardcompare(char * Target, char * Match) +{ + // Do a compare with string *string string* *string* + + // Strings should all be UC + + char Pattern[100]; + char * firststar; + + strcpy(Pattern, Match); + firststar = strchr(Pattern,'*'); + + if (firststar) + { + size_t Len = strlen(Pattern); + + if (Pattern[0] == '*' && Pattern[Len - 1] == '*') // * at start and end + { + Pattern[Len - 1] = 0; + return !(strstr(Target, &Pattern[1]) == NULL); + } + if (Pattern[0] == '*') // * at start + { + // Compare the last len - 1 chars of Target + + size_t Targlen = strlen(Target); + size_t Comparelen = Targlen - (Len - 1); + + if (Len == 1) // Just * + return TRUE; + + if (Comparelen < 0) // Too Short + return FALSE; + + return (memcmp(&Target[Comparelen], &Pattern[1], Len - 1) == 0); + } + + // Must be * at end - compare first Len-1 char + + return (memcmp(Target, Pattern, Len - 1) == 0); + } + + // No WildCards - straight strcmp + return (strcmp(Target, Pattern) == 0); +} + +#ifndef LINBPQ + +PrintMessage(HDC hDC, struct MsgInfo * Msg); + +PrintMessages(HWND hDlg, int Count, int * Indexes) +{ + int i, CurrentMsgIndex; + char MsgnoText[10]; + int Msgno; + struct MsgInfo * Msg; + int Len = MAX_PATH; + BOOL hResult; + PRINTDLG pdx = {0}; + HDC hDC; + +// CHOOSEFONT cf; + LOGFONT lf; + HFONT hFont; + + + // Initialize the PRINTDLG structure. + + pdx.lStructSize = sizeof(PRINTDLG); + pdx.hwndOwner = hWnd; + pdx.hDevMode = NULL; + pdx.hDevNames = NULL; + pdx.hDC = NULL; + pdx.Flags = PD_RETURNDC | PD_COLLATE; + pdx.nMinPage = 1; + pdx.nMaxPage = 1000; + pdx.nCopies = 1; + pdx.hInstance = 0; + pdx.lpPrintTemplateName = NULL; + + // Invoke the Print property sheet. + + hResult = PrintDlg(&pdx); + + memset(&lf, 0, sizeof(LOGFONT)); + + /* + + // Initialize members of the CHOOSEFONT structure. + + cf.lStructSize = sizeof(CHOOSEFONT); + cf.hwndOwner = (HWND)NULL; + cf.hDC = pdx.hDC; + cf.lpLogFont = &lf; + cf.iPointSize = 0; + cf.Flags = CF_PRINTERFONTS | CF_FIXEDPITCHONLY; + cf.rgbColors = RGB(0,0,0); + cf.lCustData = 0L; + cf.lpfnHook = (LPCFHOOKPROC)NULL; + cf.lpTemplateName = (LPSTR)NULL; + cf.hInstance = (HINSTANCE) NULL; + cf.lpszStyle = (LPSTR)NULL; + cf.nFontType = PRINTER_FONTTYPE; + cf.nSizeMin = 0; + cf.nSizeMax = 0; + + // Display the CHOOSEFONT common-dialog box. + + ChooseFont(&cf); + + // Create a logical font based on the user's + // selection and return a handle identifying + // that font. +*/ + + lf.lfHeight = -56; + lf.lfWeight = 600; + lf.lfOutPrecision = 3; + lf.lfClipPrecision = 2; + lf.lfQuality = 1; + lf.lfPitchAndFamily = '1'; + strcpy (lf.lfFaceName, "Courier New"); + + hFont = CreateFontIndirect(&lf); + + if (hResult) + { + // User clicked the Print button, so use the DC and other information returned in the + // PRINTDLG structure to print the document. + + DOCINFO pdi; + + pdi.cbSize = sizeof(DOCINFO); + pdi.lpszDocName = "BBS Message Print"; + pdi.lpszOutput = NULL; + pdi.lpszDatatype = "RAW"; + pdi.fwType = 0; + + hDC = pdx.hDC; + + SelectObject(hDC, hFont); + + StartDoc(hDC, &pdi); + StartPage(hDC); + + for (i = 0; i < Count; i++) + { + SendDlgItemMessage(hDlg, 0, LB_GETTEXT, Indexes[i], (LPARAM)(LPCTSTR)&MsgnoText); + + Msgno = atoi(MsgnoText); + + for (CurrentMsgIndex = 1; CurrentMsgIndex <= NumberofMessages; CurrentMsgIndex++) + { + Msg = MsgHddrPtr[CurrentMsgIndex]; + + if (Msg->number == Msgno) + { + PrintMessage(hDC, Msg); + break; + } + } + } + + EndDoc(hDC); + } + + if (pdx.hDevMode != NULL) + GlobalFree(pdx.hDevMode); + if (pdx.hDevNames != NULL) + GlobalFree(pdx.hDevNames); + + if (pdx.hDC != NULL) + DeleteDC(pdx.hDC); + + return 0; +} + +PrintMessage(HDC hDC, struct MsgInfo * Msg) +{ + int Len = MAX_PATH; + char * MsgBytes; + char * Save; + int Msglen; + + StartPage(hDC); + + Save = MsgBytes = ReadMessageFile(Msg->number); + + Msglen = Msg->length; + + if (MsgBytes) + { + char Hddr[1000]; + char FullTo[100]; + int HRes, VRes; + char * ptr1, * ptr2; + int LineLen; + + RECT Rect; + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + + sprintf(Hddr, "From: %s%s\r\nTo: %s\r\nType/Status: %c%c\r\nDate/Time: %s\r\nBid: %s\r\nTitle: %s\r\n\r\n", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); + + + if (Msg->B2Flags & B2Msg) + { + // Remove B2 Headers (up to the File: Line) + + char * ptr; + ptr = strstr(MsgBytes, "Body:"); + if (ptr) + { + Msglen = atoi(ptr + 5); + ptr = strstr(ptr, "\r\n\r\n"); + } + if (ptr) + MsgBytes = ptr + 4; + } + + HRes = GetDeviceCaps(hDC, HORZRES) - 50; + VRes = GetDeviceCaps(hDC, VERTRES) - 50; + + Rect.top = 50; + Rect.left = 50; + Rect.right = HRes; + Rect.bottom = VRes; + + DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_CALCRECT | DT_WORDBREAK); + DrawText(hDC, Hddr, strlen(Hddr), &Rect, DT_WORDBREAK); + + // process message a line at a time. When page is full, output a page break + + ptr1 = MsgBytes; + ptr2 = ptr1; + + while (Msglen-- > 0) + { + if (*ptr1++ == '\r') + { + // Output this line + + // First check if it will fit + + Rect.top = Rect.bottom; + Rect.right = HRes; + Rect.bottom = VRes; + + LineLen = ptr1 - ptr2 - 1; + + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); + + if (Rect.bottom >= VRes) + { + EndPage(hDC); + StartPage(hDC); + + Rect.top = 50; + Rect.bottom = VRes; + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_CALCRECT | DT_WORDBREAK); + } + + if (LineLen == 0) // Blank line + Rect.bottom = Rect.top + 40; + else + DrawText(hDC, ptr2, ptr1 - ptr2 - 1, &Rect, DT_WORDBREAK); + + if (*(ptr1) == '\n') + { + ptr1++; + Msglen--; + } + + ptr2 = ptr1; + } + } + + free(Save); + + EndPage(hDC); + + } + return 0; +} + +#endif + + +int ImportMessages(CIRCUIT * conn, char * FN, BOOL Nopopup) +{ + char FileName[MAX_PATH] = "Messages.in"; + int Files = 0; + int WriteLen=0; + FILE *in; + CIRCUIT dummyconn; + struct UserInfo User; + int Index = 0; + + char Buffer[100000]; + char *buf = Buffer; + + if (FN[0]) // Name supplled + strcpy(FileName, FN); + + else + { +#ifndef LINBPQ + OPENFILENAME Ofn; + + memset(&Ofn, 0, sizeof(Ofn)); + + Ofn.lStructSize = sizeof(OPENFILENAME); + Ofn.hInstance = hInst; + Ofn.hwndOwner = MainWnd; + Ofn.lpstrFilter = NULL; + Ofn.lpstrFile= FileName; + Ofn.nMaxFile = sizeof(FileName)/ sizeof(*FileName); + Ofn.lpstrFileTitle = NULL; + Ofn.nMaxFileTitle = 0; + Ofn.lpstrInitialDir = BaseDir; + Ofn.Flags = OFN_SHOWHELP | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + Ofn.lpstrTitle = NULL;//; + + if (!GetOpenFileName(&Ofn)) + return 0; +#endif + } + + in = fopen(FileName, "rb"); + + if (!(in)) + { + char msg[500]; + sprintf_s(msg, sizeof(msg), "Failed to open %s", FileName); + if (conn) + nodeprintf(conn, "%s\r", msg); +#ifdef WIN32 + else + if (Nopopup == FALSE) + MessageBox(NULL, msg, "BPQMailChat", MB_OK); +#endif + return 0; + } + + memset(&dummyconn, 0, sizeof(CIRCUIT)); + memset(&User, 0, sizeof(struct UserInfo)); + + if (conn == 0) + { + conn = &dummyconn; + + dummyconn.UserPointer = &User; // Was SYSOPCall, but I think that is wrong. + strcpy(User.Call, "IMPORT"); + User.flags |= F_EMAIL; + dummyconn.sysop = TRUE; + dummyconn.BBSFlags = BBS; + + strcpy(dummyconn.Callsign, "IMPORT"); + } + + while(fgets(Buffer, 99999, in)) + { + // First line should start SP/SB ?ST? + + char * From = NULL; + char * BID = NULL; + char * ATBBS = NULL; + char seps[] = " \t\r"; + struct MsgInfo * Msg; + char To[100]= ""; + int msglen; + char * Context; + char * Arg1, * Cmd; + +NextMessage: + + From = NULL; + BID = NULL; + ATBBS = NULL; + To[0]= 0; + + Sleep(100); + + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + + if (Buffer[0] == 0) //Blank Line + continue; + + WriteLogLine(conn, '>', Buffer, (int)strlen(Buffer), LOG_BBS); + + if (dummyconn.sysop == 0) + { + nodeprintf(conn, "%s\r", Buffer); + Flush(conn); + } + + Cmd = strtok_s(Buffer, seps, &Context); + + if (Cmd == NULL) + { + fclose(in); + return Files; + } + + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1 == NULL) + { + if (dummyconn.sysop) + Debugprintf("Bad Import Line %s", Buffer); + else + nodeprintf(conn, "Bad Import Line %s\r", Buffer); + + fclose(in); + return Files; + } + + strcpy(To, Arg1); + + if (DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + { + if (CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL)) + { + Msg = conn->TempMsg; + + // SP is Ok, read message; + + ClearQueue(conn); + + fgets(Buffer, 99999, in); + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + if (strlen(Buffer) > 60) + Buffer[60] = 0; + + strcpy(Msg->title, Buffer); + + // Read the lines + + conn->Flags |= GETTINGMESSAGE; + + Buffer[0] = 0; + + fgets(Buffer, 99999, in); + + while ((conn->Flags & GETTINGMESSAGE) && Buffer[0]) + { + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + msglen = (int)strlen(Buffer); + Buffer[msglen++] = 13; + ProcessMsgLine(conn, conn->UserPointer,Buffer, msglen); + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + } + + // Message completed (or off end of file) + + Files ++; + + ClearQueue(conn); + + if (Buffer[0]) + goto NextMessage; // We have read the SP/SB line; + else + { + fclose(in); + return Files; + } + } + else + { + // Create failed + + Flush(conn); + } + } + + // Search for next message + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + + while (Buffer[0]) + { + strlop(Buffer, 10); + strlop(Buffer, 13); // Remove cr and/or lf + + if (_stricmp(Buffer, "/EX") == 0) + { + // Found end + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + + if (dummyconn.sysop) + ClearQueue(conn); + else + Flush(conn); + + if (Buffer[0]) + goto NextMessage; // We have read the SP/SB line; + } + + Buffer[0] = 0; + fgets(Buffer, 99999, in); + } + } + + fclose(in); + + if (dummyconn.sysop) + ClearQueue(conn); + else + Flush(conn); + + return Files; +} +char * ReadMessageFileEx(struct MsgInfo * MsgRec) +{ + // Sets Message Size from File Size + + int msgno = MsgRec->number; + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes=malloc(FileSize+1); + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize]=0; + MsgRec->length = FileSize; + + return MsgBytes; +} + +char * ReadMessageFile(int msgno) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, msgno); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + MsgBytes = malloc(FileSize + 100); // A bit of space for alias substitution on B2 + + fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + MsgBytes[FileSize]=0; + + return MsgBytes; +} + + +int QueueMsg(ConnectionInfo * conn, char * msg, int len) +{ + // Add Message to queue for this connection + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // Create or extend buffer + + GetSemaphore(&OutputSEM, 0); + + conn->OutputQueue=realloc(conn->OutputQueue, conn->OutputQueueLength + len); + + if (conn->OutputQueue == NULL) + { + // relloc failed - should never happen, but clean up + + CriticalErrorHandler("realloc failed to expand output queue"); + FreeSemaphore(&OutputSEM); + return 0; + } + + memcpy(&conn->OutputQueue[conn->OutputQueueLength], msg, len); + conn->OutputQueueLength += len; + FreeSemaphore(&OutputSEM); + + return len; +} + +void TrytoSend() +{ + // call Flush on any connected streams with queued data + + ConnectionInfo * conn; + struct ConsoleInfo * Cons; + + int n; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active == TRUE) + { + Flush(conn); + + // if an FLARQ mail has been sent see if queues have cleared + + if (conn->BBSFlags & YAPPTX) + { + YAPPSendData(conn); + } + else if (conn->OutputQueue == NULL && (conn->BBSFlags & ARQMAILACK)) + { + int n = TXCount(conn->BPQStream); // All Sent and Acked? + + if (n == 0) + { + struct MsgInfo * Msg = conn->FwdMsg; + + conn->ARQClearCount--; + + if (conn->ARQClearCount <= 0) + { + Logprintf(LOG_BBS, conn, '>', "ARQ Send Complete"); + + // Mark mail as sent, and look for more + + clear_fwd_bit(Msg->fbbs, conn->UserPointer->BBSNumber); + set_fwd_bit(Msg->forw, conn->UserPointer->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(Msg->fbbs, zeros, NBMASK) == 0) + { + Msg->status = 'F'; // Mark as forwarded + Msg->datechanged=time(NULL); + } + + conn->BBSFlags &= ~ARQMAILACK; + conn->UserPointer->ForwardingInfo->MsgCount--; + + SaveMessageDatabase(); + SendARQMail(conn); // See if any more - close if not + } + } + else + conn->ARQClearCount = 10; + } + } + } +#ifndef LINBPQ + for (Cons = ConsHeader[0]; Cons; Cons = Cons->next) + { + if (Cons->Console) + Flush(Cons->Console); + } +#endif +} + + +void Flush(CIRCUIT * conn) +{ + int tosend, len, sent; + + // Try to send data to user. May be stopped by user paging or node flow control + + // UCHAR * OutputQueue; // Messages to user + // int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message + // int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again. + + // BOOL Paging; // Set if user wants paging + // int LinesSent; // Count when paging + // int PageLen; // Lines per page + + + if (conn->OutputQueue == NULL) + { + // Nothing to send. If Close after Flush is set, disconnect + + if (conn->CloseAfterFlush) + { + conn->CloseAfterFlush--; + + if (conn->CloseAfterFlush) + return; + + Disconnect(conn->BPQStream); + conn->ErrorCount = 0; + } + + return; // Nothing to send + } + tosend = conn->OutputQueueLength - conn->OutputGetPointer; + + sent=0; + + while (tosend > 0) + { + if (TXCount(conn->BPQStream) > 15) + return; // Busy + + if (conn->BBSFlags & SYSOPCHAT) // Suspend queued output while sysop chatting + return; + + if (conn->Paging && (conn->LinesSent >= conn->PageLen)) + return; + + if (tosend <= conn->paclen) + len=tosend; + else + len=conn->paclen; + + GetSemaphore(&OutputSEM, 0); + + if (conn->Paging) + { + // look for CR chars in message to send. Increment LinesSent, and stop if at limit + + UCHAR * ptr1 = &conn->OutputQueue[conn->OutputGetPointer]; + UCHAR * ptr2; + int lenleft = len; + + ptr2 = memchr(ptr1, 0x0d, len); + + while (ptr2) + { + conn->LinesSent++; + ptr2++; + lenleft = len - (int)(ptr2 - ptr1); + + if (conn->LinesSent >= conn->PageLen) + { + len = (int)(ptr2 - &conn->OutputQueue[conn->OutputGetPointer]); + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + conn->OutputGetPointer+=len; + tosend-=len; + SendUnbuffered(conn->BPQStream, "bort, Continue..>", 25); + FreeSemaphore(&OutputSEM); + return; + + } + ptr2 = memchr(ptr2, 0x0d, lenleft); + } + } + + SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len); + + conn->OutputGetPointer+=len; + + FreeSemaphore(&OutputSEM); + + tosend-=len; + sent++; + + if (sent > 15) + return; + } + + // All Sent. Free buffers and reset pointers + + conn->LinesSent = 0; + + ClearQueue(conn); +} + +VOID ClearQueue(ConnectionInfo * conn) +{ + if (conn->OutputQueue == NULL) + return; + + GetSemaphore(&OutputSEM, 0); + + free(conn->OutputQueue); + + conn->OutputQueue=NULL; + conn->OutputGetPointer=0; + conn->OutputQueueLength=0; + + FreeSemaphore(&OutputSEM); +} + + + +VOID FlagAsKilled(struct MsgInfo * Msg, BOOL SaveDB) +{ + struct UserInfo * user; + + Msg->status='K'; + Msg->datechanged=time(NULL); + + // Remove any forwarding references + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + for (user = BBSChain; user; user = user->BBSNext) + { + if (check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + user->ForwardingInfo->MsgCount--; + clear_fwd_bit(Msg->fbbs, user->BBSNumber); + } + } + } + if (SaveDB) + SaveMessageDatabase(); + RebuildNNTPList(); +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(Msg); +#endif + +} + +void DoDeliveredCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + struct MsgInfo * Msg; + + while (Arg1) + { + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + goto Next; + } + + if (Msg->type != 'T') + { + nodeprintf(conn, "Message %d not an NTS Message\r", msgno); + goto Next; + } + + if (Msg->status == 'N') + nodeprintf(conn, "Warning - Message has status N\r"); + + Msg->status = 'D'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + + nodeprintf(conn, "Message #%d Flagged as Delivered\r", msgno); + Next: + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; +} + +void DoUnholdCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + // Param is either ALL or a list of numbers + + if (Arg1 == NULL) + { + nodeprintf(conn, "No message number\r"); + return; + } + + if (_stricmp(Arg1, "ALL") == 0) + { + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if (Msg->status == 'H') + { + if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + else + Msg->status = 'N'; + + nodeprintf(conn, "Message #%d Unheld\r", Msg->number); + } + } + return; + } + + while (Arg1) + { + msgno = atoi(Arg1); + Msg = GetMsgFromNumber(msgno); + + if (Msg) + { + if (Msg->status == 'H') + { + if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + else + Msg->status = 'N'; + + nodeprintf(conn, "Message #%d Unheld\r", msgno); + } + else + { + nodeprintf(conn, "Message #%d was not held\r", msgno); + } + } + else + nodeprintf(conn, "Message #%d not found\r", msgno); + + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; +} + +void DoKillCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + switch (toupper(Cmd[1])) + { + + case 0: // Just K + + while (Arg1) + { + msgno = atoi(Arg1); + KillMessage(conn, user, msgno); + + Arg1 = strtok_s(NULL, " \r", &Context); + } + + SaveMessageDatabase(); + return; + + case 'M': // Kill Mine + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + { + if (Msg->type == 'P' && Msg->status == 'Y') + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", Msg->number); + } + } + } + + SaveMessageDatabase(); + return; + + case 'H': // Kill Held + + if (conn->sysop) + { + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + + if (Msg->status == 'H') + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", Msg->number); + } + } + } + SaveMessageDatabase(); + return; + + case '>': // K> - Kill to + + if (conn->sysop) + { + if (Arg1) + if (KillMessagesTo(conn, user, Arg1) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + } + + case '<': + + if (conn->sysop) + { + if (Arg1) + if (KillMessagesFrom(conn, user, Arg1) == 0); + BBSputs(conn, "No Messages found\r"); + + return; + } + } + + nodeprintf(conn, "*** Error: Invalid Kill option %c\r", Cmd[1]); + + return; + +} + +int KillMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call) +{ + int i, Msgs = 0; + struct MsgInfo * Msg; + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + if (Msg->status != 'K' && _stricmp(Msg->to, Call) == 0) + { + Msgs++; + KillMessage(conn, user, MsgHddrPtr[i]->number); + } + } + + SaveMessageDatabase(); + return(Msgs); +} + +int KillMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call) +{ + int i, Msgs = 0; + struct MsgInfo * Msg; + + + for (i=NumberofMessages; i>0; i--) + { + Msg = MsgHddrPtr[i]; + if (Msg->status != 'K' && _stricmp(Msg->from, Call) == 0) + { + Msgs++; + KillMessage(conn, user, MsgHddrPtr[i]->number); + } + } + + SaveMessageDatabase(); + return(Msgs); +} + +BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg) +{ + if (SYSOP || (Msg->type == 'T' && UserCantKillT == FALSE)) + return TRUE; + + if (Msg->type == 'P') + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return TRUE; + + if (Msg->type == 'B') + if (_stricmp(Msg->from, Call) == 0) + return TRUE; + + return FALSE; +} + +void KillMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) +{ + struct MsgInfo * Msg; + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL || Msg->status == 'K') + { + nodeprintf(conn, "Message %d not found\r", msgno); + return; + } + + if (OkToKillMessage(conn->sysop, user->Call, Msg)) + { + FlagAsKilled(Msg, FALSE); + nodeprintf(conn, "Message #%d Killed\r", msgno); + } + else + nodeprintf(conn, "Not your message\r"); +} + + +BOOL ListMessage(struct MsgInfo * Msg, ConnectionInfo * conn, struct TempUserInfo * Temp) +{ + char FullFrom[80]; + char FullTo[80]; + + strcpy(FullFrom, Msg->from); + + if ((_stricmp(Msg->from, "RMS:") == 0) || (_stricmp(Msg->from, "SMTP:") == 0) || + Temp->SendFullFrom || (_stricmp(Msg->emailfrom, "@winlink.org") == 0)) + strcat(FullFrom, Msg->emailfrom); + + if (_stricmp(Msg->to, "RMS") == 0) + { + sprintf(FullTo, "RMS:%s", Msg->via); + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); + } + else + + if (Msg->to[0] == 0 && Msg->via[0] != 0) + { + sprintf(FullTo, "smtp:%s", Msg->via); + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, FullTo, FullFrom, Msg->title); + } + + else + if (Msg->via[0] != 0) + { + char Via[80]; + strcpy(Via, Msg->via); + strlop(Via, '.'); // Only show first part of via + nodeprintf(conn, "%-6d %s %c%c %5d %-7s@%-6s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, Via, FullFrom, Msg->title); + } + else + nodeprintf(conn, "%-6d %s %c%c %5d %-7s %-6s %-s\r", + Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, FullFrom, Msg->title); + + // if paging, stop two before page lengh. This lets us send the continue prompt, save status + // and exit without triggering the system paging code. We can then read a message then resume listing + + if (Temp->ListActive && conn->Paging) + { + Temp->LinesSent++; + + if ((Temp->LinesSent + 1) >= conn->PageLen) + { + nodeprintf(conn, "bort, , = Continue..>"); + Temp->LastListedInPagedMode = Msg->number; + Temp->ListSuspended = TRUE; + return TRUE; + } + } + + return FALSE; +} + +void DoListCommand(ConnectionInfo * conn, struct UserInfo * user, char * Cmd, char * Arg1, BOOL Resuming, char * Context) +{ + struct TempUserInfo * Temp = user->Temp; + struct MsgInfo * Msg; + + // Allow compound selection, eg LTN or LFP + + // types P N T + // Options LL LR L< L> L@ LM LC (L* used internally for just L, ie List New + // Status N Y H F K D + + // Allowing options in any order complicates paging. May be best to parse options once and restore if paging. + + Temp->ListActive = TRUE; + Temp->LinesSent = 0; + + if (Resuming) + { + // Entered after a paging pause. Selection fields are already set up + + // We have reentered list command after a pause. The next message to list is in Temp->LastListedInPagedMode + +// Start = Temp->LastListedInPagedMode; + Temp->ListSuspended = FALSE; + } + else + { + Temp->ListRangeEnd = LatestMsg; + Temp->ListRangeStart = 1; + Temp->LLCount = 0; + Temp->SendFullFrom = 0; + Temp->ListType = 0; + Temp->ListStatus = 0; + Temp->ListSelector = 0; + Temp->UpdateLatest = 0; + Temp->LastListParams[0] = 0; + Temp->IncludeKilled = 1; // SYSOP include Killed except LM + + //Analyse L params. + + _strupr(Cmd); + + if (strcmp(Cmd, "LC") == 0) // List Bull Categories + { + ListCategories(conn); + return; + } + + // if command is just L or LR start from last listed + + if (Arg1 == NULL) + { + if (strcmp(Cmd, "L") == 0 || strcmp(Cmd, "LR") == 0) + { + if (LatestMsg == conn->lastmsg) + { + BBSputs(conn, "No New Messages\r"); + return; + } + + Temp->UpdateLatest = 1; + Temp->ListRangeStart = conn->lastmsg; + } + } + + if (strchr(Cmd, 'V')) // Verbose + Temp->SendFullFrom = 'V'; + + if (strchr(Cmd, 'R')) + Temp->ListDirn = 'R'; + else + Temp->ListDirn = '*'; // Default newest first + + Cmd++; // skip L + + if (strchr(Cmd, 'T')) + Temp->ListType = 'T'; + else if (strchr(Cmd, 'P')) + Temp->ListType = 'P'; + else if (strchr(Cmd, 'B')) + Temp->ListType = 'B'; + + if (strchr(Cmd, 'N')) + Temp->ListStatus = 'N'; + else if (strchr(Cmd, 'Y')) + Temp->ListStatus = 'Y'; + else if (strchr(Cmd, 'F')) + Temp->ListStatus = 'F'; + else if (strchr(Cmd, '$')) + Temp->ListStatus = '$'; + else if (strchr(Cmd, 'H')) + Temp->ListStatus = 'H'; + else if (strchr(Cmd, 'K')) + Temp->ListStatus = 'K'; + else if (strchr(Cmd, 'D')) + Temp->ListStatus = 'D'; + + // H or K only by Sysop + + switch (Temp->ListStatus) + { + case 'K': + case 'H': // List Status + + if (conn->sysop) + break; + + BBSputs(conn, "LH or LK can only be used by SYSOP\r"); + return; + } + + if (strchr(Cmd, '<')) + Temp->ListSelector = '<'; + else if (strchr(Cmd, '>')) + Temp->ListSelector = '>'; + else if (strchr(Cmd, '@')) + Temp->ListSelector = '@'; + else if (strchr(Cmd, 'M')) + { + Temp->ListSelector = 'M'; + Temp->IncludeKilled = FALSE; + } + + // Param could be single number, number range or call + + if (Arg1) + { + if (strchr(Cmd, 'L')) // List Last + { + // Param is number + + if (Arg1) + Temp->LLCount = atoi(Arg1); + } + else + { + // Range nnn-nnn or single value or callsign + + char * Arg2, * Arg3, * Range; + char seps[] = " \t\r"; + UINT From=LatestMsg, To=0; + + Arg2 = strtok_s(NULL, seps, &Context); + Arg3 = strtok_s(NULL, seps, &Context); + + if (Temp->ListSelector && Temp->ListSelector != 'M') + { + // < > or @ - first param is callsign + + strcpy(Temp->LastListParams, Arg1); + + // Just possible number range + + Arg1 = Arg2; + Arg2 = Arg3; + Arg3 = strtok_s(NULL, seps, &Context); + } + + if (Arg1) + { + Range = strchr(Arg1, '-'); + + // A number could be a Numeric Bull Dest (eg 44) + // I think this can only resaonably be > + + if (isdigits(Arg1)) + To = From = atoi(Arg1); + + if (Arg2) + From = atoi(Arg2); + else + { + if (Range) + { + Arg3 = strlop(Arg1, '-'); + + To = atoi(Arg1); + + if (Arg3 && Arg3[0]) + From = atoi(Arg3); + else + From = LatestMsg; + } + } + if (From > 100000 || To > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + Temp->ListRangeStart = To; + Temp->ListRangeEnd = From; + } + } + } + } + + // Run through all messages (either forwards or backwards) and list any that match all selection criteria + + while (1) + { + if (Temp->ListDirn == 'R') + Msg = GetMsgFromNumber(Temp->ListRangeStart); + else + Msg = GetMsgFromNumber(Temp->ListRangeEnd); + + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop, Temp->IncludeKilled)) // Check if user is allowed to list this message + { + // Check filters + + if (Temp->ListStatus && Temp->ListStatus != Msg->status) + goto skip; + + if (Temp->ListType && Temp->ListType != Msg->type) + goto skip; + + if (Temp->ListSelector == '<') + if (_stricmp(Msg->from, Temp->LastListParams) != 0) + goto skip; + + if (Temp->ListSelector == '>') + if (_stricmp(Msg->to, Temp->LastListParams) != 0) + goto skip; + + if (Temp->ListSelector == '@') + if (_memicmp(Msg->via, Temp->LastListParams, strlen(Temp->LastListParams)) != 0 && + (_stricmp(Temp->LastListParams, "SMTP:") != 0 || Msg->to[0] != 0)) + goto skip; + + if (Temp->ListSelector == 'M') + if (_stricmp(Msg->to, user->Call) != 0 && + (_stricmp(Msg->to, "SYSOP") != 0 || ((user->flags & F_SYSOP_IN_LM) == 0))) + + goto skip; + + if (ListMessage(Msg, conn, Temp)) + { + if (Temp->ListDirn == 'R') + Temp->ListRangeStart++; + else + Temp->ListRangeEnd--; + + return; // Hit page limit + } + + if (Temp->LLCount) + { + Temp->LLCount--; + if (Temp->LLCount == 0) + return; // LL count reached + } +skip:; + } + + if (Temp->ListRangeStart == Temp->ListRangeEnd) + { + // if using L or LR (list new) update last listed field + + if (Temp->UpdateLatest) + conn->lastmsg = LatestMsg; + + return; + } + + if (Temp->ListDirn == 'R') + Temp->ListRangeStart++; + else + Temp->ListRangeEnd--; + + if (Temp->ListRangeStart > 100000 || Temp->ListRangeEnd < 0) // Loop protection! + return; + + } + +/* + + switch (Cmd[0]) + { + + case '*': // Just L + case 'R': // LR = List Reverse + + if (Arg1) + { + // Range nnn-nnn or single value + + char * Arg2, * Arg3; + char * Context; + char seps[] = " -\t\r"; + UINT From=LatestMsg, To=0; + char * Range = strchr(Arg1, '-'); + + Arg2 = strtok_s(Arg1, seps, &Context); + Arg3 = strtok_s(NULL, seps, &Context); + + if (Arg2) + To = From = atoi(Arg2); + + if (Arg3) + From = atoi(Arg3); + else + if (Range) + From = LatestMsg; + + if (From > 100000 || To > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + if (Cmd[1] == 'R') + { + if (Start) + To = Start + 1; + + ListMessagesInRangeForwards(conn, user, user->Call, From, To, Temp->SendFullFrom); + } + else + { + if (Start) + From = Start - 1; + + ListMessagesInRange(conn, user, user->Call, From, To, Temp->SendFullFrom); + } + } + else + + if (LatestMsg == conn->lastmsg) + BBSputs(conn, "No New Messages\r"); + else if (Cmd[1] == 'R') + ListMessagesInRangeForwards(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); + else + ListMessagesInRange(conn, user, user->Call, LatestMsg, conn->lastmsg + 1, SendFullFrom); + + conn->lastmsg = LatestMsg; + + return; + + + case 'L': // List Last + + if (Arg1) + { + int i = atoi(Arg1); + int m = NumberofMessages; + + if (Resuming) + i = Temp->LLCount; + else + Temp->LLCount = i; + + for (; i>0 && m != 0; i--) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + i++; + continue; + } + + Temp->LLCount--; + + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + m--; + } + } + } + return; + + case 'M': // LM - List Mine + + if (ListMessagesTo(conn, user, user->Call, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + return; + + case '>': // L> - List to + + if (Arg1) + if (ListMessagesTo(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + + return; + + case '<': + + if (Arg1) + if (ListMessagesFrom(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + + case '@': + + if (Arg1) + if (ListMessagesAT(conn, user, Arg1, SendFullFrom, Start) == 0) + BBSputs(conn, "No Messages found\r"); + + return; + + case 'N': + case 'Y': + case 'F': + case '$': + case 'D': // Delivered NTS Traffic can be listed by anyone + { + int m = NumberofMessages; + + while (m > 0) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + continue; + } + + if (Temp->ListType) + { + if (MsgHddrPtr[m]->status == Cmd[1] && MsgHddrPtr[m]->type == Temp->ListType) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + } + else + { + if (MsgHddrPtr[m]->status == toupper(Cmd[1])) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + } + m--; + } + } + } + return; + + case 'K': + case 'H': // List Status + + if (conn->sysop) + { + int i, Msgs = Start; + + for (i=NumberofMessages; i>0; i--) + { + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (MsgHddrPtr[i]->status == toupper(Cmd[1])) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, SendFullFrom)) + return; // Hit page limit + + } + } + + if (Msgs == 0) + BBSputs(conn, "No Messages found\r"); + } + else + BBSputs(conn, "LH or LK can only be used by SYSOP\r"); + + return; + + case 'C': + { + struct NNTPRec * ptr = FirstNNTPRec; + char Cat[100]; + char NextCat[100]; + int Line = 0; + int Count; + + while (ptr) + { + // if the next name is the same, combine counts + + strcpy(Cat, ptr->NewsGroup); + strlop(Cat, '.'); + Count = ptr->Count; + Catloop: + if (ptr->Next) + { + strcpy(NextCat, ptr->Next->NewsGroup); + strlop(NextCat, '.'); + if (strcmp(Cat, NextCat) == 0) + { + ptr = ptr->Next; + Count += ptr->Count; + goto Catloop; + } + } + + nodeprintf(conn, "%-6s %-3d", Cat, Count); + Line += 10; + if (Line > 80) + { + Line = 0; + nodeprintf(conn, "\r"); + } + + ptr = ptr->Next; + } + + if (Line) + nodeprintf(conn, "\r\r"); + else + nodeprintf(conn, "\r"); + + return; + } + } + + // Could be P B or T if specified without a status + + switch (Temp->ListType) + { + case 'P': + case 'B': + case 'T': // NTS Traffic can be listed by anyone + { + int m = NumberofMessages; + + while (m > 0) + { + m = GetUserMsg(m, user->Call, conn->sysop); + + if (m > 0) + { + if (Start && MsgHddrPtr[m]->number >= Start) + { + m--; + continue; + } + + if (MsgHddrPtr[m]->type == Temp->ListType) + if (ListMessage(MsgHddrPtr[m], conn, SendFullFrom)) + return; // Hit page limit + m--; + } + } + + return; + } + } + +*/ + nodeprintf(conn, "*** Error: Invalid List option %c\r", Cmd[1]); + +} + +void ListCategories(ConnectionInfo * conn) +{ + // list bull categories + struct NNTPRec * ptr = FirstNNTPRec; + char Cat[100]; + char NextCat[100]; + int Line = 0; + int Count; + + while (ptr) + { + // if the next name is the same, combine counts + + strcpy(Cat, ptr->NewsGroup); + strlop(Cat, '.'); + Count = ptr->Count; +Catloop: + if (ptr->Next) + { + strcpy(NextCat, ptr->Next->NewsGroup); + strlop(NextCat, '.'); + if (strcmp(Cat, NextCat) == 0) + { + ptr = ptr->Next; + Count += ptr->Count; + goto Catloop; + } + } + + nodeprintf(conn, "%-6s %-3d", Cat, Count); + Line += 10; + if (Line > 80) + { + Line = 0; + nodeprintf(conn, "\r"); + } + + ptr = ptr->Next; + } + + if (Line) + nodeprintf(conn, "\r\r"); + else + nodeprintf(conn, "\r"); + + return; +} + +/* +int ListMessagesTo(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) +{ + int i, Msgs = Start; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if ((_stricmp(MsgHddrPtr[i]->to, Call) == 0) || + ((conn->sysop) && _stricmp(Call, SYSOPCall) == 0 && + _stricmp(MsgHddrPtr[i]->to, "SYSOP") == 0 && (user->flags & F_SYSOP_IN_LM))) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + break; // Hit page limit + } + } + + return(Msgs); +} + +int ListMessagesFrom(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom, int Start) +{ + int i, Msgs = 0; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (_stricmp(MsgHddrPtr[i]->from, Call) == 0) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + return Msgs; // Hit page limit + + } + } + + return(Msgs); +} + +int ListMessagesAT(ConnectionInfo * conn, struct UserInfo * user, char * Call, BOOL SendFullFrom,int Start) +{ + int i, Msgs = 0; + + for (i=NumberofMessages; i>0; i--) + { + if (MsgHddrPtr[i]->status == 'K') + continue; + + if (Start && MsgHddrPtr[i]->number >= Start) + continue; + + if (_memicmp(MsgHddrPtr[i]->via, Call, strlen(Call)) == 0 || + (_stricmp(Call, "SMTP:") == 0 && MsgHddrPtr[i]->to[0] == 0)) + { + Msgs++; + if (ListMessage(MsgHddrPtr[i], conn, Temp->SendFullFrom)) + break; // Hit page limit + } + } + + return(Msgs); +} +*/ +int GetUserMsg(int m, char * Call, BOOL SYSOP) +{ + struct MsgInfo * Msg; + + // Get Next (usually backwards) message which should be shown to this user + // ie Not Deleted, and not Private unless to or from Call + + do + { + Msg=MsgHddrPtr[m]; + + if (SYSOP) return m; // Sysop can list or read anything + + if (Msg->status != 'K') + { + + if (Msg->status != 'H') + { + if (Msg->type == 'B' || Msg->type == 'T') return m; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return m; + } + } + } + + m--; + + } while (m> 0); + + return 0; +} + + +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP, BOOL IncludeKilled) +{ + // Return TRUE if user is allowed to read message + + if (Msg->status == 'K' && IncludeKilled == 0) + return FALSE; + + if (SYSOP) + return TRUE; // Sysop can list or read anything + + if ((Msg->status != 'K') && (Msg->status != 'H')) + { + if (Msg->type == 'B' || Msg->type == 'T') return TRUE; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return TRUE; + } + } + + return FALSE; +} +/* +int GetUserMsgForwards(int m, char * Call, BOOL SYSOP) +{ + struct MsgInfo * Msg; + + // Get Next (usually backwards) message which should be shown to this user + // ie Not Deleted, and not Private unless to or from Call + + do + { + Msg=MsgHddrPtr[m]; + + if (Msg->status != 'K') + { + if (SYSOP) return m; // Sysop can list or read anything + + if (Msg->status != 'H') + { + if (Msg->type == 'B' || Msg->type == 'T') return m; + + if (Msg->type == 'P') + { + if ((_stricmp(Msg->to, Call) == 0) || (_stricmp(Msg->from, Call) == 0)) + return m; + } + } + } + + m++; + + } while (m <= NumberofMessages); + + return 0; + +} + + +void ListMessagesInRange(ConnectionInfo * conn, struct UserInfo * user, char * Call, int Start, int End, BOOL SendFullFrom) +{ + int m; + struct MsgInfo * Msg; + + for (m = Start; m >= End; m--) + { + Msg = GetMsgFromNumber(m); + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) + if (ListMessage(Msg, conn, Temp->SendFullFrom)) + return; // Hit page limit + + } +} + + +void ListMessagesInRangeForwards(ConnectionInfo * conn, struct UserInfo * user, char * Call, int End, int Start, BOOL SendFullFrom) +{ + int m; + struct MsgInfo * Msg; + + for (m = Start; m <= End; m++) + { + Msg = GetMsgFromNumber(m); + + if (Msg && CheckUserMsg(Msg, user->Call, conn->sysop)) + if (ListMessage(Msg, conn, Temp->SendFullFrom)) + return; // Hit page limit + } +} +*/ + +void DoReadCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + int msgno=-1; + int i; + struct MsgInfo * Msg; + + + switch (toupper(Cmd[1])) + { + case 0: // Just R + + while (Arg1) + { + msgno = atoi(Arg1); + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return; + } + + ReadMessage(conn, user, msgno); + Arg1 = strtok_s(NULL, " \r", &Context); + } + + return; + + case 'M': // Read Mine (Unread Messages) + + if (toupper(Cmd[2]) == 'R') + { + for (i = 1; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + if (Msg->status == 'N') + ReadMessage(conn, user, Msg->number); + } + } + else + { + for (i = NumberofMessages; i > 0; i--) + { + Msg = MsgHddrPtr[i]; + + if ((_stricmp(Msg->to, user->Call) == 0) || (conn->sysop && _stricmp(Msg->to, "SYSOP") == 0 && user->flags & F_SYSOP_IN_LM)) + if (Msg->status == 'N') + ReadMessage(conn, user, Msg->number); + } + } + + return; + } + + nodeprintf(conn, "*** Error: Invalid Read option %c\r", Cmd[1]); + + return; +} + +int RemoveLF(char * Message, int len) +{ + // Remove lf chars and nulls + + char * ptr1, * ptr2; + + ptr1 = ptr2 = Message; + + while (len-- > 0) + { + while (*ptr1 == 0 && len) + { + ptr1++; + len--; + } + + *ptr2 = *ptr1; + + if (*ptr1 == '\r') + if (*(ptr1+1) == '\n') + { + ptr1++; + len--; + } + ptr1++; + ptr2++; + } + + return (int)(ptr2 - Message); +} + + + +int RemoveNulls(char * Message, int len) +{ + // Remove nulls + + char * ptr1, * ptr2; + + ptr1 = ptr2 = Message; + + while (len-- > 0) + { + while (*ptr1 == 0 && len) + { + ptr1++; + len--; + } + + *ptr2 = *ptr1; + + ptr1++; + ptr2++; + } + + return (int)(ptr2 - Message); +} + +void ReadMessage(ConnectionInfo * conn, struct UserInfo * user, int msgno) +{ + struct MsgInfo * Msg; + char * MsgBytes, * Save; + char FullTo[100]; + int Index = 0; + + Msg = GetMsgFromNumber(msgno); + + if (Msg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return; + } + + if (!CheckUserMsg(Msg, user->Call, conn->sysop, TRUE)) + { + nodeprintf(conn, "Message %d not for you\r", msgno); + return; + } + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + + nodeprintf(conn, "From: %s%s\rTo: %s\rType/Status: %c%c\rDate/Time: %s\rBid: %s\rTitle: %s\r\r", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, Msg->title); + + MsgBytes = Save = ReadMessageFile(msgno); + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else if (Msg->type == 'T') + Index = TMSG; + + if (MsgBytes) + { + int Length = Msg->length; + + if (Msg->B2Flags & B2Msg) + { + char * ptr; + + // if message has attachments, display them if plain text + + if (Msg->B2Flags & Attachments) + { + char * FileName[100]; + int FileLen[100]; + int Files = 0; + int BodyLen, NewLen; + int i; + char *ptr2; + char Msg[512]; + int Len; + + ptr = MsgBytes; + + Len = sprintf(Msg, "Message has Attachments\r\r"); + QueueMsg(conn, Msg, Len); + + while(*ptr != 13) + { + ptr2 = strchr(ptr, 10); // Find CR + + if (memcmp(ptr, "Body: ", 6) == 0) + { + BodyLen = atoi(&ptr[6]); + } + + if (memcmp(ptr, "File: ", 6) == 0) + { + char * ptr1 = strchr(&ptr[6], ' '); // Find Space + + FileLen[Files] = atoi(&ptr[6]); + + FileName[Files++] = &ptr1[1]; + *(ptr2 - 1) = 0; + } + + ptr = ptr2; + ptr++; + } + + ptr += 2; // Over Blank Line and Separator + + NewLen = RemoveLF(ptr, BodyLen); + + QueueMsg(conn, ptr, NewLen); // Display Body + + ptr += BodyLen + 2; // to first file + + for (i = 0; i < Files; i++) + { + char Msg[512]; + int Len, n; + char * p = ptr; + char c; + + // Check if message is probably binary + + int BinCount = 0; + + NewLen = RemoveLF(ptr, FileLen[i]); // Removes LF agter CR but not on its own + + for (n = 0; n < NewLen; n++) + { + c = *p; + + if (c == 10) + *p = 13; + + if (c==0 || (c & 128)) + BinCount++; + + p++; + + } + + if (BinCount > NewLen/10) + { + // File is probably Binary + + Len = sprintf(Msg, "\rAttachment %s is a binary file\r", FileName[i]); + QueueMsg(conn, Msg, Len); + } + else + { + Len = sprintf(Msg, "\rAttachment %s\r\r", FileName[i]); + QueueMsg(conn, Msg, Len); + + user->Total.MsgsSent[Index] ++; + user->Total.BytesForwardedOut[Index] += NewLen; + + QueueMsg(conn, ptr, NewLen); + } + + ptr += FileLen[i]; + ptr +=2; // Over separator + } + goto sendEOM; + } + + // Remove B2 Headers (up to the File: Line) + + ptr = strstr(MsgBytes, "Body:"); + + if (ptr) + { + MsgBytes = ptr; + Length = (int)strlen(ptr); + } + } + + // Remove lf chars + + Length = RemoveLF(MsgBytes, Length); + + user->Total.MsgsSent[Index] ++; + user->Total.BytesForwardedOut[Index] += Length; + + QueueMsg(conn, MsgBytes, Length); + +sendEOM: + + free(Save); + + nodeprintf(conn, "\r\r[End of Message #%d from %s%s]\r", msgno, Msg->from, Msg->emailfrom); + + if ((_stricmp(Msg->to, user->Call) == 0) || ((conn->sysop) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + SendMessageReadEvent(user->Call, Msg); +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(Msg); +#endif + } + } + } + } + else + { + nodeprintf(conn, "File for Message %d not found\r", msgno); + } +} + struct MsgInfo * FindMessage(char * Call, int msgno, BOOL sysop) + { + int m=NumberofMessages; + + struct MsgInfo * Msg; + + do + { + m = GetUserMsg(m, Call, sysop); + + if (m == 0) + return NULL; + + Msg=MsgHddrPtr[m]; + + if (Msg->number == msgno) + return Msg; + + m--; + + } while (m> 0); + + return NULL; + +} + + +char * ReadInfoFile(char * File) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char * MsgBytes; + struct stat STAT; + char * ptr1 = 0, * ptr2; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s", BaseDir, File); + + if (stat(MsgFile, &STAT) == -1) + return NULL; + + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile == NULL) + return NULL; + + 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++; + } + + return MsgBytes; +} + +char * FormatDateAndTime(time_t Datim, BOOL DateOnly) +{ + struct tm *tm; + static char Date[]="xx-xxx hh:mmZ"; + + tm = gmtime(&Datim); + + if (tm) + sprintf_s(Date, sizeof(Date), "%02d-%3s %02d:%02dZ", + tm->tm_mday, month[tm->tm_mon], tm->tm_hour, tm->tm_min); + + if (DateOnly) + { + Date[6]=0; + return Date; + } + + return Date; +} + +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char * To, char ** ATBBS, char ** BID); + + +BOOL DoSendCommand(CIRCUIT * conn, struct UserInfo * user, char * Cmd, char * Arg1, char * Context) +{ + // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY + + char * From = NULL; + char * BID = NULL; + char * ATBBS = NULL; + char seps[] = " \t\r"; + struct MsgInfo * OldMsg; + char OldTitle[62]; + char NewTitle[62]; + char To[100]= ""; + int msgno; + + if (Cmd[1] == 0) Cmd[1] ='P'; // Just S means SP + + switch (toupper(Cmd[1])) + { + case 'B': + + if (RefuseBulls) + { + nodeprintf(conn, "*** Error: This system doesn't allow sending Bulls\r"); + return FALSE; + } + + if (user->flags & F_NOBULLS) + { + nodeprintf(conn, "*** Error: You are not allowed to send Bulls\r"); + return FALSE; + } + + + case 'P': + case 'T': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); + return FALSE; + } + + strcpy(To, Arg1); + + if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + return FALSE; + + return CreateMessage(conn, From, To, ATBBS, toupper(Cmd[1]), BID, NULL); + + case 'R': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: Message Number is missing\r"); + return FALSE; + } + + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return FALSE; + } + + OldMsg = FindMessage(user->Call, msgno, conn->sysop); + + if (OldMsg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return FALSE; + } + + Arg1=&OldMsg->from[0]; + + strcpy(To, Arg1); + + if (_stricmp(Arg1, "SMTP:") == 0 || _stricmp(Arg1, "RMS:") == 0 || OldMsg->emailfrom) + { + // SMTP message. Need to get the real sender from the message + + sprintf(To, "%s%s", Arg1, OldMsg->emailfrom); + } + + if (!DecodeSendParams(conn, "", &From, To, &ATBBS, &BID)) + return FALSE; + + strcpy(OldTitle, OldMsg->title); + + if (strlen(OldTitle) > 57) OldTitle[57] = 0; + + strcpy(NewTitle, "Re:"); + strcat(NewTitle, OldTitle); + + return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); + + return TRUE; + + case 'C': + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: Message Number is missing\r"); + return FALSE; + } + + msgno = atoi(Arg1); + + if (msgno > 100000) + { + BBSputs(conn, "Message Number too high\r"); + return FALSE; + } + + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1 == NULL) + { + nodeprintf(conn, "*** Error: The 'TO' callsign is missing\r"); + return FALSE; + } + + strcpy(To, Arg1); + + if (!DecodeSendParams(conn, Context, &From, To, &ATBBS, &BID)) + return FALSE; + + OldMsg = FindMessage(user->Call, msgno, conn->sysop); + + if (OldMsg == NULL) + { + nodeprintf(conn, "Message %d not found\r", msgno); + return FALSE; + } + + strcpy(OldTitle, OldMsg->title); + + if (strlen(OldTitle) > 56) OldTitle[56] = 0; + + strcpy(NewTitle, "Fwd:"); + strcat(NewTitle, OldTitle); + + conn->CopyBuffer = ReadMessageFile(msgno); + + return CreateMessage(conn, From, To, ATBBS, 'P', BID, NewTitle); + } + + + nodeprintf(conn, "*** Error: Invalid Send option %c\r", Cmd[1]); + + return FALSE; +} + +char * CheckToAddress(CIRCUIT * conn, char * Addr) +{ + // Check one element of Multiple Address + + if (conn == NULL || !(conn->BBSFlags & BBS)) + { + // if a normal user, check that TO and/or AT are known and warn if not. + + if (_stricmp(Addr, "SYSOP") == 0) + { + return _strdup(Addr); + } + + if (SendBBStoSYSOPCall) + if (_stricmp(Addr, BBSName) == 0) + return _strdup(SYSOPCall); + + + if (strchr(Addr, '@') == 0) + { + // No routing, if not a user and not known to forwarding or WP warn + + struct UserInfo * ToUser = LookupCall(Addr); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + char * NewAddr = malloc(250); + if (conn) + nodeprintf(conn, "Address %s - @%s added from HomeBBS\r", Addr, ToUser->HomeBBS); + sprintf(NewAddr, "%s@%s", Addr, ToUser->HomeBBS); + return NewAddr; + } + } + else + { + WPRecP WP = LookupWP(Addr); + + if (WP) + { + char * NewAddr = malloc(250); + + if (conn) + nodeprintf(conn, "Address %s - @%s added from WP\r", Addr, WP->first_homebbs); + sprintf(NewAddr, "%s@%s", Addr, WP->first_homebbs); + return NewAddr; + } + } + } + } + + // Check SMTP and RMS Addresses + + if ((_memicmp(Addr, "rms:", 4) == 0) || (_memicmp(Addr, "rms/", 4) == 0)) + { + Addr[3] = ':'; // Replace RMS/ with RMS: + + if (conn && !FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + } + else if ((_memicmp(Addr, "smtp:", 5) == 0) || (_memicmp(Addr, "smtp/", 5) == 0)) + { + Addr[4] = ':'; // Replace smpt/ with smtp: + + if (ISP_Gateway_Enabled) + { + if (conn && (conn->UserPointer->flags & F_EMAIL) == 0) + { + nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); + return FALSE; + } + } + else + { + if (conn) + nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); + return FALSE; + } + } + + return _strdup(Addr); +} + + +char Winlink[] = "WINLINK.ORG"; + +BOOL DecodeSendParams(CIRCUIT * conn, char * Context, char ** From, char *To, char ** ATBBS, char ** BID) +{ + char * ptr; + char seps[] = " \t\r"; + WPRecP WP; + char * ToCopy = _strdup(To); + int Len; + + conn->ToCount = 0; + + // SB WANT @ ALLCAN < N6ZFJ $4567_N0ARY + + // Having trailing ; will mess up parsing multiple addresses, so remove. + + while (To[strlen(To) - 1] == ';') + To[strlen(To) - 1] = 0; + + if (strchr(Context, ';') || strchr(To, ';')) + { + // Multiple Addresses - put address list back together + + char * p; + + To[strlen(To)] = ' '; + Context = To; + + while (p = strchr(Context, ';')) + { + // Multiple Addressees + + To = strtok_s(NULL, ";", &Context); + Len = (int)strlen(To); + conn->To = realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); + if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) + conn->ToCount++; + } + + To = strtok_s(NULL, seps, &Context); + + Len = (int)strlen(To); + conn->To=realloc(conn->To, (conn->ToCount+1) * sizeof(void *)); + if (conn->To[conn->ToCount] = CheckToAddress(conn, To)) + conn->ToCount++; + } + else + { + // Single Call + + // accept CALL!CALL for source routed message + + if (strchr(To, '@') == 0 && strchr(To, '!')) // Bang route without @ + { + char * bang = strchr(To, '!'); + + memmove(bang + 1, bang, strlen(bang)); // Move !call down one + + *ATBBS = strlop(To, '!');; + } + + // Accept call@call (without spaces) - but check for smtp addresses + + if (_memicmp(To, "smtp:", 5) != 0 && _memicmp(To, "rms:", 4) != 0 && _memicmp(To, "rms/", 4) != 0) + { + ptr = strchr(To, '@'); + + if (ptr) + { + // If looks like a valid email address, treat as such + + int tolen; + *ATBBS = strlop(To, '@'); + + strlop(To, '-'); // Cant have SSID on BBS Name + + tolen = (int)strlen(To); + + if (tolen > 6 || !CheckifPacket(*ATBBS)) + { + // Probably Email address. Add smtp: or rms: + + if (FindRMS() || strchr(*ATBBS, '!')) // have RMS or source route + sprintf(To, "rms:%s", ToCopy); + else if (ISP_Gateway_Enabled) + sprintf(To, "smtp:%s", ToCopy); + else if (isAMPRMsg(ToCopy)) + sprintf(To, "rms:%s", ToCopy); + + } + } + } + } + + free(ToCopy); + + // Look for Optional fields; + + ptr = strtok_s(NULL, seps, &Context); + + while (ptr) + { + if (strcmp(ptr, "@") == 0) + { + *ATBBS = _strupr(strtok_s(NULL, seps, &Context)); + } + else if(strcmp(ptr, "<") == 0) + { + *From = strtok_s(NULL, seps, &Context); + } + else if (ptr[0] == '$') + *BID = &ptr[1]; + else + { + nodeprintf(conn, "*** Error: Invalid Format\r"); + return FALSE; + } + ptr = strtok_s(NULL, seps, &Context); + } + + // Only allow < from a BBS + + if (*From) + { + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "*** < can only be used by a BBS\r"); + return FALSE; + } + } + + if (!*From) + *From = conn->UserPointer->Call; + + if (!(conn->BBSFlags & BBS)) + { + // if a normal user, check that TO and/or AT are known and warn if not. + + if (_stricmp(To, "SYSOP") == 0) + { + conn->LocalMsg = TRUE; + return TRUE; + } + + if (!*ATBBS && conn->ToCount == 0) + { + // No routing, if not a user and not known to forwarding or WP warn + + struct UserInfo * ToUser = LookupCall(To); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->flags & F_RMSREDIRECT) + { + // sent to Winlink + + *ATBBS = Winlink; + nodeprintf(conn, "Redirecting to winlink.org\r", *ATBBS); + } + else if (ToUser->HomeBBS[0]) + { + *ATBBS = ToUser->HomeBBS; + nodeprintf(conn, "Address @%s added from HomeBBS\r", *ATBBS); + } + else + { + conn->LocalMsg = TRUE; + } + } + else + { + conn->LocalMsg = FALSE; + WP = LookupWP(To); + + if (WP) + { + *ATBBS = WP->first_homebbs; + nodeprintf(conn, "Address @%s added from WP\r", *ATBBS); + } + } + } + } + return TRUE; +} + +BOOL CreateMessage(CIRCUIT * conn, char * From, char * ToCall, char * ATBBS, char MsgType, char * BID, char * Title) +{ + struct MsgInfo * Msg, * TestMsg; + char * via = NULL; + char * FromHA; + + // Create a temp msg header entry + + if (conn->ToCount) + { + } + else + { + if (CheckRejFilters(From, ToCall, ATBBS, BID, MsgType, 0)) + { + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - REJECTED\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error - Message Filters prevent sending this message\r"); + + return FALSE; + } + } + + Msg = malloc(sizeof (struct MsgInfo)); + + if (Msg == 0) + { + CriticalErrorHandler("malloc failed for new message header"); + return FALSE; + } + + memset(Msg, 0, sizeof (struct MsgInfo)); + + conn->TempMsg = Msg; + + Msg->type = MsgType; + + if (conn->UserPointer->flags & F_HOLDMAIL) + Msg->status = 'H'; + else + Msg->status = 'N'; + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + if (BID) + { + BIDRec * TempBID; + + // If P Message, dont immediately reject on a Duplicate BID. Check if we still have the message + // If we do, reject it. If not, accept it again. (do we need some loop protection ???) + + TempBID = LookupBID(BID); + + if (TempBID) + { + if (MsgType == 'B') + { + // Duplicate bid + + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - BID\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error- Duplicate BID\r"); + + return FALSE; + } + + TestMsg = GetMsgFromNumber(TempBID->u.msgno); + + // if the same TO we will assume the same message + + if (TestMsg && strcmp(TestMsg->to, ToCall) == 0) + { + // We have this message. If we have already forwarded it, we should accept it again + + if ((TestMsg->status == 'N') || (TestMsg->status == 'Y')|| (TestMsg->status == 'H')) + { + // Duplicate bid + + if ((conn->BBSFlags & BBS)) + { + nodeprintf(conn, "NO - BID\r"); + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + else + nodeprintf(conn, "*** Error- Duplicate BID\r"); + + return FALSE; + } + } + } + + if (strlen(BID) > 12) BID[12] = 0; + strcpy(Msg->bid, BID); + + // Save BID in temp list in case we are offered it again before completion + + TempBID = AllocateTempBIDRecord(); + strcpy(TempBID->BID, BID); + TempBID->u.conn = conn; + + } + + if (conn->ToCount) + { + } + else + { + if (_memicmp(ToCall, "rms:", 4) == 0) + { + // Could be ampr.org message + + if (!isAMPRMsg(ToCall)) + { + if (!FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + } + via=strlop(ToCall, ':'); + _strupr(ToCall); + + } + else if (_memicmp(ToCall, "rms/", 4) == 0) + { + if (!FindRMS()) + { + nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r"); + return FALSE; + } + + via=strlop(ToCall, '/'); + _strupr(ToCall); + } + else if (_memicmp(ToCall, "smtp:", 5) == 0) + { + if (ISP_Gateway_Enabled) + { + if ((conn->UserPointer->flags & F_EMAIL) == 0) + { + nodeprintf(conn, "*** Error - You need to ask the SYSOP to allow you to use Internet Mail\r"); + return FALSE; + } + via=strlop(ToCall, ':'); + ToCall[0] = 0; + } + else + { + nodeprintf(conn, "*** Error - Sending mail to smtp addresses is disabled\r"); + return FALSE; + } + } + else + { + _strupr(ToCall); + if (ATBBS) + via=_strupr(ATBBS); + } + + strlop(ToCall, '-'); // Remove any (illegal) ssid + if (strlen(ToCall) > 6) ToCall[6] = 0; + + strcpy(Msg->to, ToCall); + + if (SendBBStoSYSOPCall) + if (_stricmp(ToCall, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if (via) + { + if (strlen(via) > 40) via[40] = 0; + + strcpy(Msg->via, via); + } + + } // End of Multiple Dests + + // Look for HA in From (even if we shouldn't be getting it!) + + FromHA = strlop(From, '@'); + + + strlop(From, '-'); // Remove any (illegal) ssid + if (strlen(From) > 6) From[6] = 0; + strcpy(Msg->from, From); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + + if (Title) // Only used by SR and SC + { + strcpy(Msg->title, Title); + conn->Flags |= GETTINGMESSAGE; + + // Create initial buffer of 10K. Expand if needed later + + conn->MailBuffer=malloc(10000); + conn->MailBufferSize=10000; + + nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); + return TRUE; + } + + if (conn->BBSFlags & FLARQMODE) + return TRUE; + + if (!(conn->BBSFlags & FBBCompressed)) + conn->Flags |= GETTINGTITLE; + + if (!(conn->BBSFlags & BBS)) + nodeprintf(conn, "Enter Title (only):\r"); + else + if (!(conn->BBSFlags & FBBForwarding)) + nodeprintf(conn, "OK\r"); + + return TRUE; +} + +VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer, int msglen) +{ + + conn->Flags &= ~GETTINGTITLE; + + if (msglen == 1) + { + nodeprintf(conn, "*** Message Cancelled\r"); + SendPrompt(conn, user); + return; + } + + if (msglen > 60) msglen = 60; + + Buffer[msglen-1] = 0; + + strcpy(conn->TempMsg->title, Buffer); + + // Create initial buffer of 10K. Expand if needed later + + conn->MailBuffer=malloc(10000); + conn->MailBufferSize=10000; + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to create Message Buffer\r"); + return; + } + + conn->Flags |= GETTINGMESSAGE; + + if (!conn->BBSFlags & BBS) + nodeprintf(conn, "Enter Message Text (end with /ex or ctrl/z)\r"); + +} + +VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int msglen) +{ + char * ptr2 = NULL; + + if (((msglen < 3) && (Buffer[0] == 0x1a)) || ((msglen == 4) && (_memicmp(Buffer, "/ex", 3) == 0))) + { + int Index = 0; + + if (conn->TempMsg->type == 'P') + Index = PMSG; + else if (conn->TempMsg->type == 'B') + Index = BMSG; + else if (conn->TempMsg->type == 'T') + Index = TMSG; + + conn->Flags &= ~GETTINGMESSAGE; + + user->Total.MsgsReceived[Index]++; + user->Total.BytesForwardedIn[Index] += conn->TempMsg->length; + + if (conn->ToCount) + { + // Multiple recipients + + struct MsgInfo * Msg = conn->TempMsg; + int i; + struct MsgInfo * SaveMsg = Msg; + char * SaveBody = conn->MailBuffer; + int SaveMsgLen = Msg->length; + BOOL SentToRMS = FALSE; + int ToLen = 0; + char * ToString = zalloc(conn->ToCount * 100); + + // If no BID provided, allocate one + + if (Msg->bid[0] == 0) + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg + 1, BBSName); + + for (i = 0; i < conn->ToCount; i++) + { + char * Addr = conn->To[i]; + char * Via; + + if (_memicmp (Addr, "SMTP:", 5) == 0) + { + // For Email + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 10); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + Msg->to[0] = 0; + strcpy(Msg->via, &Addr[5]); + + CreateMessageFromBuffer(conn); + continue; + } + + if (_memicmp (Addr, "RMS:", 4) == 0) + { + // Add to B2 Message for RMS + + Addr+=4; + + Via = strlop(Addr, '@'); + + if (Via && _stricmp(Via, "winlink.org") == 0) + { + if (CheckifLocalRMSUser(Addr)) + { + // Local RMS - Leave Here + + Via = 0; // Drop Through + goto PktMsg; + } + else + { + ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr); + continue; + } + } + + ToLen = sprintf(ToString, "%sTo: %s@%s\r\n", ToString, Addr, Via); + continue; + } + + _strupr(Addr); + + Via = strlop(Addr, '@'); + + if (Via && _stricmp(Via, "winlink.org") == 0) + { + if (CheckifLocalRMSUser(Addr)) + { + // Local RMS - Leave Here + + Via = 0; // Drop Through + } + else + { + ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr); + + // Add to B2 Message for RMS + + continue; + } + } + + PktMsg: + + conn->LocalMsg = FALSE; + + // Normal BBS Message + + if (_stricmp(Addr, "SYSOP") == 0) + conn->LocalMsg = TRUE; + else + { + struct UserInfo * ToUser = LookupCall(Addr); + + if (ToUser) + conn->LocalMsg = TRUE; + } + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 10); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + strcpy(Msg->to, Addr); + + if (Via) + { + Msg->bid[0] = 0; // if we are forwarding it, we must change BID to be safe + strcpy(Msg->via, Via); + } + + CreateMessageFromBuffer(conn); + } + + if (ToLen) + { + char * B2Hddr = zalloc(ToLen + 1000); + int B2HddrLen; + char DateString[80]; + struct tm * tm; + time_t Date = time(NULL); + char Type[16] = "Private"; + + // Get Type + + if (conn->TempMsg->type == 'B') + strcpy(Type, "Bulletin"); + else if (conn->TempMsg->type == 'T') + strcpy(Type, "Traffic"); + + tm = gmtime(&Date); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000 + ToLen); + + Msg->B2Flags = B2Msg; + + B2HddrLen = sprintf(B2Hddr, + "MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\n%sSubject: %s\r\nMbo: %s\r\nBody: %d\r\n\r\n", + SaveMsg->bid, DateString, Type, + SaveMsg->from, ToString, SaveMsg->title, BBSName, SaveMsgLen); + + memcpy(conn->MailBuffer, B2Hddr, B2HddrLen); + memcpy(&conn->MailBuffer[B2HddrLen], SaveBody, SaveMsgLen); + + Msg->length += B2HddrLen; + + strcpy(Msg->to, "RMS"); + + CreateMessageFromBuffer(conn); + + free(B2Hddr); + } + + free(SaveMsg); + free(SaveBody); + conn->MailBuffer = NULL; + conn->MailBufferSize=0; + + if (!(conn->BBSFlags & BBS)) + SendPrompt(conn, conn->UserPointer); + else + if (!(conn->BBSFlags & FBBForwarding)) + { + if (conn->BBSFlags & OUTWARDCONNECT) + BBSputs(conn, "F>\r"); // if Outward connect must be reverse forward + else + BBSputs(conn, ">\r"); + } + + /* + // From a client - Create one copy with all RMS recipients, and another for each packet recipient + + // Merge all RMS To: lines + + ToLen = 0; + ToString[0] = 0; + + for (i = 0; i < Recipients; i++) + { + if (LocalMsg[i]) + continue; // For a local RMS user + + if (_stricmp(Via[i], "WINLINK.ORG") == 0 || _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + { + ToLen += strlen(HddrTo[i]); + strcat(ToString, HddrTo[i]); + } + } + + if (ToLen) + { + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], ToString, ToLen); + + conn->TempMsg->length += ToLen; + + strcpy(Msg->to, "RMS"); + strcpy(Msg->via, "winlink.org"); + + // Must Change the BID + + Msg->bid[0] = 0; + + CreateMessageFromBuffer(conn); + } + + } + + free(ToString); + + for (i = 0; i < Recipients; i++) + { + // Only Process Non - RMS Dests or local RMS Users + + if (LocalMsg[i] == 0) + if (_stricmp (Via[i], "WINLINK.ORG") == 0 || + _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + continue; + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + // Add our To: + + ToLen = strlen(HddrTo[i]); + + if (_memicmp(HddrTo[i], "CC", 2) == 0) // Replace CC: with TO: + memcpy(HddrTo[i], "To", 2); + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], HddrTo[i], ToLen); + + conn->TempMsg->length += ToLen; + + strcpy(Msg->to, RecpTo[i]); + strcpy(Msg->via, Via[i]); + + Msg->bid[0] = 0; + + CreateMessageFromBuffer(conn); + } + } // End not from RMS + + free(SaveMsg); + free(SaveBody); + conn->MailBuffer = NULL; + conn->MailBufferSize=0; + + SetupNextFBBMessage(conn); + return; + + } My__except_Routine("Process Multiple Destinations"); + + BBSputs(conn, "*** Program Error Processing Multiple Destinations\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + + return; +*/ + + conn->ToCount = 0; + + return; + } + + + CreateMessageFromBuffer(conn); + return; + + } + + Buffer[msglen++] = 0x0a; + + if ((conn->TempMsg->length + msglen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to extend Message Buffer\r"); + + conn->Flags &= ~GETTINGMESSAGE; + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, msglen); + + conn->TempMsg->length += msglen; +} + +VOID CreateMessageFromBuffer(CIRCUIT * conn) +{ + struct MsgInfo * Msg; + BIDRec * BIDRec; + char * ptr1, * ptr2 = NULL; + char * ptr3, * ptr4; + int FWDCount = 0; + char OldMess[] = "\r\n\r\nOriginal Message:\r\n\r\n"; + time_t Age; + int OurCount; + char * HoldReason = "User has Hold Messages flag set"; + struct UserInfo * user; + + +#ifndef LINBPQ + struct _EXCEPTION_POINTERS exinfo; +#endif + + // If doing SC, Append Old Message + + if (conn->CopyBuffer) + { + if ((conn->TempMsg->length + (int) strlen(conn->CopyBuffer) + 80 )> conn->MailBufferSize) + { + conn->MailBufferSize += (int)strlen(conn->CopyBuffer) + 80; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + nodeprintf(conn, "Failed to extend Message Buffer\r"); + + conn->Flags &= ~GETTINGMESSAGE; + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], OldMess, strlen(OldMess)); + + conn->TempMsg->length += (int)strlen(OldMess); + + memcpy(&conn->MailBuffer[conn->TempMsg->length], conn->CopyBuffer, strlen(conn->CopyBuffer)); + + conn->TempMsg->length += (int)strlen(conn->CopyBuffer); + + free(conn->CopyBuffer); + conn->CopyBuffer = NULL; + } + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + memcpy(Msg, conn->TempMsg, sizeof(struct MsgInfo)); + + free(conn->TempMsg); + + // Set number here so they remain in sequence + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + FreeSemaphore(&MsgNoSemaphore); + MsgnotoMsg[Msg->number] = Msg; + + if (Msg->status == 0) + Msg->status = 'N'; + + // Create BID if non supplied + + if (Msg->bid[0] == 0) + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + // if message body had R: lines, get date created from last (not very accurate, but best we can do) + + // Also check if we have had message before to detect loops + + ptr1 = conn->MailBuffer; + OurCount = 0; + + // If it is a B2 Message, Must Skip B2 Header + + if (Msg->B2Flags & B2Msg) + { + ptr1 = strstr(ptr1, "\r\n\r\n"); + if (ptr1) + ptr1 += 4; + else + ptr1 = conn->MailBuffer; + } + +nextline: + + if (memcmp(ptr1, "R:", 2) == 0) + { + // Is if ours? + + // BPQ RLINE Format R:090920/1041Z 6542@N4JOA.#WPBFL.FL.USA.NOAM BPQ1.0.2 + + ptr3 = strchr(ptr1, '@'); + ptr4 = strchr(ptr1, '.'); + + if (ptr3 && ptr4 && (ptr4 > ptr3)) + { + if (memcmp(ptr3+1, BBSName, ptr4-ptr3-1) == 0) + OurCount++; + } + + GetWPBBSInfo(ptr1); // Create WP /I record from R: Line + + // see if another + + ptr2 = ptr1; // save + ptr1 = strchr(ptr1, '\r'); + if (ptr1 == 0) + { + Debugprintf("Corrupt Message %s from %s - truncated within R: line", Msg->bid, Msg->from); + return; + } + ptr1++; + if (*ptr1 == '\n') ptr1++; + + goto nextline; + } + + // ptr2 points to last R: line (if any) + + if (ptr2) + { + struct tm rtime; + time_t result; + + memset(&rtime, 0, sizeof(struct tm)); + + if (ptr2[10] == '/') + { + // Dodgy 4 char year + + sscanf(&ptr2[2], "%04d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + rtime.tm_year -= 1900; + rtime.tm_mon--; + } + else if (ptr2[8] == '/') + { + sscanf(&ptr2[2], "%02d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + + if (rtime.tm_year < 90) + rtime.tm_year += 100; // Range 1990-2089 + rtime.tm_mon--; + } + + // Otherwise leave date as zero, which should be rejected + + // result = _mkgmtime(&rtime); + + if ((result = mktime(&rtime)) != (time_t)-1 ) + { + result -= (time_t)_MYTIMEZONE; + + Msg->datecreated = result; + Age = (time(NULL) - result)/86400; + + if ( Age < -7) + { + Msg->status = 'H'; + HoldReason = "Suspect Date Sent"; + } + else if (Age > BidLifetime || Age > MaxAge) + { + Msg->status = 'H'; + HoldReason = "Message too old"; + + } + else + GetWPInfoFromRLine(Msg->from, ptr2, result); + } + else + { + // Can't decode R: Datestamp + + Msg->status = 'H'; + HoldReason = "Corrupt R: Line - can't determine age"; + } + + if (OurCount > 1) + { + // Message is looping + + Msg->status = 'H'; + HoldReason = "Message may be looping"; + + } + } + + if (strcmp(Msg->to, "WP") == 0) + { + // If Reject WP Bulls is set, Kill message here. + // It should only get here if B2 - otherwise it should be + // rejected earlier + + if (Msg->type == 'B' && FilterWPBulls) + Msg->status = 'K'; + + } + + conn->MailBuffer[Msg->length] = 0; + + if (CheckBadWords(Msg->title) || CheckBadWords(conn->MailBuffer)) + { + Msg->status = 'H'; + HoldReason = "Bad word in title or body"; + } + + if (CheckHoldFilters(Msg, Msg->from, Msg->to, Msg->via, Msg->bid)) + { + Msg->status = 'H'; + HoldReason = "Matched Hold Filters"; + } + + if (CheckValidCall(Msg->from) == 0) + { + Msg->status = 'H'; + HoldReason = "Probable Invalid From Call"; + } + + // Process any WP Messages + + if (strcmp(Msg->to, "WP") == 0) + { + if (Msg->status == 'N') + { + ProcessWPMsg(conn->MailBuffer, Msg->length, ptr2); + + if (Msg->type == 'P') // Kill any processed private WP messages. + { + char VIA[80]; + + strcpy(VIA, Msg->via); + strlop(VIA, '.'); + + if (strcmp(VIA, BBSName) == 0) + Msg->status = 'K'; + } + } + } + + CreateMessageFile(conn, Msg); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + if (Msg->length > MaxTXSize) + { + Msg->status = 'H'; + HoldReason = "Message too long"; + + if (!(conn->BBSFlags & BBS)) + nodeprintf(conn, "*** Warning Message length exceeds sysop-defined maximum of %d - Message will be held\r", MaxTXSize); + } + + // Check for message to internal server + + if (Msg->via[0] == 0 + || _stricmp(Msg->via, BBSName) == 0 // our BBS a + || _stricmp(Msg->via, AMPRDomain) == 0) // our AMPR Address + { + if (CheckforMessagetoServer(Msg)) + { + // Flag as killed and send prompt + + FlagAsKilled(Msg, TRUE); + + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "Message %d to Server Processed and Killed.\r", Msg->number); + SendPrompt(conn, conn->UserPointer); + } + return; // no need to process further + } + } + + if (Msg->to[0]) + FWDCount = MatchMessagetoBBSList(Msg, conn); + else + { + // If addressed @winlink.org, and to a local user, Keep here. + + char * Call; + char * AT; + + // smtp or rms - don't warn no route + + FWDCount = 1; + + Call = _strupr(_strdup(Msg->via)); + AT = strlop(Call, '@'); + + if (AT && _stricmp(AT, "WINLINK.ORG") == 0) + { + struct UserInfo * user = LookupCall(Call); + + if (user) + { + if (user->flags & F_POLLRMS) + { + Logprintf(LOG_BBS, conn, '?', "SMTP Message @ winlink.org, but local RMS user - leave here"); + strcpy(Msg->to, Call); + strcpy(Msg->via, AT); + if (user->flags & F_BBS) // User is a BBS, so set FWD bit so he can get it + set_fwd_bit(Msg->fbbs, user->BBSNumber); + + } + } + } + free(Call); + } + + // Warn SYSOP if P or T forwarded in, and has nowhere to go + + if ((conn->BBSFlags & BBS) && Msg->type != 'B' && FWDCount == 0 && WarnNoRoute && + strcmp(Msg->to, "SYSOP") && strcmp(Msg->to, "WP")) + { + if (Msg->via[0]) + { + if (_stricmp(Msg->via, BBSName)) // Not for our BBS a + if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address + SendWarningToSYSOP(Msg); + } + else + { + // No via - is it for a local user? + + if (LookupCall(Msg->to) == 0) + SendWarningToSYSOP(Msg); + } + } + + if ((conn->BBSFlags & SYNCMODE) == 0) + { + if (!(conn->BBSFlags & BBS)) + { + nodeprintf(conn, "Message: %d Bid: %s Size: %d\r", Msg->number, Msg->bid, Msg->length); + + if (Msg->via[0]) + { + if (_stricmp(Msg->via, BBSName)) // Not for our BBS a + if (_stricmp(Msg->via, AMPRDomain)) // Not for our AMPR Address + + if (FWDCount == 0 && Msg->to[0] != 0) // unless smtp msg + nodeprintf(conn, "@BBS specified, but no forwarding info is available - msg may not be delivered\r"); + } + else + { + if (FWDCount == 0 && conn->LocalMsg == 0 && Msg->type != 'B') + // Not Local and no forward route + nodeprintf(conn, "Message is not for a local user, and no forwarding info is available - msg may not be delivered\r"); + } + if (conn->ToCount == 0) + SendPrompt(conn, conn->UserPointer); + } + else + { + if (!(conn->BBSFlags & FBBForwarding)) + { + if (conn->ToCount == 0) + if (conn->BBSFlags & OUTWARDCONNECT) + nodeprintf(conn, "F>\r"); // if Outward connect must be reverse forward + else + nodeprintf(conn, ">\r"); + } + } + } + + if(Msg->to[0] == 0) + SMTPMsgCreated=TRUE; + + if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + + if (Msg->status == 'H') + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, HoldReason); + SendMessageToSYSOP(Title, MailBuffer, Length); + } + + BuildNNTPList(Msg); // Build NNTP Groups list + + SaveMessageDatabase(); + SaveBIDDatabase(); + + // If Event Notifications enabled report a new message event + + user = LookupCall(Msg->to); + + SendNewMessageEvent(user->Call, Msg); +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(Msg); +#endif + + if (EnableUI) +#ifdef LINBPQ + SendMsgUI(Msg); +#else + __try + { + SendMsgUI(Msg); + } + My__except_Routine("SendMsgUI"); +#endif + + if (user && (user->flags & F_APRSMFOR)) + { + char APRS[128]; + char Call[16]; + int SSID = user->flags >> 28; + + if (SSID) + sprintf(Call, "%s-%d", Msg->to, SSID); + else + strcpy(Call, Msg->to); + + sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from); + APISendAPRSMessage(APRS, Call); + } + + return; +} + +VOID CreateMessageFile(ConnectionInfo * conn, struct MsgInfo * Msg) +{ + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + char Mess[255]; + int len; + BOOL AutoImport = FALSE; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + // If title is "Batched messages for AutoImport from BBS xxxxxx and first line is S? and it is + // for this BBS, Import file and set message as Killed. May need to strip B2 Header and R: lines + + if (strcmp(Msg->to, BBSName) == 0 && strstr(Msg->title, "Batched messages for AutoImport from BBS ")) + { + UCHAR * ptr = conn->MailBuffer; + + // If it is a B2 Message, Must Skip B2 Header + + if (Msg->B2Flags & B2Msg) + { + ptr = strstr(ptr, "\r\n\r\n"); + if (ptr) + ptr += 4; + else + ptr = conn->MailBuffer; + } + + if (memcmp(ptr, "R:", 2) == 0) + { + ptr = strstr(ptr, "\r\n\r\n"); //And remove R: Lines + if (ptr) + ptr += 4; + } + + if (*(ptr) == 'S' && ptr[2] == ' ') + { + int HeaderLen = (int)(ptr - conn->MailBuffer); + Msg->length -= HeaderLen; + memmove(conn->MailBuffer, ptr, Msg->length); + Msg->status = 'K'; + AutoImport = TRUE; + } + } + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(conn->MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + if (AutoImport) + ImportMessages(NULL, MsgFile, TRUE); + + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + if (WriteLen != Msg->length) + { + len = sprintf_s(Mess, sizeof(Mess), "Failed to create Message File\r"); + QueueMsg(conn, Mess, len); + Debugprintf(Mess); + } + return; +} + + +VOID SendUnbuffered(int stream, char * msg, int len) +{ +#ifndef LINBPQ + if (stream < 0) + WritetoConsoleWindow(stream, msg, len); + else +#endif + SendMsg(stream, msg, len); +} + +BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen); + +BOOL FindMessagestoForward (CIRCUIT * conn) +{ + struct UserInfo * user = conn->UserPointer; + +#ifndef LINBPQ + + struct _EXCEPTION_POINTERS exinfo; + + __try { +#endif + + // This is a hack so Winpack or WLE users can use forwarding + // protocols to get their messages without being defined as a BBS + + // !!IMPORTANT Getting this wrong can see message repeatedly proposed !! + // !! Anything sent using this must be killed if sent or rejected. + + // I'm not sure about this. I think I only need the PacLinkCalls + // if from RMS Express, and it always sends an FW; line + // Ah, no. What about WinPack ?? + // If from RMS Express must have Temp_B2 or BBS set or SID will + // be rejected. So maybe just Temp_B2 && BBS = 0?? + // No, someone may have Temp_B2 set and not be using WLE ?? So what ?? + + if ((user->flags & F_Temp_B2_BBS) && ((user->flags & F_BBS) == 0) || conn->RMSExpress || conn->PAT) + { + if (conn->PacLinkCalls == NULL) + { + // create a list with just the user call + + char * ptr1; + + conn->PacLinkCalls = zalloc(30); + + ptr1 = (char *)conn->PacLinkCalls; + ptr1 += 16; // Must be room for a null pointer on end (64 bit bug) + strcpy(ptr1, user->Call); + + conn->PacLinkCalls[0] = ptr1; + } + } + + if (conn->SendT && FindMessagestoForwardLoop(conn, 'T', conn->MaxTLen)) + { + conn->LastForwardType = 'T'; + return TRUE; + } + + if (conn->LastForwardType == 'T') + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (conn->SendP && FindMessagestoForwardLoop(conn, 'P', conn->MaxPLen)) + { + conn->LastForwardType = 'P'; + return TRUE; + } + + if (conn->LastForwardType == 'P') + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (conn->SendB && FindMessagestoForwardLoop(conn, 'B', conn->MaxBLen)) + { + conn->LastForwardType = 'B'; + return TRUE; + } + + conn->LastForwardType = 0; + return FALSE; +#ifndef LINBPQ + } My__except_Routine("FindMessagestoForward"); +#endif + return FALSE; + +} + + +BOOL FindMessagestoForwardLoop(CIRCUIT * conn, char Type, int MaxLen) +{ + // See if any messages are queued for this BBS + + int m; + struct MsgInfo * Msg; + struct UserInfo * user = conn->UserPointer; + struct FBBHeaderLine * FBBHeader; + BOOL Found = FALSE; + char RLine[100]; + int TotalSize = 0; + time_t NOW = time(NULL); + +// Debugprintf("FMTF entered Call %s Type %c Maxlen %d NextMsg = %d BBSNo = %d", +// conn->Callsign, Type, MaxLen, conn->NextMessagetoForward, user->BBSNumber); + + if (conn->PacLinkCalls || (conn->UserPointer->flags & F_NTSMPS)) // Looking for all messages, so reset + conn->NextMessagetoForward = 1; + + conn->FBBIndex = 0; + + for (m = conn->NextMessagetoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + // If an NTS MPS, see if anything matches + + if (Type == 'T' && (conn->UserPointer->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + int depth; + + if (Msg->type == 'T' && Msg->status == 'N' && Msg->length <= MaxLen && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + goto Forwardit; + + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + goto Forwardit; + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + goto Forwardit; + } + } + + // If forwarding to Paclink or RMS Express, look for any message matching the + // requested call list with status 'N' (maybe should also be 'P' ??) + + if (conn->PacLinkCalls) + { + int index = 1; + + char * Call = conn->PacLinkCalls[0]; + + while (Call) + { + if (Msg->type == Type && Msg->status == 'N') + { +// Debugprintf("Comparing RMS Call %s %s", Msg->to, Call); + if (_stricmp(Msg->to, Call) == 0) + if (Msg->status == 'N' && Msg->type == Type && Msg->length <= MaxLen) + goto Forwardit; + else + Debugprintf("Call Match but Wrong Type/Len %c %d", Msg->type, Msg->length); + } + Call = conn->PacLinkCalls[index++]; + } +// continue; + } + + if (Msg->type == Type && Msg->length <= MaxLen && (Msg->status != 'H') + && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + // Message to be sent - do a consistancy check (State, etc) + + Forwardit: + + if (Msg->Defered > 0) // = response received + { + Msg->Defered--; + Debugprintf("Message %d deferred", Msg->number); + continue; + } + + if ((Msg->from[0] == 0) || (Msg->to[0] == 0)) + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, "Missing From: or To: field"); + SendMessageToSYSOP(Title, MailBuffer, Length); + + Msg->status = 'H'; + continue; + } + + conn->NextMessagetoForward = m + 1; // So we don't offer again if defered + + Msg->Locked = 1; // So other MPS can't pick it up + + // if FBB forwarding add to list, eise save pointer + + if (conn->BBSFlags & FBBForwarding) + { + struct tm *tm; + time_t temp; + + FBBHeader = &conn->FBBHeaders[conn->FBBIndex++]; + + FBBHeader->FwdMsg = Msg; + FBBHeader->MsgType = Msg->type; + FBBHeader->Size = Msg->length; + TotalSize += Msg->length; + strcpy(FBBHeader->From, Msg->from); + strcpy(FBBHeader->To, Msg->to); + strcpy(FBBHeader->ATBBS, Msg->via); + strcpy(FBBHeader->BID, Msg->bid); + + // Set up R:Line, so se can add its length to the sise + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + FBBHeader->Size += sprintf_s(RLine, sizeof(RLine),"R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + Msg->number, BBSName, HRoute, RlineVer); + + // If using B2 forwarding we need the message size and Compressed size for FC proposal + + if (conn->BBSFlags & FBBB2Mode) + { + if (CreateB2Message(conn, FBBHeader, RLine) == FALSE) + { + char * MailBuffer = malloc(100); + char Title[100]; + int Length; + + // Corrupt B2 Message + + Debugprintf("Corrupt B2 Message found - Message %d will be held", Msg->number); + Msg->status = 'H'; + SaveMessageDatabase(); + + conn->FBBIndex--; + TotalSize -= Msg->length; + memset(&conn->FBBHeaders[conn->FBBIndex], 0, sizeof(struct FBBHeaderLine)); + + Length = sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, "Corrupt B2 Message"); + SendMessageToSYSOP(Title, MailBuffer, Length); + + continue; + } + } + + if (conn->FBBIndex == 5 || TotalSize > user->ForwardingInfo->MaxFBBBlockSize) + return TRUE; // Got max number or too big + + Found = TRUE; // Remember we have some + } + else + { + conn->FwdMsg = Msg; + return TRUE; + } + } + } + + return Found; +} + +BOOL SeeifMessagestoForward (int BBSNumber, CIRCUIT * conn) +{ + // See if any messages are queued for this BBS + + // if Conn is not NULL, also check Msg Type + + int m; + struct MsgInfo * Msg; + + for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + if (conn) + { + char Type = Msg->type; + + if ((conn->SendB && Type == 'B') || (conn->SendP && Type == 'P') || (conn->SendT && Type == 'T')) + { +// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); + return TRUE; + } + } + else + { +// Debugprintf("SeeifMessagestoForward BBSNo %d Msg %d", BBSNumber, Msg->number); + return TRUE; + } + } + } + + return FALSE; +} + +int CountMessagestoForward (struct UserInfo * user) +{ + // See if any messages are queued for this BBS + + int m, n=0; + struct MsgInfo * Msg; + int BBSNumber = user->BBSNumber; + int FirstMessage = FirstMessageIndextoForward; + + if ((user->flags & F_NTSMPS)) + FirstMessage = 1; + + for (m = FirstMessage; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + n++; + continue; // So we dont count twice if Flag set and NTS MPS + } + + // if an NTS MPS, also check for any matches + + if (Msg->type == 'T' && (user->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + int depth; + + if (Msg->status == 'N' && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + { + n++; + continue; + } + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + { + n++; + continue; + } + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + { + n++; + continue; + } + } + } + } + + return n; +} + +int CountBytestoForward (struct UserInfo * user) +{ + // See if any messages are queued for this BBS. If so return total bytes queued + + int m, n=0; + struct MsgInfo * Msg; + int BBSNumber = user->BBSNumber; + int FirstMessage = FirstMessageIndextoForward; + + if ((user->flags & F_NTSMPS)) + FirstMessage = 1; + + for (m = FirstMessage; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + n += Msg->length; + continue; // So we dont count twice if Flag set and NTS MPS + } + + // if an NTS MPS, also check for any matches + + if (Msg->type == 'T' && (user->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + int depth; + + if (Msg->status == 'N' && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + { + n += Msg->length; + continue; + } + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + { + n += Msg->length; + continue; + } + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + { + n += Msg->length; + continue; + } + } + } + } + + return n; +} + +int ListMessagestoForward(CIRCUIT * conn, struct UserInfo * user) +{ + // See if any messages are queued for this BBS + + int m, n=0; + struct MsgInfo * Msg; + int BBSNumber = user->BBSNumber; + int FirstMessage = FirstMessageIndextoForward; + + if ((user->flags & F_NTSMPS)) + FirstMessage = 1; + + for (m = FirstMessage; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber)) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; // So we dont count twice in Flag set and NTS MPS + } + + // if an NTS MPS, also check for any matches + + if (Msg->type == 'T' && (user->flags & F_NTSMPS)) + { + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + int depth; + + if (Msg->status == 'N' && ForwardingInfo) + { + depth = CheckBBSToForNTS(Msg, ForwardingInfo); + + if (depth > -1 && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via); + + if (depth && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + + depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via); + + if (depth > -1 && Msg->Locked == 0) + { + nodeprintf(conn, "%d %s\r", Msg->number, Msg->title); + continue; + } + } + } + } + + return n; +} + +VOID SendWarningToSYSOP(struct MsgInfo * Msg) +{ + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Length += sprintf(MailBuffer, "Warning - Message %d has nowhere to go\r\n%s@%s", Msg->number, Msg->to, Msg->via); + sprintf(Title, "Warning - Message %d has nowhere to go", Msg->number); + SendMessageToSYSOP(Title, MailBuffer, Length); +} + + + +VOID SendMessageToSYSOP(char * Title, char * MailBuffer, int Length) +{ + struct MsgInfo * Msg = AllocateMsgRecord(); + BIDRec * BIDRec; + + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + + Msg->length = Length; + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + FreeSemaphore(&MsgNoSemaphore); + + strcpy(Msg->from, "SYSTEM"); + if (SendSYStoSYSOPCall) + strcpy(Msg->to, SYSOPCall); + else + strcpy(Msg->to, "SYSOP"); + + strcpy(Msg->title, Title); + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + BIDRec = AllocateBIDRecord(); + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, NULL); + free(MailBuffer); +} + +VOID CheckBBSNumber(int i) +{ + // Make sure number is unique + + int Count = 0; + struct UserInfo * user; + + for (user = BBSChain; user; user = user->BBSNext) + { + if (user->BBSNumber == i) + { + Count++; + + if (Count > 1) + { + // Second with same number - Renumber this one + + user->BBSNumber = FindFreeBBSNumber(); + + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; // cant really do much else + + Logprintf(LOG_BBS, NULL, '?', "Duplicate BBS Number found. BBS %s Old BBSNumber %d New BBS Number %d", user->Call, i, user->BBSNumber); + + } + } + } +} + + +int FindFreeBBSNumber() +{ + // returns the lowest number not used by any bbs or message. + + struct MsgInfo * Msg; + struct UserInfo * user; + int i, m; + + for (i = 1; i<= NBBBS; i++) + { + for (user = BBSChain; user; user = user->BBSNext) + { + if (user->BBSNumber == i) + goto nexti; // In use + } + + // Not used by BBS - check messages + + for (m = 1; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + if (check_fwd_bit(Msg->fbbs, i)) + goto nexti; // In use + + if (check_fwd_bit(Msg->forw, i)) + goto nexti; // In use + } + + // Not in Use + + return i; + +nexti:; + + } + + return 0; // All used +} + +BOOL SetupNewBBS(struct UserInfo * user) +{ + user->BBSNumber = FindFreeBBSNumber(); + + if (user->BBSNumber == 0) + return FALSE; + + user->BBSNext = BBSChain; + BBSChain = user; + + SortBBSChain(); + + ReinitializeFWDStruct(user); + + return TRUE; +} + +VOID DeleteBBS(struct UserInfo * user) +{ + struct UserInfo * BBSRec, * PrevBBS = NULL; + +#ifndef LINBPQ + RemoveMenu(hFWDMenu, IDM_FORWARD_ALL + user->BBSNumber, MF_BYCOMMAND); +#endif + for (BBSRec = BBSChain; BBSRec; PrevBBS = BBSRec, BBSRec = BBSRec->BBSNext) + { + if (user == BBSRec) + { + if (PrevBBS == NULL) // First in chain; + { + BBSChain = BBSRec->BBSNext; + break; + } + PrevBBS->BBSNext = BBSRec->BBSNext; + break; + } + } +} + + +VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo); + +VOID SetupForwardingStruct(struct UserInfo * user) +{ + struct BBSForwardingInfo * ForwardingInfo; + + char Key[100] = "BBSForwarding."; + char Temp[100]; + + HKEY hKey=0; + char RegKey[100] = "SOFTWARE\\G8BPQ\\BPQ32\\BPQMailChat\\BBSForwarding\\"; + + int m; + struct MsgInfo * Msg; + + ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + + if (UsingingRegConfig == 0) + { + // Config from file + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + strcat(Key, "*"); + + strcat(Key, user->Call); + + group = config_lookup (&cfg, Key); + + if (group == NULL) // No info + return; + else + { + ForwardingInfo->TOCalls = GetMultiStringValue(group, "TOCalls"); + ForwardingInfo->ConnectScript = GetMultiStringValue(group, "ConnectScript"); + ForwardingInfo->ATCalls = GetMultiStringValue(group, "ATCalls"); + ForwardingInfo->Haddresses = GetMultiStringValue(group, "HRoutes"); + ForwardingInfo->HaddressesP = GetMultiStringValue(group, "HRoutesP"); + ForwardingInfo->FWDTimes = GetMultiStringValue(group, "FWDTimes"); + + ForwardingInfo->Enabled = GetIntValue(group, "Enabled"); + ForwardingInfo->ReverseFlag = GetIntValue(group, "RequestReverse"); + ForwardingInfo->AllowBlocked = GetIntValue(group, "AllowBlocked"); + ForwardingInfo->AllowCompressed = GetIntValue(group, "AllowCompressed"); + ForwardingInfo->AllowB1 = GetIntValue(group, "UseB1Protocol"); + ForwardingInfo->AllowB2 = GetIntValue(group, "UseB2Protocol"); + ForwardingInfo->SendCTRLZ = GetIntValue(group, "SendCTRLZ"); + + if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) + ForwardingInfo->AllowCompressed = TRUE; + + if (ForwardingInfo->AllowCompressed) + ForwardingInfo->AllowBlocked = TRUE; + + ForwardingInfo->PersonalOnly = GetIntValue(group, "FWDPersonalsOnly"); + ForwardingInfo->SendNew = GetIntValue(group, "FWDNewImmediately"); + ForwardingInfo->FwdInterval = GetIntValue(group, "FwdInterval"); + ForwardingInfo->RevFwdInterval = GetIntValue(group, "RevFWDInterval"); + ForwardingInfo->MaxFBBBlockSize = GetIntValue(group, "MaxFBBBlock"); + ForwardingInfo->ConTimeout = GetIntValue(group, "ConTimeout"); + + if (ForwardingInfo->MaxFBBBlockSize == 0) + ForwardingInfo->MaxFBBBlockSize = 10000; + + if (ForwardingInfo->FwdInterval == 0) + ForwardingInfo->FwdInterval = 3600; + + if (ForwardingInfo->ConTimeout == 0) + ForwardingInfo->ConTimeout = 120; + + GetStringValue(group, "BBSHA", Temp, 100); + + if (Temp[0]) + ForwardingInfo->BBSHA = _strdup(Temp); + else + ForwardingInfo->BBSHA = _strdup(""); + } + } + else + { +#ifndef LINBPQ + + int retCode,Type,Vallen; + + strcat(RegKey, user->Call); + retCode = RegOpenKeyEx (REGTREE, RegKey, 0, KEY_QUERY_VALUE, &hKey); + + if (retCode != ERROR_SUCCESS) + return; + else + { + ForwardingInfo->ConnectScript = RegGetMultiStringValue(hKey, "Connect Script"); + ForwardingInfo->TOCalls = RegGetMultiStringValue(hKey, "TOCalls"); + ForwardingInfo->ATCalls = RegGetMultiStringValue(hKey, "ATCalls"); + ForwardingInfo->Haddresses = RegGetMultiStringValue(hKey, "HRoutes"); + ForwardingInfo->HaddressesP = RegGetMultiStringValue(hKey, "HRoutesP"); + ForwardingInfo->FWDTimes = RegGetMultiStringValue(hKey, "FWD Times"); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Enabled", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->Enabled,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "RequestReverse", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->ReverseFlag,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "AllowCompressed", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowCompressed,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Use B1 Protocol", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB1,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "Use B2 Protocol", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->AllowB2,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "SendCTRLZ", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendCTRLZ,(ULONG *)&Vallen); + + if (ForwardingInfo->AllowB1 || ForwardingInfo->AllowB2) + ForwardingInfo->AllowCompressed = TRUE; + + Vallen=4; + retCode += RegQueryValueEx(hKey, "FWD Personals Only", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->PersonalOnly,(ULONG *)&Vallen); + + Vallen=4; + retCode += RegQueryValueEx(hKey, "FWD New Immediately", 0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->SendNew,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"FWDInterval",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->FwdInterval,(ULONG *)&Vallen); + + Vallen=4; + RegQueryValueEx(hKey,"RevFWDInterval",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->RevFwdInterval,(ULONG *)&Vallen); + + RegQueryValueEx(hKey,"MaxFBBBlock",0, + (ULONG *)&Type,(UCHAR *)&ForwardingInfo->MaxFBBBlockSize,(ULONG *)&Vallen); + + if (ForwardingInfo->MaxFBBBlockSize == 0) + ForwardingInfo->MaxFBBBlockSize = 10000; + + if (ForwardingInfo->FwdInterval == 0) + ForwardingInfo->FwdInterval = 3600; + + Vallen=0; + retCode = RegQueryValueEx(hKey,"BBSHA",0 , (ULONG *)&Type,NULL, (ULONG *)&Vallen); + + if (retCode != 0) + { + // No Key - Get from WP?? + + WPRec * ptr = LookupWP(user->Call); + + if (ptr) + { + if (ptr->first_homebbs) + { + ForwardingInfo->BBSHA = _strdup(ptr->first_homebbs); + } + } + } + + if (Vallen) + { + ForwardingInfo->BBSHA = malloc(Vallen); + RegQueryValueEx(hKey, "BBSHA", 0, (ULONG *)&Type, ForwardingInfo->BBSHA,(ULONG *)&Vallen); + } + + RegCloseKey(hKey); + } +#endif + } + + // Convert FWD Times and H Addresses + + if (ForwardingInfo->FWDTimes) + SetupFwdTimes(ForwardingInfo); + + if (ForwardingInfo->Haddresses) + SetupHAddreses(ForwardingInfo); + + if (ForwardingInfo->HaddressesP) + SetupHAddresesP(ForwardingInfo); + + if (ForwardingInfo->BBSHA) + { + if (ForwardingInfo->BBSHA[0]) + SetupHAElements(ForwardingInfo); + else + { + free(ForwardingInfo->BBSHA); + ForwardingInfo->BBSHA = NULL; + } + } + + for (m = FirstMessageIndextoForward; m <= NumberofMessages; m++) + { + Msg=MsgHddrPtr[m]; + + // If any forward bits are set, increment count on BBS record. + + if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) + { + if (Msg->type && check_fwd_bit(Msg->fbbs, user->BBSNumber)) + { + user->ForwardingInfo->MsgCount++; + } + } + } +} + +VOID * GetMultiStringValue(config_setting_t * group, char * ValueName) +{ + char * ptr1; + char * MultiString = NULL; + const char * ptr; + int Count = 0; + char ** Value; + config_setting_t *setting; + char * Save; + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + Value[0] = NULL; + + setting = config_setting_get_member (group, ValueName); + + if (setting) + { + ptr = config_setting_get_string (setting); + + Save = _strdup(ptr); // DOnt want to change config string + ptr = Save; + + while (ptr && strlen(ptr)) + { + ptr1 = strchr(ptr, '|'); + + if (ptr1) + *(ptr1++) = 0; + + if (strlen(ptr)) // ignore null elements + { + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count++] = _strdup(ptr); + } + ptr = ptr1; + } + free(Save); + } + + Value[Count] = NULL; + return Value; +} + + +VOID * RegGetMultiStringValue(HKEY hKey, char * ValueName) +{ +#ifdef LINBPQ + return NULL; +#else + int retCode,Type,Vallen; + char * MultiString = NULL; + int ptr, len; + int Count = 0; + char ** Value; + + Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values + + Value[0] = NULL; + + Vallen=0; + + + retCode = RegQueryValueEx(hKey, ValueName, 0, (ULONG *)&Type, NULL, (ULONG *)&Vallen); + + if ((retCode != 0) || (Vallen < 3)) // Two nulls means empty multistring + { + free(Value); + return FALSE; + } + + MultiString = malloc(Vallen); + + retCode = RegQueryValueEx(hKey, ValueName, 0, + (ULONG *)&Type,(UCHAR *)MultiString,(ULONG *)&Vallen); + + ptr=0; + + while (MultiString[ptr]) + { + len=strlen(&MultiString[ptr]); + + Value = realloc(Value, (Count+2) * sizeof(void *)); + Value[Count++] = _strdup(&MultiString[ptr]); + ptr+= (len + 1); + } + + Value[Count] = NULL; + + free(MultiString); + + return Value; +#endif +} + +VOID FreeForwardingStruct(struct UserInfo * user) +{ + struct BBSForwardingInfo * ForwardingInfo; + int i; + + + ForwardingInfo = user->ForwardingInfo; + + FreeList(ForwardingInfo->TOCalls); + FreeList(ForwardingInfo->ATCalls); + FreeList(ForwardingInfo->Haddresses); + FreeList(ForwardingInfo->HaddressesP); + + i=0; + if(ForwardingInfo->HADDRS) + { + while(ForwardingInfo->HADDRS[i]) + { + FreeList(ForwardingInfo->HADDRS[i]); + i++; + } + free(ForwardingInfo->HADDRS); + free(ForwardingInfo->HADDROffet); + } + + i=0; + if(ForwardingInfo->HADDRSP) + { + while(ForwardingInfo->HADDRSP[i]) + { + FreeList(ForwardingInfo->HADDRSP[i]); + i++; + } + free(ForwardingInfo->HADDRSP); + } + + FreeList(ForwardingInfo->ConnectScript); + FreeList(ForwardingInfo->FWDTimes); + if (ForwardingInfo->FWDBands) + { + i=0; + while(ForwardingInfo->FWDBands[i]) + { + free(ForwardingInfo->FWDBands[i]); + i++; + } + free(ForwardingInfo->FWDBands); + } + if (ForwardingInfo->BBSHAElements) + { + i=0; + while(ForwardingInfo->BBSHAElements[i]) + { + free(ForwardingInfo->BBSHAElements[i]); + i++; + } + free(ForwardingInfo->BBSHAElements); + } + free(ForwardingInfo->BBSHA); + +} + +VOID FreeList(char ** Hddr) +{ + VOID ** Save; + + if (Hddr) + { + Save = (void **)Hddr; + while(Hddr[0]) + { + free(Hddr[0]); + Hddr++; + } + free(Save); + } +} + + +VOID ReinitializeFWDStruct(struct UserInfo * user) +{ + if (user->ForwardingInfo) + { + FreeForwardingStruct(user); + free(user->ForwardingInfo); + } + + SetupForwardingStruct(user); + +} + +VOID SetupFwdTimes(struct BBSForwardingInfo * ForwardingInfo) +{ + char ** Times = ForwardingInfo->FWDTimes; + int Start, End; + int Count = 0; + + ForwardingInfo->FWDBands = zalloc(sizeof(struct FWDBAND)); + + if (Times) + { + while(Times[0]) + { + ForwardingInfo->FWDBands = realloc(ForwardingInfo->FWDBands, (Count+2)* sizeof(struct FWDBAND)); + ForwardingInfo->FWDBands[Count] = zalloc(sizeof(struct FWDBAND)); + + Start = atoi(Times[0]); + End = atoi(&Times[0][5]); + + ForwardingInfo->FWDBands[Count]->FWDStartBand = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; + ForwardingInfo->FWDBands[Count]->FWDEndBand = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; + + Count++; + Times++; + } + ForwardingInfo->FWDBands[Count] = NULL; + } +} +void StartForwarding(int BBSNumber, char ** TempScript) +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + time_t NOW = time(NULL); + + + for (user = BBSChain; user; user = user->BBSNext) + { + // See if any messages are queued for this BBS + + ForwardingInfo = user->ForwardingInfo; + + if ((BBSNumber == 0) || (user->BBSNumber == BBSNumber)) + if (ForwardingInfo) + if (ForwardingInfo->Enabled || BBSNumber) // Menu Command overrides enable + if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) + if (BBSNumber || SeeifMessagestoForward(user->BBSNumber, NULL) || + (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) // Menu Command overrides Reverse + { + user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used + + // See if TempScript requested + + if (user->ForwardingInfo->TempConnectScript) + FreeList(user->ForwardingInfo->TempConnectScript); + + user->ForwardingInfo->TempConnectScript = TempScript; + + if (ConnecttoBBS(user)) + ForwardingInfo->Forwarding = TRUE; + } + } + + return; +} + +size_t fwritex(CIRCUIT * conn, void * _Str, size_t _Size, size_t _Count, FILE * _File) +{ + if (_File) + return fwrite(_Str, _Size, _Count, _File); + + // Appending to MailBuffer + + memcpy(&conn->MailBuffer[conn->InputLen], _Str, _Count); + conn->InputLen += (int)_Count; + + return _Count; +} + + +BOOL ForwardMessagestoFile(CIRCUIT * conn, char * FN) +{ + BOOL AddCRLF = FALSE; + BOOL AutoImport = FALSE; + FILE * Handle = NULL; + char * Context; + BOOL Email = FALSE; + time_t now = time(NULL); + char * param; + + FN = strtok_s(FN, " ,", &Context); + + param = strtok_s(NULL, " ,", &Context); + + if (param) + { + if (_stricmp(param, "ADDCRLF") == 0) + AddCRLF = TRUE; + + if (_stricmp(param, "AutoImport") == 0) + AutoImport = TRUE; + + param = strtok_s(NULL, " ,", &Context); + + if (param) + { + if (_stricmp(param, "ADDCRLF") == 0) + AddCRLF = TRUE; + + if (_stricmp(param, "AutoImport") == 0) + AutoImport = TRUE; + + } + } + // If FN is an email address, write to a temp file, and send via rms or emali gateway + + if (strchr(FN, '@') || _memicmp(FN, "RMS:", 4) == 0) + { + Email = TRUE; + AddCRLF =TRUE; + conn->MailBuffer=malloc(100000); + conn->MailBufferSize=100000; + conn->InputLen = 0; + } + else + { + Handle = fopen(FN, "ab"); + + if (Handle == NULL) + { + int err = GetLastError(); + Logprintf(LOG_BBS, conn, '!', "Failed to open Export File %s", FN); + return FALSE; + } + } + + while (FindMessagestoForward(conn)) + { + struct MsgInfo * Msg; + struct tm * tm; + time_t temp; + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + int MsgLen; + char * MsgPtr; + char Line[256]; + int len; + struct UserInfo * user = conn->UserPointer; + int Index = 0; + + Msg = conn->FwdMsg; + + if (Email) + if (conn->InputLen + Msg->length + 500 > conn->MailBufferSize) + break; + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else if (Msg->type == 'T') + Index = TMSG; + + + if (Msg->via[0]) + len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, + Msg->via, Msg->from, Msg->bid); + else + len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); + + fwritex(conn, Line, 1, len, Handle); + + len = sprintf(Line, "%s\r\n", Msg->title); + fwritex(conn, Line, 1, len, Handle); + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r\n"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + MsgLen = conn->FwdMsg->length; + + // If a B2 Message, remove B2 Header + + if (conn->FwdMsg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + MsgPtr = strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + conn->FwdMsg->number, BBSName, HRoute, RlineVer); + + fwritex(conn, Line, 1, len, Handle); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + fwritex(conn, "\r\n", 1, 2, Handle); + + fwritex(conn, MsgPtr, 1, MsgLen, Handle); + + if (MsgPtr[MsgLen - 2] == '\r') + fwritex(conn, "/EX\r\n", 1, 5, Handle); + else + fwritex(conn, "\r\n/EX\r\n", 1, 7, Handle); + + if (AddCRLF) + fwritex(conn, "\r\n", 1, 2, Handle); + + free(MsgBytes); + + user->Total.MsgsSent[Index]++; + user->Total.BytesForwardedOut[Index] += MsgLen; + + Msg->datechanged = now; + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->UserPointer->ForwardingInfo->MsgCount--; + } + + if (Email) + { + struct MsgInfo * Msg; + BIDRec * BIDRec; + + if (conn->InputLen == 0) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + return TRUE; + } + + // Allocate a message Record slot + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + FreeSemaphore(&MsgNoSemaphore); + MsgnotoMsg[Msg->number] = Msg; + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datecreated = Msg->datechanged = Msg->datereceived = now; + + strcpy(Msg->from, BBSName); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + if (AutoImport) + sprintf(Msg->title, "Batched messages for AutoImport from BBS %s", BBSName); + else + sprintf(Msg->title, "Batched messages from BBS %s", BBSName); + + Msg->length = conn->InputLen; + CreateMessageFile(conn, Msg); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + if (_memicmp(FN, "SMTP:", 5) == 0) + { + strcpy(Msg->via, &FN[5]); + SMTPMsgCreated=TRUE; + } + else + { + strcpy(Msg->to, "RMS"); + if (_memicmp(FN, "RMS:", 4) == 0) + strcpy(Msg->via, &FN[4]); + else + strcpy(Msg->via, FN); + } + + MatchMessagetoBBSList(Msg, conn); + + SaveMessageDatabase(); + SaveBIDDatabase(); + } + else + fclose(Handle); + + SaveMessageDatabase(); + return TRUE; +} + +BOOL ForwardMessagetoFile(struct MsgInfo * Msg, FILE * Handle) +{ + struct tm * tm; + time_t temp; + + char * MsgBytes = ReadMessageFile(Msg->number); + char * MsgPtr; + char Line[256]; + int len; + int MsgLen = Msg->length; + + if (Msg->via[0]) + len = sprintf(Line, "S%c %s @ %s < %s $%s\r\n", Msg->type, Msg->to, + Msg->via, Msg->from, Msg->bid); + else + len = sprintf(Line, "S%c %s < %s $%s\r\n", Msg->type, Msg->to, Msg->from, Msg->bid); + + fwrite(Line, 1, len, Handle); + + len = sprintf(Line, "%s\r\n", Msg->title); + fwrite(Line, 1, len, Handle); + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r\n"); + MsgLen = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + + // If a B2 Message, remove B2 Header + + if (Msg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + + MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &Msg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + len = sprintf(Line, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + Msg->number, BBSName, HRoute, RlineVer); + + fwrite(Line, 1, len, Handle); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + fwrite("\r\n", 1, 2, Handle); + + fwrite(MsgPtr, 1, MsgLen, Handle); + + if (MsgPtr[MsgLen - 2] == '\r') + fwrite("/EX\r\n", 1, 5, Handle); + else + fwrite("\r\n/EX\r\n", 1, 7, Handle); + + free(MsgBytes); + + return TRUE; + +} + +BOOL ConnecttoBBS (struct UserInfo * user) +{ + int n, p; + CIRCUIT * conn; + struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo; + + for (n = NumberofStreams-1; n >= 0 ; n--) + { + conn = &Connections[n]; + + if (conn->Active == FALSE) + { + p = conn->BPQStream; + memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything + conn->BPQStream = p; + + // Can't set Active until Connected or Stuck Session detertor can clear session. + // But must set Active before Connected() runs or will appear is Incoming Connect. + // Connected() is semaphored, so get semaphore before ConnectUsingAppl + // Probably better to semaphore lost session code instead + + + strcpy(conn->Callsign, user->Call); + conn->BBSFlags |= (RunningConnectScript | OUTWARDCONNECT); + conn->UserPointer = user; + + Logprintf(LOG_BBS, conn, '|', "Connecting to BBS %s", user->Call); + + ForwardingInfo->MoreLines = TRUE; + + GetSemaphore(&ConSemaphore, 1); + conn->Active = TRUE; + ConnectUsingAppl(conn->BPQStream, BBSApplMask); + FreeSemaphore(&ConSemaphore); + + // If we are sending to a dump pms we may need to connect using the message sender's callsign. + // But we wont know until we run the connect script, which is a bit late to change call. Could add + // flag to forwarding config, but easier to look for SETCALLTOSENDER in the connect script. + + if (strstr(ForwardingInfo->ConnectScript[0], "SETCALLTOSENDER")) + { + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + // We have a message to send + + struct MsgInfo * Msg; + unsigned char AXCall[7]; + + Msg = conn->FwdMsg; + ConvToAX25(Msg->from, AXCall); + ChangeSessionCallsign(p, AXCall); + + conn->BBSFlags |= TEXTFORWARDING | SETCALLTOSENDER | NEWPACCOM; + conn->NextMessagetoForward = 0; // was set by FindMessages + } + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = FALSE; + } +#ifdef LINBPQ + { + BPQVECSTRUC * SESS; + SESS = &BPQHOSTVECTOR[conn->BPQStream - 1]; + + if (SESS->HOSTSESSION == NULL) + { + Logprintf(LOG_BBS, NULL, '|', "No L4 Sessions for connect to BBS %s", user->Call); + return FALSE; + } + + SESS->HOSTSESSION->Secure_Session = 1; + } +#endif + + strcpy(conn->Callsign, user->Call); + + // Connected Event will trigger connect to remote system + + RefreshMainWindow(); + + return TRUE; + } + } + + Logprintf(LOG_BBS, NULL, '|', "No Free Streams for connect to BBS %s", user->Call); + + return FALSE; + +} + +struct DelayParam +{ + struct UserInfo * User; + CIRCUIT * conn; + int Delay; +}; + +struct DelayParam DParam; // Not 100% safe, but near enough + +VOID ConnectDelayThread(struct DelayParam * DParam) +{ + struct UserInfo * User = DParam->User; + int Delay = DParam->Delay; + + User->ForwardingInfo->Forwarding = TRUE; // Minimize window for two connects + + Sleep(Delay); + + User->ForwardingInfo->Forwarding = TRUE; + ConnecttoBBS(User); + + return; +} + +VOID ConnectPauseThread(struct DelayParam * DParam) +{ + CIRCUIT * conn = DParam->conn; + int Delay = DParam->Delay; + char Msg[] = "Pause Ok\r "; + + Sleep(Delay); + + ProcessBBSConnectScript(conn, Msg, 9); + + return; +} + + +/* +BOOL ProcessBBSConnectScriptInner(CIRCUIT * conn, char * Buffer, int len); + + +BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) +{ + BOOL Ret; + GetSemaphore(&ScriptSEM); + Ret = ProcessBBSConnectScriptInner(conn, Buffer, len); + FreeSemaphore(&ScriptSEM); + + return Ret; +} +*/ + +BOOL ProcessBBSConnectScript(CIRCUIT * conn, char * Buffer, int len) +{ + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + char ** Scripts; + char callsign[10]; + int port, sesstype, paclen, maxframe, l4window; + char * ptr, * ptr2; + + WriteLogLine(conn, '<',Buffer, len-1, LOG_BBS); + + Buffer[len]=0; + _strupr(Buffer); + + if (ForwardingInfo->TempConnectScript) + Scripts = ForwardingInfo->TempConnectScript; + else + Scripts = ForwardingInfo->ConnectScript; + + if (ForwardingInfo->ScriptIndex == -1) + { + // First Entry - if first line is TIMES, check and skip forward if necessary + + int n = 0; + int Start, End; + time_t now = time(NULL), StartSecs, EndSecs; + char * Line; + + if (Localtime) + now -= (time_t)_MYTIMEZONE; + + now %= 86400; + Line = Scripts[n]; + + // Skip comments + + while (Line && ((strcmp(Line, " ") == 0 || Line[0] == ';' || Line[0] == '#'))) + { + n++; + Line = Scripts[n]; + } + + if (Line == NULL) + { + // No more lines - Disconnect + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Line, "TIMES", 5) == 0) + { + NextBand: + Start = atoi(&Line[6]); + End = atoi(&Line[11]); + + StartSecs = (time_t)(Start / 100) * 3600 + (Start % 100) * 60; + EndSecs = (time_t)(End / 100) * 3600 + (End % 100) * 60 + 59; + + if ((StartSecs <= now) && (EndSecs >= now)) + goto InBand; // In band + + // Look for next TIME + NextLine: + Line = Scripts[++n]; + + if (Line == NULL) + { + // No more lines - Disconnect + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Line, "TIMES", 5) != 0) + goto NextLine; + else + goto NextBand; +InBand: + ForwardingInfo->ScriptIndex = n; + } + + } + else + { + // Dont check first time through + + if (strcmp(Buffer, "*** CONNECTED ") != 0) + { + if (Scripts[ForwardingInfo->ScriptIndex] == NULL || + _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished + _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished + { + ForwardingInfo->MoreLines = FALSE; + } + if (!ForwardingInfo->MoreLines) + goto CheckForSID; + } + } + + if (strstr(Buffer, "BUSY") || strstr(Buffer, "FAILURE") || + (strstr(Buffer, "DOWNLINK") && strstr(Buffer, "ATTEMPTING") == 0) || + strstr(Buffer, "SORRY") || strstr(Buffer, "INVALID") || strstr(Buffer, "RETRIED") || + strstr(Buffer, "NO CONNECTION TO") || strstr(Buffer, "ERROR - ") || + strstr(Buffer, "UNABLE TO CONNECT") || strstr(Buffer, "DISCONNECTED") || + strstr(Buffer, "FAILED TO CONNECT") || strstr(Buffer, "REJECTED")) + { + // Connect Failed + + char * Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + int Delay = 1000; + + // Look for an alternative connect block (Starting with ELSE) + + ElseLoop: + + // Skip any comments + + while (Cmd && ((strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#'))) + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + + // TIMES terminates a script + + if (Cmd == 0 || _memicmp(Cmd, "TIMES", 5) == 0) // Only Check until script is finished + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "ELSE", 4) != 0) + { + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + goto ElseLoop; + } + + if (_memicmp(&Cmd[5], "DELAY", 5) == 0) + Delay = atoi(&Cmd[10]) * 1000; + else + Delay = 1000; + + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + + DParam.Delay = Delay; + DParam.User = conn->UserPointer; + + _beginthread((void (*)(void *))ConnectDelayThread, 0, &DParam); + + return FALSE; + } + + // The pointer is only updated when we get the connect, so we can tell when the last line is acked + // The first entry is always from Connected event, so don't have to worry about testing entry -1 below + + + // NETROM to KA node returns + + //c 1 milsw + //WIRAC:N9PMO-2} Connected to MILSW + //###CONNECTED TO NODE MILSW(N9ZXS) CHANNEL A + //You have reached N9ZXS's KA-Node MILSW + //ENTER COMMAND: B,C,J,N, or Help ? + + //C KB9PRF-7 + //###LINK MADE + //###CONNECTED TO NODE KB9PRF-7(KB9PRF-4) CHANNEL A + + // Look for (Space)Connected so we aren't fooled by ###CONNECTED TO NODE, which is not + // an indication of a connect. + + if (strstr(Buffer, " CONNECTED") || strstr(Buffer, "PACLEN") || strstr(Buffer, "IDLETIME") || + strstr(Buffer, "OK") || strstr(Buffer, "###LINK MADE") || strstr(Buffer, "VIRTUAL CIRCUIT ESTABLISHED")) + { + // If connected to SYNC, save IP address and port + + char * Cmd; + + if (strstr(Buffer, "*** CONNECTED TO SYNC")) + { + char * IPAddr = &Buffer[22]; + char * Port = strlop(IPAddr, ':'); + + if (Port) + { + if (conn->SyncHost) + free(conn->SyncHost); + + conn->SyncHost = _strdup(IPAddr); + conn->SyncPort = atoi(Port); + } + } + + if (conn->SkipConn) + { + conn->SkipConn = FALSE; + return TRUE; + } + + LoopBack: + + Cmd = Scripts[++ForwardingInfo->ScriptIndex]; + + // Only Check until script is finished + + if (Cmd && (strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#')) + goto LoopBack; // Blank line + + if (Cmd && _memicmp(Cmd, "TIMES", 5) != 0 && _memicmp(Cmd, "ELSE", 4) != 0) // Only Check until script is finished + { + if (_memicmp(Cmd, "MSGTYPE", 7) == 0) + { + char * ptr; + + // Select Types to send. Only send types in param. Only reverse if R in param + + _strupr(Cmd); + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = FALSE; + + strcpy(conn->MSGTYPES, &Cmd[8]); + + if (strchr(&Cmd[8], 'R')) conn->DoReverse = TRUE; + + ptr = strchr(&Cmd[8], 'B'); + + if (ptr) + { + conn->SendB = TRUE; + conn->MaxBLen = atoi(++ptr); + if (conn->MaxBLen == 0) conn->MaxBLen = 99999999; + } + + ptr = strchr(&Cmd[8], 'T'); + + if (ptr) + { + conn->SendT = TRUE; + conn->MaxTLen = atoi(++ptr); + if (conn->MaxTLen == 0) conn->MaxTLen = 99999999; + } + ptr = strchr(&Cmd[8], 'P'); + + if (ptr) + { + conn->SendP = TRUE; + conn->MaxPLen = atoi(++ptr); + if (conn->MaxPLen == 0) conn->MaxPLen = 99999999; + } + + // If nothing to do, terminate script + + if (conn->DoReverse || SeeifMessagestoForward(conn->UserPointer->BBSNumber, conn)) + goto LoopBack; + + Logprintf(LOG_BBS, conn, '?', "Nothing to do - quitting"); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "INTERLOCK ", 10) == 0) + { + // Used to limit connects on a port to 1 + + int Port; + char Option[80]; + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + sscanf(&Cmd[10], "%d %s", &Port, &Option[0]); + + if (CountConnectionsOnPort(Port)) + { + Logprintf(LOG_BBS, conn, '?', "Interlocked Port is busy - quitting"); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + goto LoopBack; + } + + if (_memicmp(Cmd, "IDLETIME ", 9) == 0) + { + int idle = atoi(&Cmd[9]); + + ChangeSessionIdletime(conn->BPQStream, idle); + goto LoopBack; + } + + if (_memicmp(Cmd, "RADIO AUTH", 10) == 0) + { + // Generate a Password to enable RADIO commands on a remote node + char AuthCommand[80]; + + _strupr(Cmd); + strcpy(AuthCommand, Cmd); + + CreateOneTimePassword(&AuthCommand[11], &Cmd[11], 0); + + nodeprintf(conn, "%s\r", AuthCommand); + return TRUE; + } + + if (_memicmp(Cmd, "SKIPCON", 7) == 0) + { + // Remote Node sends Connected in CTEXT - we need to swallow it + + conn->SkipConn = TRUE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SendWL2KPM", 10) == 0|| _memicmp(Cmd, "SendWL2KFW", 10) == 0) + { + // Send ;FW: command + + conn->SendWL2KFW = TRUE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SKIPPROMPT", 10) == 0) + { + // Remote Node sends > at end of CTEXT - we need to swallow it + + conn->SkipPrompt++; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "TEXTFORWARDING", 10) == 0) + { + conn->BBSFlags |= TEXTFORWARDING; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SETCALLTOSENDER", 15) == 0) + { + conn->BBSFlags |= TEXTFORWARDING | SETCALLTOSENDER; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "RADIOONLY", 9) == 0) + { + conn->BBSFlags |= WINLINKRO; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "SYNC", 4) == 0) + { + conn->BBSFlags |= SYNCMODE; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "NEEDLF", 6) == 0) + { + conn->BBSFlags |= NEEDLF; + goto CheckForEnd; + } + + if (_memicmp(Cmd, "MCASTRX", 6) == 0) + { + conn->BBSFlags |= MCASTRX; + conn->MCastListenTime = atoi(&Cmd[7]) * 6; // Time to run session for *6 as value is mins put timer ticks 10 secs + + // send MCAST to Node + + nodeprintfEx(conn, "MCAST\r"); + return TRUE; + } + + if (_memicmp(Cmd, "FLARQ", 5) == 0) + { + conn->BBSFlags |= FLARQMAIL; + + CheckForEnd: + if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || + memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished + memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished + ForwardingInfo->MoreLines = FALSE; + + goto LoopBack; + } + if (_memicmp(Cmd, "PAUSE", 5) == 0) + { + // Pause script + + Logprintf(LOG_BBS, conn, '?', "Script %s", Cmd); + + DParam.Delay = atoi(&Cmd[6]) * 1000; + DParam.conn = conn; + + _beginthread((void (*)(void *))ConnectPauseThread, 0, &DParam); + + return TRUE; + } + + if (_memicmp(Cmd, "FILE", 4) == 0) + { + if (Cmd[4] == 0) + { + // Missing Filename + + Logprintf(LOG_BBS, conn, '!', "Export file name missing"); + } + else + ForwardMessagestoFile(conn, &Cmd[5]); + + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (_memicmp(Cmd, "SMTP", 4) == 0) + { + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + + SendAMPRSMTP(conn); + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + + if (_memicmp(Cmd, "IMPORT", 6) == 0) + { + char * File, * Context; + int Num; + char * Temp = _strdup(Cmd); + + File = strtok_s(&Temp[6], " ", &Context); + + if (File && File[0]) + { + Num = ImportMessages(NULL, File, TRUE); + + Logprintf(LOG_BBS, NULL, '|', "Imported %d Message(s) from %s", Num, File); + + if (Context && _stricmp(Context, "delete") == 0) + DeleteFile(File); + } + free(Temp); + + if (Scripts[ForwardingInfo->ScriptIndex + 1] == NULL || + memcmp(Scripts[ForwardingInfo->ScriptIndex +1], "TIMES", 5) == 0 || // Only Check until script is finished + memcmp(Scripts[ForwardingInfo->ScriptIndex + 1], "ELSE", 4) == 0) // Only Check until script is finished + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + goto LoopBack; + } + + // Anything else is sent to Node + + // Replace \ with # so can send commands starting with # + + if (Cmd[0] == '\\') + { + Cmd[0] = '#'; + nodeprintfEx(conn, "%s\r", Cmd); + Cmd[0] = '\\'; // Put \ back in script + } + else + nodeprintfEx(conn, "%s\r", Cmd); + + return TRUE; + } + + // End of script. + + ForwardingInfo->MoreLines = FALSE; + + if (conn->BBSFlags & MCASTRX) + { + // No session with Multicast, so no SID + + conn->BBSFlags &= ~RunningConnectScript; + return TRUE; + } + + if (conn->BBSFlags & FLARQMAIL) + { + // FLARQ doesnt send a prompt - Just send message(es) + + conn->UserPointer->Total.ConnectsOut++; + conn->BBSFlags &= ~RunningConnectScript; + ForwardingInfo->LastReverseForward = time(NULL); + + // Update Paclen + + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + + if (paclen > 0) + conn->paclen = paclen; + + SendARQMail(conn); + return TRUE; + } + + + return TRUE; + } + + ptr = strchr(Buffer, '}'); + + if (ptr && ForwardingInfo->MoreLines) // Beware it could be part of ctext + { + // Could be respsonse to Node Command + + ptr+=2; + + ptr2 = strchr(&ptr[0], ' '); + + if (ptr2) + { + if (_memicmp(ptr, Scripts[ForwardingInfo->ScriptIndex], ptr2-ptr) == 0) // Reply to last sscript command + { + if (Scripts[ForwardingInfo->ScriptIndex+1] && _memicmp(Scripts[ForwardingInfo->ScriptIndex+1], "else", 4) == 0) + { + // stray match or misconfigured + + return TRUE; + } + + ForwardingInfo->ScriptIndex++; + + if (Scripts[ForwardingInfo->ScriptIndex]) + if (_memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) != 0) + nodeprintf(conn, "%s\r", Scripts[ForwardingInfo->ScriptIndex]); + + return TRUE; + } + } + } + + // Not Success or Fail. If last line is still outstanding, wait fot Response + // else look for SID or Prompt + + if (conn->SkipPrompt && Buffer[len-2] == '>') + { + conn->SkipPrompt--; + return TRUE; + } + + if (ForwardingInfo->MoreLines) + return TRUE; + + // No more steps, Look for SID or Prompt + +CheckForSID: + + if (strstr(Buffer, "POSYNCHELLO")) // RMS RELAY Sync process + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + ForwardingInfo->LastReverseForward = time(NULL); + + ProcessLine(conn, 0, Buffer, len); + return FALSE; + } + + if (strstr(Buffer, "SORRY, NO")) // URONODE + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + if (memcmp(Buffer, ";PQ: ", 5) == 0) + { + // Secure CMS challenge + + int Len; + struct UserInfo * User = conn->UserPointer; + char * Pass = User->CMSPass; + int Response ; + char RespString[12]; + char ConnectingCall[10]; + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + + SESS += conn->BPQStream - 1; + + ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); + + strlop(ConnectingCall, ' '); + + if (Pass[0] == 0) + { + Pass = User->pass; // Old Way + if (Pass[0] == 0) + { + strlop(ConnectingCall, '-'); + User = LookupCall(ConnectingCall); + if (User) + Pass = User->CMSPass; + } + } + + // + + Response = GetCMSHash(&Buffer[5], Pass); + + sprintf(RespString, "%010d", Response); + + Len = sprintf(conn->SecureMsg, ";PR: %s\r", &RespString[2]); + + // Save challengs in case needed for FW lines + + strcpy(conn->PQChallenge, &Buffer[5]); + + return FALSE; + } + + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + { + // Update PACLEN + + GetConnectionInfo(conn->BPQStream, callsign, &port, &sesstype, &paclen, &maxframe, &l4window); + + if (paclen > 0) + conn->paclen = paclen; + + + Parse_SID(conn, &Buffer[1], len-4); + + if (conn->BBSFlags & FBBForwarding) + { + conn->FBBIndex = 0; // ready for first block; + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + conn->FBBChecksum = 0; + } + + return TRUE; + } + + if (memcmp(Buffer, "[PAKET ", 7) == 0) + { + conn->BBSFlags |= BBS; + conn->BBSFlags |= MBLFORWARDING; + } + + if (Buffer[len-2] == '>') + { + if (conn->SkipPrompt) + { + conn->SkipPrompt--; + return TRUE; + } + + conn->NextMessagetoForward = FirstMessageIndextoForward; + conn->UserPointer->Total.ConnectsOut++; + conn->BBSFlags &= ~RunningConnectScript; + ForwardingInfo->LastReverseForward = time(NULL); + + if (memcmp(Buffer, "[AEA PK", 7) == 0 || (conn->BBSFlags & TEXTFORWARDING)) + { + // PK232. Don't send a SID, and switch to Text Mode + + conn->BBSFlags |= (BBS | TEXTFORWARDING); + conn->Flags |= SENDTITLE; + + // Send Message. There is no mechanism for reverse forwarding + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg; + + // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ + + Msg = conn->FwdMsg; + + if ((conn->BBSFlags & SETCALLTOSENDER)) + nodeprintf(conn, "S%c %s @ %s \r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call); + else + nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, + Msg->from, Msg->bid); + } + else + { + conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered + Disconnect(conn->BPQStream); + return FALSE; + } + + return TRUE; + } + + if (strcmp(conn->Callsign, "RMS") == 0 || conn->SendWL2KFW) + { + // Build a ;FW: line with all calls with PollRMS Set + + // According to Lee if you use secure login the first + // must be the BBS call + // Actually I don't think we need the first, + // as that is implied + + // If a secure password is available send the new + // call|response format. + + // I think this should use the session callsign, which + // normally will be the BBS ApplCall, and not the BBS Name, + // but coudl be changed by *** LINKED + + int i, s; + char FWLine[10000] = ";FW: "; + struct UserInfo * user; + char RMSCall[20]; + char ConnectingCall[10]; + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + + SESS += conn->BPQStream - 1; + + ConvFromAX25(SESS->HOSTSESSION->L4USER, ConnectingCall); + strlop(ConnectingCall, ' '); + + strcat (FWLine, ConnectingCall); + + for (i = 0; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + + if (user->flags & F_POLLRMS) + { + if (user->RMSSSIDBits == 0) user->RMSSSIDBits = 1; + + for (s = 0; s < 16; s++) + { + if (user->RMSSSIDBits & (1 << s)) + { + if (s) + sprintf(RMSCall, "%s-%d", user->Call, s); + else + sprintf(RMSCall, "%s", user->Call); + + // We added connectingcall at front + + if (strcmp(RMSCall, ConnectingCall) != 0) + { + strcat(FWLine, " "); + strcat(FWLine, RMSCall); + + if (user->CMSPass[0]) + { + int Response = GetCMSHash(conn->PQChallenge, user->CMSPass); + char RespString[12]; + + sprintf(RespString, "%010d", Response); + strcat(FWLine, "|"); + strcat(FWLine, &RespString[2]); + } + } + } + } + } + } + + strcat(FWLine, "\r"); + + nodeprintf(conn, FWLine); + } + + // Only declare B1 and B2 if other end did, and we are configued for it + + nodeprintfEx(conn, BBSSID, "BPQ-", + Ver[0], Ver[1], Ver[2], Ver[3], + (conn->BBSFlags & FBBCompressed) ? "B" : "", + (conn->BBSFlags & FBBB1Mode && !(conn->BBSFlags & FBBB2Mode)) ? "1" : "", + (conn->BBSFlags & FBBB2Mode) ? "2" : "", + (conn->BBSFlags & FBBForwarding) ? "F" : "", + (conn->BBSFlags & WINLINKRO) ? "" : "J"); + + if (conn->SecureMsg[0]) + { + struct UserInfo * user; + BBSputs(conn, conn->SecureMsg); + conn->SecureMsg[0] = 0; + + // Also send a Location Comment Line + + //; GM8BPQ-10 DE G8BPQ (IO92KX) + //; WL2K DE GM8BPQ () (PAT) + + user = LookupCall(BBSName); + + if (LOC && LOC[0]) + nodeprintf(conn, "; WL2K DE %s (%s)\r", BBSName, LOC); + } + + if (conn->BPQBBS && conn->MSGTYPES[0]) + + // Send a ; MSGTYPES to control what he sends us + + nodeprintf(conn, "; MSGTYPES %s\r", conn->MSGTYPES); + + if (conn->BBSFlags & FBBForwarding) + { + if (!FBBDoForward(conn)) // Send proposal if anthing to forward + { + if (conn->DoReverse) + FBBputs(conn, "FF\r"); + else + { + FBBputs(conn, "FQ\r"); + conn->CloseAfterFlush = 20; // 2 Secs + } + } + + return TRUE; + } + + return TRUE; + } + + return TRUE; +} + +VOID Parse_SID(CIRCUIT * conn, char * SID, int len) +{ + ChangeSessionIdletime(conn->BPQStream, BBSIDLETIME); // Default Idletime for BBS Sessions + + // scan backwards for first '-' + + if (strstr(SID, "BPQCHATSERVER")) + { + Disconnect(conn->BPQStream); + return; + } + + if (strstr(SID, "RMS Ex") || strstr(SID, "Winlink Ex")) + { + conn->RMSExpress = TRUE; + conn->Paclink = FALSE; + conn->PAT = FALSE; + + // Set new RMS Users as RMS User + + if (conn->NewUser) + conn->UserPointer->flags |= F_Temp_B2_BBS; + } + + if (stristr(SID, "PAT")) + { + // Set new PAT Users as RMS User + + conn->RMSExpress = FALSE; + conn->Paclink = FALSE; + conn->PAT = TRUE; + + if (conn->NewUser) + conn->UserPointer->flags |= F_Temp_B2_BBS; + } + if (strstr(SID, "Paclink")) + { + conn->RMSExpress = FALSE; + conn->Paclink = TRUE; + } + + if (strstr(SID, "WL2K-")) + { + conn->WL2K = TRUE; + conn->BBSFlags |= WINLINKRO; + } + + if (strstr(SID, "MFJ-")) + { + conn->BBSFlags |= MFJMODE; + } + + if (_memicmp(SID, "OpenBCM", 7) == 0) + { + // We should really only do this on Telnet Connections, as OpenBCM flag is used to remove relnet transparency + + + conn->OpenBCM = TRUE; + } + + if (_memicmp(SID, "PMS-3.2", 7) == 0) + { + // Paccom TNC that doesn't send newline prompt ater receiving subject + + conn->BBSFlags |= NEWPACCOM; + } + + // See if BPQ for selective forwarding + + if (strstr(SID, "BPQ")) + conn->BPQBBS = TRUE; + + while (len > 0) + { + switch (SID[len--]) + { + case '-': + + len=0; + break; + + case '$': + + conn->BBSFlags |= BBS | MBLFORWARDING; + conn->Paging = FALSE; + + break; + + case 'F': // FBB Blocked Forwarding + + // We now support blocked uncompressed. Not necessarily compatible with FBB + + if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) + { + // We need to allocate a forwarding structure + + conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; + conn->UserPointer->ForwardingInfo->AllowBlocked = TRUE; + conn->UserPointer->BBSNumber = NBBBS; + } + + if (conn->UserPointer->ForwardingInfo->AllowBlocked) + { + conn->BBSFlags |= FBBForwarding | BBS; + conn->BBSFlags &= ~MBLFORWARDING; + + conn->Paging = FALSE; + + if ((conn->UserPointer->ForwardingInfo == NULL) && (conn->UserPointer->flags & F_PMS)) + { + // We need to allocate a forwarding structure + + conn->UserPointer->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + conn->UserPointer->ForwardingInfo->AllowCompressed = TRUE; + conn->UserPointer->BBSNumber = NBBBS; + } + + // Allocate a Header Block + + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + } + break; + + case 'J': + + // Suspected to be associated with Winlink Radio Only + + conn->BBSFlags &= ~WINLINKRO; + break; + + case 'B': + + if (conn->UserPointer->ForwardingInfo->AllowCompressed) + { + conn->BBSFlags |= FBBCompressed; + conn->DontSaveRestartData = FALSE; // Allow restarts + + // Look for 1 or 2 or 12 as next 2 chars + + if (SID[len+2] == '1') + { + if (conn->UserPointer->ForwardingInfo->AllowB1 || + conn->UserPointer->ForwardingInfo->AllowB2) // B2 implies B1 + conn->BBSFlags |= FBBB1Mode; + + if (SID[len+3] == '2') + if (conn->UserPointer->ForwardingInfo->AllowB2) + conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) + + break; + } + + if (SID[len+2] == '2') + { + if (conn->UserPointer->ForwardingInfo->AllowB2) + conn->BBSFlags |= FBBB1Mode | FBBB2Mode; // B2 uses B1 mode (crc on front of file) + + if (conn->UserPointer->ForwardingInfo->AllowB1) + conn->BBSFlags |= FBBB1Mode; // B2 should allow fallback to B1 (but RMS doesnt!) + + } + break; + } + + break; + } + } + + // Only allow blocked non-binary to other BPQ Nodes + + if ((conn->BBSFlags & FBBForwarding) && ((conn->BBSFlags & FBBCompressed) == 0) && (conn->BPQBBS == 0)) + { + // Switch back to MBL + + conn->BBSFlags |= MBLFORWARDING; + conn->BBSFlags &= ~FBBForwarding; // Turn off FBB Blocked + } + + return; +} + +VOID BBSSlowTimer() +{ + ConnectionInfo * conn; + int n; + + // Called every 10 seconds + + MCastTimer(); + + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (conn->Active == TRUE) + { + // Check for stuck BBS sessions (BBS session but no Node Session) + + int state; + + GetSemaphore(&ConSemaphore, 1); + SessionStateNoAck(conn->BPQStream, &state); + FreeSemaphore(&ConSemaphore); + + if (state == 0) // No Node Session + { + // is it safe just to clear Active ?? + + conn->InputMode = 0; // So Disconnect wont save partial transfer + conn->BBSFlags = 0; + Disconnected (conn->BPQStream); + continue; + } + + if (conn->BBSFlags & MCASTRX) + MCastConTimer(conn); + + + // Check SIDTImers - used to detect failure to compete SID Handshake + + if (conn->SIDResponseTimer) + { + conn->SIDResponseTimer--; + if (conn->SIDResponseTimer == 0) + { + // Disconnect Session + + Disconnect(conn->BPQStream); + } + } + } + } + + // Flush logs + + for (n = 0; n < 4; n++) + { + if (LogHandle[n]) + { + time_t LT = time(NULL); + if ((LT - LastLogTime[n]) > 30) + { + LastLogTime[n] = LT; + fclose(LogHandle[n]); + LogHandle[n] = NULL; + } + } + } +} + + +VOID FWDTimerProc() +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + time_t NOW = time(NULL); + + // Entered every 2 seconds + + for (user = BBSChain; user; user = user->BBSNext) + { + // See if any messages are queued for this BBS + + ForwardingInfo = user->ForwardingInfo; + ForwardingInfo->FwdTimer += 2; + + if (ForwardingInfo->FwdTimer >= ForwardingInfo->FwdInterval) + { + ForwardingInfo->FwdTimer=0; + + if (ForwardingInfo->FWDBands && ForwardingInfo->FWDBands[0]) + { + // Check Timebands + + struct FWDBAND ** Bands = ForwardingInfo->FWDBands; + int Count = 0; + time_t now = time(NULL); + + if (Localtime) + now -= (time_t)_MYTIMEZONE; + + now %= 86400; // Secs in day + + while(Bands[Count]) + { + if ((Bands[Count]->FWDStartBand < now) && (Bands[Count]->FWDEndBand >= now)) + goto FWD; // In band + + Count++; + } + continue; // Out of bands + } + FWD: + if (ForwardingInfo->Enabled) + { + if (ForwardingInfo->ConnectScript && (ForwardingInfo->Forwarding == 0) && ForwardingInfo->ConnectScript[0]) + { + //Temp Debug Code + +// Debugprintf("ReverseFlag = %d, Msgs to Forward Flag %d Msgs to Forward Count %d", +// ForwardingInfo->ReverseFlag, +// SeeifMessagestoForward(user->BBSNumber, NULL), +// CountMessagestoForward(user)); + + if (SeeifMessagestoForward(user->BBSNumber, NULL) || + (ForwardingInfo->ReverseFlag && ((NOW - ForwardingInfo->LastReverseForward) >= ForwardingInfo->RevFwdInterval))) + + { + user->ForwardingInfo->ScriptIndex = -1; // Incremented before being used + + + // remove any old TempScript + + if (user->ForwardingInfo->TempConnectScript) + { + FreeList(user->ForwardingInfo->TempConnectScript); + user->ForwardingInfo->TempConnectScript = NULL; + } + + if (ConnecttoBBS(user)) + ForwardingInfo->Forwarding = TRUE; + } + } + } + } + } +} + +VOID * _zalloc_dbg(size_t len, int type, char * file, int line) +{ + // ?? malloc and clear + + void * ptr; + +#ifdef WIN32 + ptr=_malloc_dbg(len, type, file, line); +#else + ptr = malloc(len); +#endif + if (ptr) + memset(ptr, 0, len); + + return ptr; +} + +struct MsgInfo * FindMessageByNumber(int msgno) + { + int m=NumberofMessages; + + struct MsgInfo * Msg; + + do + { + Msg=MsgHddrPtr[m]; + + if (Msg->number == msgno) + return Msg; + + if (Msg->number && Msg->number < msgno) // sometimes get zero msg number + return NULL; // Not found + + m--; + + } while (m > 0); + + return NULL; +} + +struct MsgInfo * FindMessageByBID(char * BID) +{ + int m = NumberofMessages; + + struct MsgInfo * Msg; + + while (m > 0) + { + Msg = MsgHddrPtr[m]; + + if (strcmp(Msg->bid, BID) == 0) + return Msg; + + m--; + } + + return NULL; +} + +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len) +{ + unsigned char hash[50]; + unsigned char key[100]; + unsigned int i, j = 0, val1, val2; + unsigned char hostname[100]=""; + + gethostname(hostname, 100); + + strcpy(key, hostname); + strcat(key, ISPPOP3Name); + + md5(key, hash); + memcpy(&hash[16], hash, 16); // in case very long password + + // String is now encoded as hex pairs, but still need to decode old format + + for (i=0; i < len; i++) + { + if (Encrypt[i] < '0' || Encrypt[i] > 'F') + goto OldFormat; + } + + // Only '0' to 'F' + + for (i=0; i < len; i++) + { + val1 = Encrypt[i++]; + val1 -= '0'; + if (val1 > 9) + val1 -= 7; + + val2 = Encrypt[i]; + val2 -= '0'; + if (val2 > 9) + val2 -= 7; + + Pass[j] = (val1 << 4) | val2; + Pass[j] ^= hash[j]; + j++; + } + + return; + +OldFormat: + + for (i=0; i < len; i++) + { + Pass[i] = Encrypt[i] ^ hash[i]; + } + + return; +} + +int EncryptPass(char * Pass, char * Encrypt) +{ + unsigned char hash[50]; + unsigned char key[100]; + unsigned int i, val; + unsigned char hostname[100]; + unsigned char extendedpass[100]; + unsigned int passlen; + unsigned char * ptr; + + gethostname(hostname, 100); + + strcpy(key, hostname); + strcat(key, ISPPOP3Name); + + md5(key, hash); + memcpy(&hash[16], hash, 16); // in case very long password + + // if password is less than 16 chars, extend with zeros + + passlen=(int)strlen(Pass); + + strcpy(extendedpass, Pass); + + if (passlen < 16) + { + for (i=passlen+1; i <= 16; i++) + { + extendedpass[i] = 0; + } + + passlen = 16; + } + + ptr = Encrypt; + Encrypt[0] = 0; + + for (i=0; i < passlen; i++) + { + val = extendedpass[i] ^ hash[i]; + ptr += sprintf(ptr, "%02X", val); + } + + return passlen * 2; +} + + + +VOID SaveIntValue(config_setting_t * group, char * name, int value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT); + if(setting) + config_setting_set_int(setting, value); +} + +VOID SaveInt64Value(config_setting_t * group, char * name, long long value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_INT64); + if(setting) + config_setting_set_int64(setting, value); +} + +VOID SaveFloatValue(config_setting_t * group, char * name, double value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_FLOAT); + if (setting) + config_setting_set_float(setting, value); +} + +VOID SaveStringValue(config_setting_t * group, char * name, char * value) +{ + config_setting_t *setting; + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, value); + +} + + +VOID SaveOverride(config_setting_t * group, char * name, struct Override ** values) +{ + config_setting_t *setting; + struct Override ** Calls; + char Multi[10000]; + char * ptr = &Multi[1]; + + *ptr = 0; + + if (values) + { + Calls = values; + + while(Calls[0]) + { + ptr += sprintf(ptr, "%s, %d|", Calls[0]->Call, Calls[0]->Days); + Calls++; + } + *(--ptr) = 0; + } + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, &Multi[1]); + +} + + +VOID SaveMultiStringValue(config_setting_t * group, char * name, char ** values) +{ + config_setting_t *setting; + char ** Calls; + char Multi[100000]; + char * ptr = &Multi[1]; + + *ptr = 0; + + if (values) + { + Calls = values; + + while(Calls[0]) + { + strcpy(ptr, Calls[0]); + ptr += strlen(Calls[0]); + *(ptr++) = '|'; + Calls++; + } + *(--ptr) = 0; + } + + setting = config_setting_add(group, name, CONFIG_TYPE_STRING); + if (setting) + config_setting_set_string(setting, &Multi[1]); + +} + +int configSaved = 0; + +VOID SaveConfig(char * ConfigName) +{ + struct UserInfo * user; + struct BBSForwardingInfo * ForwardingInfo ; + config_setting_t *root, *group, *bbs; + int i; + char Size[80]; + struct BBSForwardingInfo DummyForwardingInfo; + char Line[1024]; + char FBBString[8192]= ""; + FBBFilter * p = Filters; + char * ptr = FBBString; + + GetSemaphore(&ConfigSEM, 60); + + if (configSaved == 0) + { + // only create backup once per run + + CopyConfigFile(ConfigName); + configSaved = 1; + } + + memset(&DummyForwardingInfo, 0, sizeof(struct BBSForwardingInfo)); + + // Get rid of old config before saving + + config_destroy(&cfg); + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + root = config_root_setting(&cfg); + + group = config_setting_add(root, "main", CONFIG_TYPE_GROUP); + + SaveIntValue(group, "Streams", MaxStreams); + SaveIntValue(group, "BBSApplNum", BBSApplNum); + SaveStringValue(group, "BBSName", BBSName); + SaveStringValue(group, "SYSOPCall", SYSOPCall); + SaveStringValue(group, "H-Route", HRoute); + SaveStringValue(group, "AMPRDomain", AMPRDomain); + SaveIntValue(group, "EnableUI", EnableUI); + SaveIntValue(group, "RefuseBulls", RefuseBulls); + SaveIntValue(group, "OnlyKnown", OnlyKnown); + SaveIntValue(group, "reportMailEvents", reportMailEvents); + SaveIntValue(group, "SendSYStoSYSOPCall", SendSYStoSYSOPCall); + SaveIntValue(group, "SendBBStoSYSOPCall", SendBBStoSYSOPCall); + SaveIntValue(group, "DontHoldNewUsers", DontHoldNewUsers); + SaveIntValue(group, "DefaultNoWINLINK", DefaultNoWINLINK); + SaveIntValue(group, "AllowAnon", AllowAnon); + SaveIntValue(group, "DontNeedHomeBBS", DontNeedHomeBBS); + SaveIntValue(group, "DontCheckFromCall", DontCheckFromCall); + SaveIntValue(group, "UserCantKillT", UserCantKillT); + + SaveIntValue(group, "ForwardToMe", ForwardToMe); + SaveIntValue(group, "SMTPPort", SMTPInPort); + SaveIntValue(group, "POP3Port", POP3InPort); + SaveIntValue(group, "NNTPPort", NNTPInPort); + SaveIntValue(group, "RemoteEmail", RemoteEmail); + SaveIntValue(group, "SendAMPRDirect", SendAMPRDirect); + + SaveIntValue(group, "MailForInterval", MailForInterval); + SaveStringValue(group, "MailForText", MailForText); + + EncryptedPassLen = EncryptPass(ISPAccountPass, EncryptedISPAccountPass); + + SaveIntValue(group, "AuthenticateSMTP", SMTPAuthNeeded); + + SaveIntValue(group, "MulticastRX", MulticastRX); + + SaveIntValue(group, "SMTPGatewayEnabled", ISP_Gateway_Enabled); + SaveIntValue(group, "ISPSMTPPort", ISPSMTPPort); + SaveIntValue(group, "ISPPOP3Port", ISPPOP3Port); + SaveIntValue(group, "POP3PollingInterval", ISPPOP3Interval); + + SaveStringValue(group, "MyDomain", MyDomain); + SaveStringValue(group, "ISPSMTPName", ISPSMTPName); + SaveStringValue(group, "ISPEHLOName", ISPEHLOName); + SaveStringValue(group, "ISPPOP3Name", ISPPOP3Name); + SaveStringValue(group, "ISPAccountName", ISPAccountName); + SaveStringValue(group, "ISPAccountPass", EncryptedISPAccountPass); + + + // Save Window Sizes + +#ifndef LINBPQ + + if (ConsoleRect.right) + { + sprintf(Size,"%d,%d,%d,%d",ConsoleRect.left, ConsoleRect.right, + ConsoleRect.top, ConsoleRect.bottom); + + SaveStringValue(group, "ConsoleSize", Size); + } + + sprintf(Size,"%d,%d,%d,%d,%d",MonitorRect.left,MonitorRect.right,MonitorRect.top,MonitorRect.bottom, hMonitor ? 1 : 0); + SaveStringValue(group, "MonitorSize", Size); + + sprintf(Size,"%d,%d,%d,%d",MainRect.left,MainRect.right,MainRect.top,MainRect.bottom); + SaveStringValue(group, "WindowSize", Size); + + SaveIntValue(group, "Bells", Bells); + SaveIntValue(group, "FlashOnBell", FlashOnBell); + SaveIntValue(group, "StripLF", StripLF); + SaveIntValue(group, "WarnWrap", WarnWrap); + SaveIntValue(group, "WrapInput", WrapInput); + SaveIntValue(group, "FlashOnConnect", FlashOnConnect); + SaveIntValue(group, "CloseWindowOnBye", CloseWindowOnBye); + +#endif + + SaveIntValue(group, "Log_BBS", LogBBS); + SaveIntValue(group, "Log_TCP", LogTCP); + + sprintf(Size,"%d,%d,%d,%d", Ver[0], Ver[1], Ver[2], Ver[3]); + SaveStringValue(group, "Version", Size); + + // Save Welcome Messages and prompts + + SaveStringValue(group, "WelcomeMsg", WelcomeMsg); + SaveStringValue(group, "NewUserWelcomeMsg", NewWelcomeMsg); + SaveStringValue(group, "ExpertWelcomeMsg", ExpertWelcomeMsg); + + SaveStringValue(group, "Prompt", Prompt); + SaveStringValue(group, "NewUserPrompt", NewPrompt); + SaveStringValue(group, "ExpertPrompt", ExpertPrompt); + SaveStringValue(group, "SignoffMsg", SignoffMsg); + + SaveMultiStringValue(group, "RejFrom", RejFrom); + SaveMultiStringValue(group, "RejTo", RejTo); + SaveMultiStringValue(group, "RejAt", RejAt); + SaveMultiStringValue(group, "RejBID", RejBID); + + SaveMultiStringValue(group, "HoldFrom", HoldFrom); + SaveMultiStringValue(group, "HoldTo", HoldTo); + SaveMultiStringValue(group, "HoldAt", HoldAt); + SaveMultiStringValue(group, "HoldBID", HoldBID); + + // Save FBB Filters + + while (p) + { + ptr += sprintf(ptr, "%c|%c|%s|%s|%s|%s|%d|", + p->Action, p->Type, p->From, p->TO, p->AT, p->BID, p->MaxLen); + + p = p->Next; + } + + SaveStringValue(group, "FBBFilters", FBBString); + + SaveIntValue(group, "SendWP", SendWP); + SaveIntValue(group, "SendWPType", SendWPType); + SaveIntValue(group, "FilterWPBulls", FilterWPBulls); + SaveIntValue(group, "NoWPGuesses", NoWPGuesses); + + SaveStringValue(group, "SendWPTO", SendWPTO); + SaveStringValue(group, "SendWPVIA", SendWPVIA); + + SaveMultiStringValue(group, "SendWPAddrs", SendWPAddrs); + + // Save Forwarding Config + + // Interval and Max Sizes and Aliases are not user specific + + SaveIntValue(group, "MaxTXSize", MaxTXSize); + SaveIntValue(group, "MaxRXSize", MaxRXSize); + SaveIntValue(group, "ReaddressLocal", ReaddressLocal); + SaveIntValue(group, "ReaddressReceived", ReaddressReceived); + SaveIntValue(group, "WarnNoRoute", WarnNoRoute); + SaveIntValue(group, "Localtime", Localtime); + SaveIntValue(group, "SendPtoMultiple", SendPtoMultiple); + SaveIntValue(group, "FOURCHARCONT", FOURCHARCONT); + + SaveMultiStringValue(group, "FWDAliases", AliasText); + + bbs = config_setting_add(root, "BBSForwarding", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofUsers; i++) + { + user = UserRecPtr[i]; + ForwardingInfo = user->ForwardingInfo; + + if (ForwardingInfo == NULL) + continue; + + if (memcmp(ForwardingInfo, &DummyForwardingInfo, sizeof(struct BBSForwardingInfo)) == 0) + continue; // Ignore empty records; + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + { + char Key[20] = "*"; + strcat (Key, user->Call); + group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); + } + else + group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); + + SaveMultiStringValue(group, "TOCalls", ForwardingInfo->TOCalls); + SaveMultiStringValue(group, "ConnectScript", ForwardingInfo->ConnectScript); + SaveMultiStringValue(group, "ATCalls", ForwardingInfo->ATCalls); + SaveMultiStringValue(group, "HRoutes", ForwardingInfo->Haddresses); + SaveMultiStringValue(group, "HRoutesP", ForwardingInfo->HaddressesP); + SaveMultiStringValue(group, "FWDTimes", ForwardingInfo->FWDTimes); + + SaveIntValue(group, "Enabled", ForwardingInfo->Enabled); + SaveIntValue(group, "RequestReverse", ForwardingInfo->ReverseFlag); + SaveIntValue(group, "AllowBlocked", ForwardingInfo->AllowBlocked); + SaveIntValue(group, "AllowCompressed", ForwardingInfo->AllowCompressed); + SaveIntValue(group, "UseB1Protocol", ForwardingInfo->AllowB1); + SaveIntValue(group, "UseB2Protocol", ForwardingInfo->AllowB2); + SaveIntValue(group, "SendCTRLZ", ForwardingInfo->SendCTRLZ); + + SaveIntValue(group, "FWDPersonalsOnly", ForwardingInfo->PersonalOnly); + SaveIntValue(group, "FWDNewImmediately", ForwardingInfo->SendNew); + SaveIntValue(group, "FwdInterval", ForwardingInfo->FwdInterval); + SaveIntValue(group, "RevFWDInterval", ForwardingInfo->RevFwdInterval); + SaveIntValue(group, "MaxFBBBlock", ForwardingInfo->MaxFBBBlockSize); + SaveIntValue(group, "ConTimeout", ForwardingInfo->ConTimeout); + + SaveStringValue(group, "BBSHA", ForwardingInfo->BBSHA); + } + + + // Save Housekeeping config + + group = config_setting_add(root, "Housekeeping", CONFIG_TYPE_GROUP); + + SaveInt64Value(group, "LastHouseKeepingTime", LastHouseKeepingTime); + SaveInt64Value(group, "LastTrafficTime", LastTrafficTime); + SaveIntValue(group, "MaxMsgno", MaxMsgno); + SaveIntValue(group, "BidLifetime", BidLifetime); + SaveIntValue(group, "MaxAge", MaxAge); + SaveIntValue(group, "LogLifetime", LogAge); + SaveIntValue(group, "LogLifetime", LogAge); + SaveIntValue(group, "MaintInterval", MaintInterval); + SaveIntValue(group, "UserLifetime", UserLifetime); + SaveIntValue(group, "MaintTime", MaintTime); + SaveFloatValue(group, "PR", PR); + SaveFloatValue(group, "PUR", PUR); + SaveFloatValue(group, "PF", PF); + SaveFloatValue(group, "PNF", PNF); + SaveIntValue(group, "BF", BF); + SaveIntValue(group, "BNF", BNF); + SaveIntValue(group, "NTSD", NTSD); + SaveIntValue(group, "NTSF", NTSF); + SaveIntValue(group, "NTSU", NTSU); +// SaveIntValue(group, "AP", AP); +// SaveIntValue(group, "AB", AB); + SaveIntValue(group, "DeletetoRecycleBin", DeletetoRecycleBin); + SaveIntValue(group, "SuppressMaintEmail", SuppressMaintEmail); + SaveIntValue(group, "MaintSaveReg", SaveRegDuringMaint); + SaveIntValue(group, "OverrideUnsent", OverrideUnsent); + SaveIntValue(group, "SendNonDeliveryMsgs", SendNonDeliveryMsgs); + SaveIntValue(group, "GenerateTrafficReport", GenerateTrafficReport); + + SaveOverride(group, "LTFROM", LTFROM); + SaveOverride(group, "LTTO", LTTO); + SaveOverride(group, "LTAT", LTAT); + + // Save UI config + + for (i=1; i <= GetNumberofPorts(); i++) + { + char Key[100]; + + sprintf(Key, "UIPort%d", i); + + group = config_setting_add(root, Key, CONFIG_TYPE_GROUP); + + if (group) + { + SaveIntValue(group, "Enabled", UIEnabled[i]); + SaveIntValue(group, "SendMF", UIMF[i]); + SaveIntValue(group, "SendHDDR", UIHDDR[i]); + SaveIntValue(group, "SendNull", UINull[i]); + + if (UIDigi[i]) + SaveStringValue(group, "Digis", UIDigi[i]); + } + } + + // Save User Config + + bbs = config_setting_add(root, "BBSUsers", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofUsers; i++) + { + char stats[256], stats2[256]; + struct MsgStats * Stats; + char Key[20] = "*"; + + user = UserRecPtr[i]; + + if (isdigit(user->Call[0]) || user->Call[0] == '_') + { + strcat (Key, user->Call); +// group = config_setting_add(bbs, Key, CONFIG_TYPE_GROUP); + } + else + { + strcpy(Key, user->Call); +// group = config_setting_add(bbs, user->Call, CONFIG_TYPE_GROUP); + } + /* + SaveStringValue(group, "Name", user->Name); + SaveStringValue(group, "Address", user->Address); + SaveStringValue(group, "HomeBBS", user->HomeBBS); + SaveStringValue(group, "QRA", user->QRA); + SaveStringValue(group, "pass", user->pass); + SaveStringValue(group, "ZIP", user->ZIP); + SaveStringValue(group, "CMSPass", user->CMSPass); + + SaveIntValue(group, "lastmsg", user->lastmsg); + SaveIntValue(group, "flags", user->flags); + SaveIntValue(group, "PageLen", user->PageLen); + SaveIntValue(group, "BBSNumber", user->BBSNumber); + SaveIntValue(group, "RMSSSIDBits", user->RMSSSIDBits); + SaveIntValue(group, "WebSeqNo", user->WebSeqNo); + + SaveInt64Value(group, "TimeLastConnected", user->TimeLastConnected); +*/ + Stats = &user->Total; + +// sprintf(stats, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + sprintf(stats, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", + Stats->ConnectsIn, Stats->ConnectsOut, + Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], + Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], + Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], + Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], + Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], + Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); + +// SaveStringValue(group, "Totsl", stats); + + Stats = &user->Last; + + sprintf(stats2, "%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d,%.0d", +// sprintf(stats2, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + Stats->ConnectsIn, Stats->ConnectsOut, + Stats->MsgsReceived[0], Stats->MsgsReceived[1], Stats->MsgsReceived[2], Stats->MsgsReceived[3], + Stats->MsgsSent[0], Stats->MsgsSent[1], Stats->MsgsSent[2], Stats->MsgsSent[3], + Stats->MsgsRejectedIn[0], Stats->MsgsRejectedIn[1], Stats->MsgsRejectedIn[2], Stats->MsgsRejectedIn[3], + Stats->MsgsRejectedOut[0], Stats->MsgsRejectedOut[1], Stats->MsgsRejectedOut[2], Stats->MsgsRejectedOut[3], + Stats->BytesForwardedIn[0], Stats->BytesForwardedIn[1], Stats->BytesForwardedIn[2], Stats->BytesForwardedIn[3], + Stats->BytesForwardedOut[0], Stats->BytesForwardedOut[1], Stats->BytesForwardedOut[2], Stats->BytesForwardedOut[3]); + +// SaveStringValue(group, "Last", stats2); + + sprintf(Line,"%s^%s^%s^%s^%s^%s^%s^%d^%d^%d^%d^%d^%d^%lld^%s^%s", + user->Name, user->Address, user->HomeBBS, user->QRA, user->pass, user->ZIP, user->CMSPass, + user->lastmsg, user->flags, user->PageLen, user->BBSNumber, user->RMSSSIDBits, user->WebSeqNo, + user->TimeLastConnected, stats, stats2); + + if (strlen(Line) < 10) + continue; + + SaveStringValue(bbs, Key, Line); + } + +/* + wp = config_setting_add(root, "WP", CONFIG_TYPE_GROUP); + + for (i = 0; i <= NumberofWPrecs; i++) + { + char WPString[1024]; + long long val1, val2; + + WP = WPRecPtr[i]; + val1 = WP->last_modif; + val2 = WP->last_seen; + + sprintf(Key, "R%d", i); + + sprintf(WPString, "%s|%s|%d|%d|%d|%s|%s|%s|%s|%s|%s|%ld|%ld", + &WP->callsign[0], &WP->name[0], WP->Type, WP->changed, WP->seen, &WP->first_homebbs[0], + &WP->secnd_homebbs[0], &WP->first_zip[0], &WP->secnd_zip[0], &WP->first_qth[0], &WP->secnd_qth[0], + val1, val2); + + SaveStringValue(wp, Key, WPString); + } + + // Save Message Headers + + msgs = config_setting_add(root, "MSGS", CONFIG_TYPE_GROUP); + + memset(MsgHddrPtr[0], 0, sizeof(struct MsgInfo)); + + MsgHddrPtr[0]->type = 'X'; + MsgHddrPtr[0]->status = '2'; + MsgHddrPtr[0]->number = 0; + MsgHddrPtr[0]->length = LatestMsg; + + + for (i = 0; i <= NumberofMessages; i++) + { + Msg = MsgHddrPtr[i]; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString1[n * 2], "%02X", Msg->fbbs[n]); + + n = 39; + while (n >=0 && HEXString1[n] == '0') + HEXString1[n--] = 0; + + for (n = 0; n < NBMASK; n++) + sprintf(&HEXString2[n * 2], "%02X", Msg->forw[n]); + + n = 39; + while (n >= 0 && HEXString2[n] == '0') + HEXString2[n--] = 0; + + sprintf(Key, "R%d", Msg->number); + + n = sprintf(Line, "%c|%c|%d|%lld|%s|%s|%s|%s|%s|%d|%lld|%lld|%s|%s|%s|%d|%s", Msg->type, Msg->status, + Msg->length, Msg->datereceived, &Msg->bbsfrom[0], &Msg->via[0], &Msg->from[0], + &Msg->to[0], &Msg->bid[0], Msg->B2Flags, Msg->datecreated, Msg->datechanged, HEXString1, HEXString2, + &Msg->emailfrom[0], Msg->UTF8, &Msg->title[0]); + + SaveStringValue(msgs, Key, Line); + } + + // Save Bids + + msgs = config_setting_add(root, "BIDS", CONFIG_TYPE_GROUP); + + for (i=1; i <= NumberofBIDs; i++) + { + sprintf(Key, "R%s", BIDRecPtr[i]->BID); + sprintf(Line, "%d|%d", BIDRecPtr[i]->mode, BIDRecPtr[i]->u.timestamp); + SaveStringValue(msgs, Key, Line); + } + +#ifdef LINBPQ + + if(!config_write_file(&cfg,"/dev/shm/linmail.cfg.temp" )) + { + print("Error while writing file.\n"); + config_destroy(&cfg); + FreeSemaphore(&ConfigSEM); + return; + } + + CopyFile("/dev/shm/linmail.cfg.temp", ConfigName, FALSE); + +#else +*/ + if(! config_write_file(&cfg, ConfigName)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + FreeSemaphore(&ConfigSEM); + + return; + } + +//#endif + + config_destroy(&cfg); + +/* + +#ifndef LINBPQ + + // Save a copy with current Date/Time Stamp for debugging + + { + char Backup[MAX_PATH]; + time_t LT; + struct tm * tm; + + LT = time(NULL); + tm = gmtime(<); + + sprintf(Backup,"%s.%02d%02d%02d%02d%02d.save", ConfigName, tm->tm_year-100, tm->tm_mon+1, + tm->tm_mday, tm->tm_hour, tm->tm_min); + + CopyFile(ConfigName, Backup, FALSE); // Copy to .bak + } +#endif +*/ + + FreeSemaphore(&ConfigSEM); +} + +int GetIntValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return 0; +} + +long long GetInt64Value(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int64 (setting); + + return 0; +} + +double GetFloatValue(config_setting_t * group, char * name) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + + if (setting) + { + return config_setting_get_float (setting); + } + return 0; +} + +int GetIntValueWithDefault(config_setting_t * group, char * name, int Default) +{ + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + return config_setting_get_int (setting); + + return Default; +} + + +BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen) +{ + char * str; + config_setting_t *setting; + + setting = config_setting_get_member (group, name); + if (setting) + { + str = (char *)config_setting_get_string (setting); + if (strlen(str) > maxlen) + { + Debugprintf("Suspect config record %s", str); + str[maxlen] = 0; + } + strcpy(value, str); + return TRUE; + } + value[0] = 0; + return FALSE; +} + +BOOL GetConfig(char * ConfigName) +{ + int i; + char Size[80]; + config_setting_t *setting; + char * ptr1; + char FBBString[8192]= ""; + FBBFilter f; + config_init(&cfg); + + /* Read the file. If there is an error, report it and exit. */ + + if(! config_read_file(&cfg, ConfigName)) + { + char Msg[256]; + sprintf(Msg, "Config File Line %d - %s\n", + config_error_line(&cfg), config_error_text(&cfg)); +#ifdef WIN32 + MessageBox(NULL, Msg, "BPQMail", MB_ICONSTOP); +#else + printf("%s", Msg); +#endif + config_destroy(&cfg); + return(EXIT_FAILURE); + } +/* +#if LIBCONFIG_VER_MINOR > 5 + config_set_option(&cfg, CONFIG_OPTION_AUTOCONVERT, 1); +#else + config_set_auto_convert (&cfg, 1); +#endif +*/ + group = config_lookup (&cfg, "main"); + + if (group == NULL) + return EXIT_FAILURE; + + SMTPInPort = GetIntValue(group, "SMTPPort"); + POP3InPort = GetIntValue(group, "POP3Port"); + NNTPInPort = GetIntValue(group, "NNTPPort"); + RemoteEmail = GetIntValue(group, "RemoteEmail"); + MaxStreams = GetIntValue(group, "Streams"); + BBSApplNum = GetIntValue(group, "BBSApplNum"); + EnableUI = GetIntValue(group, "EnableUI"); + MailForInterval = GetIntValue(group, "MailForInterval"); + RefuseBulls = GetIntValue(group, "RefuseBulls"); + OnlyKnown = GetIntValue(group, "OnlyKnown"); + reportMailEvents = GetIntValue(group, "reportMailEvents"); + + SendSYStoSYSOPCall = GetIntValue(group, "SendSYStoSYSOPCall"); + SendBBStoSYSOPCall = GetIntValue(group, "SendBBStoSYSOPCall"); + DontHoldNewUsers = GetIntValue(group, "DontHoldNewUsers"); + DefaultNoWINLINK = GetIntValue(group, "DefaultNoWINLINK"); + ForwardToMe = GetIntValue(group, "ForwardToMe"); + AllowAnon = GetIntValue(group, "AllowAnon"); + UserCantKillT = GetIntValue(group, "UserCantKillT"); + + DontNeedHomeBBS = GetIntValue(group, "DontNeedHomeBBS"); + DontCheckFromCall = GetIntValue(group, "DontCheckFromCall"); + MaxTXSize = GetIntValue(group, "MaxTXSize"); + MaxRXSize = GetIntValue(group, "MaxRXSize"); + ReaddressLocal = GetIntValue(group, "ReaddressLocal"); + ReaddressReceived = GetIntValue(group, "ReaddressReceived"); + WarnNoRoute = GetIntValue(group, "WarnNoRoute"); + SendPtoMultiple = GetIntValue(group, "SendPtoMultiple"); + FOURCHARCONT = GetIntValue(group, "FOURCHARCONT"); + + Localtime = GetIntValue(group, "Localtime"); + AliasText = GetMultiStringValue(group, "FWDAliases"); + GetStringValue(group, "BBSName", BBSName, 100); + GetStringValue(group, "MailForText", MailForText, 100); + GetStringValue(group, "SYSOPCall", SYSOPCall, 100); + GetStringValue(group, "H-Route", HRoute, 100); + GetStringValue(group, "AMPRDomain", AMPRDomain, 100); + SendAMPRDirect = GetIntValue(group, "SendAMPRDirect"); + ISP_Gateway_Enabled = GetIntValue(group, "SMTPGatewayEnabled"); + ISPPOP3Interval = GetIntValue(group, "POP3PollingInterval"); + GetStringValue(group, "MyDomain", MyDomain, 50); + GetStringValue(group, "ISPSMTPName", ISPSMTPName, 50); + GetStringValue(group, "ISPPOP3Name", ISPPOP3Name, 50); + ISPSMTPPort = GetIntValue(group, "ISPSMTPPort"); + ISPPOP3Port = GetIntValue(group, "ISPPOP3Port"); + GetStringValue(group, "ISPAccountName", ISPAccountName, 50); + GetStringValue(group, "ISPAccountPass", EncryptedISPAccountPass, 100); + + sprintf(SignoffMsg, "73 de %s\r", BBSName); // Default + GetStringValue(group, "SignoffMsg", ISPAccountName, 50); + + DecryptPass(EncryptedISPAccountPass, ISPAccountPass, (int)strlen(EncryptedISPAccountPass)); + + SMTPAuthNeeded = GetIntValue(group, "AuthenticateSMTP"); + LogBBS = GetIntValue(group, "Log_BBS"); + LogTCP = GetIntValue(group, "Log_TCP"); + + MulticastRX = GetIntValue(group, "MulticastRX"); + +#ifndef LINBPQ + + GetStringValue(group, "MonitorSize", Size, sizeof(Size)); + sscanf(Size,"%d,%d,%d,%d,%d",&MonitorRect.left,&MonitorRect.right,&MonitorRect.top,&MonitorRect.bottom,&OpenMon); + + GetStringValue(group, "WindowSize", Size, sizeof(Size)); + sscanf(Size,"%d,%d,%d,%d",&MainRect.left,&MainRect.right,&MainRect.top,&MainRect.bottom); + + Bells = GetIntValue(group, "Bells"); + + FlashOnBell = GetIntValue(group, "FlashOnBell"); + + StripLF = GetIntValue(group, "StripLF"); + CloseWindowOnBye = GetIntValue(group, "CloseWindowOnBye"); + WarnWrap = GetIntValue(group, "WarnWrap"); + WrapInput = GetIntValue(group, "WrapInput"); + FlashOnConnect = GetIntValue(group, "FlashOnConnect"); + + GetStringValue(group, "ConsoleSize", Size, 80); + sscanf(Size,"%d,%d,%d,%d,%d", &ConsoleRect.left, &ConsoleRect.right, + &ConsoleRect.top, &ConsoleRect.bottom,&OpenConsole); + +#endif + + // Get Welcome Messages + + setting = config_setting_get_member (group, "WelcomeMsg"); + + if (setting && setting->value.sval[0]) + { + WelcomeMsg = _strdup(config_setting_get_string (setting)); + } + else + WelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); + + + setting = config_setting_get_member (group, "NewUserWelcomeMsg"); + + if (setting && setting->value.sval[0]) + NewWelcomeMsg = _strdup(config_setting_get_string (setting)); + else + NewWelcomeMsg = _strdup("Hello $I. Latest Message is $L, Last listed is $Z\r\n"); + + + setting = config_setting_get_member (group, "ExpertWelcomeMsg"); + + if (setting && setting->value.sval[0]) + ExpertWelcomeMsg = _strdup(config_setting_get_string (setting)); + else + ExpertWelcomeMsg = _strdup(""); + + // Get Prompts + + setting = config_setting_get_member (group, "Prompt"); + + if (setting && setting->value.sval[0]) + Prompt = _strdup(config_setting_get_string (setting)); + else + { + Prompt = malloc(20); + sprintf(Prompt, "de %s>\r\n", BBSName); + } + + setting = config_setting_get_member (group, "NewUserPrompt"); + + if (setting && setting->value.sval[0]) + NewPrompt = _strdup(config_setting_get_string (setting)); + else + { + NewPrompt = malloc(20); + sprintf(NewPrompt, "de %s>\r\n", BBSName); + } + + setting = config_setting_get_member (group, "ExpertPrompt"); + + if (setting && setting->value.sval[0]) + ExpertPrompt = _strdup(config_setting_get_string (setting)); + else + { + ExpertPrompt = malloc(20); + sprintf(ExpertPrompt, "de %s>\r\n", BBSName); + } + + TidyPrompts(); + + RejFrom = GetMultiStringValue(group, "RejFrom"); + RejTo = GetMultiStringValue(group, "RejTo"); + RejAt = GetMultiStringValue(group, "RejAt"); + RejBID = GetMultiStringValue(group, "RejBID"); + + HoldFrom = GetMultiStringValue(group, "HoldFrom"); + HoldTo = GetMultiStringValue(group, "HoldTo"); + HoldAt = GetMultiStringValue(group, "HoldAt"); + HoldBID = GetMultiStringValue(group, "HoldBID"); + + // Get FBB Filters + + GetStringValue(group, "FBBFilters", FBBString, sizeof(FBBString)); + + ptr1 = FBBString; + + // delete old list + + while(Filters && Filters->Next) + { + FBBFilter * next = Filters->Next; + free(Filters); + Filters = next; + } + + free(Filters); + Filters = NULL; + + while (ptr1 && ptr1[0]) + { + FBBFilter * PFilter; + + f.Action = ptr1[0]; + f.Type = ptr1[2]; + ptr1 = &ptr1[4]; + + memcpy(f.From, ptr1, 10); + strlop(f.From, '|'); + ptr1 = strlop(ptr1, '|'); + + memcpy(f.TO, ptr1, 10); + strlop(f.TO, '|'); + ptr1 = strlop(ptr1, '|'); + + memcpy(f.AT, ptr1, 10); + strlop(f.AT, '|'); + ptr1 = strlop(ptr1, '|'); + + memcpy(f.BID, ptr1, 10); + strlop(f.BID, '|'); + ptr1 = strlop(ptr1, '|'); + + f.MaxLen = atoi(ptr1); + + // add to list + + f.Next = 0; + + PFilter = zalloc(sizeof(FBBFilter)); + + memcpy(PFilter, &f, sizeof(FBBFilter)); + + if (Filters == 0) + Filters = PFilter; + else + { + FBBFilter * p = Filters; + + while (p->Next) + p = p->Next; + + p->Next = PFilter; + } + + ptr1 = strlop(ptr1, '|'); + } + + + + + +//f.Action, f.Type, f.From, f.TO, f.AT, f.BID, &f.MaxLen); + +/* while (p) + { + ptr += sprintf(ptr, "%c|%c|%s|%s|%s|%s|%d|", + p->Action, p->Type, p->From, p->TO, p->AT, p->BID, p->MaxLen); + + p = p->Next; + } + +*/ + + // Send WP Params + + SendWP = GetIntValue(group, "SendWP"); + SendWPType = GetIntValue(group, "SendWPType"); + + GetStringValue(group, "SendWPTO", SendWPTO, sizeof(SendWPTO)); + GetStringValue(group, "SendWPVIA", SendWPVIA, sizeof(SendWPVIA)); + + SendWPAddrs = GetMultiStringValue(group, "SendWPAddrs"); + + FilterWPBulls = GetIntValue(group, "FilterWPBulls"); + NoWPGuesses = GetIntValue(group, "NoWPGuesses"); + + if (SendWPAddrs[0] == NULL && SendWPTO[0]) + { + // convert old format TO and VIA to entry in SendWPAddrs + + SendWPAddrs = realloc(SendWPAddrs, 8); // Add entry + + if (SendWPVIA[0]) + { + char WP[256]; + + sprintf(WP, "%s@%s", SendWPTO, SendWPVIA); + SendWPAddrs[0] = _strdup(WP); + } + else + SendWPAddrs[0] = _strdup(SendWPTO); + + + SendWPAddrs[1] = 0; + + SendWPTO[0] = 0; + SendWPVIA[0] = 0; + } + + GetStringValue(group, "Version", Size, sizeof(Size)); + sscanf(Size,"%d,%d,%d,%d", &LastVer[0], &LastVer[1], &LastVer[2], &LastVer[3]); + + for (i =1 ; i <= GetNumberofPorts(); i++) + { + char Key[100]; + + sprintf(Key, "UIPort%d", i); + + group = config_lookup (&cfg, Key); + + if (group) + { + UIEnabled[i] = GetIntValue(group, "Enabled"); + UIMF[i] = GetIntValueWithDefault(group, "SendMF", UIEnabled[i]); + UIHDDR[i] = GetIntValueWithDefault(group, "SendHDDR", UIEnabled[i]); + UINull[i] = GetIntValue(group, "SendNull"); + Size[0] = 0; + GetStringValue(group, "Digis", Size, sizeof(Size)); + if (Size[0]) + UIDigi[i] = _strdup(Size); + } + } + + group = config_lookup (&cfg, "Housekeeping"); + + if (group) + { + LastHouseKeepingTime = GetIntValue(group, "LastHouseKeepingTime"); + LastTrafficTime = GetIntValue(group, "LastTrafficTime"); + MaxMsgno = GetIntValue(group, "MaxMsgno"); + LogAge = GetIntValue(group, "LogLifetime"); + BidLifetime = GetIntValue(group, "BidLifetime"); + MaxAge = GetIntValue(group, "MaxAge"); + if (MaxAge == 0) + MaxAge = 30; + UserLifetime = GetIntValue(group, "UserLifetime"); + MaintInterval = GetIntValue(group, "MaintInterval"); + + if (MaintInterval == 0) + MaintInterval = 24; + + MaintTime = GetIntValue(group, "MaintTime"); + + PR = GetFloatValue(group, "PR"); + PUR = GetFloatValue(group, "PUR"); + PF = GetFloatValue(group, "PF"); + PNF = GetFloatValue(group, "PNF"); + + BF = GetIntValue(group, "BF"); + BNF = GetIntValue(group, "BNF"); + NTSD = GetIntValue(group, "NTSD"); + NTSU = GetIntValue(group, "NTSU"); + NTSF = GetIntValue(group, "NTSF"); +// AP = GetIntValue(group, "AP"); +// AB = GetIntValue(group, "AB"); + DeletetoRecycleBin = GetIntValue(group, "DeletetoRecycleBin"); + SuppressMaintEmail = GetIntValue(group, "SuppressMaintEmail"); + SaveRegDuringMaint = GetIntValue(group, "MaintSaveReg"); + OverrideUnsent = GetIntValue(group, "OverrideUnsent"); + SendNonDeliveryMsgs = GetIntValue(group, "SendNonDeliveryMsgs"); + OverrideUnsent = GetIntValue(group, "OverrideUnsent"); + GenerateTrafficReport = GetIntValueWithDefault(group, "GenerateTrafficReport", 1); + + LTFROM = GetOverrides(group, "LTFROM"); + LTTO = GetOverrides(group, "LTTO"); + LTAT = GetOverrides(group, "LTAT"); + } + + return EXIT_SUCCESS; +} + + +int Connected(int Stream) +{ + int n, Mask; + CIRCUIT * conn; + struct UserInfo * user = NULL; + char callsign[10]; + int port, paclen, maxframe, l4window; + char ConnectedMsg[] = "*** CONNECTED "; + char Msg[100]; + char Title[100]; + int64_t Freq = 0; + int Mode = 0; + BPQVECSTRUC * SESS; + TRANSPORTENTRY * Sess1 = NULL, * Sess2; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active) + { + // Probably an outgoing connect + + ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + conn->ErrorCount = 0; + + if (conn->BBSFlags & RunningConnectScript) + { + // BBS Outgoing Connect + + conn->paclen = 236; + + // Run first line of connect script + + ChangeSessionIdletime(Stream, BBSIDLETIME); // Default Idletime for BBS Sessions + ProcessBBSConnectScript(conn, ConnectedMsg, 15); + return 0; + } + } + + // Incoming Connect + + // Try to find port, freq, mode, etc + +#ifdef LINBPQ + SESS = &BPQHOSTVECTOR[0]; +#else + SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + SESS +=(Stream - 1); + + if (SESS) + Sess1 = SESS->HOSTSESSION; + + if (Sess1) + { + Sess2 = Sess1->L4CROSSLINK; + + if (Sess2) + { + // See if L2 session - if so, get info from WL2K report line + + // if Session has report info, use it + + if (Sess2->Mode) + { + Freq = Sess2->Frequency; + Mode = Sess2->Mode; + } + else if (Sess2->L4CIRCUITTYPE & L2LINK) + { + LINKTABLE * LINK = Sess2->L4TARGET.LINK; + PORTCONTROLX * PORT = LINK->LINKPORT; + + Freq = PORT->WL2KInfo.Freq; + Mode = PORT->WL2KInfo.mode; + } + else + { + if (Sess2->RMSCall[0]) + { + Freq = Sess2->Frequency; + Mode = Sess2->Mode; + } + } + } + } + + memset(conn, 0, sizeof(ConnectionInfo)); // Clear everything + conn->Active = TRUE; + conn->BPQStream = Stream; + ChangeSessionIdletime(Stream, USERIDLETIME); // Default Idletime for BBS Sessions + + conn->SendB = conn->SendP = conn->SendT = conn->DoReverse = TRUE; + conn->MaxBLen = conn->MaxPLen = conn->MaxTLen = 99999999; + conn->ErrorCount = 0; + + conn->Secure_Session = GetConnectionInfo(Stream, callsign, + &port, &conn->SessType, &paclen, &maxframe, &l4window); + + strlop(callsign, ' '); // Remove trailing spaces + + if (strcmp(&callsign[strlen(callsign) - 2], "-T") == 0) + conn->RadioOnlyMode = 'T'; + else if (strcmp(&callsign[strlen(callsign) - 2], "-R") == 0) + conn->RadioOnlyMode = 'R'; + else + conn->RadioOnlyMode = 0; + + memcpy(conn->Callsign, callsign, 10); + + strlop(callsign, '-'); // Remove any SSID + + user = LookupCall(callsign); + + if (user == NULL) + { + int Length=0; + + if (OnlyKnown) + { + // Unknown users not allowed + + n = sprintf_s(Msg, sizeof(Msg), "Incoming Connect from unknown user %s Rejected", callsign); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + Disconnect(Stream); + return 0; + } + + user = AllocateUserRecord(callsign); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (SendNewUserMessage) + { + int64_t LongFreq = Freq; + + char * MailBuffer = malloc(100); + + if (Freq == 0 && port) + { + // Get Port Freq if available + + char FreqString[256]; + +#ifdef WIN32 + if (pGetPortFrequency) + LongFreq = pGetPortFrequency(port, FreqString); +#else + LongFreq = GetPortFrequency(port, FreqString); +#endif + } + Length += sprintf(MailBuffer, "New User %s Connected to Mailbox on Port %d Freq %d Mode %ld\r\n", callsign, port, LongFreq, Mode); + + sprintf(Title, "New User %s", callsign); + + SendMessageToSYSOP(Title, MailBuffer, Length); + } + + if (user == NULL) return 0; // Cant happen?? + + if (!DontHoldNewUsers) + user->flags |= F_HOLDMAIL; + + if (DefaultNoWINLINK) + user->flags |= F_NOWINLINK; + + // Always set WLE User - can't see it doing any harm + + user->flags |= F_Temp_B2_BBS; + + conn->NewUser = TRUE; + } + + user->TimeLastConnected = time(NULL); + user->Total.ConnectsIn++; + + conn->UserPointer = user; + + conn->lastmsg = user->lastmsg; + + conn->NextMessagetoForward = FirstMessageIndextoForward; + + if (paclen == 0) + { + paclen = 236; + + if (conn->SessType & Sess_PACTOR) + paclen = 100; + } + + conn->paclen = paclen; + + // Set SYSOP flag if user is defined as SYSOP and Host Session + + if (((conn->SessType & Sess_BPQHOST) == Sess_BPQHOST) && (user->flags & F_SYSOP)) + conn->sysop = TRUE; + + if (conn->Secure_Session && (user->flags & F_SYSOP)) + conn->sysop = TRUE; + + Mask = 1 << (GetApplNum(Stream) - 1); + + if (user->flags & F_Excluded) + { + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s Rejected by Exclude Flag", user->Call); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + Disconnect(Stream); + return 0; + } + + if (port) + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s on Port %d Freq %d Mode %s", + user->Call, port, Freq, WL2KModes[Mode]); + else + n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call); + + // Send SID and Prompt (Unless Sync) + + if (user->ForwardingInfo && user->ForwardingInfo->ConTimeout) + conn->SIDResponseTimer = user->ForwardingInfo->ConTimeout / 10; // 10 sec ticks + else + conn->SIDResponseTimer = 12; // Allow a couple of minutes for response to SID + + { + BOOL B1 = FALSE, B2 = FALSE, BIN = FALSE, BLOCKED = FALSE; + BOOL WL2KRO = FALSE; + + struct BBSForwardingInfo * ForwardingInfo; + + if (conn->RadioOnlyMode == 'R') + WL2KRO = 1; + + conn->PageLen = user->PageLen; + conn->Paging = (user->PageLen > 0); + + if (user->flags & F_Temp_B2_BBS) + { + // An RMS Express user that needs a temporary BBS struct + + if (user->ForwardingInfo == NULL) + { + // we now save the Forwarding info if BBS flag is cleared, + // so there may already be a ForwardingInfo + + user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + } + + if (user->BBSNumber == 0) + user->BBSNumber = NBBBS; + + ForwardingInfo = user->ForwardingInfo; + + ForwardingInfo->AllowCompressed = TRUE; + B1 = ForwardingInfo->AllowB1 = FALSE; + B2 = ForwardingInfo->AllowB2 = TRUE; + BLOCKED = ForwardingInfo->AllowBlocked = TRUE; + } + + if (conn->NewUser) + { + BLOCKED = TRUE; + BIN = TRUE; + B2 = TRUE; + } + + if (user->ForwardingInfo) + { + BLOCKED = user->ForwardingInfo->AllowBlocked; + if (BLOCKED) + { + BIN = user->ForwardingInfo->AllowCompressed; + B1 = user->ForwardingInfo->AllowB1; + B2 = user->ForwardingInfo->AllowB2; + } + } + + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + if (conn->RadioOnlyMode) + nodeprintf(conn,";WL2K-Radio/Internet_Network\r"); + + if (!(conn->BBSFlags & SYNCMODE)) + { + + nodeprintf(conn, BBSSID, "BPQ-", + Ver[0], Ver[1], Ver[2], Ver[3], + BIN ? "B" : "", B1 ? "1" : "", B2 ? "2" : "", + BLOCKED ? "FW": "", WL2KRO ? "" : "J"); + + // if (user->flags & F_Temp_B2_BBS) + // nodeprintf(conn,";PQ: 66427529\r"); + + // nodeprintf(conn,"[WL2K-BPQ.1.0.4.39-B2FWIHJM$]\r"); + } + } + + if ((user->Name[0] == 0) & AllowAnon) + strcpy(user->Name, user->Call); + + if (!(conn->BBSFlags & SYNCMODE)) + { + if (user->Name[0] == 0) + { + conn->Flags |= GETTINGUSER; + BBSputs(conn, NewUserPrompt); + } + else + SendWelcomeMsg(Stream, conn, user); + } + else + { + // Seems to be a timing problem - see if this fixes it + + Sleep(500); + } + + RefreshMainWindow(); + + return 0; + } + } + + return 0; +} + +int Disconnected (int Stream) +{ + struct UserInfo * user = NULL; + CIRCUIT * conn; + int n; + char Msg[255]; + int len; + char DiscMsg[] = "DISCONNECTED "; + + for (n = 0; n <= NumberofStreams-1; n++) + { + conn=&Connections[n]; + + if (Stream == conn->BPQStream) + { + if (conn->Active == FALSE) + return 0; + + // if still running connect script, reenter it to see if + // there is an else + + if (conn->BBSFlags & RunningConnectScript) + { + // We need to see if we got as far as connnected, + // as if we have we need to reset the connect script + // over the ELSE + + struct BBSForwardingInfo * ForwardingInfo = conn->UserPointer->ForwardingInfo; + char ** Scripts; + + if (ForwardingInfo->TempConnectScript) + Scripts = ForwardingInfo->TempConnectScript; + else + Scripts = ForwardingInfo->ConnectScript; + + // First see if any script left + + if (Scripts[ForwardingInfo->ScriptIndex]) + { + if (ForwardingInfo->MoreLines == FALSE) + { + // Have reached end of script, so need to set back over ELSE + + ForwardingInfo->ScriptIndex--; + ForwardingInfo->MoreLines = TRUE; + } + + // if (Scripts[ForwardingInfo->ScriptIndex] == NULL || + // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "TIMES", 5) == 0 || // Only Check until script is finished + // _memicmp(Scripts[ForwardingInfo->ScriptIndex], "ELSE", 4) == 0) // Only Check until script is finished + + + ProcessBBSConnectScript(conn, DiscMsg, 15); + return 0; + } + } + + user = conn->UserPointer; + + if (user && (conn->lastmsg > user->lastmsg)) + { + user->lastmsg = conn->lastmsg; + SaveUserDatabase(); + } + + + // if sysop was chatting to user clear link +#ifndef LINBPQ + if (conn->BBSFlags & SYSOPCHAT) + { + SendUnbuffered(-1, "User has disconnected\n", 23); + BBSConsole.Console->SysopChatStream = 0; + } +#endif + ClearQueue(conn); + + if (conn->PacLinkCalls) + free(conn->PacLinkCalls); + + if (conn->InputBuffer) + { + free(conn->InputBuffer); + conn->InputBuffer = NULL; + conn->InputBufferLen = 0; + } + + /* ---- G7TAJ PG SERVER ---- */ + if (conn->UserPointer && conn->UserPointer->Temp && conn->UserPointer->Temp->RUNPGPARAMS) + { + Debugprintf("Freeing RUNPGPARAMS"); + free(conn->UserPointer->Temp->RUNPGPARAMS); + conn->UserPointer->Temp->RUNPGPARAMS = NULL; + } + + /*------- G7TAJ END --------- */ + + if (conn->InputMode == 'B') + { + // Save partly received message for a restart + + if (conn->BBSFlags & FBBB1Mode) + if (conn->Paclink == 0) // Paclink doesn't do restarts + if (strcmp(conn->Callsign, "RMS") != 0) // Neither does RMS Packet. + if (conn->DontSaveRestartData == FALSE) + SaveFBBBinary(conn); + } + + conn->Active = FALSE; + + if (conn->FwdMsg) + conn->FwdMsg->Locked = 0; // Unlock + + RefreshMainWindow(); + + RemoveTempBIDS(conn); + + len=sprintf_s(Msg, sizeof(Msg), "%s Disconnected", conn->Callsign); + WriteLogLine(conn, '|',Msg, len, LOG_BBS); + + if (conn->FBBHeaders) + { + struct FBBHeaderLine * FBBHeader; + int n; + + for (n = 0; n < 5; n++) + { + FBBHeader = &conn->FBBHeaders[n]; + + if (FBBHeader->FwdMsg) + FBBHeader->FwdMsg->Locked = 0; // Unlock + + } + + free(conn->FBBHeaders); + conn->FBBHeaders = NULL; + } + + if (conn->UserPointer) + { + struct BBSForwardingInfo * FWDInfo = conn->UserPointer->ForwardingInfo; + + if (FWDInfo) + { + FWDInfo->Forwarding = FALSE; + +// if (FWDInfo->UserCall[0]) // Will be set if RMS +// { +// FindNextRMSUser(FWDInfo); +// } +// else + FWDInfo->FwdTimer = 0; + } + } + + conn->BBSFlags = 0; // Clear ARQ Mode + + return 0; + } + } + return 0; +} + +int DoReceivedData(int Stream) +{ + int count, InputLen; + size_t MsgLen; + int n; + CIRCUIT * conn; + struct UserInfo * user; + char * ptr, * ptr2; + char * Buffer; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + if (Stream == conn->BPQStream) + { + conn->SIDResponseTimer = 0; // Got a message, so cancel timeout. + + do + { + // May have several messages per packet, or message split over packets + + OuterLoop: + if (conn->InputLen + 1000 > conn->InputBufferLen ) // Shouldnt have lines longer than this in text mode + { + conn->InputBufferLen += 1000; + conn->InputBuffer = realloc(conn->InputBuffer, conn->InputBufferLen); + } + + GetMsg(Stream, &conn->InputBuffer[conn->InputLen], &InputLen, &count); + + if (InputLen == 0 && conn->InputMode != 'Y') + return 0; + + conn->InputLen += InputLen; + + if (conn->InputLen == 0) return 0; + + conn->Watchdog = 900; // 15 Minutes + + if (conn->InputMode == 'Y') // YAPP + { + if (ProcessYAPPMessage(conn)) // Returns TRUE if there could be more to process + goto OuterLoop; + + return 0; + } + + + /* ---------- G7TAJ START - PG server --------- */ + + if (conn->InputMode == 'P') // Inside PG Server + { + user = conn->UserPointer; + run_pg(conn, user); + return 0; + } + /* ---------- G7TAJ END --------- */ + + if (conn->InputMode == 'B') + { + // if in OpenBCM mode, remove FF transparency + + if (conn->OpenBCM) // Telnet, so escape any 0xFF + { + unsigned char * ptr1 = conn->InputBuffer; + unsigned char * ptr2; + int Len; + unsigned char c; + + // We can come through here again for the + // same data as we wait for a full packet + // So only check last InputLen bytes + + ptr1 += (conn->InputLen - InputLen); + ptr2 = ptr1; + Len = InputLen; + + while (Len--) + { + c = *(ptr1++); + + if (conn->InTelnetExcape) // Last char was ff + { + conn->InTelnetExcape = FALSE; + continue; + } + + *(ptr2++) = c; + + if (c == 0xff) // + conn->InTelnetExcape = TRUE; + } + + conn->InputLen = (int)(ptr2 - conn->InputBuffer); + } + + UnpackFBBBinary(conn); + goto OuterLoop; + } + else + { + + loop: + + if (conn->InputLen == 1 && conn->InputBuffer[0] == 0) // Single Null + { + conn->InputLen = 0; + return 0; + } + + user = conn->UserPointer; + + if (conn->BBSFlags & (MCASTRX | SYNCMODE)) + { + // MCAST and SYNCMODE deliver full packets + + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); + + conn->InputLen=0; + continue; + } + + // This looks for CR, CRLF, LF or CR/Null and removes any LF or NULL, + // but this relies on both arriving in same packet. + // Need to check for LF and start of packet and ignore it + // But what if client is only using LF?? + // (WLE sends SID with CRLF, other packets with CR only) + + // We don't get here on the data part of a binary transfer, so + // don't need to worry about messing up binary data. + + ptr = memchr(conn->InputBuffer, '\r', conn->InputLen); + ptr2 = memchr(conn->InputBuffer, '\n', conn->InputLen); + + if (ptr) + conn->usingCR = 1; + + if ((ptr2 && ptr2 < ptr) || ptr == 0) // LF before CR, or no CR + ptr = ptr2; // Use LF + + if (ptr) // CR or LF in buffer + { + conn->lastLineEnd = *(ptr); + + *(ptr) = '\r'; // In case was LF + + ptr2 = &conn->InputBuffer[conn->InputLen]; + + if (++ptr == ptr2) + { + // Usual Case - single msg in buffer + + // if Length is 1 and Term is LF and normal line end is CR + // this is from a split CRLF - Ignore it + + if (conn->InputLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) + Debugprintf("BPQMail split Line End Detected"); + else + { + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, conn->InputBuffer, conn->InputLen); + else + ProcessLine(conn, user, conn->InputBuffer, conn->InputLen); + } + conn->InputLen=0; + } + else + { + // buffer contains more that 1 message + + MsgLen = conn->InputLen - (ptr2-ptr); + + Buffer = malloc(MsgLen + 100); + + memcpy(Buffer, conn->InputBuffer, MsgLen); + + // if Length is 1 and Term is LF and normal line end is CR + // this is from a split CRLF - Ignore it + + if (MsgLen == 1 && conn->lastLineEnd == 0x0a && conn->usingCR) + Debugprintf("BPQMail split Line End Detected"); + else + { + if (conn->BBSFlags & RunningConnectScript) + ProcessBBSConnectScript(conn, Buffer, (int)MsgLen); + else + ProcessLine(conn, user, Buffer, (int)MsgLen); + } + free(Buffer); + + if (*ptr == 0 || *ptr == '\n') + { + /// CR LF or CR Null + + ptr++; + conn->InputLen--; + } + + memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen); + + conn->InputLen -= (int)MsgLen; + + goto loop; + + } + } + else + { + // Could be a YAPP Header + + + if (conn->InputLen == 2 && conn->InputBuffer[0] == ENQ && conn->InputBuffer[1] == 1) // YAPP Send_Init + { + UCHAR YAPPRR[2]; + YAPPRR[0] = ACK; + YAPPRR[1] = 1; + + conn->InputMode = 'Y'; + QueueMsg(conn, YAPPRR, 2); + + conn->InputLen = 0; + return 0; + } + } + } + + } while (count > 0); + + return 0; + } + } + + // Socket not found + + return 0; + +} +int DoBBSMonitorData(int Stream) +{ +// UCHAR Buffer[1000]; + UCHAR buff[500]; + + int len = 0,count=0; + int stamp; + + do + { + stamp=GetRaw(Stream, buff,&len,&count); + + if (len == 0) return 0; + + SeeifBBSUIFrame((struct _MESSAGEX *)buff, len); + } + + while (count > 0); + + + return 0; + +} + +VOID ProcessFLARQLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int MsgLen) +{ + Buffer[MsgLen] = 0; + + if (MsgLen == 1 && Buffer[0] == 13) + return; + + if (strcmp(Buffer, "ARQ::ETX\r") == 0) + { + // Decode it. + + UCHAR * ptr1, * ptr2, * ptr3; + int len, linelen; + struct MsgInfo * Msg = conn->TempMsg; + time_t Date; + char FullTo[100]; + char FullFrom[100]; + char ** RecpTo = NULL; // May be several Recipients + char ** HddrTo = NULL; // May be several Recipients + char ** Via = NULL; // May be several Recipients + int LocalMsg[1000] ; // Set if Recipient is a local wl2k address + + int B2To; // Offset to To: fields in B2 header + int Recipients = 0; + int RMSMsgs = 0, BBSMsgs = 0; + +// Msg->B2Flags |= B2Msg; + + + ptr1 = conn->MailBuffer; + len = Msg->length; + ptr1[len] = 0; + + if (strstr(ptr1, "ARQ:ENCODING::")) + { + // a file, not a message. If is called "BBSPOLL" do a reverse forward else Ignore for now + + _strupr(conn->MailBuffer); + if (strstr(conn->MailBuffer, "BBSPOLL")) + { + SendARQMail(conn); + } + + free(conn->MailBuffer); + conn->MailBuffer = NULL; + conn->MailBufferSize = 0; + + return; + } + Loop: + ptr2 = strchr(ptr1, '\r'); + + linelen = (int)(ptr2 - ptr1); + + if (_memicmp(ptr1, "From:", 5) == 0 && linelen > 6) // Can have empty From: + { + char SaveFrom[100]; + char * FromHA; + + memcpy(FullFrom, ptr1, linelen); + FullFrom[linelen] = 0; + + // B2 From may now contain an @BBS + + strcpy(SaveFrom, FullFrom); + + FromHA = strlop(SaveFrom, '@'); + + if (strlen(SaveFrom) > 12) SaveFrom[12] = 0; + + strcpy(Msg->from, &SaveFrom[6]); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + + // Remove any SSID + + ptr3 = strchr(Msg->from, '-'); + if (ptr3) *ptr3 = 0; + + } + else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0) + { + HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *)); + HddrTo[Recipients] = zalloc(100); + + memset(FullTo, 0, 99); + memcpy(FullTo, &ptr1[4], linelen-4); + memcpy(HddrTo[Recipients], ptr1, linelen+2); + LocalMsg[Recipients] = FALSE; + + _strupr(FullTo); + + B2To = (int)(ptr1 - conn->MailBuffer); + + if (_memicmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + strcpy(FullTo, "RMS"); + strcpy(Msg->via, &FullTo[4]); + } + else + { + ptr3 = strchr(FullTo, '@'); + + if (ptr3) + { + *ptr3++ = 0; + strcpy(Msg->via, ptr3); + } + else + Msg->via[0] = 0; + } + + if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) + { + // Airmail Sends MARS messages as SMTP + + if (CheckifPacket(Msg->via)) + { + // Packet Message + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + _strupr(Msg->via); + + // Update the saved to: line (remove the smtp:) + + strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); + BBSMsgs++; + goto BBSMsg; + } + + // If a winlink.org address we need to convert to call + + if (_stricmp(Msg->via, "winlink.org") == 0) + { + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + { + memcpy(Msg->via, &ptr1[9], linelen); + Msg->via[linelen - 9] = 0; + strcpy(FullTo,"RMS"); + } +// FullTo[0] = 0; + + BBSMsg: + _strupr(FullTo); + _strupr(Msg->via); + } + + if (memcmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + } + + if (strcmp(Msg->via, "RMS") == 0) + { + // replace RMS with @winlink.org + + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo); + } + + if (strlen(FullTo) > 6) + FullTo[6] = 0; + + strlop(FullTo, '-'); + + strcpy(Msg->to, FullTo); + + if (SendBBStoSYSOPCall) + if (_stricmp(FullTo, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0)) + { + // No routing - check @BBS and WP + + struct UserInfo * ToUser = LookupCall(FullTo); + + Msg->via[0] = 0; // In case BPQ and not found + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + } + } + else + { + WPRecP WP = LookupWP(FullTo); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + + } + } + + // Fix To: address in B2 Header + + if (Msg->via[0]) + sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via); + else + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + + } + + RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *)); + RecpTo[Recipients] = zalloc(10); + + Via=realloc(Via, (Recipients+1) * sizeof(void *)); + Via[Recipients] = zalloc(50); + + strcpy(Via[Recipients], Msg->via); + strcpy(RecpTo[Recipients++], FullTo); + + // Remove the To: Line from the buffer + + } + else if (_memicmp(ptr1, "Type:", 4) == 0) + { + if (ptr1[6] == 'N') + Msg->type = 'T'; // NTS + else + Msg->type = ptr1[6]; + } + else if (_memicmp(ptr1, "Subject:", 8) == 0) + { + size_t Subjlen = ptr2 - &ptr1[9]; + if (Subjlen > 60) Subjlen = 60; + memcpy(Msg->title, &ptr1[9], Subjlen); + + goto ProcessBody; + } +// else if (_memicmp(ptr1, "Body:", 4) == 0) +// { +// MsgLen = atoi(&ptr1[5]); +// StartofMsg = ptr1; +// } + else if (_memicmp(ptr1, "File:", 5) == 0) + { + Msg->B2Flags |= Attachments; + } + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: 2009/07/25 10:08 + + sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d:%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + sscanf(&ptr1[5], "%02d/%02d/%04d %02d:%02d:%02d", + &rtime.tm_mday, &rtime.tm_mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + Msg->datecreated = Date; + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip cr + goto Loop; + } + + + // Processed all headers +ProcessBody: + + ptr2 +=2; // skip crlf + + Msg->length = (int)(&conn->MailBuffer[Msg->length] - ptr2); + + memmove(conn->MailBuffer, ptr2, Msg->length); + + CreateMessageFromBuffer(conn); + + conn->BBSFlags = 0; // Clear ARQ Mode + return; + } + + // File away the data + + Buffer[MsgLen++] = 0x0a; // BBS Msgs stored with crlf + + if ((conn->TempMsg->length + MsgLen) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, MsgLen); + + conn->TempMsg->length += MsgLen; + + return; + + // Not sure what to do yet with files, but will process emails (using text style forwarding + +/* +ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::96 +ARQ::STX +//FLARQ COMPOSER +Date: 16/01/2014 22:26:06 +To: g8bpq +From: +Subject: test message + +Hello +Hello + +ARQ::ETX +*/ + + return; +} + +VOID ProcessTextFwdLine(ConnectionInfo * conn, struct UserInfo * user, char * Buffer, int len) +{ + Buffer[len] = 0; +// Debugprintf(Buffer); + + // With TNC2 body prompt is a single CR, so that shouldn't be ignored. + + // If thia causes problems with other TNC PMS implementations I'll have to revisit this + +// if (len == 1 && Buffer[0] == 13) +// return; + + if (conn->Flags & SENDTITLE) + { + // Waiting for Subject: prompt + + struct MsgInfo * Msg = conn->FwdMsg; + + nodeprintf(conn, "%s\r", Msg->title); + + conn->Flags &= ~SENDTITLE; + conn->Flags |= SENDBODY; + + // New Paccom PMS (V3.2) doesn't prompt for body so drop through and send it + if ((conn->BBSFlags & NEWPACCOM) == 0) + return; + + } + + if (conn->Flags & SENDBODY) + { + // Waiting for Enter Message Prompt + + struct tm * tm; + time_t temp; + + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + char * MsgPtr; + int MsgLen; + int Index = 0; + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + MsgPtr = MsgBytes; + MsgLen = conn->FwdMsg->length; + + // If a B2 Message, remove B2 Header + + if (conn->FwdMsg->B2Flags & B2Msg) + { + // Remove all B2 Headers, and all but the first part. + + MsgPtr = strstr(MsgBytes, "Body:"); + + if (MsgPtr) + { + MsgLen = atoi(&MsgPtr[5]); + MsgPtr= strstr(MsgBytes, "\r\n\r\n"); // Blank Line after headers + + if (MsgPtr) + MsgPtr +=4; + else + MsgPtr = MsgBytes; + + } + else + MsgPtr = MsgBytes; + } + + memcpy(&temp, &conn->FwdMsg->datereceived, sizeof(time_t)); + tm = gmtime(&temp); + + nodeprintf(conn, "R:%02d%02d%02d/%02d%02dZ %d@%s.%s %s\r", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, + conn->FwdMsg->number, BBSName, HRoute, RlineVer); + + if (memcmp(MsgPtr, "R:", 2) != 0) // No R line, so must be our message - put blank line after header + BBSputs(conn, "\r"); + + MsgLen = RemoveLF(MsgPtr, MsgLen); + + QueueMsg(conn, MsgPtr, MsgLen); + + if (user->ForwardingInfo->SendCTRLZ) + nodeprintf(conn, "\r\x1a"); + else + nodeprintf(conn, "\r/ex\r"); + + free(MsgBytes); + + conn->FBBMsgsSent = TRUE; + + + if (conn->FwdMsg->type == 'P') + Index = PMSG; + else if (conn->FwdMsg->type == 'B') + Index = BMSG; + else if (conn->FwdMsg->type == 'T') + Index = TMSG; + + user->Total.MsgsSent[Index]++; + user->Total.BytesForwardedOut[Index] += MsgLen; + + conn->Flags &= ~SENDBODY; + conn->Flags |= WAITPROMPT; + + return; + } + + if (conn->Flags & WAITPROMPT) + { + if (Buffer[len-2] != '>') + return; + + conn->Flags &= ~WAITPROMPT; + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + SaveMessageDatabase(); + +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(conn->FwdMsg); +#endif + + conn->UserPointer->ForwardingInfo->MsgCount--; + + // See if any more to forward + + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg; + + // If we are using SETCALLTOSENDER make sure this message is from the same sender + +#ifdef LINBPQ + BPQVECSTRUC * SESS = &BPQHOSTVECTOR[0]; +#else + BPQVECSTRUC * SESS = (BPQVECSTRUC *)BPQHOSTVECPTR; +#endif + unsigned char AXCall[7]; + + Msg = conn->FwdMsg; + ConvToAX25(Msg->from, AXCall); + if (memcmp(SESS[conn->BPQStream - 1].HOSTSESSION->L4USER, AXCall, 7) != 0) + { + Disconnect(conn->BPQStream); + return; + } + + // Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ + + conn->Flags |= SENDTITLE; + + + if ((conn->BBSFlags & SETCALLTOSENDER)) + nodeprintf(conn, "S%c %s @ %s \r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call); + else + nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to, + (Msg->via[0]) ? Msg->via : conn->UserPointer->Call, + Msg->from, Msg->bid); + } + else + { + Disconnect(conn->BPQStream); + } + return; + } +} + + +#define N 2048 /* buffer size */ +#define F 60 /* lookahead buffer size */ +#define THRESHOLD 2 +#define NIL N /* leaf of tree */ + +extern UCHAR * infile; + +BOOL CheckforMIME(SocketConn * sockptr, char * Msg, char ** Body, int * MsgLen); + +/* ---G7TAJ PG Server --- */ +#ifndef WIN32 + +#define verbose 1 +#define TRUE 1 +#define FALSE 0 +#include +#include + +// G8BPQ Version of Steve G7TAJ's code + +int pgret = 9999; +int pindex = 0; + +void sigchild_handler(int sig , siginfo_t * siginfo, void * ucontext) +{ +/* • SIGCHLD fills in si_pid, si_uid, si_status, si_utime, and + si_stime, providing information about the child. The si_pid + field is the process ID of the child; si_uid is the child's + real user ID. The si_status field contains the exit status of + the child (if si_code is CLD_EXITED), or the signal number + that caused the process to change state. +*/ +// printf("SIGCHLD PID %d Code %d status %d\n", siginfo->si_pid, siginfo->si_code, siginfo->si_status); + pgret = siginfo->si_status; +} + + +void run_pg(CIRCUIT * conn, struct UserInfo * user) +{ + register char *cp; + FILE *iop; + int argc, pdes[2]; + pid_t pid; + + pgret = 9999; + + int index = user->Temp->PG_INDEX; + + iop = NULL; + + conn->InputBuffer[conn->InputLen] = 0; + strlop(conn->InputBuffer, 13); + conn->InputLen = 0; + + if (!user->Temp->RUNPGPARAMS) + user->Temp->RUNPGPARAMS = (RUNPGARGS_PTR) zalloc(sizeof(RUNPGARGS)); + + user->Temp->RUNPGPARAMS->user = user; + user->Temp->RUNPGPARAMS->conn = conn; + strncpy(user->Temp->RUNPGPARAMS->InputBuffer, conn->InputBuffer, 80); // needs to be length of actual input! + user->Temp->RUNPGPARAMS->Len = conn->InputLen; + + if (conn == 0 || user == 0) + { + Debugprintf("run_pg conn or user null"); + return; + } + + // Build command line. Parmas are: + + // - Callsign (format as F6FBB-8). + // - Level number (0 is the first time, up to 99). + // - Flags of the user (binary number as user`s mask of INIT.SRV). + // - Record number of the user in INF.SYS. + // - Received data (each word is a new argument). + + // BPQ doesn't support params 3 and 4 (but may supply copy of user record later) + + char cmd[20]; + char *ptr = cmd; + char pg_dir[MAX_PATH]; + char log_file[50] = "pg.log"; + char call[10]; + char data[80]; + char line[80]; + size_t bufsize = 80; + + strcpy(pg_dir, BaseDir); + strcat(pg_dir, "/PG/"); + sprintf(cmd, "./%s", SERVERLIST[user->Temp->PG_SERVER][1] ); + + sprintf(line, "%s%s", pg_dir, SERVERLIST[user->Temp->PG_SERVER][1]); +// printf("PG Prog %s%s\n", pg_dir, SERVERLIST[user->Temp->PG_SERVER][1]); + + // check file exists and is executable + + if (access(line, F_OK) == -1 || access(line, X_OK) == -1) + { + Debugprintf("%s FileNotFound || not executable", line); + BBSputs(conn, "Error running PG Server\r"); + conn->InputMode=0; + SendPrompt(conn, user); + return; + } + + strcpy(call, conn->UserPointer->Call); + index = user->Temp->PG_INDEX; + + // remove ';' from input for security reasons + + ptr = strchr(user->Temp->RUNPGPARAMS->InputBuffer, ';'); + if (ptr) + *ptr = '\0'; + + sprintf(data, "%s %d 0 0 %s", call, index, user->Temp->RUNPGPARAMS->InputBuffer); +// printf("PG Params %s\n", data); + + conn->InputBufferLen = 0; + + char buf[256]; + + sprintf (buf, "%s %s", line, data); // buf is command to exec +// printf ("PG exec cmd %s\n", buf); + + // Create pipe for reading PG program STDOUT + + if (pipe(pdes) < 0) + { + Debugprintf("run_pg pipe failed"); + BBSputs(conn, "Error running PG Server (pipe() failed)\r"); + conn->InputMode=0; + SendPrompt(conn, user); + return; + } + + // We will just fork and execute program. For now don't create a new thread + + // Trap sigchild so we can tell when it exits and get return code + + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_flags = SA_RESETHAND | SA_SIGINFO; // Restore default handler when called + act.sa_sigaction = sigchild_handler; + sigaction(SIGCHLD, &act, NULL); + + switch(pid = fork()) + { + case -1: /* error */ + (void)close(pdes[0]); + (void)close(pdes[1]); + Debugprintf("run_pg fork failed"); + BBSputs(conn, "Error running PG Server (fork() failed)\r"); + conn->InputMode=0; + SendPrompt(conn, user); + + return; + + case 0: /* child */ + + if (pdes[1] != 1) + { + dup2(pdes[1], 1); + dup2(pdes[1], 2); + (void)close(pdes[1]); + } + (void)close(pdes[0]); + + setpgid(0, pid); + + char *args[] = {"sh", "-c", buf, NULL}; + execve("/bin/sh", args, NULL); + + _exit(1); + } + + /* parent */ + +// printf("child PID %d\n", pid); + + struct timespec duration; + duration.tv_sec = 5; + duration.tv_nsec = 0; + + nanosleep(&duration, &duration); // Will be interrupted by SIGCHLD + +// printf("PG retcode %d\n", pgret); + + if (pgret == 9999) // Process still running + { + BBSputs(conn, "PG Program Looping\r"); + kill(pid, SIGKILL); + user->Temp->PG_INDEX = 0; + conn->InputMode=0; + SendPrompt(conn, user); + return; + } + + if (pgret > 127) + { + // Probably killed by signal + + int err = pgret - 128; + char errmsg[256]; + + sprintf(errmsg, "PG Signal %s received\n", strsignal(err)); + + BBSputs(conn, errmsg); + user->Temp->PG_INDEX = 0; + conn->InputMode=0; + SendPrompt(conn, user); + return; + } + + // Send STDOUT from PG program to BBS user + + iop = fdopen(pdes[0], "r"); + (void)close(pdes[1]); + + char buffer[128]; + while (fgets(buffer, sizeof(buffer), iop) != NULL) + { + BBSputs(conn, buffer); + buffer[0] = '\0'; + } + + switch (pgret) + { + case -1: // ERROR or forced closed + case 0: + index=0; // Goodbye/Exit + conn->InputMode=0; + SendPrompt(conn, user); + break; + case 1: + index++; // inc & keep in PG + break; + + case 2: + + index=0; // disconnect + conn->InputMode=0; + Disconnect(conn->BPQStream); + break; + + case 3: + Debugprintf("data->BBS & end"); + break; + + case 4: + Debugprintf("data->BBS and inc %d", pindex++); + break; + case 5: + Debugprintf("call no inc %d", pgret); + break; + + default: + BBSputs(conn, "PG unexexpected response\r"); + user->Temp->PG_INDEX = 0; + conn->InputMode=0; + SendPrompt(conn, user); + return; + } + + + user->Temp->PG_INDEX = index; + +// printf("runpg return index = %d\n", index); +} + + +/*---- G7TAJ END ----- */ + +#else + +#define BUFSIZE 4096 + +HANDLE g_hChildStd_IN_Rd = NULL; +HANDLE g_hChildStd_IN_Wr = NULL; +HANDLE g_hChildStd_OUT_Rd = NULL; +HANDLE g_hChildStd_OUT_Wr = NULL; + +HANDLE g_hInputFile = NULL; + +int CreateChildProcess(void); +void WriteToPipe(void); +void ReadFromPipe(void); + + +void run_pg( CIRCUIT * conn, struct UserInfo * user ) +{ + // Run PG program, read anything from program's stdout to the user + + int retcode = -1; + SECURITY_ATTRIBUTES saAttr; + char szCmdline[256] = "C:\\test\\hello.exe g8bpq 0"; + PROCESS_INFORMATION piProcInfo; + STARTUPINFO siStartInfo; + BOOL bSuccess = FALSE; + DWORD dwRead; + CHAR chBuf[BUFSIZE]; + int index = 0; + int ret = 0; + + // if first entry allocate RUNPGPARAMS + if (!user->Temp->RUNPGPARAMS) + { + user->Temp->RUNPGPARAMS = (RUNPGARGS_PTR) zalloc(sizeof(RUNPGARGS)); + } + + user->Temp->RUNPGPARAMS->user = user; + user->Temp->RUNPGPARAMS->conn = conn; + strncpy(user->Temp->RUNPGPARAMS->InputBuffer, conn->InputBuffer, 80); // needs to be length of actual input! + user->Temp->RUNPGPARAMS->Len = conn->InputLen; + index = user->Temp->PG_INDEX; + + conn->InputBuffer[conn->InputLen] = 0; + strlop(conn->InputBuffer, 13); + conn->InputLen = 0; + + // Build command line. Parmas are: + + // - Callsign (format as F6FBB-8). + // - Level number (0 is the first time, up to 99). + // - Flags of the user (binary number as user`s mask of INIT.SRV). + // - Record number of the user in INF.SYS. + // - Received data (each word is a new argument). + + // BPQ doesn't support params 3 and 4 (but may supply copy of user record later) + + sprintf(szCmdline, "%s/PG/%s %s %d 0 0 %s", BaseDir, + SERVERLIST[user->Temp->PG_SERVER][1], user->Call, index, conn->InputBuffer); + + // Set the bInheritHandle flag so pipe handles are inherited. + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + // Create a pipe for the child process's STDOUT. + + if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) ) + return; + + // Ensure the read handle to the pipe for STDOUT is not inherited. + + if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ) + return; + + // Create the child process. + + + // Set up members of the PROCESS_INFORMATION structure. + + ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) ); + + // Set up members of the STARTUPINFO structure. + // This structure specifies the STDIN and STDOUT handles for redirection. + + ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) ); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.hStdError = g_hChildStd_OUT_Wr; + siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; + siStartInfo.hStdInput = g_hChildStd_IN_Rd; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + // Create the child process. + + bSuccess = CreateProcess(NULL, + szCmdline, // command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &piProcInfo); // receives PROCESS_INFORMATION + + // If an error occurs, exit the application. + + if (!bSuccess) + retcode = -1; + else + { + // Wait until child process exits. + + if (WaitForSingleObject(piProcInfo.hProcess, 5000) == 0) // Wait max 5 seconds + { + // Success + + GetExitCodeProcess(piProcInfo.hProcess, &retcode); + } + else + { + // Failed or ran too long - kill + + TerminateProcess(piProcInfo.hProcess, 0); + } + + // Close handles to the child process and its primary thread. + // Some applications might keep these handles to monitor the status + // of the child process, for example. + + CloseHandle(piProcInfo.hProcess); + CloseHandle(piProcInfo.hThread); + + // Close handles to the stdin and stdout pipes no longer needed by the child process. + // If they are not explicitly closed, there is no way to recognize that the child process has ended. + + CloseHandle(g_hChildStd_OUT_Wr); + + // Send output to User + + for (;;) + { + bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL); + if( ! bSuccess || dwRead == 0 ) break; + + chBuf[dwRead] = 0; + + if (retcode == 4) + ProcessLine(conn, user, chBuf, dwRead); + else + BBSputs(conn, chBuf); + + if (! bSuccess ) break; + } + } + + + switch (retcode) + { + case -1: // ERROR or forced closed + + BBSputs(conn, "Problem running PG program\r"); + index=0; + conn->InputMode=0; + SendPrompt(conn, user); + break; + + case 0: + + // Goodbye/Exit + + index=0; + conn->InputMode=0; + SendPrompt(conn, user); + break; + + case 1: + index++; // inc & keep in PG + break; + + case 2: + index=0; // disconnect + conn->InputMode=0; + Disconnect(conn->BPQStream); + break; + + case 3: + printf("data->BBS & end\n"); + break; + + case 4: + + // Send Output to BBS - was done above + break; + + case 5: + printf("call no inc %d\n", ret); + break; + } + + user->Temp->PG_INDEX=index; + + // The remaining open handles are cleaned up when this process terminates. + // To avoid resource leaks in a larger application, close handles explicitly. + + return; +} + + + +#endif + + +VOID ProcessLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + char * Cmd, * Arg1; + char * Context; + char seps[] = " \t\r"; + int CmdLen; + + if (_memicmp(Buffer, "POSYNCLOGON", 11) == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->BBSFlags |= SYNCMODE; + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + + Sleep(500); + + BBSputs(conn, "OK\r"); + Flush(conn); + return; + } + + if (_memicmp(Buffer, "POSYNCHELLO", 11) == 0) + { + // This is first message received after connecting to SYNC + // Save Callsign + + char Reply[32]; + conn->BBSFlags |= SYNCMODE; + conn->FBBHeaders = zalloc(5 * sizeof(struct FBBHeaderLine)); + + sprintf(Reply, "POSYNCLOGON %s\r", BBSName); + BBSputs(conn, Reply); + return; + } + + if (conn->BBSFlags & SYNCMODE) + { + ProcessSyncModeMessage(conn, user, Buffer, len); + return; + } + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // A few messages should be trapped here and result in an immediate disconnect, whatever mode I think the session is in (it could be wrong) + + // *** Protocol Error + // Already Connected + // Invalid Command + + if (_memicmp(Buffer, "Already Connected", 17) == 0 || + _memicmp(Buffer, "Invalid Command", 15) == 0 || + _memicmp(Buffer, "*** Protocol Error", 18) == 0) + { + conn->BBSFlags |= DISCONNECTING; + Disconnect(conn->BPQStream); + return; + } + + if (conn->BBSFlags & FBBForwarding) + { + ProcessFBBLine(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & FLARQMODE) + { + ProcessFLARQLine(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & MCASTRX) + { + ProcessMCASTLine(conn, user, Buffer, len); + return; + } + + + if (conn->BBSFlags & TEXTFORWARDING) + { + ProcessTextFwdLine(conn, user, Buffer, len); + return; + } + + // if chatting to sysop pass message to BBS console + + if (conn->BBSFlags & SYSOPCHAT) + { + SendUnbuffered(-1, Buffer,len); + return; + } + + if (conn->Flags & GETTINGMESSAGE) + { + ProcessMsgLine(conn, user, Buffer, len); + return; + } + if (conn->Flags & GETTINGTITLE) + { + ProcessMsgTitle(conn, user, Buffer, len); + return; + } + + if (conn->BBSFlags & MBLFORWARDING) + { + ProcessMBLLine(conn, user, Buffer, len); + return; + } + + if (conn->Flags & GETTINGUSER || conn->NewUser) // Could be new user but dont need name + { + if (memcmp(Buffer, ";FW:", 4) == 0 || Buffer[0] == '[') + { + struct BBSForwardingInfo * ForwardingInfo; + + conn->Flags &= ~GETTINGUSER; + + // New User is a BBS - create a temp struct for it + + if ((user->flags & (F_BBS | F_Temp_B2_BBS)) == 0) // It could already be a BBS without a user name + { + // Not defined as BBS - allocate and initialise forwarding structure + + user->flags |= F_Temp_B2_BBS; + + // An RMS Express user that needs a temporary BBS struct + + ForwardingInfo = user->ForwardingInfo = zalloc(sizeof(struct BBSForwardingInfo)); + + ForwardingInfo->AllowCompressed = TRUE; + ForwardingInfo->AllowBlocked = TRUE; + conn->UserPointer->ForwardingInfo->AllowB2 = TRUE; + } + SaveUserDatabase(); + } + else + { + if (conn->Flags & GETTINGUSER) + { + conn->Flags &= ~GETTINGUSER; + if (len > 18) + len = 18; + + memcpy(user->Name, Buffer, len-1); + SendWelcomeMsg(conn->BPQStream, conn, user); + SaveUserDatabase(); + UpdateWPWithUserInfo(user); + return; + } + } + } + + // Process Command + + if (conn->Paging && (conn->LinesSent >= conn->PageLen)) + { + // Waiting for paging prompt + + if (len > 1) + { + if (_memicmp(Buffer, "Abort", 1) == 0) + { + ClearQueue(conn); + conn->LinesSent = 0; + + nodeprintf(conn, AbortedMsg); + + if (conn->UserPointer->Temp->ListSuspended) + nodeprintf(conn, "bort, , = Continue..>"); + + SendPrompt(conn, user); + return; + } + } + + conn->LinesSent = 0; + return; + } + + if (user->Temp->ListSuspended) + { + // Paging limit hit when listing. User may abort, continue, or read one or more messages + + ProcessSuspendedListCommand(conn, user, Buffer, len); + return; + } + if (len == 1) + { + SendPrompt(conn, user); + return; + } + + Buffer[len] = 0; + + if (strstr(Buffer, "ARQ:FILE:")) + { + // Message from FLARQ + + conn->BBSFlags |= FLARQMODE; + strcpy(conn->ARQFilename, &Buffer[10]); // Will need name when we decide what to do with files + + // Create a Temp Messge Stucture + + CreateMessage(conn, conn->Callsign, "", "", 'P', NULL, NULL); + + Buffer[len++] = 0x0a; // BBS Msgs stored with crlf + + if ((conn->TempMsg->length + len) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); + + conn->TempMsg->length += len; + + return; + } + if (Buffer[0] == ';') // WL2K Comment + { + if (memcmp(Buffer, ";FW:", 4) == 0) + { + // Paclink User Select (poll for list) + + char * ptr1,* ptr2, * ptr3; + int index=0; + + // Convert string to Multistring + + Buffer[len-1] = 0; + + conn->PacLinkCalls = zalloc(len*3); + + ptr1 = &Buffer[5]; + ptr2 = (char *)conn->PacLinkCalls; + ptr2 += (len * 2); + strcpy(ptr2, ptr1); + + while (ptr2) + { + ptr3 = strlop(ptr2, ' '); + + if (strlen(ptr2)) + conn->PacLinkCalls[index++] = ptr2; + + ptr2 = ptr3; + } + + return; + } + + if (memcmp(Buffer, ";FR:", 4) == 0) + { + // New Message from TriMode - Just igonre till I know what to do with it + + return; + } + + // Ignore other ';' message + + return; + } + + + + if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID + { + // If a BBS, set BBS Flag + + if (user->flags & ( F_BBS | F_Temp_B2_BBS)) + { + if (user->ForwardingInfo) + { + if (user->ForwardingInfo->Forwarding && ((conn->BBSFlags & OUTWARDCONNECT) == 0)) + { + BBSputs(conn, "Already Connected\r"); + Flush(conn); + Sleep(500); + Disconnect(conn->BPQStream); + return; + } + } + + if (user->ForwardingInfo) + { + user->ForwardingInfo->Forwarding = TRUE; + user->ForwardingInfo->FwdTimer = 0; // So we dont send to immediately + } + } + + if (user->flags & ( F_BBS | F_PMS | F_Temp_B2_BBS)) + { + Parse_SID(conn, &Buffer[1], len-4); + + if (conn->BBSFlags & FBBForwarding) + { + conn->FBBIndex = 0; // ready for first block; + conn->FBBChecksum = 0; + memset(&conn->FBBHeaders[0], 0, 5 * sizeof(struct FBBHeaderLine)); + } + else + FBBputs(conn, ">\r"); + + } + + return; + } + + Cmd = strtok_s(Buffer, seps, &Context); + + if (Cmd == NULL) + { + if (!CheckForTooManyErrors(conn)) + BBSputs(conn, "Invalid Command\r"); + + SendPrompt(conn, user); + return; + } + + Arg1 = strtok_s(NULL, seps, &Context); + CmdLen = (int)strlen(Cmd); + + // Check List first. If any other, save last listed to user record. + + if (_memicmp(Cmd, "L", 1) == 0 && _memicmp(Cmd, "LISTFILES", 3) != 0) + { + DoListCommand(conn, user, Cmd, Arg1, FALSE, Context); + SendPrompt(conn, user); + return; + } + + if (conn->lastmsg > user->lastmsg) + { + user->lastmsg = conn->lastmsg; + SaveUserDatabase(); + } + + if (_stricmp(Cmd, "SHOWRMSPOLL") == 0) + { + DoShowRMSCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "AUTH") == 0) + { + DoAuthCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "Abort", 1) == 0) + { + ClearQueue(conn); + conn->LinesSent = 0; + + nodeprintf(conn, AbortedMsg); + + if (conn->UserPointer->Temp->ListSuspended) + nodeprintf(conn, "bort, , = Continue..>"); + + SendPrompt(conn, user); + return; + } + if (_memicmp(Cmd, "Bye", CmdLen) == 0 || _stricmp(Cmd, "ELSE") == 0) + { + ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); + Flush(conn); + Sleep(1000); + + if (conn->BPQStream > 0) + Disconnect(conn->BPQStream); +#ifndef LINBPQ + else + CloseConsole(conn->BPQStream); +#endif + return; + } + if (_memicmp(Cmd, "Node", 4) == 0) + { + ExpandAndSendMessage(conn, SignoffMsg, LOG_BBS); + Flush(conn); + Sleep(1000); + + if (conn->BPQStream > 0) + ReturntoNode(conn->BPQStream); +#ifndef LINBPQ + else + CloseConsole(conn->BPQStream); +#endif + return; + } + + if (_memicmp(Cmd, "IDLETIME", 4) == 0) + { + DoSetIdleTime(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "SETNEXTMESSAGENUMBER") == 0) + { + DoSetMsgNo(conn, user, Arg1, Context); + return; + } + + if (strlen(Cmd) < 12 && _memicmp(Cmd, "D", 1) == 0) + { + DoDeliveredCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "K", 1) == 0) + { + DoKillCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + + if (_memicmp(Cmd, "LISTFILES", 3) == 0 || _memicmp(Cmd, "FILES", 5) == 0) + { + ListFiles(conn, user, Arg1); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "READFILE", 4) == 0) + { + ReadBBSFile(conn, user, Arg1); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "REROUTEMSGS", 7) == 0) + { + if (conn->sysop == 0) + nodeprintf(conn, "Reroute Messages needs SYSOP status\r"); + else + { + ReRouteMessages(); + nodeprintf(conn, "Ok\r"); + } + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "YAPP", 4) == 0) + { + YAPPSendFile(conn, user, Arg1); + return; + } + + if (_memicmp(Cmd, "UH", 2) == 0 && conn->sysop) + { + DoUnholdCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_stricmp(Cmd, "IMPORT") == 0) + { + DoImportCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "EXPORT") == 0) + { + DoExportCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "I", 1) == 0) + { + char * Save; + char * MsgBytes; + + if (Arg1) + { + // User WP lookup + + DoWPLookup(conn, user, Cmd[1], Arg1); + SendPrompt(conn, user); + return; + } + + + MsgBytes = Save = ReadInfoFile("info.txt"); + if (MsgBytes) + { + int Length; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(Save); + } + else + BBSputs(conn, "SYSOP has not created an INFO file\r"); + + + SendPrompt(conn, user); + return; + } + + + if (_memicmp(Cmd, "Name", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 17) + Arg1[17] = 0; + + strcpy(user->Name, Arg1); + UpdateWPWithUserInfo(user); + + } + + SendWelcomeMsg(conn->BPQStream, conn, user); + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "OP", 2) == 0) + { + int Lines; + + // Paging Control. Param is number of lines per page + + if (Arg1) + { + Lines = atoi(Arg1); + + if (Lines) // Sanity Check + { + if (Lines < 10) + { + nodeprintf(conn,"Page Length %d is too short\r", Lines); + SendPrompt(conn, user); + return; + } + } + + user->PageLen = Lines; + conn->PageLen = Lines; + conn->Paging = (Lines > 0); + SaveUserDatabase(); + } + + nodeprintf(conn,"Page Length is %d\r", user->PageLen); + SendPrompt(conn, user); + + return; + } + + if (_memicmp(Cmd, "QTH", CmdLen) == 0) + { + if (Arg1) + { + // QTH may contain spaces, so put back together, and just split at cr + + Arg1[strlen(Arg1)] = ' '; + strtok_s(Arg1, "\r", &Context); + + if (strlen(Arg1) > 60) + Arg1[60] = 0; + + strcpy(user->Address, Arg1); + UpdateWPWithUserInfo(user); + + } + + nodeprintf(conn,"QTH is %s\r", user->Address); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "ZIP", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 8) + Arg1[8] = 0; + + strcpy(user->ZIP, _strupr(Arg1)); + UpdateWPWithUserInfo(user); + } + + nodeprintf(conn,"ZIP is %s\r", user->ZIP); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if (_memicmp(Cmd, "CMSPASS", 7) == 0) + { + if (Arg1 == 0) + { + nodeprintf(conn,"Must specify a password\r"); + } + else + { + if (strlen(Arg1) > 15) + Arg1[15] = 0; + + strcpy(user->CMSPass, Arg1); + nodeprintf(conn,"CMS Password Set\r"); + SaveUserDatabase(); + } + + SendPrompt(conn, user); + + return; + } + + if (_memicmp(Cmd, "PASS", CmdLen) == 0) + { + if (Arg1 == 0) + { + nodeprintf(conn,"Must specify a password\r"); + } + else + { + if (strlen(Arg1) > 12) + Arg1[12] = 0; + + strcpy(user->pass, Arg1); + nodeprintf(conn,"BBS Password Set\r"); + SaveUserDatabase(); + } + + SendPrompt(conn, user); + + return; + } + + + if (_memicmp(Cmd, "R", 1) == 0) + { + DoReadCommand(conn, user, Cmd, Arg1, Context); + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "S", 1) == 0) + { + if (!DoSendCommand(conn, user, Cmd, Arg1, Context)) + SendPrompt(conn, user); + return; + } + + if ((_memicmp(Cmd, "Help", CmdLen) == 0) || (_memicmp(Cmd, "?", 1) == 0)) + { + char * Save; + char * MsgBytes = Save = ReadInfoFile("help.txt"); + + if (MsgBytes) + { + int Length; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(Save); + } + else + { + BBSputs(conn, "A - Abort Output\r"); + BBSputs(conn, "B - Logoff\r"); + BBSputs(conn, "CMSPASS Password - Set CMS Password\r"); + BBSputs(conn, "D - Flag NTS Message(s) as Delivered - D num\r"); + BBSputs(conn, "HOMEBBS - Display or get HomeBBS\r"); + BBSputs(conn, "INFO - Display information about this BBS\r"); + BBSputs(conn, "I CALL - Lookup CALL in WP Allows *CALL CALL* *CALL* wildcards\r"); + BBSputs(conn, "I@ PARAM - Lookup @BBS in WP\r"); + BBSputs(conn, "IZ PARAM - Lookup Zip Codes in WP\r"); + BBSputs(conn, "IH PARAM - Lookup HA elements in WP - eg USA EU etc\r"); + + BBSputs(conn, "K - Kill Message(s) - K num, KM (Kill my read messages)\r"); + BBSputs(conn, "L - List Message(s) - \r"); + BBSputs(conn, " L = List New, LR = List New (Oldest first)\r"); + BBSputs(conn, " LM = List Mine, L> Call, L< Call, L@ = List to, from or at\r"); + BBSputs(conn, " LL num = List msg num, L num-num = List Range\r"); + BBSputs(conn, " LN LY LH LK LF L$ LD = List Message with corresponding Status\r"); + BBSputs(conn, " LB LP LT = List Mesaage with corresponding Type\r"); + BBSputs(conn, " LC = List TO fields of all active bulletins\r"); + BBSputs(conn, " You can combine most selections eg LMP, LMN LB< G8BPQ\r"); + BBSputs(conn, "LISTFILES or FILES - List files available for download\r"); + + BBSputs(conn, "N Name - Set Name\r"); + BBSputs(conn, "NODE - Return to Node\r"); + BBSputs(conn, "OP n - Set Page Length (Output will pause every n lines)\r"); + BBSputs(conn, "PASS Password - Set BBS Password\r"); + BBSputs(conn, "POLLRMS - Manage Polling for messages from RMS \r"); + BBSputs(conn, "Q QTH - Set QTH\r"); + BBSputs(conn, "R - Read Message(s) - R num \r"); + BBSputs(conn, " RM (Read new messages to me), RMR (RM oldest first)\r"); + BBSputs(conn, "READ Name - Read File\r"); + + BBSputs(conn, "S - Send Message - S or SP Send Personal, SB Send Bull, ST Send NTS,\r"); + BBSputs(conn, " SR Num - Send Reply, SC Num - Send Copy\r"); + BBSputs(conn, "X - Toggle Expert Mode\r"); + BBSputs(conn, "YAPP - Download file from BBS using YAPP protocol\r"); + if (conn->sysop) + { + BBSputs(conn, "DOHOUSEKEEPING - Run Housekeeping process\r"); + BBSputs(conn, "EU - Edit User Flags - Type EU for Help\r"); + BBSputs(conn, "EXPORT - Export messages to file - Type EXPORT for Help\r"); + BBSputs(conn, "FWD - Control Forwarding - Type FWD for Help\r"); + BBSputs(conn, "IMPORT - Import messages from file - Type IMPORT for Help\r"); + BBSputs(conn, "REROUTEMSGS - Rerun message routing process\r"); + BBSputs(conn, "SETNEXTMESSAGENUMBER - Sets next message number\r"); + BBSputs(conn, "SHOWRMSPOLL - Displays your RMS polling list\r"); + BBSputs(conn, "UH - Unhold Message(s) - UH ALL or UH num num num...\r"); + } + } + + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "Ver", CmdLen) == 0) + { + nodeprintf(conn, "BBS Version %s\rNode Version %s\r", VersionStringWithBuild, GetVersionString()); + + SendPrompt(conn, user); + return; + } + + if (_memicmp(Cmd, "HOMEBBS", CmdLen) == 0) + { + if (Arg1) + { + if (strlen(Arg1) > 40) Arg1[40] = 0; + + strcpy(user->HomeBBS, _strupr(Arg1)); + UpdateWPWithUserInfo(user); + + if (!strchr(Arg1, '.')) + BBSputs(conn, "Please enter HA with HomeBBS eg g8bpq.gbr.eu - this will help message routing\r"); + } + + nodeprintf(conn,"HomeBBS is %s\r", user->HomeBBS); + SendPrompt(conn, user); + + SaveUserDatabase(); + + return; + } + + if ((_memicmp(Cmd, "EDITUSER", 5) == 0) || (_memicmp(Cmd, "EU", 2) == 0)) + { + DoEditUserCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "POLLRMS") == 0) + { + DoPollRMSCmd(conn, user, Arg1, Context); + return; + } + + if (_stricmp(Cmd, "DOHOUSEKEEPING") == 0) + { + DoHousekeepingCmd(conn, user, Arg1, Context); + return; + } + + + if (_stricmp(Cmd, "FWD") == 0) + { + DoFwdCmd(conn, user, Arg1, Context); + return; + } + + if (_memicmp(Cmd, "X", 1) == 0) + { + user->flags ^= F_Expert; + + if (user->flags & F_Expert) + BBSputs(conn, "Expert Mode\r"); + else + BBSputs(conn, "Expert Mode off\r"); + + SaveUserDatabase(); + SendPrompt(conn, user); + return; + } + + /*---- G7TAJ PG Server ----- */ + + + if (_stricmp(Cmd, "PG") == 0) + { + if ( NUM_SERVERS == 0 ) + { + BBSputs(conn, "No PG Servers currently defined\r"); + SendPrompt(conn, user); + Flush(conn); + return; + } + + if ( !Arg1 ) + { + char reply[80]; + int i; + for (i=0; i< NUM_SERVERS; i++ ) + { + sprintf(reply, "%s -> %s\r", SERVERLIST[i][0], SERVERLIST[i][2]); + BBSputs(conn, reply); + } + SendPrompt(conn, user); + return; + } + else + { + int i; + for (i=0; i < NUM_SERVERS; i++ ) + { + if ( _stricmp( _strupr(Arg1), SERVERLIST[i][0] ) == 0 ) { + user->Temp->PG_SERVER = i; // index to server to run + user->Temp->PG_INDEX = 0; // newly starting PG + conn->InputMode = 'P'; // Inside PG Server + + // conn->InputBuffer is altered above and split into Cmd,Arg1,Context + // so put it back and call PG (removing PG) + sprintf( conn->InputBuffer, "%s", Context); + conn->InputLen = strlen(Context); + run_pg( conn, user ); + return; + } + } + BBSputs(conn, "No server found\r"); + SendPrompt(conn, user); + return; + } + } + + /*---- G7TAJ END ---- */ + + if (conn->Flags == 0) + { + if (!CheckForTooManyErrors(conn)) + BBSputs(conn, "Invalid Command\r"); + + SendPrompt(conn, user); + } + + // Send if possible + + Flush(conn); +} + +VOID __cdecl nprintf(CIRCUIT * conn, const char * format, ...) +{ + // seems to be printf to a socket + + char buff[600]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(buff, format, arglist); + + BBSputs(conn, buff); +} + +// Code to delete obsolete files from Mail folder + +#ifdef WIN32 + +int DeleteRedundantMessages() +{ + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + char File[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + int Msgno; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, MailDir); + strcat(szDir, "\\*.mes"); + + + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + return 0; + } + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + OutputDebugString(ffd.cFileName); + } + else + { + Msgno = atoi(&ffd.cFileName[2]); + + if (MsgnotoMsg[Msgno] == 0) + { + sprintf(File, "%s/%s%c", MailDir, ffd.cFileName, 0); + Debugprintf("Tidy Mail - Delete %s\n", File); + +// if (DeletetoRecycleBin) + DeletetoRecycle(File); +// else +// DeleteFile(File); + } + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + return 0; +} + +#else + +#include + +int MsgFilter(const struct dirent * dir) +{ + return (strstr(dir->d_name, ".mes") != 0); +} + +int DeleteRedundantMessages() +{ + struct dirent **namelist; + int n; + struct stat STAT; + int Msgno = 0, res; + char File[100]; + + n = scandir("Mail", &namelist, MsgFilter, alphasort); + + if (n < 0) + perror("scandir"); + else + { + while(n--) + { + if (stat(namelist[n]->d_name, &STAT) == 0); + { + Msgno = atoi(&namelist[n]->d_name[2]); + + if (MsgnotoMsg[Msgno] == 0) + { + sprintf(File, "Mail/%s", namelist[n]->d_name); + printf("Deleting %s\n", File); + unlink(File); + } + } + free(namelist[n]); + } + free(namelist); + } + return 0; +} +#endif + +VOID TidyWelcomeMsg(char ** pPrompt) +{ + // Make sure Welcome Message doesn't ends with > + + char * Prompt = *pPrompt; + + int i = (int)strlen(Prompt) - 1; + + *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it + + Prompt = *pPrompt; + + while (Prompt[i] == 10 || Prompt[i] == 13) + { + Prompt[i--] = 0; + } + + while (i >= 0 && Prompt[i] == '>') + Prompt[i--] = 0; + + strcat(Prompt, "\r\n"); +} + +VOID TidyPrompt(char ** pPrompt) +{ + // Make sure prompt ends > CR LF + + char * Prompt = *pPrompt; + + int i = (int)strlen(Prompt) - 1; + + *pPrompt = realloc(Prompt, i + 5); // In case we need to expand it + + Prompt = *pPrompt; + + while (Prompt[i] == 10 || Prompt[i] == 13) + { + Prompt[i--] = 0; + } + + if (Prompt[i] != '>') + strcat(Prompt, ">"); + + strcat(Prompt, "\r\n"); +} + +VOID TidyPrompts() +{ + TidyPrompt(&Prompt); + TidyPrompt(&NewPrompt); + TidyPrompt(&ExpertPrompt); +} + +BOOL SendARQMail(CIRCUIT * conn) +{ + conn->NextMessagetoForward = FirstMessageIndextoForward; + + // Send Message. There is no mechanism for reverse forwarding + + if (FindMessagestoForward(conn)) + { + struct MsgInfo * Msg; + char MsgHddr[512]; + int HddrLen; + char TimeString[64]; + char * WholeMessage; + + char * MsgBytes = ReadMessageFile(conn->FwdMsg->number); + int MsgLen; + + if (MsgBytes == 0) + { + MsgBytes = _strdup("Message file not found\r"); + conn->FwdMsg->length = (int)strlen(MsgBytes); + } + + Msg = conn->FwdMsg; + WholeMessage = malloc(Msg->length + 512); + + FormatTime(TimeString, (time_t)Msg->datecreated); + +/* +ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::96 +ARQ::STX +//FLARQ COMPOSER +Date: 16/01/2014 22:26:06 +To: g8bpq +From: +Subject: test message + +Hello +Hello + +ARQ::ETX +*/ + Logprintf(LOG_BBS, conn, '>', "ARQ Send Msg %d From %s To %s", Msg->number, Msg->from, Msg->to); + + HddrLen = sprintf(MsgHddr, "Date: %s\nTo: %s\nFrom: %s\nSubject %s\n\n", + TimeString, Msg->to, Msg->from, Msg->title); + + MsgLen = sprintf(WholeMessage, "ARQ:FILE::Msg%s_%d\nARQ:EMAIL::\nARQ:SIZE::%d\nARQ::STX\n%s%s\nARQ::ETX\n", + BBSName, Msg->number, (int)(HddrLen + strlen(MsgBytes)), MsgHddr, MsgBytes); + + WholeMessage[MsgLen] = 0; + QueueMsg(conn,WholeMessage, MsgLen); + + free(WholeMessage); + free(MsgBytes); + + // FLARQ doesn't ACK the message, so set flag to look for all acked + + conn->BBSFlags |= ARQMAILACK; + conn->ARQClearCount = 10; // To make sure clear isn't reported too soon + + return TRUE; + } + + // Nothing to send - close + + Logprintf(LOG_BBS, conn, '>', "ARQ Send - Nothing to Send - Closing"); + + conn->CloseAfterFlush = 20; + return FALSE; +} + +char *stristr (char *ch1, char *ch2) +{ + char *chN1, *chN2; + char *chNdx; + char *chRet = NULL; + + chN1 = _strdup (ch1); + chN2 = _strdup (ch2); + if (chN1 && chN2) + { + chNdx = chN1; + while (*chNdx) + { + *chNdx = (char) tolower (*chNdx); + chNdx ++; + } + chNdx = chN2; + while (*chNdx) + { + *chNdx = (char) tolower (*chNdx); + chNdx ++; + } + + chNdx = strstr (chN1, chN2); + if (chNdx) + chRet = ch1 + (chNdx - chN1); + } + free (chN1); + free (chN2); + return chRet; +} + +#ifdef WIN32 + +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetBPQDirectory()); + strcat(szDir, "\\BPQMailChat\\Files\\*.*"); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + nodeprintf(conn, "No Files\r"); + return; + } + + // List all the files in the directory with some info about them. + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + {} + else + { + if (filename == NULL || stristr(ffd.cFileName, filename)) + nodeprintf(conn, "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); +} + +#else + +#include + +void ListFiles(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + struct dirent **namelist; + int n, i; + struct stat STAT; + time_t now = time(NULL); + int Age = 0, res; + char FN[256]; + + n = scandir("Files", &namelist, NULL, alphasort); + + if (n < 0) + perror("scandir"); + else + { + for (i = 0; i < n; i++) + { + sprintf(FN, "Files/%s", namelist[i]->d_name); + + if (filename == NULL || stristr(namelist[i]->d_name, filename)) + if (FN[6] != '.' && stat(FN, &STAT) == 0) + nodeprintf(conn, "%s %d\r", namelist[i]->d_name, STAT.st_size); + + free(namelist[i]); + } + free(namelist); + } + return; +} +#endif + +void ReadBBSFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + char * MsgBytes; + + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + struct stat STAT; + + if (filename == NULL) + { + nodeprintf(conn, "Missing Filename\r"); + return; + } + + if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) + { + nodeprintf(conn, "Invalid filename\r"); + return; + } + + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + int Length; + + MsgBytes=malloc(FileSize+1); + fread(MsgBytes, 1, FileSize, hFile); + fclose(hFile); + + MsgBytes[FileSize]=0; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + QueueMsg(conn, MsgBytes, Length); + free(MsgBytes); + + nodeprintf(conn, "\r\r[End of File %s]\r", filename); + return; + } + } + + nodeprintf(conn, "File %s not found\r", filename); +} + +VOID ProcessSuspendedListCommand(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + struct TempUserInfo * Temp = user->Temp; + + Buffer[len] = 0; + + // Command entered during listing pause. May be A R or C (or ) + + if (Buffer[0] == 'A' || Buffer[0] == 'a') + { + // Abort + + Temp->ListActive = Temp->ListSuspended = FALSE; + SendPrompt(conn, user); + return; + } + + if (_memicmp(Buffer, "R ", 2) == 0) + { + // Read Message(es) + + int msgno; + char * ptr; + char * Context; + + ptr = strtok_s(&Buffer[2], " ", &Context); + + while (ptr) + { + msgno = atoi(ptr); + ReadMessage(conn, user, msgno); + + ptr = strtok_s(NULL, " ", &Context); + } + + nodeprintf(conn, "bort, , = Continue..>"); + return; + } + + if (Buffer[0] == 'C' || Buffer[0] == 'c' || Buffer[0] == '\r' ) + { + // Resume Listing from where we left off + + DoListCommand(conn, user, Temp->LastListCommand, Temp->LastListParams, TRUE, ""); + SendPrompt(conn, user); + return; + } + + nodeprintf(conn, "bort, , = Continue..>"); + +} +/* +CreateMessageWithAttachments() +{ + int i; + char * ptr, * ptr2, * ptr3, * ptr4; + char Boundary[1000]; + BOOL Multipart = FALSE; + BOOL ALT = FALSE; + int Partlen; + char * Save; + BOOL Base64 = FALSE; + BOOL QuotedP = FALSE; + + char FileName[100][250] = {""}; + int FileLen[100]; + char * FileBody[100]; + char * MallocSave[100]; + UCHAR * NewMsg; + + int Files = 0; + + ptr = Msg; + + if ((sockptr->MailSize + 2000) > sockptr->MailBufferSize) + { + sockptr->MailBufferSize += 2000; + sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize); + + if (sockptr->MailBuffer == NULL) + { + CriticalErrorHandler("Failed to extend Message Buffer"); + shutdown(sockptr->socket, 0); + return FALSE; + } + } + + + NewMsg = sockptr->MailBuffer + 1000; + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", FileLen[0]); + + for (i = 1; i < Files; i++) + { + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[i], FileName[i]); + } + + NewMsg += sprintf(NewMsg, "\r\n"); + + for (i = 0; i < Files; i++) + { + memcpy(NewMsg, FileBody[i], FileLen[i]); + NewMsg += FileLen[i]; + free(MallocSave[i]); + NewMsg += sprintf(NewMsg, "\r\n"); + } + + *MsgLen = NewMsg - (sockptr->MailBuffer + 1000); + *Body = sockptr->MailBuffer + 1000; + + return TRUE; // B2 Message +} + +*/ +VOID CreateUserReport() +{ + struct UserInfo * User; + int i; + char Line[200]; + int len; + char File[MAX_PATH]; + FILE * hFile; + + sprintf(File, "%s/UserList.csv", BaseDir); + + hFile = fopen(File, "wb"); + + if (hFile == NULL) + { + Debugprintf("Failed to create UserList.csv"); + return; + } + + for (i=1; i <= NumberofUsers; i++) + { + User = UserRecPtr[i]; + + len = sprintf(Line, "%s,%d,%s,%x,%s,\"%s\",%x,%s,%s,%s\r\n", + User->Call, + User->lastmsg, + FormatDateAndTime((time_t)User->TimeLastConnected, FALSE), + User->flags, + User->Name, + User->Address, + User->RMSSSIDBits, + User->HomeBBS, + User->QRA, + User->ZIP +// struct MsgStats Total; +// struct MsgStats Last; + ); + fwrite(Line, 1, len, hFile); + } + + fclose(hFile); +} + +BOOL ProcessYAPPMessage(CIRCUIT * conn) +{ + int Len = conn->InputLen; + UCHAR * Msg = conn->InputBuffer; + int pktLen = Msg[1]; + char Reply[2] = {ACK}; + int NameLen, SizeLen, OptLen; + char * ptr; + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + char Mess[255]; + int len; + char * FN = &Msg[2]; + + switch (Msg[0]) + { + case ENQ: // YAPP Send_Init + + // Shouldn't occur in session. Reset state + + Mess[0] = ACK; + Mess[1] = 1; + QueueMsg(conn, Mess, 2); + Flush(conn); + conn->InputLen = 0; + if (conn->MailBuffer) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + } + return TRUE; + + case SOH: + + // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) + + // YAPPC has date/time in dos format + + if (Len < Msg[1] + 1) + return 0; + + NameLen = (int)strlen(FN); + strcpy(conn->ARQFilename, FN); + ptr = &Msg[3 + NameLen]; + SizeLen = (int)strlen(ptr); + FileSize = atoi(ptr); + + // Check file name for unsafe characters (.. / \) + + if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) + { + Mess[0] = NAK; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPP File Name %s invalid\r", FN); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + OptLen = pktLen - (NameLen + SizeLen + 2); + + conn->YAPPDate = 0; + + if (OptLen >= 8) // We have a Date/Time for YAPPC + { + ptr = ptr + SizeLen + 1; + conn->YAPPDate = strtol(ptr, NULL, 16); + } + + // Check Size + + if (FileSize > MaxRXSize) + { + Mess[0] = NAK; + Mess[1] = sprintf(&Mess[2], "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); + QueueMsg(conn, Mess, Mess[1] + 2); + + Flush(conn); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s size %d larger than limit %d\r", conn->ARQFilename, FileSize, MaxRXSize); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + // Make sure file does not exist + + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + Mess[0] = NAK; + Mess[1] = sprintf(&Mess[2], "YAPP File %s already exists\r", conn->ARQFilename);; + QueueMsg(conn, Mess, Mess[1] + 2); + + Flush(conn); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP File %s already exists\r", conn->ARQFilename); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + fclose(hFile); + + conn->InputLen = 0; + conn->InputMode = 0; + + return FALSE; + } + + + conn->MailBufferSize = FileSize; + conn->MailBuffer=malloc(FileSize); + conn->YAPPLen = 0; + + if (conn->YAPPDate) // If present use YAPPC + Reply[1] = ACK; //Receive_TPK + else + Reply[1] = 2; //Rcv_File + + QueueMsg(conn, Reply, 2); + + len = sprintf_s(Mess, sizeof(Mess), "YAPP upload to %s started", conn->ARQFilename); + WriteLogLine(conn, '!', Mess, len, LOG_BBS); + + conn->InputLen = 0; + return FALSE; + + case STX: + + // Data Packet + + // Check we have it all + + if (conn->YAPPDate) // If present use YAPPC so have checksum + { + if (pktLen > (Len - 3)) // -3 for header and checksum + return 0; // Wait for rest + } + else + { + if (pktLen > (Len - 2)) // -2 for header + return 0; // Wait for rest + } + + // Save data and remove from buffer + + // if YAPPC check checksum + + if (conn->YAPPDate) + { + UCHAR Sum = 0; + int i; + UCHAR * uptr = &Msg[2]; + + i = pktLen; + + while(i--) + Sum += *(uptr++); + + if (Sum != *uptr) + { + // Checksum Error + + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPPC Checksum Error\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + return TRUE; + } + } + + if ((conn->YAPPLen) + pktLen > conn->MailBufferSize) + { + // Too Big ?? + + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "YAPP Too much data received\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + return TRUE; + } + + + memcpy(&conn->MailBuffer[conn->YAPPLen], &Msg[2], pktLen); + conn->YAPPLen += pktLen; + + if (conn->YAPPDate) + ++pktLen; // Add Checksum + + conn->InputLen -= (pktLen + 2); + memmove(conn->InputBuffer, &conn->InputBuffer[pktLen + 2], conn->InputLen); + + return TRUE; + + case ETX: + + // End Data + + + + if (conn->YAPPLen == conn->MailBufferSize) + { + // All received + + int ret; + DWORD Written = 0; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, conn->ARQFilename); + +#ifdef WIN32 + hFile = CreateFile(MsgFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + ret = WriteFile(hFile, conn->MailBuffer, conn->YAPPLen, &Written, NULL); + + if (conn->YAPPDate) + { + FILETIME FileTime; + struct tm TM; + struct timeval times[2]; + time_t TT; +/* + The MS-DOS date. The date is a packed value with the following format. + + cant use DosDateTimeToFileTime on Linux + + Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + wFatTime + The MS-DOS time. The time is a packed value with the following format. + Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) +*/ + memset(&TM, 0, sizeof(TM)); + + TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; + TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); + TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); + + TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); + TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; + TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; + + Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); + + TT = mktime(&TM); + times[0].tv_sec = times[1].tv_sec = + times[0].tv_usec = times[1].tv_usec = 0; + + DosDateTimeToFileTime((WORD)(conn->YAPPDate >> 16), (WORD)conn->YAPPDate & 0xFFFF, &FileTime); + ret = SetFileTime(hFile, &FileTime, &FileTime, &FileTime); + ret = GetLastError(); + + } + CloseHandle(hFile); + } +#else + + hFile = fopen(MsgFile, "wb"); + if (hFile) + { + Written = fwrite(conn->MailBuffer, 1, conn->YAPPLen, hFile); + fclose(hFile); + + if (conn->YAPPDate) + { + struct tm TM; + struct timeval times[2]; +/* + The MS-DOS date. The date is a packed value with the following format. + + cant use DosDateTimeToFileTime on Linux + + Bits Description + 0-4 Day of the month (1–31) + 5-8 Month (1 = January, 2 = February, and so on) + 9-15 Year offset from 1980 (add 1980 to get actual year) + wFatTime + The MS-DOS time. The time is a packed value with the following format. + Bits Description + 0-4 Second divided by 2 + 5-10 Minute (0–59) + 11-15 Hour (0–23 on a 24-hour clock) +*/ + memset(&TM, 0, sizeof(TM)); + + TM.tm_sec = (conn->YAPPDate & 0x1f) << 1; + TM.tm_min = ((conn->YAPPDate >> 5) & 0x3f); + TM.tm_hour = ((conn->YAPPDate >> 11) & 0x1f); + + TM.tm_mday = ((conn->YAPPDate >> 16) & 0x1f); + TM.tm_mon = ((conn->YAPPDate >> 21) & 0xf) - 1; + TM.tm_year = ((conn->YAPPDate >> 25) & 0x7f) + 80; + + Debugprintf("%d %d %d %d %d %d", TM.tm_year, TM.tm_mon, TM.tm_mday, TM.tm_hour, TM.tm_min, TM.tm_sec); + + times[0].tv_sec = times[1].tv_sec = mktime(&TM); + times[0].tv_usec = times[1].tv_usec = 0; + } + } +#endif + + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + + if (Written != conn->YAPPLen) + { + Mess[0] = CAN; + Mess[1] = 0; + QueueMsg(conn, Mess, 2); + Flush(conn); + len = sprintf_s(Mess, sizeof(Mess), "Failed to save YAPP File\r"); + QueueMsg(conn, Mess, len); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + conn->InputLen = 0; + conn->InputMode = 0; + } + } + + Reply[1] = 3; //Ack_EOF + QueueMsg(conn, Reply, 2); + Flush(conn); + conn->InputLen = 0; + + return TRUE; + + case EOT: + + // End Session + + Reply[1] = 4; // Ack_EOT + QueueMsg(conn, Reply, 2); + Flush(conn); + conn->InputLen = 0; + conn->InputMode = 0; + + len = sprintf_s(Mess, sizeof(Mess), "YAPP file %s received\r", conn->ARQFilename); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + QueueMsg(conn, Mess, len); + SendPrompt(conn, conn->UserPointer); + + return TRUE; + + case CAN: + + // Abort + + Mess[0] = ACK; + Mess[1] = 5; // CAN Ack + QueueMsg(conn, Mess, 2); + Flush(conn); + + if (conn->MailBuffer) + { + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + } + + // There may be a reason after the CAN + + len = Msg[1]; + + if (len) + { + char * errormsg = &Msg[2]; + errormsg[len] = 0; + nodeprintf(conn, "File Rejected - %s\r", errormsg); + } + else + + nodeprintf(conn, "File Rejected\r"); + + + len = sprintf_s(Mess, sizeof(Mess), "YAPP Transfer cancelled by Terminal\r"); + WriteLogLine(conn, '!', Mess, len - 1, LOG_BBS); + + conn->InputLen = 0; + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + + return FALSE; + + case ACK: + + switch (Msg[1]) + { + case 1: // Rcv_Rdy + + // HD Send_Hdr SOH len (Filename) NUL (File Size in ASCII) NUL (Opt) + + len = (int)strlen(conn->ARQFilename) + 3; + + strcpy(&Mess[2], conn->ARQFilename); + len += sprintf(&Mess[len], "%d", conn->MailBufferSize); + len++; // include null + Mess[0] = SOH; + Mess[1] = len - 2; + + QueueMsg(conn, Mess, len); + Flush(conn); + conn->InputLen = 0; + + return FALSE; + + case 2: + + // Start sending message + + YAPPSendData(conn); + conn->InputLen = 0; + return FALSE; + + case 3: + + // ACK EOF - Send EOT + + + Mess[0] = EOT; + Mess[1] = 1; + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->InputLen = 0; + return FALSE; + + case 4: + + // ACK EOT + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + + conn->InputLen = 0; + return FALSE; + + default: + conn->InputLen = 0; + return FALSE; + + + + } + + case NAK: + + // Either Reject or Restart + + // RE Resume NAK len R NULL (File size in ASCII) NULL + + if (conn->InputLen > 2 && Msg[2] == 'R' && Msg[3] == 0) + { + int posn = atoi(&Msg[4]); + + conn->YAPPLen += posn; + conn->MailBufferSize -= posn; + + YAPPSendData(conn); + conn->InputLen = 0; + return FALSE; + + } + + // There may be a reason after the ack + + len = Msg[1]; + + if (len) + { + char * errormsg = &Msg[2]; + errormsg[len] = 0; + nodeprintf(conn, "File Rejected - %s\r", errormsg); + } + else + + nodeprintf(conn, "File Rejected\r"); + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + conn->InputLen = 0; + SendPrompt(conn, conn->UserPointer); + return FALSE; + } + + nodeprintf(conn, "Unexpected message during YAPP Transfer. Transfer canncelled\r"); + + conn->InputMode = 0; + conn->BBSFlags &= ~YAPPTX; + conn->InputLen = 0; + SendPrompt(conn, conn->UserPointer); + + return FALSE; + +} + +void YAPPSendFile(ConnectionInfo * conn, struct UserInfo * user, char * filename) +{ + int FileSize; + char MsgFile[MAX_PATH]; + FILE * hFile; + struct stat STAT; + + if (filename == NULL) + { + nodeprintf(conn, "Filename missing\r"); + SendPrompt(conn, user); + return; + } + + if (strstr(filename, "..") || strchr(filename, '/') || strchr(filename, '\\')) + { + nodeprintf(conn, "Invalid filename\r"); + SendPrompt(conn, user); + return; + } + + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, filename); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", filename); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + char Mess[255]; + strcpy(conn->ARQFilename, filename); + conn->MailBuffer = malloc(FileSize); + conn->MailBufferSize = FileSize; + conn->YAPPLen = 0; + fread(conn->MailBuffer, 1, FileSize, hFile); + fclose(hFile); + + Mess[0] = ENQ; + Mess[1] = 1; + + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->InputMode = 'Y'; + + return; + } + } + + nodeprintf(conn, "File %s not found\r", filename); + SendPrompt(conn, user); +} + +void YAPPSendData(ConnectionInfo * conn) +{ + char Mess[258]; + + conn->BBSFlags |= YAPPTX; + + while (TXCount(conn->BPQStream) < 15) + { + int Left = conn->MailBufferSize; + + if (Left == 0) + { + // Finished - send End Data + + Mess[0] = ETX; + Mess[1] = 1; + + QueueMsg(conn, Mess, 2); + Flush(conn); + + conn->BBSFlags &= ~YAPPTX; + break; + } + + if (Left > conn->paclen - 2) // 2 byte header + Left = conn->paclen -2; + + memcpy(&Mess[2], &conn->MailBuffer[conn->YAPPLen], Left); + Mess[0] = STX; + Mess[1] = Left; + + QueueMsg(conn, Mess, Left + 2); + Flush(conn); + + conn->YAPPLen += Left; + conn->MailBufferSize -= Left; + } +} + +char * AddUser(char * Call, char * password, BOOL BBSFlag) +{ + struct UserInfo * USER; + + strlop(Call, '-'); + + if (strlen(Call) > 6) + Call[6] = 0; + + _strupr(Call); + + if (Call[0] == 0 || LookupCall(Call)) + { + return("User already exists\r\n"); + } + + USER = AllocateUserRecord(Call); + USER->Temp = zalloc(sizeof (struct TempUserInfo)); + + if (strlen(password) > 12) + password[12] = 0; + + strcpy(USER->pass, password); + + if (BBSFlag) + { + if(SetupNewBBS(USER)) + USER->flags |= F_BBS; + else + printf("Cannot set user to be a BBS - you already have 160 BBS's defined\r\n"); + } + + SaveUserDatabase(); + UpdateWPWithUserInfo(USER); + + return("User added\r\n"); +} + +// Server Support Code + +// For the moment only internal REQDIR and REQFIL. + +// May add WPSERV and user implemented servers +/* +F6FBB BBS > + SP REQDIR @ F6ABJ.FRA.EU + Title of message : + YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU + Text of message : + /EX + + F6FBB BBS > + SP REQFIL @ F6ABJ.FRA.EU + Title of message : + DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU + Text of message : + /EX + + Note Text not used. + +*/ + +VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To); + +BOOL ProcessReqDir(struct MsgInfo * Msg) +{ + char * Buffer; + int Len = 0; + char * ptr; + + // Parse title - gives directory and return address + + // YAPP\*.ZIP @ F6FBB.FMLR.FRA.EU + + // At the moment we don't allow subdirectories but no harm handling here + + char Pattern[64]; + char * Address; + char * filename = NULL; // ?? Pattern Match ?? + +#ifdef WIN32 + + WIN32_FIND_DATA ffd; + + char szDir[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + +#else + + #include + + struct dirent **namelist; + int n, i; + struct stat STAT; + int res; + char FN[256]; + +#endif + + strcpy(Pattern, Msg->title); + + ptr = strchr(Pattern, '@'); + + if (ptr == NULL) + + // if we don't have return address no point + // but could we default to sender?? + + return FALSE; + + *ptr++ = 0; // Terminate Path + + strlop(Pattern, ' '); + + while (*ptr == ' ') + ptr++; // accept with or without spaces round @ + + Address = ptr; + + ptr = Buffer = malloc(MaxTXSize); + +#ifdef WIN32 + + // Prepare string for use with FindFile functions. First, copy the + // string to a buffer, then append '\*' to the directory name. + + strcpy(szDir, GetBPQDirectory()); + strcat(szDir, "\\BPQMailChat\\Files\\"); + strcat(szDir, Pattern); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + Len = sprintf(Buffer, "No Files\r"); + } + else + { + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + {} + else + { + if (filename == NULL || stristr(ffd.cFileName, filename)) + Len += sprintf(&Buffer[Len], "%s %d\r", ffd.cFileName, ffd.nFileSizeLow); + } + } + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + } + +#else + + n = scandir("Files", &namelist, NULL, alphasort); + + if (n < 0) + perror("scandir"); + else + { + for (i = 0; i < n; i++) + { + sprintf(FN, "Files/%s", namelist[i]->d_name); + + if (filename == NULL || stristr(namelist[i]->d_name, filename)) + if (FN[6] != '.' && stat(FN, &STAT) == 0) + Len += sprintf(&Buffer[Len], "%s %d\r", namelist[i]->d_name, STAT.st_size); + + free(namelist[i]); + } + free(namelist); + } + +#endif + + // Build Message + + SendServerReply("REQDIR Reply", Buffer, Len, _strupr(Address)); + return TRUE; +} + +/* + ' Augment Message ID with the Message Pickup Station we're directing this message to. + ' + Dim strAugmentedMessageID As String + If GetMidRMS(MessageId) <> "" Then + ' The MPS RMS is already set on the message ID + strAugmentedMessageID = MessageId + strMPS = GetMidRMS(MessageId) + ' "@R" at the end of the MID means route message only via radio + If GetMidForwarding(MessageId) = "" And (blnRadioOnly Or UploadThroughInternet()) Then + strAugmentedMessageID &= "@" & strHFOnlyFlag + End If + ElseIf strMPS <> "" Then + ' Add MPS to the message ID + strAugmentedMessageID = MessageId & "@" & strMPS + ' "@R" at the end of the MID means route message only via radio + If blnRadioOnly Or UploadThroughInternet() Then + strAugmentedMessageID &= "@" & strHFOnlyFlag + End If + Else + strAugmentedMessageID = MessageId + End If + +*/ + +void ProcessSyncModeMessage(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int len) +{ + Buffer[len] = 0; + + if (conn->Flags & GETTINGSYNCMESSAGE) + { + // Data + + if ((conn->TempMsg->length + len) > conn->MailBufferSize) + { + conn->MailBufferSize += 10000; + conn->MailBuffer = realloc(conn->MailBuffer, conn->MailBufferSize); + + if (conn->MailBuffer == NULL) + { + BBSputs(conn, "*** Failed to extend Message Buffer\r"); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + } + + memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, len); + + conn->TempMsg->length += len; + + if (conn->TempMsg->length >= conn->SyncCompressedLen) + { + // Complete - decompress it + + conn->BBSFlags |= FBBCompressed; + Decode(conn, 1); + + conn->Flags &= !GETTINGSYNCMESSAGE; + + BBSputs(conn, "OK\r"); + return; + } + return; + } + + if (conn->Flags & PROPOSINGSYNCMSG) + { + // Waiting for response to TR AddMessage + + if (strcmp(Buffer, "OK\r") == 0) + { + char Msg[256]; + int n; + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // Send the message, it has already been built + + conn->Flags &= !PROPOSINGSYNCMSG; + conn->Flags |= SENDINGSYNCMSG; + + n = sprintf_s(Msg, sizeof(Msg), "Sending SYNC message %s", conn->FwdMsg->bid); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + QueueMsg(conn, conn->SyncMessage, conn->SyncCompressedLen); + return; + } + + if (strcmp(Buffer, "NO\r") == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // Message Rejected - ? duplicate + + if (conn->FwdMsg) + { + // Zap the entry + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + conn->UserPointer->ForwardingInfo->MsgCount--; + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->FwdMsg->Locked = 0; // Unlock + } + } + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->Flags &= !PROPOSINGSYNCMSG; + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (conn->Flags & SENDINGSYNCMSG) + { + if (strcmp(Buffer, "OK\r") == 0) + { + // Message Sent + + conn->Flags &= !SENDINGSYNCMSG; + free(conn->SyncMessage); + + if (conn->FwdMsg) + { + char Msg[256]; + int n; + + n = sprintf_s(Msg, sizeof(Msg), "SYNC message %s Sent", conn->FwdMsg->bid); + WriteLogLine(conn, '|',Msg, n, LOG_BBS); + + clear_fwd_bit(conn->FwdMsg->fbbs, user->BBSNumber); + set_fwd_bit(conn->FwdMsg->forw, user->BBSNumber); + conn->UserPointer->ForwardingInfo->MsgCount--; + + // Only mark as forwarded if sent to all BBSs that should have it + + if (memcmp(conn->FwdMsg->fbbs, zeros, NBMASK) == 0) + { + conn->FwdMsg->status = 'F'; // Mark as forwarded + conn->FwdMsg->datechanged=time(NULL); + } + + conn->FwdMsg->Locked = 0; // Unlock + } + + // drop through to send any more + } + else + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + conn->Flags &= !SENDINGSYNCMSG; + free(conn->SyncMessage); + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + + return; + } + } + + if (strcmp(Buffer, "OK\r") == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + // Send Message(?s) to RMS Relay SYNC + +/* +OK +>TR AddMessage_V5JLSGH591JR 786 1219 522 True +BYE*/ + if (FindMessagestoForward(conn) && conn->FwdMsg) + { + struct MsgInfo * Msg = conn->FwdMsg; + char Buffer[128]; + char * Message; + + Message = FormatSYNCMessage(conn, Msg); + + // Need to compress it + + conn->SyncMessage = malloc(conn->SyncXMLLen + conn->SyncMsgLen + 4096); + + conn->SyncCompressedLen = Encode(Message, conn->SyncMessage, conn->SyncXMLLen + conn->SyncMsgLen, 0, 1); + + sprintf(Buffer, "TR AddMessage_%s %d %d %d True\r", // The True on end indicates compressed + Msg->bid, conn->SyncCompressedLen, conn->SyncXMLLen, conn->SyncMsgLen); + + free(Message); + + conn->Flags |= PROPOSINGSYNCMSG; + + BBSputs(conn, Buffer); + return; + } + + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (memcmp(Buffer, "TR ", 2) == 0) + { + // Messages have TR_COMMAND_BID Compressed Len XML Len Bosy Len + + char * Command; + char * BIDptr; + + BIDRec * BID; + char *ptr2, *context; + + // TR AddMessage_1145_G8BPQ 727 1202 440 True + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + + Command = strtok_s(&Buffer[3], "_", &context); + BIDptr = strtok_s(NULL, " ", &context); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncCompressedLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncXMLLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + conn->SyncMsgLen = atoi(ptr2); + ptr2 = strtok_s(NULL, " ", &context); + + // If addmessage need to check bid doesn't exist + + if (strcmp(Command, "AddMessage") == 0) + { + strlop(BIDptr, '@'); // sometimes has @CALL@R + if (strlen(BIDptr) > 12) + BIDptr[12] = 0; + + BID = LookupBID(BIDptr); + + if (BID) + { + BBSputs(conn, "Rejected - Duplicate BID\r"); + return; + } + } + + conn->TempMsg = zalloc(sizeof(struct MsgInfo)); + + conn->Flags |= GETTINGSYNCMESSAGE; + + BBSputs(conn, "OK\r"); + return; + } + + if (memcmp(Buffer, "BYE\r", 4) == 0) + { + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; + } + + if (memcmp(Buffer, "BBS\r", 4) == 0) + { + // Out of Sync + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + conn->BBSFlags &= ~SYNCMODE; + return; + } + + WriteLogLine(conn, '<', Buffer, len-1, LOG_BBS); + WriteLogLine(conn, '<', "Unexpected SYNC Message", 23, LOG_BBS); + + BBSputs(conn, "BYE\r"); + conn->CloseAfterFlush = 20; // 2 Secs + conn->BBSFlags &= ~SYNCMODE; + return; +} +BOOL ProcessReqFile(struct MsgInfo * Msg) +{ + char FN[128]; + char * Buffer; + int Len = 0; + char * ptr; + struct stat STAT; + char MsgFile[MAX_PATH]; + FILE * hFile; + int FileSize; + char * MsgBytes; + + // Parse title - gives file and return address + + // DEMOS\ESSAI.TXT @ F6FBB.FMLR.FRA.EU + + // At the moment we don't allow subdirectories but no harm handling here + + char * Address; + char * filename = NULL; // ?? Pattern Match ?? + + strcpy(FN, Msg->title); + + ptr = strchr(FN, '@'); + + if (ptr == NULL) + + // if we don't have return address no point + // but could we default to sender?? + + return FALSE; + + *ptr++ = 0; // Terminate Path + + strlop(FN, ' '); + + while (*ptr == ' ') + ptr++; // accept with or without spaces round @ + + Address = ptr; + + ptr = Buffer = malloc(MaxTXSize + 1); // Allow terminating Null + + // Build Message + + if (FN == NULL) + { + Len = sprintf(Buffer, "Missing Filename\r"); + } + else if (strstr(FN, "..") || strchr(FN, '/') || strchr(FN, '\\')) + { + Len = sprintf(Buffer,"Invalid filename %s\r", FN); + } + else + { + if (BaseDir[0]) + sprintf_s(MsgFile, sizeof(MsgFile), "%s/Files/%s", BaseDir, FN); + else + sprintf_s(MsgFile, sizeof(MsgFile), "Files/%s", FN); + + if (stat(MsgFile, &STAT) != -1) + { + FileSize = STAT.st_size; + + hFile = fopen(MsgFile, "rb"); + + if (hFile) + { + int Length; + + if (FileSize > MaxTXSize) + FileSize = MaxTXSize; // Truncate to max size + + MsgBytes=malloc(FileSize+1); + fread(MsgBytes, 1, FileSize, hFile); + fclose(hFile); + + MsgBytes[FileSize]=0; + + // Remove lf chars + + Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes)); + + Len = sprintf(Buffer, "%s", MsgBytes); + free(MsgBytes); + } + } + else + Len = sprintf(Buffer, "File %s not found\r", FN); + } + + SendServerReply("REQFIL Reply", Buffer, Len, _strupr(Address)); + return TRUE; +} + +BOOL CheckforMessagetoServer(struct MsgInfo * Msg) +{ + if (_stricmp(Msg->to, "REQDIR") == 0) + return ProcessReqDir(Msg); + + if (_stricmp(Msg->to, "REQFIL") == 0) + return ProcessReqFile(Msg); + + return FALSE; +} + +VOID SendServerReply(char * Title, char * MailBuffer, int Length, char * To) +{ + struct MsgInfo * Msg = AllocateMsgRecord(); + BIDRec * BIDRec; + char * Via; + char MsgFile[MAX_PATH]; + FILE * hFile; + size_t WriteLen=0; + + Msg->length = Length; + + GetSemaphore(&MsgNoSemaphore, 0); + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + FreeSemaphore(&MsgNoSemaphore); + + strcpy(Msg->from, BBSName); + Via = strlop(To, '@'); + + if (Via) + strcpy(Msg->via, Via); + + strcpy(Msg->to, To); + strcpy(Msg->title, Title); + + Msg->type = 'P'; + Msg->status = 'N'; + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName); + + BIDRec = AllocateBIDRecord(); + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, NULL); + free(MailBuffer); +} + +void SendRequestSync(CIRCUIT * conn) +{ + // Only need XML Header + + char * Buffer = malloc(4096); + int Len = 0; + + struct tm *tm; + char Date[32]; + char MsgTime[32]; + time_t Time = time(NULL); + + tm = gmtime(&Time); + + sprintf_s(Date, sizeof(Date), "%04d%02d%02d%02d%02d%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + sprintf_s(MsgTime, sizeof(Date), "%04d/%02d/%02d %02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + Len += sprintf(&Buffer[Len], "\r\n"); + + Len += sprintf(&Buffer[Len], "\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " request_sync\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Date); + Len += sprintf(&Buffer[Len], " %s\r\n", BBSName); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " BBSName\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", conn->SyncHost); + Len += sprintf(&Buffer[Len], " %d\r\n", conn->SyncPort); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], "\r\n"); + +/* + + + + request_sync + 20230205100652 + GI8BPQ + + + GI8BPQ + + 127.0.0.1 + 8780 + + + +*/ + + // Need to compress it + + conn->SyncXMLLen = Len; + conn->SyncMsgLen = 0; + + conn->SyncMessage = malloc(conn->SyncXMLLen + 4096); + + conn->SyncCompressedLen = Encode(Buffer, conn->SyncMessage, conn->SyncXMLLen, 0, 1); + + sprintf(Buffer, "TR RequestSync_%s_%d %d %d 0 True\r", // The True on end indicates compressed + 50, conn->SyncCompressedLen, conn->SyncXMLLen); + + free(Buffer); + + conn->Flags |= REQUESTINGSYNC; + + BBSputs(conn, Buffer); + return; +} + + +void ProcessSyncXML(CIRCUIT * conn, char * XML) +{ + // Process XML from RMS Relay Sync + + // All seem to start + + // + // + // + // + + char * Type = strstr(XML, ""); + + if (Type == NULL) + return; + + Type += strlen(""); + + if (memcmp(Type, "rms_location", 12) == 0) + { + return; + } + + + if (memcmp(Type, "request_sync", 12) == 0) + { + char * Call; + struct UserInfo * BBSREC; + + // This isn't requesting a poll, it is asking to be added as a sync partner + + Call = strstr(Type, ""); + + if (Call == NULL) + return; + + Call += 10; + strlop(Call, '<'); + BBSREC = FindBBS(Call); + + if (BBSREC == NULL) + return; + + if (BBSREC->ForwardingInfo->Forwarding == 0) + StartForwarding(BBSREC->BBSNumber, NULL); + + return; + } + + if (memcmp(Type, "remove_message", 14) == 0) + { + char * MID = strstr(Type, ""); + struct MsgInfo * Msg; + + if (MID == NULL) + return; + + MID += 11; + strlop(MID, '<'); + + strlop(MID, '@'); // sometimes has @CALL@R + if (strlen(MID) > 12) + MID[12] = 0; + + Msg = FindMessageByBID(MID); + + if (Msg == NULL) + return; + + Logprintf(LOG_BBS, conn, '|', "Killing Msg %d %s", Msg->number, Msg->bid); + + FlagAsKilled(Msg, TRUE); + return; + } + + if (memcmp(Type, "delivered", 9) == 0) + { + char * MID = strstr(Type, ""); + struct MsgInfo * Msg; + + if (MID == NULL) + return; + + MID += 11; + strlop(MID, '<'); + + strlop(MID, '@'); // sometimes has @CALL@R + if (strlen(MID) > 12) + MID[12] = 0; + + Msg = FindMessageByBID(MID); + + if (Msg == NULL) + return; + + Logprintf(LOG_BBS, conn, '|', "Message Msg %d %s Delivered", Msg->number, Msg->bid); + return; + } + + Debugprintf(Type); + return; + +/* + + + + request_sync + 20230205100652 + GI8BPQ + + + GI8BPQ + + 127.0.0.1 + 8780 + + + +} + + + + delivered + 20230205093113 + G8BPQ + + + 10845_GM8BPB + G8BPQ + G8BPQ + 3 + + + + Public Enum MessageDeliveryMethod + ' + ' Method used to deliver a message. None if the message hasn't been delivered. + ' + Unspecified = -1 + None = 0 + Telnet = 1 + CMS = 2 + Radio = 3 + Email = 4 +End Enum +*/ +} + +int ReformatSyncMessage(CIRCUIT * conn) +{ + // Message has been decompressed - reformat to look like a WLE message + + char * MsgBit; + char *ptr1, *ptr2; + int linelen; + char FullFrom[80]; + char FullTo[80]; + char BID[80]; + time_t Date; + char Mon[80]; + char Subject[80]; + int i = 0; + char * Boundary; + char * Input; + char * via = NULL; + char * NewMsg = conn->MailBuffer; + char * SaveMsg = NewMsg; + char DateString[80]; + struct tm * tm; + char Type[16] = "Private"; + char * part[100] = {""}; + char * partname[100]; + int partLen[100]; + char xml[4096]; + + // Message has an XML header then the message + + // The XML may have control info, so examine it. + + /* + Date: Mon, 25 Oct 2021 10:22:00 -0000 + From: GM8BPQ + Subject: Test + To: 2E1BGT + Message-ID: ALYJQJRXVQAO + X-Source: GM8BPQ + X-Relay: G8BPQ + MIME-Version: 1.0 + MIME-Version: 1.0 + Content-Type: multipart/mixed; boundary="boundaryBSoxlw==" + + --boundaryBSoxlw== + Content-Type: text/plain; charset="iso-8859-1" + Content-Transfer-Encoding: quoted-printable + + Hello Hello + + --boundaryBSoxlw==-- + */ + + // I think the best way is to reformat as if from Winlink Express, then pass + //through the normal B2 code. + +// WriteLogLine(conn, '<', conn->MailBuffer, conn->TempMsg->length, LOG_BBS); + + // display the message for testing + + conn->MailBuffer[conn->TempMsg->length] = 0; + +// OutputDebugString(conn->MailBuffer); + memcpy(xml, conn->MailBuffer, conn->SyncXMLLen); + xml[conn->SyncXMLLen] = 0; + + if (conn->SyncMsgLen == 0) + { + // No message, Just xml. Looks like a status report + + ProcessSyncXML(conn, xml); + return 0; + } + + MsgBit = &conn->MailBuffer[conn->SyncXMLLen]; + conn->TempMsg->length -= conn->SyncXMLLen; + + ptr1 = MsgBit; + +Loop: + + ptr2 = strchr(ptr1, '\r'); + + linelen = (int)(ptr2 - ptr1); + + if (_memicmp(ptr1, "From:", 5) == 0) + { + memcpy(FullFrom, &ptr1[6], linelen - 6); + FullFrom[linelen - 6] = 0; + } + + if (_memicmp(ptr1, "To:", 3) == 0) + { + memcpy(FullTo, &ptr1[4], linelen - 4); + FullTo[linelen - 4] = 0; + } + + else if (_memicmp(ptr1, "Subject:", 8) == 0) + { + memcpy(Subject, &ptr1[9], linelen - 9); + Subject[linelen - 9] = 0; + } + + else if (_memicmp(ptr1, "Message-ID", 10) == 0) + { + memcpy(BID, &ptr1[12], linelen - 12); + BID[linelen - 12] = 0; + } + + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: Mon, 25 Oct 2021 10:22:00 -0000 + + sscanf(&ptr1[11], "%02d %s %04d %02d:%02d:%02d", + &rtime.tm_mday, &Mon, &rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec); + + rtime.tm_year -= 1900; + + for (i = 0; i < 12; i++) + { + if (strcmp(Mon, month[i]) == 0) + break; + } + + rtime.tm_mon = i; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip crlf + goto Loop; + } + + // Unpack Body - seems to be multipart even if only one + + // Can't we just send the whole body through ?? + // No, Attachment format is different + + // Mbo: GM8BPQ + // Body: 17 + // File: 1471 leadercoeffs.txt + + Input = MsgBit; + Boundary = initMultipartUnpack(&Input); + + i = 0; + + if (Boundary) + { + // input should be start of part + + // Find End of part - ie -- Boundary + CRLF or -- + + char * ptr, * saveptr; + char * Msgptr; + size_t BLen = strlen(Boundary); + size_t Partlen; + + saveptr = Msgptr = ptr = Input; + + while(ptr) // Just in case we run off end + { + if (*ptr == '-' && *(ptr+1) == '-') + { + if (memcmp(&ptr[2], Boundary, BLen) == 0) + { + // Found Boundary + + char * p1, *p2, *ptr3, *ptr4; + int llen; + int Base64 = 0; + int QuotedP = 0; + char * BoundaryStart = ptr; + + Partlen = ptr - Msgptr; + + ptr += (BLen + 2); // End of Boundary + + if (*ptr == '-') // Terminating Boundary + Input = NULL; + else + Input = ptr + 2; + + // Will check for quoted printable + + p1 = Msgptr; +Loop2: + p2 = strchr(p1, '\r'); + llen = (int)(p2 - p1); + + if (llen) + { + + if (_memicmp(p1, "Content-Transfer-Encoding:", 26) == 0) + { + if (_memicmp(&p1[27], "base64", 6) == 0) + Base64 = TRUE; + else if (_memicmp(&p1[27], "quoted", 6) == 0) + QuotedP = TRUE; + } + else if (_memicmp(p1, "Content-Disposition: ", 21) == 0) + { + ptr3 = strstr(&p1[21], "name"); + + if (ptr3) + { + ptr3 += 5; + if (*ptr3 == '"') ptr3++; + ptr4 = strchr(ptr3, '"'); + if (ptr4) *ptr4 = 0; + + partname[i] = ptr3; + } + } + + if (llen) // Not Null line + { + p1 = p2 + 2; // Skip crlf + goto Loop2; + } + } + + part[i] = strstr(p2, "\r\n"); // Over separator + + if (part[i]) + { + part[i] += 2; + partLen[i] = BoundaryStart - part[i] - 2; + if (QuotedP) + partLen[i] = decode_quoted_printable(part[i], partLen[i]); + else if (Base64) + { + int Len = partLen[i], NewLen; + char * ptr = part[i]; + char * ptr2 = part[i]; + + // WLE sends base64 with embedded crlf, so remove them + + while (Len-- > 0) + { + if ((*ptr) != 10 && (*ptr) != 13) + *(ptr2++) = *(ptr++); + else + ptr ++; + } + + Len = ptr2 - part[i]; + ptr = part[i]; + ptr2 = part[i]; + + while (Len > 0) + { + decodeblock(ptr, ptr2); + ptr += 4; + ptr2 += 3; + Len -= 4; + } + + NewLen = (int)(ptr2 - part[i]); + + if (*(ptr-1) == '=') + NewLen--; + + if (*(ptr-2) == '=') + NewLen--; + + partLen[i] = NewLen; + } + } + Msgptr = ptr = Input; + i++; + continue; } + + // See if more parts + } + ptr++; + } + ptr++; + } + + + // Build the message + + tm = gmtime(&Date); + + sprintf(DateString, "%04d/%02d/%02d %02d:%02d", + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + NewMsg += sprintf(NewMsg, + "MID: %s\r\n" + "Date: %s\r\n" + "Type: %s\r\n" + "From: %s\r\n", + BID, DateString, Type, FullFrom); + +// if (ToCalls) +// { +// int i; + +// for (i = 0; i < Calls; i++) +// NewMsg += sprintf(NewMsg, "To: %s\r\n", ToCalls[i]); + +// } +// else + { + NewMsg += sprintf(NewMsg, "To: %s\r\n", + FullTo); + } +// if (WebMail->CC && WebMail->CC[0]) +// NewMsg += sprintf(NewMsg, "CC: %s\r\n", WebMail->CC); + + NewMsg += sprintf(NewMsg, + "Subject: %s\r\n" + "Mbo: %s\r\n", + Subject, BBSName); + + // Write the Body: line and any File Lines + + NewMsg += sprintf(NewMsg, "Body: %d\r\n", partLen[0]); + + i = 1; + + while (part[i]) + { + NewMsg += sprintf(NewMsg, "File: %d %s\r\n", + partLen[i], partname[i]); + + i++; + } + + NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line to end header + + // Now add parts + + i = 0; + + while (part[i]) + { + memmove(NewMsg, part[i], partLen[i]); + NewMsg += partLen[i]; + i++; + NewMsg += sprintf(NewMsg, "\r\n"); // Blank Line between attachments + } + + conn->TempMsg->length = NewMsg - SaveMsg; + conn->TempMsg->datereceived = conn->TempMsg->datechanged = time(NULL); + conn->TempMsg->datecreated = Date; + strcpy(conn->TempMsg->bid, BID); + + if (strlen(Subject) > 60) + Subject[60] = 0; + + strcpy(conn->TempMsg->title, Subject); + + return TRUE; +} + +char * FormatSYNCMessage(CIRCUIT * conn, struct MsgInfo * Msg) +{ + // First an XML Header + + char * Buffer = malloc(4096 + Msg->length); + int Len = 0; + + struct tm *tm; + char Date[32]; + char MsgTime[32]; + char Separator[33]=""; + time_t Time = time(NULL); + char * MailBuffer; + int BodyLen; + char * Encoded; + + // Get the message - may need length in header + + MailBuffer = ReadMessageFile(Msg->number); + + BodyLen = Msg->length; + + // Remove any B2 Header + + if (Msg->B2Flags & B2Msg) + { + // Remove B2 Headers (up to the File: Line) + + char * ptr; + ptr = strstr(MailBuffer, "Body:"); + if (ptr) + { + BodyLen = atoi(ptr + 5); + ptr = strstr(ptr, "\r\n\r\n"); + } + if (ptr) + { + memcpy(MailBuffer, ptr + 4, BodyLen); + MailBuffer[BodyLen] = 0; + } + } + + // encode body as quoted printable; + + Encoded = malloc(Msg->length * 3); + + BodyLen = encode_quoted_printable(MailBuffer, Encoded, BodyLen); + + // Create multipart Boundary + + CreateOneTimePassword(&Separator[0], "Key", 0); + CreateOneTimePassword(&Separator[16], "Key", 1); + + + tm = gmtime(&Time); + + sprintf_s(Date, sizeof(Date), "%04d%02d%02d%02d%02d%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + tm = gmtime((time_t *)&Msg->datecreated); + + sprintf_s(MsgTime, sizeof(Date), "%04d/%02d/%02d %02d:%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + Len += sprintf(&Buffer[Len], "\r\n"); + + Len += sprintf(&Buffer[Len], "\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " add_message\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Date); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->bid); + Len += sprintf(&Buffer[Len], " \r\n", MsgTime); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], " 2\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", (Msg->B2Flags & Attachments) ? "true" : "false"); + Len += sprintf(&Buffer[Len], " %d\r\n", BodyLen); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->title); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->bid); + Len += sprintf(&Buffer[Len], " 450443\r\n"); + Len += sprintf(&Buffer[Len], " %s\r\n", Msg->to); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " 0\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " True\r\n"); + Len += sprintf(&Buffer[Len], " False\r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], " \r\n"); + Len += sprintf(&Buffer[Len], "\r\n"); + +// Debugprintf(Buffer); + + conn->SyncXMLLen = Len; + + Len += sprintf(&Buffer[Len], "Date: Sat, 04 Feb 2023 11:19:00 +0000\r\n"); + Len += sprintf(&Buffer[Len], "From: %s\r\n", Msg->from); + Len += sprintf(&Buffer[Len], "Subject: %s\r\n", Msg->title); + Len += sprintf(&Buffer[Len], "To: %s\r\n", Msg->to); + Len += sprintf(&Buffer[Len], "Message-ID: %s\r\n", Msg->bid); +// Len += sprintf(&Buffer[Len], "X-Source: G8BPQ\r\n"); +// Len += sprintf(&Buffer[Len], "X-Location: 52.979167N, 1.125000W (GRID SQUARE)\r\n"); +// Len += sprintf(&Buffer[Len], "X-RMS-Originator: G8BPQ\r\n"); +// Len += sprintf(&Buffer[Len], "X-RMS-Path: G8BPQ@2023-02-04-11:19:29\r\n"); + Len += sprintf(&Buffer[Len], "X-Relay: %s\r\n", BBSName); + + Len += sprintf(&Buffer[Len], "MIME-Version: 1.0\r\n"); + Len += sprintf(&Buffer[Len], "Content-Type: multipart/mixed; boundary=\"%s\"\r\n", Separator); + + Len += sprintf(&Buffer[Len], "\r\n"); // Blank line before separator + Len += sprintf(&Buffer[Len], "--%s\r\n", Separator); + Len += sprintf(&Buffer[Len], "Content-Type: text/plain; charset=\"iso-8859-1\"\r\n"); + Len += sprintf(&Buffer[Len], "Content-Transfer-Encoding: quoted-printable\r\n"); + Len += sprintf(&Buffer[Len], "\r\n"); // Blank line before body + + Len += sprintf(&Buffer[Len], "%s\r\n", Encoded); + Len += sprintf(&Buffer[Len], "--%s--\r\n", Separator); + + conn->SyncMsgLen = Len - conn->SyncXMLLen; + + free(Encoded); + free(MailBuffer); + + return Buffer; +} + +int encode_quoted_printable(char *s, char * out, int Len) +{ + int n = 0; + char * start = out; + + while(Len--) + { + if (n >= 73 && *s != 10 && *s != 13) + {strcpy(out, "=\r\n"); n = 0; out +=3;} + if (*s == 10 || *s == 13) {putchar(*s); n = 0;} + else if (*s<32 || *s==61 || *s>126) + out += sprintf(out, "=%02x", (unsigned char)*s); + else if (*s != 32 || (*(s+1) != 10 && *(s+1) != 13)) + {*(out++) = *s; n++;} + else n += printf("=20"); + + s++; + } + *out = 0; + + return out - start; +} + +int decode_quoted_printable(char *ptr, int len) +{ + // overwrite input with decoded version + + char * ptr2 = ptr; + char * End = ptr + len; + char * Start = ptr; + + while (ptr < End) + { + if ((*ptr) == '=') + { + char c = *(++ptr); + char d; + + c = c - 48; + if (c < 0) + { + // = CRLF as a soft break + + ptr += 2; + continue; + } + if (c > 9) c -= 7; + d = *(++ptr); + d = d - 48; + if (d > 9) d -= 7; + + *(ptr2) = c << 4 | d; + ptr2++; + ptr++; + } + else + *ptr2++ = *ptr++; + } + return ptr2 - Start; +} + + +VOID GetPGConfig() +{ + char FN[256]; + FILE *file; + char buf[256],errbuf[256]; + char * p_prog, * p_name, * p_desc; + int n = 0; + int i = 0; + + + strcpy(FN, BaseDir); + strcat(FN, "/"); + strcat(FN, "PG/PGList.txt"); + + if ((file = fopen(FN, "r")) == NULL) + { + return; + } + + while(fgets(buf, 255, file) != NULL) + { + if ( buf[0] == '#') + continue; + + strcpy(errbuf,buf); // save in case of error + + p_prog = strtok(buf, ",\n\r"); + p_name = strtok(NULL, ",\n\r"); + p_desc = strtok(NULL, ",\n\r"); + + + if (p_desc && p_desc[0]) + { + while(*(p_name) == ' ') // Remove leading spaces + p_name++; + while(*(p_desc) == ' ') + p_desc++; + SERVERLIST[n][0] = _strdup(p_prog); + SERVERLIST[n][1] = _strdup(p_name); + SERVERLIST[n++][2] = _strdup(p_desc); + } + if (n > 255) + break; + } + + + NUM_SERVERS = n; + fclose(file); + + /*------- G7TAJ PG SERVER ----------*/ + Debugprintf("Number of PG Servers = %d", NUM_SERVERS ); + for (i=0; i< NUM_SERVERS; i++ ) + { + Debugprintf("Server #%d,%s,%s,%s", i, SERVERLIST[i][0], SERVERLIST[i][1], SERVERLIST[i][2]); + } + /*------- G7TAJ END ----------*/ + +} + +void SendMessageReadEvent(char * call, struct MsgInfo * Msg) +{ + if (reportMailEvents) + { + char msg[512]; + + //12345 B 2053 TEST@ALL F6FBB 920325 This is the subject + + struct tm *tm = gmtime((time_t *)&Msg->datecreated); + + sprintf_s(msg, sizeof(msg),"%-6d %c %c %6d %-13s %-6s %02d%02d%02d %s\r", + Msg->number, Msg->type, Msg->status, Msg->length, Msg->to, + Msg->from, tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Msg->title); + +// sprintf(msg, "%s Read %d\r", user->Call, Msg->number); + +#ifdef WIN32 + if (pRunEventProgram) + pRunEventProgram("MailMsgRead.exe", msg); +#else + { + char prog[256]; + sprintf(prog, "%s/%s", BPQDirectory, "MailMsgRead"); + RunEventProgram(prog, msg); + } +#endif + } +} + +void SendMessageForwardedToM0LTE(char * call, struct MsgInfo * Msg) +{ +} + + +void SendNewMessageEvent(char * call, struct MsgInfo * Msg) +{ + if (reportMailEvents) + { + char msg[512]; + + //12345 B 2053 TEST@ALL F6FBB 920325 This is the subject + + struct tm *tm = gmtime((time_t *)&Msg->datecreated); + + sprintf_s(msg, sizeof(msg),"%-6d %c %c %6d %-13s %-6s %02d%02d%02d %s\r", + Msg->number, Msg->type, Msg->status, Msg->length, Msg->to, + Msg->from, tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Msg->title); + +#ifdef WIN32 + if (pRunEventProgram) + pRunEventProgram("MailNewMsg.exe", msg); +#else + { + char prog[256]; + sprintf(prog, "%s/%s", BPQDirectory, "MailNewMsg"); + RunEventProgram(prog, msg); + } +#endif + } +} + + + diff --git a/.svn/pristine/63/63d598559cd0d9984b4bb32fd1c5b484e2e5d8a1.svn-base b/.svn/pristine/63/63d598559cd0d9984b4bb32fd1c5b484e2e5d8a1.svn-base new file mode 100644 index 0000000..8eefa77 --- /dev/null +++ b/.svn/pristine/63/63d598559cd0d9984b4bb32fd1c5b484e2e5d8a1.svn-base @@ -0,0 +1,934 @@ + +/* pngget.c - retrieval of values from info struct + * + * libpng 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" + +png_uint_32 PNGAPI +png_get_valid(png_structp png_ptr, png_infop info_ptr, png_uint_32 flag) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->valid & flag); + else + return(0); +} + +png_uint_32 PNGAPI +png_get_rowbytes(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->rowbytes); + else + return(0); +} + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +png_bytepp PNGAPI +png_get_rows(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->row_pointers); + else + return(0); +} +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* easy access to info, added in libpng-0.99 */ +png_uint_32 PNGAPI +png_get_image_width(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->width; + } + return (0); +} + +png_uint_32 PNGAPI +png_get_image_height(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->height; + } + return (0); +} + +png_byte PNGAPI +png_get_bit_depth(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->bit_depth; + } + return (0); +} + +png_byte PNGAPI +png_get_color_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->color_type; + } + return (0); +} + +png_byte PNGAPI +png_get_filter_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->filter_type; + } + return (0); +} + +png_byte PNGAPI +png_get_interlace_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->interlace_type; + } + return (0); +} + +png_byte PNGAPI +png_get_compression_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->compression_type; + } + return (0); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + else return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + else return (info_ptr->y_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER || + info_ptr->x_pixels_per_unit != info_ptr->y_pixels_per_unit) + return (0); + else return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED +float PNGAPI +png_get_pixel_aspect_ratio(png_structp png_ptr, png_infop info_ptr) + { + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_aspect_ratio"); + if (info_ptr->x_pixels_per_unit == 0) + return ((float)0.0); + else + return ((float)((float)info_ptr->y_pixels_per_unit + /(float)info_ptr->x_pixels_per_unit)); + } +#else + return (0.0); +#endif + return ((float)0.0); +} +#endif + +png_int_32 PNGAPI +png_get_x_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + else return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + else return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_x_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + else return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + else return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +png_uint_32 PNGAPI +png_get_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_x_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_y_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +float PNGAPI +png_get_x_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_x_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +float PNGAPI +png_get_y_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_y_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +#if defined(PNG_pHYs_SUPPORTED) +png_uint_32 PNGAPI +png_get_pHYs_dpi(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function\n", "pHYs"); + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + if(*unit_type == 1) + { + if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50); + if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50); + } + } + } + return (retval); +} +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* png_get_channels really belongs in here, too, but it's been around longer */ + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +png_byte PNGAPI +png_get_channels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->channels); + else + return (0); +} + +png_bytep PNGAPI +png_get_signature(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->signature); + else + return (NULL); +} + +#if defined(PNG_bKGD_SUPPORTED) +png_uint_32 PNGAPI +png_get_bKGD(png_structp png_ptr, png_infop info_ptr, + png_color_16p *background) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) + && background != NULL) + { + png_debug1(1, "in %s retrieval function\n", "bKGD"); + *background = &(info_ptr->background); + return (PNG_INFO_bKGD); + } + return (0); +} +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM(png_structp png_ptr, png_infop info_ptr, + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + png_debug1(1, "in %s retrieval function\n", "cHRM"); + if (white_x != NULL) + *white_x = (double)info_ptr->x_white; + if (white_y != NULL) + *white_y = (double)info_ptr->y_white; + if (red_x != NULL) + *red_x = (double)info_ptr->x_red; + if (red_y != NULL) + *red_y = (double)info_ptr->y_red; + if (green_x != NULL) + *green_x = (double)info_ptr->x_green; + if (green_y != NULL) + *green_y = (double)info_ptr->y_green; + if (blue_x != NULL) + *blue_x = (double)info_ptr->x_blue; + if (blue_y != NULL) + *blue_y = (double)info_ptr->y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, + png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, + png_fixed_point *blue_x, png_fixed_point *blue_y) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + png_debug1(1, "in %s retrieval function\n", "cHRM"); + if (white_x != NULL) + *white_x = info_ptr->int_x_white; + if (white_y != NULL) + *white_y = info_ptr->int_y_white; + if (red_x != NULL) + *red_x = info_ptr->int_x_red; + if (red_y != NULL) + *red_y = info_ptr->int_y_red; + if (green_x != NULL) + *green_x = info_ptr->int_x_green; + if (green_y != NULL) + *green_y = info_ptr->int_y_green; + if (blue_x != NULL) + *blue_x = info_ptr->int_x_blue; + if (blue_y != NULL) + *blue_y = info_ptr->int_y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA(png_structp png_ptr, png_infop info_ptr, double *file_gamma) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && file_gamma != NULL) + { + png_debug1(1, "in %s retrieval function\n", "gAMA"); + *file_gamma = (double)info_ptr->gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *int_file_gamma) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && int_file_gamma != NULL) + { + png_debug1(1, "in %s retrieval function\n", "gAMA"); + *int_file_gamma = info_ptr->int_gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#endif + +#if defined(PNG_sRGB_SUPPORTED) +png_uint_32 PNGAPI +png_get_sRGB(png_structp png_ptr, png_infop info_ptr, int *file_srgb_intent) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB) + && file_srgb_intent != NULL) + { + png_debug1(1, "in %s retrieval function\n", "sRGB"); + *file_srgb_intent = (int)info_ptr->srgb_intent; + return (PNG_INFO_sRGB); + } + return (0); +} +#endif + +#if defined(PNG_iCCP_SUPPORTED) +png_uint_32 PNGAPI +png_get_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP) + && name != NULL && profile != NULL && proflen != NULL) + { + png_debug1(1, "in %s retrieval function\n", "iCCP"); + *name = info_ptr->iccp_name; + *profile = info_ptr->iccp_profile; + /* compression_type is a dummy so the API won't have to change + if we introduce multiple compression types later. */ + *proflen = (int)info_ptr->iccp_proflen; + *compression_type = (int)info_ptr->iccp_compression; + return (PNG_INFO_iCCP); + } + return (0); +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +png_uint_32 PNGAPI +png_get_sPLT(png_structp png_ptr, png_infop info_ptr, + png_sPLT_tpp spalettes) +{ + if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL) + *spalettes = info_ptr->splt_palettes; + return ((png_uint_32)info_ptr->splt_palettes_num); +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +png_uint_32 PNGAPI +png_get_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p *hist) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) + && hist != NULL) + { + png_debug1(1, "in %s retrieval function\n", "hIST"); + *hist = info_ptr->hist; + return (PNG_INFO_hIST); + } + return (0); +} +#endif + +png_uint_32 PNGAPI +png_get_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *width, png_uint_32 *height, int *bit_depth, + int *color_type, int *interlace_type, int *compression_type, + int *filter_type) + +{ + if (png_ptr != NULL && info_ptr != NULL && width != NULL && height != NULL && + bit_depth != NULL && color_type != NULL) + { + png_debug1(1, "in %s retrieval function\n", "IHDR"); + *width = info_ptr->width; + *height = info_ptr->height; + *bit_depth = info_ptr->bit_depth; + if (info_ptr->bit_depth < 1 || info_ptr->bit_depth > 16) + png_error(png_ptr, "Invalid bit depth"); + *color_type = info_ptr->color_type; + if (info_ptr->color_type > 6) + png_error(png_ptr, "Invalid color type"); + if (compression_type != NULL) + *compression_type = info_ptr->compression_type; + if (filter_type != NULL) + *filter_type = info_ptr->filter_type; + if (interlace_type != NULL) + *interlace_type = info_ptr->interlace_type; + + /* check for potential overflow of rowbytes */ + if (*width == 0 || *width > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image width"); + if (*height == 0 || *height > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image height"); + if (info_ptr->width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + { + png_warning(png_ptr, + "Width too large for libpng to process image data."); + } + return (1); + } + return (0); +} + +#if defined(PNG_oFFs_SUPPORTED) +png_uint_32 PNGAPI +png_get_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) + && offset_x != NULL && offset_y != NULL && unit_type != NULL) + { + png_debug1(1, "in %s retrieval function\n", "oFFs"); + *offset_x = info_ptr->x_offset; + *offset_y = info_ptr->y_offset; + *unit_type = (int)info_ptr->offset_unit_type; + return (PNG_INFO_oFFs); + } + return (0); +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +png_uint_32 PNGAPI +png_get_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams, + png_charp *units, png_charpp *params) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) + && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && + nparams != NULL && units != NULL && params != NULL) + { + png_debug1(1, "in %s retrieval function\n", "pCAL"); + *purpose = info_ptr->pcal_purpose; + *X0 = info_ptr->pcal_X0; + *X1 = info_ptr->pcal_X1; + *type = (int)info_ptr->pcal_type; + *nparams = (int)info_ptr->pcal_nparams; + *units = info_ptr->pcal_units; + *params = info_ptr->pcal_params; + return (PNG_INFO_pCAL); + } + return (0); +} +#endif + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL(png_structp png_ptr, png_infop info_ptr, + int *unit, double *width, double *height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_pixel_width; + *height = info_ptr->scal_pixel_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int *unit, png_charpp width, png_charpp height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_s_width; + *height = info_ptr->scal_s_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#endif +#endif +#endif + +#if defined(PNG_pHYs_SUPPORTED) +png_uint_32 PNGAPI +png_get_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function\n", "pHYs"); + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + } + } + return (retval); +} +#endif + +png_uint_32 PNGAPI +png_get_PLTE(png_structp png_ptr, png_infop info_ptr, png_colorp *palette, + int *num_palette) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE) + && palette != NULL) + { + png_debug1(1, "in %s retrieval function\n", "PLTE"); + *palette = info_ptr->palette; + *num_palette = info_ptr->num_palette; + png_debug1(3, "num_palette = %d\n", *num_palette); + return (PNG_INFO_PLTE); + } + return (0); +} + +#if defined(PNG_sBIT_SUPPORTED) +png_uint_32 PNGAPI +png_get_sBIT(png_structp png_ptr, png_infop info_ptr, png_color_8p *sig_bit) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) + && sig_bit != NULL) + { + png_debug1(1, "in %s retrieval function\n", "sBIT"); + *sig_bit = &(info_ptr->sig_bit); + return (PNG_INFO_sBIT); + } + return (0); +} +#endif + +#if defined(PNG_TEXT_SUPPORTED) +png_uint_32 PNGAPI +png_get_text(png_structp png_ptr, png_infop info_ptr, png_textp *text_ptr, + int *num_text) +{ + if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0) + { + png_debug1(1, "in %s retrieval function\n", + (png_ptr->chunk_name[0] == '\0' ? "text" + : (png_const_charp)png_ptr->chunk_name)); + if (text_ptr != NULL) + *text_ptr = info_ptr->text; + if (num_text != NULL) + *num_text = info_ptr->num_text; + return ((png_uint_32)info_ptr->num_text); + } + if (num_text != NULL) + *num_text = 0; + return(0); +} +#endif + +#if defined(PNG_tIME_SUPPORTED) +png_uint_32 PNGAPI +png_get_tIME(png_structp png_ptr, png_infop info_ptr, png_timep *mod_time) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) + && mod_time != NULL) + { + png_debug1(1, "in %s retrieval function\n", "tIME"); + *mod_time = &(info_ptr->mod_time); + return (PNG_INFO_tIME); + } + return (0); +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +png_uint_32 PNGAPI +png_get_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep *trans, int *num_trans, png_color_16p *trans_values) +{ + png_uint_32 retval = 0; + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_debug1(1, "in %s retrieval function\n", "tRNS"); + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (trans != NULL) + { + *trans = info_ptr->trans; + retval |= PNG_INFO_tRNS; + } + if (trans_values != NULL) + *trans_values = &(info_ptr->trans_values); + } + else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */ + { + if (trans_values != NULL) + { + *trans_values = &(info_ptr->trans_values); + retval |= PNG_INFO_tRNS; + } + if(trans != NULL) + *trans = NULL; + } + if(num_trans != NULL) + { + *num_trans = info_ptr->num_trans; + retval |= PNG_INFO_tRNS; + } + } + return (retval); +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +png_uint_32 PNGAPI +png_get_unknown_chunks(png_structp png_ptr, png_infop info_ptr, + png_unknown_chunkpp unknowns) +{ + if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL) + *unknowns = info_ptr->unknown_chunks; + return ((png_uint_32)info_ptr->unknown_chunks_num); +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +png_byte PNGAPI +png_get_rgb_to_gray_status (png_structp png_ptr) +{ + return (png_byte)(png_ptr? png_ptr->rgb_to_gray_status : 0); +} +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) +png_voidp PNGAPI +png_get_user_chunk_ptr(png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_chunk_ptr : NULL); +} +#endif + +#ifdef PNG_WRITE_SUPPORTED +png_uint_32 PNGAPI +png_get_compression_buffer_size(png_structp png_ptr) +{ + return (png_uint_32)(png_ptr? png_ptr->zbuf_size : 0L); +} +#endif + +#ifndef PNG_1_0_X +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +/* this function was added to libpng 1.2.0 and should exist by default */ +png_uint_32 PNGAPI +png_get_asm_flags (png_structp png_ptr) +{ + return (png_uint_32)(png_ptr? png_ptr->asm_flags : 0L); +} + +/* this function was added to libpng 1.2.0 and should exist by default */ +png_uint_32 PNGAPI +png_get_asm_flagmask (int flag_select) +{ + png_uint_32 settable_asm_flags = 0; + + if (flag_select & PNG_SELECT_READ) + settable_asm_flags |= + PNG_ASM_FLAG_MMX_READ_COMBINE_ROW | + PNG_ASM_FLAG_MMX_READ_INTERLACE | + PNG_ASM_FLAG_MMX_READ_FILTER_SUB | + PNG_ASM_FLAG_MMX_READ_FILTER_UP | + PNG_ASM_FLAG_MMX_READ_FILTER_AVG | + PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ; + /* no non-MMX flags yet */ + +#if 0 + /* GRR: no write-flags yet, either, but someday... */ + if (flag_select & PNG_SELECT_WRITE) + settable_asm_flags |= + PNG_ASM_FLAG_MMX_WRITE_ [whatever] ; +#endif /* 0 */ + + return settable_asm_flags; /* _theoretically_ settable capabilities only */ +} +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) + /* GRR: could add this: && defined(PNG_MMX_CODE_SUPPORTED) */ +/* this function was added to libpng 1.2.0 */ +png_uint_32 PNGAPI +png_get_mmx_flagmask (int flag_select, int *compilerID) +{ + png_uint_32 settable_mmx_flags = 0; + + if (flag_select & PNG_SELECT_READ) + settable_mmx_flags |= + PNG_ASM_FLAG_MMX_READ_COMBINE_ROW | + PNG_ASM_FLAG_MMX_READ_INTERLACE | + PNG_ASM_FLAG_MMX_READ_FILTER_SUB | + PNG_ASM_FLAG_MMX_READ_FILTER_UP | + PNG_ASM_FLAG_MMX_READ_FILTER_AVG | + PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ; +#if 0 + /* GRR: no MMX write support yet, but someday... */ + if (flag_select & PNG_SELECT_WRITE) + settable_mmx_flags |= + PNG_ASM_FLAG_MMX_WRITE_ [whatever] ; +#endif /* 0 */ + + if (compilerID != NULL) { +#ifdef PNG_USE_PNGVCRD + *compilerID = 1; /* MSVC */ +#else +#ifdef PNG_USE_PNGGCCRD + *compilerID = 2; /* gcc/gas */ +#else + *compilerID = -1; /* unknown (i.e., no asm/MMX code compiled) */ +#endif +#endif + } + + return settable_mmx_flags; /* _theoretically_ settable capabilities only */ +} + +/* this function was added to libpng 1.2.0 */ +png_byte PNGAPI +png_get_mmx_bitdepth_threshold (png_structp png_ptr) +{ + return (png_byte)(png_ptr? png_ptr->mmx_bitdepth_threshold : 0); +} + +/* this function was added to libpng 1.2.0 */ +png_uint_32 PNGAPI +png_get_mmx_rowbytes_threshold (png_structp png_ptr) +{ + return (png_uint_32)(png_ptr? png_ptr->mmx_rowbytes_threshold : 0L); +} +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* these functions were added to libpng 1.2.6 */ +png_uint_32 PNGAPI +png_get_user_width_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_width_max : 0); +} +png_uint_32 PNGAPI +png_get_user_height_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_height_max : 0); +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + +#endif /* ?PNG_1_0_X */ diff --git a/.svn/pristine/64/64c803e384e74f7d8289e97e8f6a16d891fb3edf.svn-base b/.svn/pristine/64/64c803e384e74f7d8289e97e8f6a16d891fb3edf.svn-base new file mode 100644 index 0000000..309cf34 --- /dev/null +++ b/.svn/pristine/64/64c803e384e74f7d8289e97e8f6a16d891fb3edf.svn-base @@ -0,0 +1,5286 @@ +/* +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 +*/ + + +//#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#define _CRT_SECURE_NO_DEPRECATE + +#define DllImport + +#include "cheaders.h" +#include + +#include "tncinfo.h" +#include "time.h" +#include "bpq32.h" +#include "telnetserver.h" + +// This is needed to link with a lib built from source + +#ifdef WIN32 +#define ZEXPORT __stdcall +#endif + +#include + +#define CKernel +#include "httpconnectioninfo.h" + +extern int MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS, L3FRAMES; +extern int NUMBEROFNODES, MAXDESTS, L4CONNECTSOUT, L4CONNECTSIN, L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES; +extern int STATSTIME; +extern TRANSPORTENTRY * L4TABLE; +extern BPQVECSTRUC BPQHOSTVECTOR[]; +extern BOOL APRSApplConnected; +extern char VersionString[]; +VOID FormatTime3(char * Time, time_t cTime); +DllExport int APIENTRY Get_APPLMASK(int Stream); +VOID SaveUIConfig(); +int ProcessNodeSignon(SOCKET sock, struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL); +VOID SetupUI(int Port); +VOID SendUIBeacon(int Port); +VOID GetParam(char * input, char * key, char * value); +VOID ARDOPAbort(struct TNCINFO * TNC); +VOID WriteMiniDump(); +BOOL KillTNC(struct TNCINFO * TNC); +BOOL RestartTNC(struct TNCINFO * TNC); +int GetAISPageInfo(char * Buffer, int ais, int adsb); +int GetAPRSPageInfo(char * Buffer, double N, double S, double W, double E, int aprs, int ais, int adsb); +unsigned char * Compressit(unsigned char * In, int Len, int * OutLen); +char * stristr (char *ch1, char *ch2); +int GetAPRSIcon(unsigned char * _REPLYBUFFER, char * NodeURL); +char * GetStandardPage(char * FN, int * Len); +BOOL SHA1PasswordHash(char * String, char * Hash); +char * byte_base64_encode(char *str, int len); +int APIProcessHTTPMessage(char * response, char * Method, char * URL, char * request, BOOL LOCAL, BOOL COOKIE); +int RHPProcessHTTPMessage(struct ConnectionInfo * conn, char * response, char * Method, char * URL, char * request, BOOL LOCAL, BOOL COOKIE); +unsigned char * Compressit(unsigned char * In, int Len, int * OutLen); +int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen); + +extern struct ROUTE * NEIGHBOURS; +extern int ROUTE_LEN; +extern int MAXNEIGHBOURS; + +extern struct DEST_LIST * DESTS; // NODE LIST +extern int DEST_LIST_LEN; +extern int MAXDESTS; // MAX NODES IN SYSTEM + +extern struct _LINKTABLE * LINKS; +extern int LINK_TABLE_LEN; +extern int MAXLINKS; +extern char * RigWebPage; +extern COLORREF Colours[256]; + +extern BOOL IncludesMail; +extern BOOL IncludesChat; + +extern BOOL APRSWeb; +extern BOOL RigActive; + +extern HKEY REGTREE; + +extern BOOL APRSActive; + +extern UCHAR LogDirectory[]; + +extern struct RIGPORTINFO * PORTInfo[34]; +extern int NumberofPorts; + +extern UCHAR ConfigDirectory[260]; + +extern struct AXIPPORTINFO * Portlist[]; + +VOID sendandcheck(SOCKET sock, const char * Buffer, int Len); +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 ProcessMailHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen, int InputLen, char * Token); +void ProcessChatHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen); +struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot); +int SetupNodeMenu(char * Buff, int SYSOP); +int StatusProc(char * Buff); +int ProcessMailSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL); +int ProcessMailAPISignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL); +int ProcessChatSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL); +VOID APRSProcessHTTPMessage(SOCKET sock, char * MsgPtr, BOOL LOCAL, BOOL COOKIE); + + +static struct HTTPConnectionInfo * SessionList; // active term mode sessions + +char Mycall[10]; + +char MAILPipeFileName[] = "\\\\.\\pipe\\BPQMAILWebPipe"; +char CHATPipeFileName[] = "\\\\.\\pipe\\BPQCHATWebPipe"; + +char Index[] = "%s's BPQ32 Web Server

" +"" +"" +"
Node PagesAPRS Pages
"; + +char IndexNoAPRS[] = "" +""; + +//char APRSBit[] = "
APRS Pages"; + +//char MailBit[] = "Mail Mgmt" +// "WebMail"; +//char ChatBit[] = "Chat Mgmt"; + +char Tail[] = ""; + +char RouteHddr[] = "

Routes

" +""; + +char RouteLine[] = ""; +char xNodeHddr[] = "
" +"
PortCallQualityNode CountFrame CountRetriesPercentMaxframeFrackLast HeardQueuedRem Qual
%s%d%s%s%d%d%d%d%d%%d%d%02d:%02d%d%d
" +"
" +"" +"
" +"

Nodes %s

"; + +char NodeHddr[] = "
" +"" +"" +"
" +"

Nodes %s

"; + +char NodeLine[] = ""; + + +char StatsHddr[] = "

Node Stats

%s:%s
" +""; + +char PortStatsHddr[] = "

Stats for Port %d

"; + +char PortStatsLine[] = ""; + + +char Beacons[] = "

Beacon Configuration for Port %d

You need to be signed in to save changes

%s %d
" +"" +"
" +"" +"" +"" +"" +"" +"
Send Interval (Minutes)
To
Path
Send From File
Text
" +"" + +"

" +""; + + +char LinkHddr[] = "

Links

" +""; + +char LinkLine[] = ""; + +char UserHddr[] = "

Sessions

Far CallOur CallPortax.25 stateLink Typeax.25 Version
%s%s%d%s%s%d
"; + +char UserLine[] = ""; + +char TermSignon[] = "BPQ32 Node %s Terminal Access" +"

BPQ32 Node %s Terminal Access

" +"

Please enter username and password to access the node

" +"" +"
%s%s%s
" +"" +"
User
Password
" +"

" +""; + + +char PassError[] = "

Sorry, User or Password is invalid - please try again

"; + +char BusyError[] = "

Sorry, No sessions available - please try later

"; + +char LostSession[] = "Sorry, Session had been lost - refresh page to sign in again"; +char NoSessions[] = "Sorry, No Sessions available - refresh page to try again"; + +char TermPage[] = "" +"BPQ32 Node %s" +"" +"" +"

BPQ32 Node %s

" +"
" +"

" +"" +"" +""; + +char TermOutput[] = "" +"" +"" +"" +"" +"" +"" +"
\""; + + +// font-family:monospace;background-color:black;color:lawngreen;font-size:12px + +char TermOutputTail[] = "
"; + +/* +char InputLine[] = "" +"
" +"" +"
"; +*/ +char InputLine[] = "" +"
" +"\" id=inp type=text text width=100%% name=input />" +"
"; + +static char NodeSignon[] = "BPQ32 Node SYSOP Access" +"

BPQ32 Node %s SYSOP Access

" +"

This page sets Cookies. Don't continue if you object to this

" +"

Please enter Callsign and Password to access the Node

" +"
" +"" +"" +"
User
Password
" +"

"; + + +static char MailSignon[] = "BPQ32 Mail Server Access" +"

BPQ32 Mail Server %s Access

" +"

Please enter Callsign and Password to access the BBS

" +"
" +"" +"" +"
User
Password
" +"

"; + +static char ChatSignon[] = "BPQ32 Chat Server Access" +"

BPQ32 Chat Server %s Access

" +"

Please enter Callsign and Password to access the Chat Server

" +"
" +"" +"" +"
User
Password
" +"

"; + + +static char MailLostSession[] = "" +"
" +"Sorry, Session had been lost

    " +"
"; + + +static char ConfigEditPage[] = "" +"Edit Config" +"
" +"

" +"
"; + +static char EXCEPTMSG[80] = ""; + + +void UndoTransparency(char * input) +{ + char * ptr1, * ptr2; + char c; + int hex; + + if (input == NULL) + return; + + ptr1 = ptr2 = input; + + // Convert any %xx constructs + + while (1) + { + c = *(ptr1++); + + if (c == 0) + break; + + if (c == '%') + { + c = *(ptr1++); + if(isdigit(c)) + hex = (c - '0') << 4; + else + hex = (tolower(c) - 'a' + 10) << 4; + + c = *(ptr1++); + if(isdigit(c)) + hex += (c - '0'); + else + hex += (tolower(c) - 'a' + 10); + + *(ptr2++) = hex; + } + else if (c == '+') + *(ptr2++) = 32; + else + *(ptr2++) = c; + } + *ptr2 = 0; +} + + + + +VOID PollSession(struct HTTPConnectionInfo * Session) +{ + int state, change; + int count, len; + char Msg[400] = ""; + char Formatted[8192]; + char * ptr1, * ptr2; + char c; + int Line; + + // Poll Node + + SessionState(Session->Stream, &state, &change); + + if (change == 1) + { + int Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + if (state == 1)// Connected + Session->ScreenLines[Line] = _strdup("*** Connected
\r\n"); + else + Session->ScreenLines[Line] = _strdup("*** Disconnected
\r\n"); + + if (Line == 99) + Session->LastLine = 0; + + Session->Changed = TRUE; + } + + if (RXCount(Session->Stream) > 0) + { + int realLen = 0; + + do + { + GetMsg(Session->Stream, &Msg[0], &len, &count); + + // replace cr with
and space with   + + + ptr1 = Msg; + ptr2 = &Formatted[0]; + + if (Session->PartLine) + { + // Last line was incomplete - append to it + + realLen = Session->PartLine; + + Line = Session->LastLine - 1; + + if (Line < 0) + Line = 99; + + strcpy(Formatted, Session->ScreenLines[Line]); + ptr2 += strlen(Formatted); + + Session->LastLine = Line; + Session->PartLine = FALSE; + } + + while (len--) + { + c = *(ptr1++); + realLen++; + + if (c == 13) + { + int LineLen; + + strcpy(ptr2, "
\r\n"); + + // Write to screen + + Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + LineLen = (int)strlen(Formatted); + + // if line starts with a colour code, process it + + if (Formatted[0] == 0x1b && LineLen > 1) + { + int ColourCode = Formatted[1] - 10; + COLORREF Colour = Colours[ColourCode]; + char ColString[30]; + + memmove(&Formatted[20], &Formatted[2], LineLen); + sprintf(ColString, "", GetRValue(Colour), GetGValue(Colour), GetBValue(Colour)); + memcpy(Formatted, ColString, 20); + strcat(Formatted, ""); + LineLen =+ 28; + } + + Session->ScreenLineLen[Line] = LineLen; + Session->ScreenLines[Line] = _strdup(Formatted); + + if (Line == 99) + Session->LastLine = 0; + + ptr2 = &Formatted[0]; + realLen = 0; + + } + else if (c == 32) + { + memcpy(ptr2, " ", 6); + ptr2 += 6; + + // Make sure line isn't too long + // but beware of spaces expanded to   - count chars in line + + if ((realLen) > 100) + { + strcpy(ptr2, "
\r\n"); + + Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + Session->ScreenLines[Line] = _strdup(Formatted); + + if (Line == 99) + Session->LastLine = 0; + + ptr2 = &Formatted[0]; + realLen = 0; + } + } + else if (c == '>') + { + memcpy(ptr2, ">", 4); + ptr2 += 4; + } + else if (c == '<') + { + memcpy(ptr2, "<", 4); + ptr2 += 4; + } + else + *(ptr2++) = c; + + } + + *ptr2 = 0; + + if (ptr2 != &Formatted[0]) + { + // Incomplete line + + // Save to screen + + Line = Session->LastLine++; + free(Session->ScreenLines[Line]); + + Session->ScreenLines[Line] = _strdup(Formatted); + + if (Line == 99) + Session->LastLine = 0; + + Session->PartLine = realLen; + } + + // strcat(Session->ScreenBuffer, Formatted); + Session->Changed = TRUE; + + } while (count > 0); + } +} + + +VOID HTTPTimer() +{ + // Run every tick. Check for status change and data available + + struct HTTPConnectionInfo * Session = SessionList; // active term mode sessions + struct HTTPConnectionInfo * PreviousSession = NULL; + +// inf(); + + while (Session) + { + Session->KillTimer++; + + if (Session->Key[0] != 'T') + { + PreviousSession = Session; + Session = Session->Next; + continue; + } + + if (Session->KillTimer > 3000) // Around 5 mins + { + int i; + int Stream = Session->Stream; + + for (i = 0; i < 100; i++) + { + free(Session->ScreenLines[i]); + } + + SessionControl(Stream, 2, 0); + SessionState(Stream, &i, &i); + DeallocateStream(Stream); + + if (PreviousSession) + PreviousSession->Next = Session->Next; // Remove from chain + else + SessionList = Session->Next; + + free(Session); + + break; + } + + PollSession(Session); + + // if (Session->ResponseTimer == 0 && Session->Changed) + // Debugprintf("Data to send but no outstanding GET"); + + if (Session->ResponseTimer) + { + Session->ResponseTimer--; + + if (Session->ResponseTimer == 0 || Session->Changed) + { + SOCKET sock = Session->sock; + char _REPLYBUFFER[100000]; + int ReplyLen; + char Header[256]; + int HeaderLen; + int Last = Session->LastLine; + int n; + struct TNCINFO * TNC = Session->TNC; + struct TCPINFO * TCP = 0; + + if (TNC) + TCP = TNC->TCPInfo; + + if (TCP && TCP->WebTermCSS) + sprintf(_REPLYBUFFER, TermOutput, TCP->WebTermCSS); + else + sprintf(_REPLYBUFFER, TermOutput, ""); + + for (n = Last;;) + { + if ((strlen(Session->ScreenLines[n]) + strlen(_REPLYBUFFER)) < 99999) + strcat(_REPLYBUFFER, Session->ScreenLines[n]); + + if (n == 99) + n = -1; + + if (++n == Last) + break; + } + + ReplyLen = (int)strlen(_REPLYBUFFER); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", TermOutputTail); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + + Session->ResponseTimer = Session->Changed = 0; + } + } + PreviousSession = Session; + Session = Session->Next; + } +} + +struct HTTPConnectionInfo * AllocateSession(SOCKET sock, char Mode) +{ + time_t KeyVal; + struct HTTPConnectionInfo * Session = zalloc(sizeof(struct HTTPConnectionInfo)); + int i; + + if (Session == NULL) + return NULL; + + if (Mode == 'T') + { + // Terminal + + for (i = 0; i < 20; i++) + Session->ScreenLines[i] = _strdup("Scroll to end
"); + + for (i = 20; i < 100; i++) + Session->ScreenLines[i] = _strdup("
\r\n"); + + Session->Stream = FindFreeStream(); + + if (Session->Stream == 0) + return NULL; + + SessionControl(Session->Stream, 1, 0); + } + + KeyVal = (int)sock * time(NULL); + + sprintf(Session->Key, "%c%012X", Mode, (int)KeyVal); + + if (SessionList) + Session->Next = SessionList; + + SessionList = Session; + + return Session; +} + +struct HTTPConnectionInfo * FindSession(char * Key) +{ + struct HTTPConnectionInfo * Session = SessionList; + + while (Session) + { + if (strcmp(Session->Key, Key) == 0) + return Session; + + Session = Session->Next; + } + + return NULL; +} + +void ProcessTermInput(SOCKET sock, char * MsgPtr, int MsgLen, char * Key) +{ + char _REPLYBUFFER[2048]; + int ReplyLen; + char Header[256]; + int HeaderLen; + int State; + struct HTTPConnectionInfo * Session = FindSession(Key); + int Stream; + int maxlen = 1000; + + + if (Session == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, "%s", LostSession); + } + else + { + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * end = &MsgPtr[MsgLen]; + int Line = Session->LastLine++; + char * ptr1, * ptr2; + char c; + UCHAR hex; + + int msglen = end - input; + + struct TNCINFO * TNC = Session->TNC; + struct TCPINFO * TCP = 0; + + if (TNC) + TCP = TNC->TCPInfo; + + if (TCP && TCP->WebTermCSS) + maxlen -= strlen(TCP->WebTermCSS); + + if (MsgLen > maxlen) + { + Session->KillTimer = 99999; // close session + return; + } + + + if (TCP && TCP->WebTermCSS) + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, TCP->WebTermCSS); + else + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, ""); + + + Stream = Session->Stream; + + input += 10; + ptr1 = ptr2 = input; + + // Convert any %xx constructs + + while (ptr1 != end) + { + c = *(ptr1++); + if (c == '%') + { + c = *(ptr1++); + if(isdigit(c)) + hex = (c - '0') << 4; + else + hex = (tolower(c) - 'a' + 10) << 4; + + c = *(ptr1++); + if(isdigit(c)) + hex += (c - '0'); + else + hex += (tolower(c) - 'a' + 10); + + *(ptr2++) = hex; + } + else if (c == '+') + *(ptr2++) = 32; + else + *(ptr2++) = c; + } + + end = ptr2; + + *ptr2 = 0; + + strcat(input, "
\r\n"); + + free(Session->ScreenLines[Line]); + + Session->ScreenLines[Line] = _strdup(input); + + if (Line == 99) + Session->LastLine = 0; + + *end++ = 13; + *end = 0; + + SessionStateNoAck(Stream, &State); + + if (State == 0) + { + char AXCall[10]; + SessionControl(Stream, 1, 0); + if (BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION == NULL) + { + //No L4 sessions free + + ReplyLen = sprintf(_REPLYBUFFER, "%s", NoSessions); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return; + } + + ConvToAX25(Session->HTTPCall, AXCall); + ChangeSessionCallsign(Stream, AXCall); + if (Session->USER) + BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION->Secure_Session = Session->USER->Secure; + else + Debugprintf("HTTP Term Session->USER is NULL"); + } + + SendMsg(Stream, input, (int)(end - input)); + Session->Changed = TRUE; + Session->KillTimer = 0; + } + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); +} + + +void ProcessTermClose(SOCKET sock, char * MsgPtr, int MsgLen, char * Key, int LOCAL) +{ + char _REPLYBUFFER[8192]; + int ReplyLen = sprintf(_REPLYBUFFER, InputLine, Key, ""); + char Header[256]; + int HeaderLen; + struct HTTPConnectionInfo * Session = FindSession(Key); + + if (Session) + { + Session->KillTimer = 99999; + } + + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); +} + +int ProcessTermSignon(struct TNCINFO * TNC, SOCKET sock, char * MsgPtr, int MsgLen, int LOCAL) +{ + char _REPLYBUFFER[8192]; + int ReplyLen; + char Header[256]; + int HeaderLen; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Context, * Appl; + char NoApp[] = ""; + struct TCPINFO * TCP = TNC->TCPInfo; + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + goto Sendit; + } + user = strtok_s(&input[9], "&", &Context); + password = strtok_s(NULL, "=", &Context); + password = strtok_s(NULL, "&", &Context); + + Appl = strtok_s(NULL, "=", &Context); + Appl = strtok_s(NULL, "&", &Context); + + if (Appl == 0) + Appl = NoApp; + + if (password == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Appl); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PassError); + goto Sendit; + } + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if ((strcmp(password, USER->Password) == 0) && + ((_stricmp(user, USER->UserName) == 0 ) || (_stricmp(USER->UserName, "ANON") == 0))) + { + // ok + + struct HTTPConnectionInfo * Session = AllocateSession(sock, 'T'); + + if (Session) + { + char AXCall[10]; + ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, Session->Key, Session->Key, Session->Key); + if (_stricmp(USER->UserName, "ANON") == 0) + strcpy(Session->HTTPCall, _strupr(user)); + else + strcpy(Session->HTTPCall, USER->Callsign); + ConvToAX25(Session->HTTPCall, AXCall); + ChangeSessionCallsign(Session->Stream, AXCall); + BPQHOSTVECTOR[Session->Stream -1].HOSTSESSION->Secure_Session = USER->Secure; + Session->USER = USER; + + if (USER->Appl[0]) + SendMsg(Session->Stream, USER->Appl, (int)strlen(USER->Appl)); + } + else + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", BusyError); + } + break; + } + } + + if (i == TCP->NumberofUsers) + { + // Not found + + ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Appl); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PassError); + } + + } + +Sendit: + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; +} + +char * LookupKey(char * Key) +{ + if (strcmp(Key, "##MY_CALLSIGN##") == 0) + { + char Mycall[10]; + memcpy(Mycall, &MYNODECALL, 10); + strlop(Mycall, ' '); + + return _strdup(Mycall); + } + return NULL; +} + + +int ProcessSpecialPage(char * Buffer, int FileSize) +{ + // replaces ##xxx### constructs with the requested data + + char * NewMessage = malloc(100000); + char * ptr1 = Buffer, * ptr2, * ptr3, * ptr4, * NewPtr = NewMessage; + int PrevLen; + int BytesLeft = FileSize; + int NewFileSize = FileSize; + char * StripPtr = ptr1; + + // strip comments blocks + + while (ptr4 = strstr(ptr1, ""); + if (ptr2) + { + PrevLen = (int)(ptr4 - ptr1); + memcpy(StripPtr, ptr1, PrevLen); + StripPtr += PrevLen; + ptr1 = ptr2 + 3; + BytesLeft = (int)(FileSize - (ptr1 - Buffer)); + } + } + + memcpy(StripPtr, ptr1, BytesLeft); + StripPtr += BytesLeft; + + BytesLeft = (int)(StripPtr - Buffer); + + FileSize = BytesLeft; + NewFileSize = FileSize; + ptr1 = Buffer; + ptr1[FileSize] = 0; + +loop: + ptr2 = strstr(ptr1, "##"); + + if (ptr2) + { + PrevLen = (int)(ptr2 - ptr1); // Bytes before special text + + ptr3 = strstr(ptr2+2, "##"); + + if (ptr3) + { + char Key[80] = ""; + int KeyLen; + char * NewText; + int NewTextLen; + + ptr3 += 2; + KeyLen = (int)(ptr3 - ptr2); + + if (KeyLen < 80) + memcpy(Key, ptr2, KeyLen); + + NewText = LookupKey(Key); + + if (NewText) + { + NewTextLen = (int)strlen(NewText); + NewFileSize = NewFileSize + NewTextLen - KeyLen; + // NewMessage = realloc(NewMessage, NewFileSize); + + memcpy(NewPtr, ptr1, PrevLen); + NewPtr += PrevLen; + memcpy(NewPtr, NewText, NewTextLen); + NewPtr += NewTextLen; + + free(NewText); + NewText = NULL; + } + else + { + // Key not found, so just leave + + memcpy(NewPtr, ptr1, PrevLen + KeyLen); + NewPtr += (PrevLen + KeyLen); + } + + ptr1 = ptr3; // Continue scan from here + BytesLeft = (int)(Buffer + FileSize - ptr3); + } + else // Unmatched ## + { + memcpy(NewPtr, ptr1, PrevLen + 2); + NewPtr += (PrevLen + 2); + ptr1 = ptr2 + 2; + } + goto loop; + } + + // Copy Rest + + memcpy(NewPtr, ptr1, BytesLeft); + NewMessage[NewFileSize] = 0; + + strcpy(Buffer, NewMessage); + free(NewMessage); + + return NewFileSize; +} + +int SendMessageFile(SOCKET sock, char * FN, BOOL OnlyifExists, int allowDeflate) +{ + int FileSize = 0, Sent, Loops = 0; + char * MsgBytes; + char MsgFile[512]; + FILE * hFile; + int ReadLen; + BOOL Special = FALSE; + int Len; + int HeaderLen; + char Header[256]; + char TimeString[64]; + char FileTimeString[64]; + struct stat STAT; + char * ptr; + char * Compressed = 0; + char Encoding[] = "Content-Encoding: deflate\r\n"; + char Type[64] = "Content-Type: text/html\r\n"; + +#ifdef WIN32 + + struct _EXCEPTION_POINTERS exinfo; + strcpy(EXCEPTMSG, "SendMessageFile"); + + __try { +#endif + + UndoTransparency(FN); + + if (strstr(FN, "..")) + { + FN[0] = '/'; + FN[1] = 0; + } + + if (strlen(FN) > 256) + { + FN[256] = 0; + Debugprintf("HTTP File Name too long %s", FN); + } + + if (strcmp(FN, "/") == 0) + if (APRSActive) + sprintf(MsgFile, "%s/HTML/index.html", BPQDirectory); + else + sprintf(MsgFile, "%s/HTML/indexnoaprs.html", BPQDirectory); + else + sprintf(MsgFile, "%s/HTML%s", BPQDirectory, FN); + + + // First see if file exists so we can override standard ones in code + + if (stat(MsgFile, &STAT) == 0 && (hFile = fopen(MsgFile, "rb"))) + { + FileSize = STAT.st_size; + + MsgBytes = zalloc(FileSize + 1); + + ReadLen = (int)fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + // ft.QuadPart -= 116444736000000000; + // ft.QuadPart /= 10000000; + + // ctime = ft.LowPart; + + FormatTime3(FileTimeString, STAT.st_ctime); + } + else + { + // See if it is a hard coded file + + MsgBytes = GetStandardPage(&FN[1], &FileSize); + + if (MsgBytes) + { + if (FileSize == 0) + FileSize = strlen(MsgBytes); + + FormatTime3(FileTimeString, 0); + } + else + { + if (OnlyifExists) // Set if we dont want an error response if missing + return -1; + + Len = sprintf(Header, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + send(sock, Header, Len, 0); + return 0; + } + } + + // if HTML file, look for ##...## substitutions + + if ((strcmp(FN, "/") == 0 || strstr(FN, "htm" ) || strstr(FN, "HTM")) && strstr(MsgBytes, "##" )) + { + FileSize = ProcessSpecialPage(MsgBytes, FileSize); + FormatTime3(FileTimeString, time(NULL)); + + } + + FormatTime3(TimeString, time(NULL)); + + ptr = FN; + + while (strchr(ptr, '.')) + { + ptr = strchr(ptr, '.'); + ++ptr; + } + + if (_stricmp(ptr, "js") == 0) + strcpy(Type, "Content-Type: text/javascript\r\n"); + + if (_stricmp(ptr, "css") == 0) + strcpy(Type, "Content-Type: text/css\r\n"); + + if (_stricmp(ptr, "pdf") == 0) + strcpy(Type, "Content-Type: application/pdf\r\n"); + + if (allowDeflate) + { + Compressed = Compressit(MsgBytes, FileSize, &FileSize); + } + else + { + Encoding[0] = 0; + Compressed = MsgBytes; + } + + if (_stricmp(ptr, "jpg") == 0 || _stricmp(ptr, "jpeg") == 0 || _stricmp(ptr, "png") == 0 || + _stricmp(ptr, "gif") == 0 || _stricmp(ptr, "bmp") == 0 || _stricmp(ptr, "ico") == 0) + strcpy(Type, "Content-Type: image\r\n"); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "%s%s" + "\r\n", FileSize, TimeString, FileTimeString, Type, Encoding); + + send(sock, Header, HeaderLen, 0); + + Sent = send(sock, Compressed, FileSize, 0); + + while (Sent != FileSize && Loops++ < 3000) // 100 secs max + { + if (Sent > 0) // something sent + { +// Debugprintf("%d out of %d sent", Sent, FileSize); + FileSize -= Sent; + memmove(Compressed, &Compressed[Sent], FileSize); + } + + Sleep(30); + Sent = send(sock, Compressed, FileSize, 0); + } + +// Debugprintf("%d out of %d sent %d loops", Sent, FileSize, Loops); + + + free (MsgBytes); + if (allowDeflate) + free (Compressed); + +#ifdef WIN32 + } +#include "StdExcept.c" + Debugprintf("Sending FIle %s", FN); +} +#endif + +return 0; +} + +VOID sendandcheck(SOCKET sock, const char * Buffer, int Len) +{ + int Loops = 0; + int Sent = send(sock, Buffer, Len, 0); + char * Copy = NULL; + + while (Sent != Len && Loops++ < 300) // 10 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, Len, Loops); + + if (Copy == NULL) + { + Copy = malloc(Len); + memcpy(Copy, Buffer, Len); + } + + if (Sent > 0) // something sent + { + Len -= Sent; + memmove(Copy, &Copy[Sent], Len); + } + + Sleep(30); + Sent = send(sock, Copy, Len, 0); + } + + if (Copy) + free(Copy); + + return; +} + +int RefreshTermWindow(struct TCPINFO * TCP, struct HTTPConnectionInfo * Session, char * _REPLYBUFFER) +{ + char Msg[400] = ""; + int HeaderLen, ReplyLen; + char Header[256]; + + PollSession(Session); // See if anything received + + if (Session->Changed) + { + int Last = Session->LastLine; + int n; + + if (TCP && TCP->WebTermCSS) + sprintf(_REPLYBUFFER, TermOutput, TCP->WebTermCSS); + else + sprintf(_REPLYBUFFER, TermOutput, ""); + + for (n = Last;;) + { + strcat(_REPLYBUFFER, Session->ScreenLines[n]); + + if (n == 99) + n = -1; + + if (++n == Last) + break; + } + + Session->Changed = 0; + + ReplyLen = (int)strlen(_REPLYBUFFER); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", TermOutputTail); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen); + sendandcheck(Session->sock, Header, HeaderLen); + sendandcheck(Session->sock, _REPLYBUFFER, ReplyLen); + + return 1; + } + else + return 0; +} + +int SetupNodeMenu(char * Buff, int LOCAL) +{ + int Len = 0, i; + struct TNCINFO * TNC; + int top = 0, left = 0; + + char NodeMenuHeader[] = "%s's BPQ32 Web Server" + "" + + "" + "

BPQ32 Node %s

" + "

" + "" + "" + "" + "" + "" + "" + "%s%s%s%s%s%s"; + + char DriverBit[] = "" + ""; + + char APRSBit[] = ""; + + char MailBit[] = "" + ""; + + char ChatBit[] = ""; + char SigninBit[] = ""; + + char NodeTail[] = + "" + "
RoutesNodesPortsLinksUsersStatsTerminalDriver WindowsStream StatusAPRS PagesMail MgmtWebMailChat MgmtSYSOP SigninEdit Config" + "
"; + + + Len = sprintf(Buff, NodeMenuHeader, Mycall); + + for (i=1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + if (TNC == NULL) + continue; + + if (TNC->WebWindowProc) + { + Len += sprintf(&Buff[Len], NodeMenuLine, i, TNC->WebWinX, TNC->WebWinY, top, left); + top += 22; + left += 22; + } + } + + Len += sprintf(&Buff[Len], NodeMenuRest, Mycall, + DriverBit, + (APRSWeb)?APRSBit:"", + (IncludesMail)?MailBit:"", (IncludesChat)?ChatBit:"", (LOCAL)?"":SigninBit, NodeTail); + + return Len; +} + +VOID SaveConfigFile(SOCKET sock , char * MsgPtr, char * Rest, int LOCAL) +{ + int ReplyLen = 0; + char * ptr, * ptr1, * ptr2, *input; + char c; + int MsgLen, WriteLen = 0; + char inputname[250]="bpq32.cfg"; + FILE *fp1; + char Header[256]; + int HeaderLen; + char Reply[4096]; + char Mess[256]; + char Backup1[MAX_PATH]; + char Backup2[MAX_PATH]; + struct stat STAT; + + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input) + { + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + // ReplyLen = sprintf(Reply, "%s", ""); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return; + } + + ptr = strstr(input, "&Save="); + + if (ptr) + { + *ptr = 0; + + // Undo any % transparency + + ptr1 = ptr2 = input + 8; + + c = *(ptr1++); + + while (c) + { + if (c == '%') + { + int n; + int m = *(ptr1++) - '0'; + if (m > 9) m = m - 7; + n = *(ptr1++) - '0'; + if (n > 9) n = n - 7; + + c = m * 16 + n; + } + else if (c == '+') + c = ' '; + +#ifndef WIN32 + if (c != 13) // Strip CR if Linux +#endif + *(ptr2++) = c; + + c = *(ptr1++); + + } + + *(ptr2++) = 0; + + MsgLen = (int)strlen(input + 8); + + if (ConfigDirectory[0] == 0) + { + strcpy(inputname, "bpq32.cfg"); + } + else + { + strcpy(inputname,ConfigDirectory); + strcat(inputname,"/"); + strcat(inputname, "bpq32.cfg"); + } + + // Make a backup of the config + + // Keep 4 Generations + + strcpy(Backup2, inputname); + strcat(Backup2, ".bak.3"); + + strcpy(Backup1, inputname); + strcat(Backup1, ".bak.2"); + + DeleteFile(Backup2); // Remove old .bak.3 + MoveFile(Backup1, Backup2); // Move .bak.2 to .bak.3 + + strcpy(Backup2, inputname); + strcat(Backup2, ".bak.1"); + + MoveFile(Backup2, Backup1); // Move .bak.1 to .bak.2 + + strcpy(Backup1, inputname); + strcat(Backup1, ".bak"); + + MoveFile(Backup1, Backup2); // Move .bak to .bak.1 + + CopyFile(inputname, Backup1, FALSE); // Copy to .bak + + // Get length to compare with new length + + stat(inputname, &STAT); + + fp1 = fopen(inputname, "wb"); + + if (fp1) + { + WriteLen = (int)fwrite(input + 8, 1, MsgLen, fp1); + fclose(fp1); + } + + if (WriteLen != MsgLen) + sprintf_s(Mess, sizeof(Mess), "Failed to write Config File"); + else + sprintf_s(Mess, sizeof(Mess), "Configuration Saved, Orig Length %d New Length %d", + STAT.st_size, MsgLen); + } + + ReplyLen = sprintf(Reply, "", Mess); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + } + return; +} + +// Compress using deflate. Caller must free output buffer after use + +unsigned char * Compressit(unsigned char * In, int Len, int * OutLen) +{ + z_stream defstream; + int maxSize; + unsigned char * Out; + + defstream.zalloc = Z_NULL; + defstream.zfree = Z_NULL; + defstream.opaque = Z_NULL; + + defstream.avail_in = Len; // size of input + defstream.next_in = (Bytef *)In; // input char array + + deflateInit(&defstream, Z_BEST_COMPRESSION); + maxSize = deflateBound(&defstream, Len); + + Out = malloc(maxSize); + + defstream.avail_out = maxSize; // size of output + defstream.next_out = (Bytef *)Out; // output char array + + deflate(&defstream, Z_FINISH); + deflateEnd(&defstream); + + *OutLen = defstream.total_out; + + return Out; +} + + +int InnerProcessHTTPMessage(struct ConnectionInfo * conn) +{ + struct TCPINFO * TCP = conn->TNC->TCPInfo; + SOCKET sock = conn->socket; + char * MsgPtr = conn->InputBuffer; + int MsgLen = conn->InputLen; + int InputLen = 0; + int OutputLen = 0; + int Bufferlen; + struct HTTPConnectionInfo CI; + struct HTTPConnectionInfo * sockptr = &CI; + struct HTTPConnectionInfo * Session = NULL; + + char URL[100000]; + char * ptr; + char * encPtr = 0; + int allowDeflate = 0; + char * Compressed = 0; + char * HostPtr = 0; + + char * Context, * Method, * NodeURL = 0, * Key; + char _REPLYBUFFER[250000]; + char Reply[250000]; + + int ReplyLen = 0; + char Header[256]; + int HeaderLen; + char TimeString[64]; + BOOL LOCAL = FALSE; + BOOL COOKIE = FALSE; + int Len; + char * WebSock = 0; + + char PortsHddr[] = "

Ports

" + ""; + +// char PortLine[] = ""; + + char PortLineWithBeacon[] = "" + "\r\n"; + + char SessionPortLine[] = "" + "\r\n"; + + char PortLineWithDriver[] = "" + "\r\n"; + + + char PortLineWithBeaconAndDriver[] = "" + "" + "\r\n"; + + char RigControlLine[] = "" + "\r\n"; + + + char Encoding[] = "Content-Encoding: deflate\r\n"; + +#ifdef WIN32xx + + struct _EXCEPTION_POINTERS exinfo; + strcpy(EXCEPTMSG, "ProcessHTTPMessage"); + + __try { +#endif + + Len = (int)strlen(MsgPtr); + if (Len > 100000) + return 0; + + strcpy(URL, MsgPtr); + + HostPtr = strstr(MsgPtr, "Host: "); + + WebSock = strstr(MsgPtr, "Upgrade: websocket"); + + if (HostPtr) + { + uint32_t Host; + char Hostname[32]= ""; + struct LOCALNET * LocalNet = conn->TNC->TCPInfo->LocalNets; + + HostPtr += 6; + memcpy(Hostname, HostPtr, 31); + strlop(Hostname, ':'); + Host = inet_addr(Hostname); + + if (strcmp(Hostname, "127.0.0.1") == 0) + LOCAL = TRUE; + else + { + if (conn->sin.sin_family != AF_INET6) + { + while(LocalNet) + { + uint32_t MaskedHost = conn->sin.sin_addr.s_addr & LocalNet->Mask; + if (MaskedHost == LocalNet->Network) + { + LOCAL = 1; + break; + } + LocalNet = LocalNet->Next; + } + } + } + } + + encPtr = stristr(MsgPtr, "Accept-Encoding:"); + + if (encPtr && stristr(encPtr, "deflate")) + allowDeflate = 1; + else + Encoding[0] = 0; + + ptr = strstr(MsgPtr, "BPQSessionCookie=N"); + + if (ptr) + { + COOKIE = TRUE; + Key = ptr + 17; + ptr = strchr(Key, ','); + if (ptr) + { + *ptr = 0; + Session = FindSession(Key); + *ptr = ','; + } + else + { + ptr = strchr(Key, 13); + if (ptr) + { + *ptr = 0; + Session = FindSession(Key); + *ptr = 13; + } + } + } + + if (WebSock) + { + // Websock connection request - Reply and remember state. + + char KeyMsg[128]; + char Webx[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // Fixed UID + char Hash[64] = ""; + char * Hash64; // base 64 version + char * ptr; + + //Sec-WebSocket-Key: l622yZS3n+zI+hR6SVWkPw== + + char ReplyMsg[] = + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n" +// "Sec-WebSocket-Protocol: chat\r\n" + "\r\n"; + + ptr = strstr(MsgPtr, "Sec-WebSocket-Key:"); + + if (ptr) + { + ptr += 18; + while (*ptr == ' ') + ptr++; + + memcpy(KeyMsg, ptr, 40); + strlop(KeyMsg, 13); + strlop(KeyMsg, ' '); + strcat(KeyMsg, Webx); + + SHA1PasswordHash(KeyMsg, Hash); + Hash64 = byte_base64_encode(Hash, 20); + + conn->WebSocks = 1; + strlop(&URL[4], ' '); + strcpy(conn->WebURL, &URL[4]); + + ReplyLen = sprintf(Reply, ReplyMsg, Hash64); + + free(Hash64); + goto Returnit; + + } + } + + + ptr = strstr(URL, " HTTP"); + + if (ptr) + *ptr = 0; + + Method = strtok_s(URL, " ", &Context); + + memcpy(Mycall, &MYNODECALL, 10); + strlop(Mycall, ' '); + + + // Look for API messages + + if (_memicmp(Context, "/api/", 5) == 0 || _stricmp(Context, "/api") == 0) + { + char * Compressed; + + // if for mail api process signon here and rearrange url from + // api/v1/mail to mail/api/v1 so it goes to mail handler later + + if (_memicmp(Context, "/api/v1/mail/", 13) == 0) + { + memcpy(MsgPtr, "GET /mail/api/v1/", 17); + + if (memcmp(&Context[13], "login", 5) == 0) + { + ReplyLen = ProcessMailAPISignon(TCP, MsgPtr, "M", Reply, &Session, FALSE, LOCAL); + memcpy(MsgPtr, "GET /mail/api/v1/", 17); + + if (ReplyLen) // Error message + goto Returnit; + } + + memcpy(Context, "/mail/api/v1/", 13); + goto doHeader; + } + else + { + ReplyLen = APIProcessHTTPMessage(_REPLYBUFFER, Method, Context, MsgPtr, LOCAL, COOKIE); + + if (memcmp(_REPLYBUFFER, "HTTP", 4) == 0) + { + // Full Message - just send it + + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + + return 0; + } + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/json\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "%s\r\n", ReplyLen, Encoding); + + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + } + + if (_memicmp(Context, "/rhp/", 5) == 0 || _stricmp(Context, "/rhp") == 0) + { + { + ReplyLen = RHPProcessHTTPMessage(conn, _REPLYBUFFER, Method, Context, MsgPtr, LOCAL, COOKIE); + + if (memcmp(_REPLYBUFFER, "HTTP", 4) == 0) + { + // Full Message - just send it + + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + + return 0; + } + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\n" + "Content-Length: %d\r\n" + "Content-Type: application/json\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "%s\r\n", ReplyLen, Encoding); + + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + } + + + // APRS process internally + + if (_memicmp(Context, "/APRS/", 6) == 0 || _stricmp(Context, "/APRS") == 0) + { + APRSProcessHTTPMessage(sock, MsgPtr, LOCAL, COOKIE); + return 0; + } + + + if (_stricmp(Context, "/Node/Signon?Node") == 0) + { + char * IContext; + + NodeURL = strtok_s(Context, "?", &IContext); + Key = strtok_s(NULL, "?", &IContext); + + ProcessNodeSignon(sock, TCP, MsgPtr, Key, Reply, &Session, LOCAL); + return 0; + + } + + // If for Mail or Chat, check for a session, and send login screen if necessary + + // Moved here to simplify operation with both internal and external clients + + if (_memicmp(Context, "/Mail/", 6) == 0) + { + int RLen = 0; + char Appl; + char * input; + char * IContext; + + NodeURL = strtok_s(Context, "?", &IContext); + Key = strtok_s(NULL, "?", &IContext); + + if (_stricmp(NodeURL, "/Mail/Signon") == 0) + { + ReplyLen = ProcessMailSignon(TCP, MsgPtr, Key, Reply, &Session, FALSE, LOCAL); + + if (ReplyLen) + { + goto Returnit; + } + +#ifdef LINBPQ + strcpy(Context, "/Mail/Header"); +#else + strcpy(MsgPtr, "POST /Mail/Header"); +#endif + goto doHeader; + } + + if (_stricmp(NodeURL, "/Mail/Lost") == 0) + { + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input && strstr(input, "Cancel=Exit")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + RLen = ReplyLen; + goto Returnit; + } + if (Key) + Appl = Key[0]; + + Key = 0; + } + + if (Key == 0 || Key[0] == 0) + { + // No Session + + // if not local send a signon screen, else create a user session + + if (LOCAL || COOKIE) + { + Session = AllocateSession(sock, 'M'); + + if (Session) + { + strcpy(Context, "/Mail/Header"); + goto doHeader; + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + RLen = ReplyLen; + + goto Returnit; + + } + + ReplyLen = sprintf(Reply, MailSignon, Mycall, Mycall); + + RLen = ReplyLen; + goto Returnit; + } + + Session = FindSession(Key); + + + if (Session == NULL && _memicmp(Context, "/Mail/API/", 10) != 0) + { + ReplyLen = sprintf(Reply, MailLostSession, Key); + RLen = ReplyLen; + goto Returnit; + } + } + + if (_memicmp(Context, "/Chat/", 6) == 0) + { + int RLen = 0; + char Appl; + char * input; + char * IContext; + + + HostPtr = strstr(MsgPtr, "Host: "); + + if (HostPtr) + { + uint32_t Host; + char Hostname[32]= ""; + struct LOCALNET * LocalNet = conn->TNC->TCPInfo->LocalNets; + + HostPtr += 6; + memcpy(Hostname, HostPtr, 31); + strlop(Hostname, ':'); + Host = inet_addr(Hostname); + + if (strcmp(Hostname, "127.0.0.1") == 0) + LOCAL = TRUE; + else while(LocalNet) + { + uint32_t MaskedHost = Host & LocalNet->Mask; + if (MaskedHost == LocalNet->Network) + { + char * rest; + LOCAL = 1; + rest = strchr(HostPtr, 13); + if (rest) + { + memmove(HostPtr + 9, rest, strlen(rest) + 1); + memcpy(HostPtr, "127.0.0.1", 9); + break; + } + } + LocalNet = LocalNet->Next; + } + } + + NodeURL = strtok_s(Context, "?", &IContext); + Key = strtok_s(NULL, "?", &IContext); + + if (_stricmp(NodeURL, "/Chat/Signon") == 0) + { + ReplyLen = ProcessChatSignon(TCP, MsgPtr, Key, Reply, &Session, LOCAL); + + if (ReplyLen) + { + goto Returnit; + } + +#ifdef LINBPQ + strcpy(Context, "/Chat/Header"); +#else + strcpy(MsgPtr, "POST /Chat/Header"); +#endif + goto doHeader; + } + + if (_stricmp(NodeURL, "/Chat/Lost") == 0) + { + input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + + if (input && strstr(input, "Cancel=Exit")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + RLen = ReplyLen; + goto Returnit; + } + if (Key) + Appl = Key[0]; + + Key = 0; + } + + if (Key == 0 || Key[0] == 0) + { + // No Session + + // if not local send a signon screen, else create a user session + + if (LOCAL || COOKIE) + { + Session = AllocateSession(sock, 'C'); + + if (Session) + { + strcpy(Context, "/Chat/Header"); + goto doHeader; + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + RLen = ReplyLen; + + goto Returnit; + + } + + ReplyLen = sprintf(Reply, ChatSignon, Mycall, Mycall); + + RLen = ReplyLen; + goto Returnit; + } + + Session = FindSession(Key); + + if (Session == NULL) + { + int Sent, Loops = 0; + ReplyLen = sprintf(Reply, MailLostSession, Key); + RLen = ReplyLen; +Returnit: + if (memcmp(Reply, "HTTP", 4) == 0) + { + // Full Header provided by appl - just send it + + // Send may block + + Sent = send(sock, Reply, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(Reply, &Reply[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sock, Reply, ReplyLen, 0); + } + + return 0; + } + + if (NodeURL && _memicmp(NodeURL, "/mail/api/", 10) != 0) + { + // Add tail + + strcpy(&Reply[ReplyLen], Tail); + ReplyLen += strlen(Tail); + } + + // compress if allowed + + if (allowDeflate) + Compressed = Compressit(Reply, ReplyLen, &ReplyLen); + else + Compressed = Reply; + + if (NodeURL && _memicmp(NodeURL, "/mail/api/", 10) == 0) + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: application/json\r\nConnection: close\r\n%s\r\n", ReplyLen, Encoding); + else + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n%s\r\n", ReplyLen, Encoding); + + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + } + +doHeader: + +#ifdef LINBPQ + + if ((_memicmp(Context, "/MAIL/", 6) == 0) || (_memicmp(Context, "/WebMail", 8) == 0)) + { + char _REPLYBUFFER[250000]; + struct HTTPConnectionInfo Dummy = {0}; + int Sent, Loops = 0; + char token[16] = ""; + + // look for auth header + + const char * auth_header = "Authorization: Bearer "; + char * token_begin = strstr(MsgPtr, auth_header); + int Flags = 0, n; + + char * Tok; + char * param; + + if (token_begin) + { + // Using Auth Header + + // Extract the token from the request (assuming it's present in the request headers) + + token_begin += strlen(auth_header); // Move to the beginning of the token + strncpy(token, token_begin, 13); + token[13] = '\0'; // Null-terminate the token + } + + ReplyLen = 0; + + if (Session == 0) + Session = &Dummy; + + if (LOCAL) + Session->TNC = (void *)1; // TNC only used for Web Terminal Sessions + else + Session->TNC = (void *)0; + + ProcessMailHTTPMessage(Session, Method, Context, MsgPtr, _REPLYBUFFER, &ReplyLen, MsgLen, token); + + if (Context && _memicmp(Context, "/mail/api/", 10) == 0) + { + if (memcmp(_REPLYBUFFER, "HTTP", 4) == 0) + { + // Full Header provided by appl - just send it + + // Send may block + + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + } + return 0; + } + + // compress if allowed + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: application/json\r\nConnection: close\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + + if (memcmp(_REPLYBUFFER, "HTTP", 4) == 0) + { + // Full Header provided by appl - just send it + + // Send may block + + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + } + return 0; + } + + if (Context && _memicmp(Context, "/mail/api/", 10) != 0) + { + + // Add tail + + strcpy(&_REPLYBUFFER[ReplyLen], Tail); + ReplyLen += strlen(Tail); + + } + + // compress if allowed + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = Reply; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + + +/* + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + + // Send may block + + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + + if (Sent == -1) + return 0; + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sock, _REPLYBUFFER, ReplyLen, 0); + } + + send(sock, Tail, (int)strlen(Tail), 0); + return 0; +*/ + } + + if (_memicmp(Context, "/CHAT/", 6) == 0) + { + char _REPLYBUFFER[100000]; + + ReplyLen = 0; + + ProcessChatHTTPMessage(Session, Method, Context, MsgPtr, _REPLYBUFFER, &ReplyLen); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 0; + + } + + + /* + Sent = send(sock, _REPLYBUFFER, InputLen, 0); + + while (Sent != InputLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], InputLen); + } + + Sleep(30); + Sent = send(sock, _REPLYBUFFER, InputLen, 0); + } + return 0; + } + */ +#else + + // Pass to MailChat if active + + NodeURL = Context; + + if ((_memicmp(Context, "/MAIL/", 6) == 0) || (_memicmp(Context, "/WebMail", 8) == 0)) + { + // If for Mail, Pass to Mail Server via Named Pipe + + HANDLE hPipe; + + hPipe = CreateFile(MAILPipeFileName, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hPipe == (HANDLE)-1) + { + InputLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 28\r\n\r\nMail Data is not available\r\n"); + send(sock, Reply, InputLen, 0); + } + else + { + // int Sent; + int Loops = 0; + struct HTTPConnectionInfo Dummy = {0}; + + if (Session == 0) + Session = &Dummy; + + if (LOCAL) + Session->TNC = (struct TNCINFO *)(uintptr_t)1; // TNC is only used on Web Terminal Sessions so can reuse as LOCAL flag + else + Session->TNC = 0; + + WriteFile(hPipe, Session, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + WriteFile(hPipe, MsgPtr, MsgLen, &InputLen, NULL); + + + ReadFile(hPipe, Session, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + ReadFile(hPipe, Reply, 250000, &ReplyLen, NULL); + if (ReplyLen <= 0) + { + InputLen = GetLastError(); + } + + CloseHandle(hPipe); + goto Returnit; + } + return 0; + } + + if (_memicmp(Context, "/CHAT/", 6) == 0) + { + HANDLE hPipe; + + hPipe = CreateFile(CHATPipeFileName, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hPipe == (HANDLE)-1) + { + InputLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 28\r\n\r\nChat Data is not available\r\n"); + send(sock, Reply, InputLen, 0); + } + else + { + // int Sent; + int Loops = 0; + + WriteFile(hPipe, Session, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + WriteFile(hPipe, MsgPtr, MsgLen, &InputLen, NULL); + + + ReadFile(hPipe, Session, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + ReadFile(hPipe, Reply, 100000, &ReplyLen, NULL); + if (ReplyLen <= 0) + { + InputLen = GetLastError(); + } + + CloseHandle(hPipe); + goto Returnit; + } + return 0; + } + +#endif + + NodeURL = strtok_s(NULL, "?", &Context); + + if (NodeURL == NULL) + return 0; + + if (strcmp(Method, "POST") == 0) + { + if (_stricmp(NodeURL, "/Node/freqOffset") == 0) + { + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + int port = atoi(Context); + + if (input == 0) + return 1; + + input += 4; + + if (port > 0 && port <= MaxBPQPortNo) + { + struct TNCINFO * TNC = TNCInfo[port]; + char value[6]; + + if (TNC == 0) + return 1; + + TNC->TXOffset = atoi(input); +#ifdef WIN32 + sprintf(value, "%d", TNC->TXOffset); + MySetWindowText(TNC->xIDC_TXTUNEVAL, value); + SendMessage(TNC->xIDC_TXTUNE, TBM_SETPOS, (WPARAM) TRUE, (LPARAM) TNC->TXOffset); // min. & max. positions + +#endif + } + return 1; + } + + if (_stricmp(NodeURL, "/Node/PortAction") == 0) + { + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + int port = atoi(Context); + + if (input == 0) + return 1; + + input += 4; + + if (port > 0 && port <= MaxBPQPortNo) + { + struct TNCINFO * TNC = TNCInfo[port]; + + if (TNC == 0) + return 1; + + if (LOCAL == FALSE && COOKIE == FALSE) + return 1; + + if (strcmp(input, "Abort") == 0) + { + if (TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + } + else if (strcmp(input, "Kill") == 0) + { + TNC->DontRestart = TRUE; + KillTNC(TNC); + } + else if (strcmp(input, "KillRestart") == 0) + { + TNC->DontRestart = FALSE; + KillTNC(TNC); + RestartTNC(TNC); + + } + } + return 1; + } + + if (_stricmp(NodeURL, "/TermInput") == 0) + { + ProcessTermInput(sock, MsgPtr, MsgLen, Context); + return 0; + } + + if (_stricmp(NodeURL, "/Node/TermSignon") == 0) + { + ProcessTermSignon(conn->TNC, sock, MsgPtr, MsgLen, LOCAL); + } + + if (_stricmp(NodeURL, "/Node/Signon") == 0) + { + ProcessNodeSignon(sock, TCP, MsgPtr, Key, Reply, &Session, LOCAL); + return 0; + } + + if (_stricmp(NodeURL, "/Node/TermClose") == 0) + { + ProcessTermClose(sock, MsgPtr, MsgLen, Context, LOCAL); + return 0; + } + + if (_stricmp(NodeURL, "/Node/BeaconAction") == 0) + { + char Header[256]; + int HeaderLen; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + int Port; + char Param[100]; +#ifndef LINBPQ + int retCode, disp; + char Key[80]; + HKEY hKey; +#endif + struct PORTCONTROL * PORT; + int Slot = 0; + + + if (LOCAL == FALSE && COOKIE == FALSE) + { + // Send Not Authorized + + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Not authorized - please sign in"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; + } + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; + } + + GetParam(input, "Port=", &Param[0]); + Port = atoi(&Param[0]); + PORT = GetPortTableEntryFromPortNum(Port); // Need slot not number + if (PORT) + Slot = PORT->PortSlot; + + GetParam(input, "Every=", &Param[0]); + Interval[Slot] = atoi(&Param[0]); + + GetParam(input, "Dest=", &Param[0]); + _strupr(Param); + strcpy(UIUIDEST[Slot], &Param[0]); + + GetParam(input, "Path=", &Param[0]); + _strupr(Param); + if (UIUIDigi[Slot]) + free(UIUIDigi[Slot]); + UIUIDigi[Slot] = _strdup(&Param[0]); + + GetParam(input, "File=", &Param[0]); + strcpy(FN[Slot], &Param[0]); + GetParam(input, "Text=", &Param[0]); + strcpy(Message[Slot], &Param[0]); + + MinCounter[Slot] = Interval[Slot]; + + SendFromFile[Slot] = 0; + + if (FN[Slot][0]) + SendFromFile[Slot] = 1; + + SetupUI(Slot); + +#ifdef LINBPQ + SaveUIConfig(); +#else + SaveUIConfig(); + + wsprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil\\UIPort%d", Port); + + retCode = RegCreateKeyEx(REGTREE, + Key, 0, 0, 0, KEY_ALL_ACCESS, NULL, &hKey, &disp); + + if (retCode == ERROR_SUCCESS) + { + retCode = RegSetValueEx(hKey, "UIDEST", 0, REG_SZ,(BYTE *)&UIUIDEST[Port][0], strlen(&UIUIDEST[Port][0])); + retCode = RegSetValueEx(hKey, "FileName", 0, REG_SZ,(BYTE *)&FN[Port][0], strlen(&FN[Port][0])); + retCode = RegSetValueEx(hKey, "Message", 0, REG_SZ,(BYTE *)&Message[Port][0], strlen(&Message[Port][0])); + retCode = RegSetValueEx(hKey, "Interval", 0, REG_DWORD,(BYTE *)&Interval[Port], 4); + retCode = RegSetValueEx(hKey, "SendFromFile", 0, REG_DWORD,(BYTE *)&SendFromFile[Port], 4); + retCode = RegSetValueEx(hKey, "Digis",0, REG_SZ, UIUIDigi[Port], strlen(UIUIDigi[Port])); + + RegCloseKey(hKey); + } +#endif + if (strstr(input, "Test=Test")) + SendUIBeacon(Slot); + + + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], Beacons, Port, + Interval[Slot], &UIUIDEST[Slot][0], &UIUIDigi[Slot][0], &FN[Slot][0], &Message[Slot][0], Port); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; + } + + if (_stricmp(NodeURL, "/Node/CfgSave") == 0) + { + // Save Config File + + SaveConfigFile(sock, MsgPtr, Key, LOCAL); + return 0; + } + + if (_stricmp(NodeURL, "/Node/LogAction") == 0) + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 1; + } + + + if (_stricmp(NodeURL, "/Node/ARDOPAbort") == 0) + { + int port = atoi(Context); + + if (port > 0 && port <= MaxBPQPortNo) + { + struct TNCINFO * TNC = TNCInfo[port]; + + if (TNC && TNC->ForcedCloseProc) + TNC->ForcedCloseProc(TNC, 0); + + + if (TNC && TNC->WebWindowProc) + ReplyLen = TNC->WebWindowProc(TNC, _REPLYBUFFER, LOCAL); + + + ReplyLen = sprintf(Reply, "", "Ok"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + // goto SendResp; + + // HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + strlen(Tail)); + // send(sock, Header, HeaderLen, 0); + // send(sock, _REPLYBUFFER, ReplyLen, 0); + // send(sock, Tail, strlen(Tail), 0); + + return 1; + } + + } + + send(sock, _REPLYBUFFER, InputLen, 0); + return 0; + } + + if (_stricmp(NodeURL, "/") == 0 || _stricmp(NodeURL, "/Index.html") == 0) + { + // Send if present, else use default + + Bufferlen = SendMessageFile(sock, NodeURL, TRUE, allowDeflate); // return -1 if not found + + if (Bufferlen != -1) + return 0; // We've sent it + else + { + if (APRSApplConnected) + ReplyLen = sprintf(_REPLYBUFFER, Index, Mycall, Mycall); + else + ReplyLen = sprintf(_REPLYBUFFER, IndexNoAPRS, Mycall, Mycall); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 0; + } + } + + else if (_stricmp(NodeURL, "/NodeMenu.html") == 0 || _stricmp(NodeURL, "/Node/NodeMenu.html") == 0) + { + // Send if present, else use default + + char Menu[] = "/NodeMenu.html"; + + Bufferlen = SendMessageFile(sock, Menu, TRUE, allowDeflate); // return -1 if not found + + if (Bufferlen != -1) + return 0; // We've sent it + } + + else if (_memicmp(NodeURL, "/aisdata.txt", 12) == 0) + { + char * Compressed; + ReplyLen = GetAISPageInfo(_REPLYBUFFER, 1, 1); + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: Text\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + + else if (_memicmp(NodeURL, "/aprsdata.txt", 13) == 0) + { + char * Compressed; + char * ptr; + double N, S, W, E; + int aprs = 1, ais = 1, adsb = 1; + + ptr = &NodeURL[14]; + + N = atof(ptr); + ptr = strlop(ptr, '|'); + S = atof(ptr); + ptr = strlop(ptr, '|'); + W = atof(ptr); + ptr = strlop(ptr, '|'); + E = atof(ptr); + ptr = strlop(ptr, '|'); + if (ptr) + { + aprs = atoi(ptr); + ptr = strlop(ptr, '|'); + ais = atoi(ptr); + ptr = strlop(ptr, '|'); + adsb = atoi(ptr); + } + ReplyLen = GetAPRSPageInfo(_REPLYBUFFER, N, S, W, E, aprs, ais, adsb); + + if (ReplyLen < 240000) + ReplyLen += GetAISPageInfo(&_REPLYBUFFER[ReplyLen], ais, adsb); + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %d\r\nContent-Type: Text\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + + else if (_memicmp(NodeURL, "/portstats.txt", 15) == 0) + { + char * Compressed; + char * ptr; + int port; + struct PORTCONTROL * PORT; + + ptr = &NodeURL[15]; + + port = atoi(ptr); + + PORT = GetPortTableEntryFromPortNum(port); + + ReplyLen = 0; + + if (PORT && PORT->TX) + { + // We send the last 24 hours worth of data. Buffer is cyclic so oldest byte is at StatsPointer + + int first = PORT->StatsPointer; + int firstlen = 1440 - first; + + memcpy(&_REPLYBUFFER[0], &PORT->TX[first], firstlen); + memcpy(&_REPLYBUFFER[firstlen], PORT->TX, first); + + memcpy(&_REPLYBUFFER[1440], &PORT->BUSY[first], firstlen); + memcpy(&_REPLYBUFFER[1440 + firstlen], PORT->BUSY, first); + + ReplyLen = 2880; + } + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %d\r\nContent-Type: Text\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + } + + + else if (_memicmp(NodeURL, "/Icon", 5) == 0 && _memicmp(&NodeURL[10], ".png", 4) == 0) + { + // APRS internal Icon + + char * Compressed; + + ReplyLen = GetAPRSIcon(_REPLYBUFFER, NodeURL); + + if (allowDeflate) + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + else + Compressed = _REPLYBUFFER; + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: Text\r\n%s\r\n", ReplyLen, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + + return 0; + + } + + else if (_memicmp(NodeURL, "/NODE/", 6)) + { + // Not Node, See if a local file + + Bufferlen = SendMessageFile(sock, NodeURL, FALSE, allowDeflate); // Send error if not found + return 0; + } + + // Node URL + + { + + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + + if (_stricmp(NodeURL, "/Node/webproc.css") == 0) + { + char WebprocCSS[] = + ".dropbtn {position: relative; border: 1px solid black;padding:1px;}\r\n" + ".dropdown {position: relative; display: inline-block;}\r\n" + ".dropdown-content {display: none; position: absolute;background-color: #ccc; " + "min-width: 160px; box-shadow: 0px 8px 16px 0px rgba(0,0,00.2); z-index: 1;}\r\n" + ".dropdown-content a {color: black; padding: 1px 1px;text-decoration:none;display:block;}" + ".dropdown-content a:hover {background-color: #dddfff;}\r\n" + ".dropdown:hover .dropdown-content {display: block;}\r\n" + ".dropdown:hover .dropbtn {background-color: #ddd;}\r\n" + "input.btn:active {background:black;color:white;}\r\n" + "submit.btn:active {background:black;color:white;}\r\n"; + ReplyLen = sprintf(_REPLYBUFFER, "%s", WebprocCSS); + } + + else if (_stricmp(NodeURL, "/Node/Killandrestart") == 0) + { + int port = atoi(Context); + + if (port > 0 && port <= MaxBPQPortNo) + { + struct TNCINFO * TNC = TNCInfo[port]; + + KillTNC(TNC); + TNC->DontRestart = FALSE; + RestartTNC(TNC); + + if (TNC && TNC->WebWindowProc) + ReplyLen = TNC->WebWindowProc(TNC, _REPLYBUFFER, LOCAL); + + } + } + + else if (_stricmp(NodeURL, "/Node/Port") == 0 || _stricmp(NodeURL, "/Node/ARDOPAbort") == 0) + { + int port = atoi(Context); + + if (port > 0 && port <= MaxBPQPortNo) + { + struct TNCINFO * TNC = TNCInfo[port]; + + if (TNC && TNC->WebWindowProc) + ReplyLen = TNC->WebWindowProc(TNC, _REPLYBUFFER, LOCAL); + } + + } + + else if (_stricmp(NodeURL, "/Node/Streams") == 0) + { + ReplyLen = StatusProc(_REPLYBUFFER); + } + + else if (_stricmp(NodeURL, "/Node/Stats.html") == 0) + { + struct tm * TM; + char UPTime[50]; + time_t szClock = STATSTIME * 60; + + TM = gmtime(&szClock); + + sprintf(UPTime, "%.2d:%.2d:%.2d", TM->tm_yday, TM->tm_hour, TM->tm_min); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", StatsHddr); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Version", VersionString); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Uptime (Days Hours Mins)", UPTime); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Semaphore: Get-Rel/Clashes", Semaphore.Gets - Semaphore.Rels, Semaphore.Clashes); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Buffers: Max/Cur/Min/Out/Wait", MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "Known Nodes/Max Nodes", NUMBEROFNODES, MAXDESTS); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "L4 Connects Sent/Rxed ", L4CONNECTSOUT, L4CONNECTSIN); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "L4 Frames TX/RX/Resent/Reseq", L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + "L3 Frames Relayed", L3FRAMES); + + } + + else if (_stricmp(NodeURL, "/Node/RigControl.html") == 0) + { + char Test[] = + "\r\n" + "Rigcontrol\r\n" + "\r\n" + "\r\n" + "\r\n" + "
Waiting for data...
\r\n" + "\r\n"; + + + char NoRigCtl[] = + "\r\n" + "Rigcontrol\r\n" + "\r\n" + "\r\n" + "
RigControl Not Configured...
\r\n" + "\r\n"; + + if (RigWebPage) + ReplyLen = sprintf(_REPLYBUFFER, "%s", Test); + else + ReplyLen = sprintf(_REPLYBUFFER, "%s", NoRigCtl); + } + + else if (_stricmp(NodeURL, "/Node/ShowLog.html") == 0) + { + char ShowLogPage[] = + "" + "" + "Log Display" + "" + "
" + "
" +// "" + "" + "" + "
"; + + char * _REPLYBUFFER; + int ReplyLen; + char Header[256]; + int HeaderLen; + char * CfgBytes; + int CfgLen; + char inputname[250]; + FILE *fp1; + struct stat STAT; + char DummyKey[] = "DummyKey"; + time_t T; + struct tm * tm; + char Name[64] = ""; + + T = time(NULL); + tm = gmtime(&T); + + if (LOCAL == FALSE && COOKIE == FALSE) + { + // Send Not Authorized + + char _REPLYBUFFER[4096]; + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Not authorized - please sign in"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return 1; + } + + if (COOKIE == FALSE) + Key = DummyKey; + + if (memcmp(Context, "date=", 5) == 0) + { + memset(tm, 0, sizeof(struct tm)); + tm->tm_year = atoi(&Context[5]) - 1900; + tm->tm_mon = atoi(&Context[10]) - 1; + tm->tm_mday = atoi(&Context[13]); + } + + + + if (strcmp(Context, "input=Back") == 0) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return 1; + } + + if (LogDirectory[0] == 0) + { + strcpy(inputname, "logs/"); + } + else + { + strcpy(inputname,LogDirectory); + strcat(inputname,"/"); + strcat(inputname, "/logs/"); + } + + if (strstr(Context, "CMS")) + { + sprintf(Name, "CMSAccess_%04d%02d%02d.log", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday); + } + else if (strstr(Context, "Debug")) + { + sprintf(Name, "log_%02d%02d%02d_DEBUG.txt", + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + } + else if (strstr(Context, "BBS")) + { + sprintf(Name, "log_%02d%02d%02d_BBS.txt", + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + } + else if (strstr(Context, "Chat")) + { + sprintf(Name, "log_%02d%02d%02d_CHAT.txt", + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + } + else if (strstr(Context, "Telnet")) + { + sprintf(Name, "Telnet_%02d%02d%02d.log", + tm->tm_year - 100, tm->tm_mon+1, tm->tm_mday); + } + + strcat(inputname, Name); + + if (stat(inputname, &STAT) == -1) + { + CfgBytes = malloc(256); + sprintf(CfgBytes, "Log %s not found", inputname); + CfgLen = strlen(CfgBytes); + } + else + { + fp1 = fopen(inputname, "rb"); + + if (fp1 == 0) + { + CfgBytes = malloc(256); + sprintf(CfgBytes, "Log %s not found", inputname); + CfgLen = strlen(CfgBytes); + } + else + { + CfgLen = STAT.st_size; + + CfgBytes = malloc(CfgLen + 1); + + CfgLen = (int)fread(CfgBytes, 1, CfgLen, fp1); + CfgBytes[CfgLen] = 0; + } + } + + _REPLYBUFFER = malloc(CfgLen + 1000); + + ReplyLen = sprintf(_REPLYBUFFER, ShowLogPage, CfgBytes); + free (CfgBytes); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + sendandcheck(sock, Tail, (int)strlen(Tail)); + free (_REPLYBUFFER); + + return 1; + } + + else if (_stricmp(NodeURL, "/Node/EditCfg.html") == 0) + { + char * _REPLYBUFFER; + int ReplyLen; + char Header[256]; + int HeaderLen; + char * CfgBytes; + int CfgLen; + char inputname[250]="bpq32.cfg"; + FILE *fp1; + struct stat STAT; + char DummyKey[] = "DummyKey"; + + if (LOCAL == FALSE && COOKIE == FALSE) + { + // Send Not Authorized + + char _REPLYBUFFER[4096]; + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Not authorized - please sign in"); + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + send(sock, Header, HeaderLen, 0); + send(sock, _REPLYBUFFER, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + return 1; + } + + if (COOKIE ==FALSE) + Key = DummyKey; + + if (ConfigDirectory[0] == 0) + { + strcpy(inputname, "bpq32.cfg"); + } + else + { + strcpy(inputname,ConfigDirectory); + strcat(inputname,"/"); + strcat(inputname, "bpq32.cfg"); + } + + + if (stat(inputname, &STAT) == -1) + { + CfgBytes = _strdup("Config File not found"); + } + else + { + fp1 = fopen(inputname, "rb"); + + if (fp1 == 0) + { + CfgBytes = _strdup("Config File not found"); + } + else + { + CfgLen = STAT.st_size; + + CfgBytes = malloc(CfgLen + 1); + + CfgLen = (int)fread(CfgBytes, 1, CfgLen, fp1); + CfgBytes[CfgLen] = 0; + } + } + + _REPLYBUFFER = malloc(CfgLen + 1000); + + ReplyLen = sprintf(_REPLYBUFFER, ConfigEditPage, Key, CfgBytes); + free (CfgBytes); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", ReplyLen + (int)strlen(Tail)); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, _REPLYBUFFER, ReplyLen); + sendandcheck(sock, Tail, (int)strlen(Tail)); + free (_REPLYBUFFER); + + return 1; + } + + + + if (_stricmp(NodeURL, "/Node/PortBeacons") == 0) + { + char * PortChar = strtok_s(NULL, "&", &Context); + int PortNo = atoi(PortChar); + struct PORTCONTROL * PORT; + int PortSlot = 0; + + PORT = GetPortTableEntryFromPortNum(PortNo); // Need slot not number + if (PORT) + PortSlot = PORT->PortSlot; + + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], Beacons, PortNo, + Interval[PortSlot], &UIUIDEST[PortSlot][0], &UIUIDigi[PortSlot][0], &FN[PortSlot][0], &Message[PortSlot][0], PortNo); + } + + + + if (_stricmp(NodeURL, "/Node/PortStats") == 0) + { + struct _EXTPORTDATA * Port; + + char * PortChar = strtok_s(NULL, "&", &Context); + int PortNo = atoi(PortChar); + int Protocol; + int PortType; + + // char PORTTYPE; // H/W TYPE + // 0 = ASYNC, 2 = PC120, 4 = DRSI + // 6 = TOSH, 8 = QUAD, 10 = RLC100 + // 12 = RLC400 14 = INTERNAL 16 = EXTERNAL + +#define KISS 0 +#define NETROM 2 +#define HDLC 6 +#define L2 8 +#define WINMOR 10 + + + // char PROTOCOL; // PORT PROTOCOL + // 0 = KISS, 2 = NETROM, 4 = BPQKISS + //; 6 = HDLC, 8 = L2 + + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsHddr, PortNo); + + Port = (struct _EXTPORTDATA *)GetPortTableEntryFromPortNum(PortNo); + + if (Port == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, "Invalid Port"); + goto SendResp; + } + + Protocol = Port->PORTCONTROL.PROTOCOL; + PortType = Port->PORTCONTROL.PROTOCOL; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Frames Digied", Port->PORTCONTROL.L2DIGIED); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Frames Heard", Port->PORTCONTROL.L2FRAMES); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Frames Received", Port->PORTCONTROL.L2FRAMESFORUS); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Frames Sent", Port->PORTCONTROL.L2FRAMESSENT); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Timeouts", Port->PORTCONTROL.L2TIMEOUTS); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "REJ Frames Received", Port->PORTCONTROL.L2REJCOUNT); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "RX out of Seq", Port->PORTCONTROL.L2OUTOFSEQ); + // ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "L2 Resequenced", Port->PORTCONTROL.L2RESEQ); + if (Protocol == HDLC) + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "Underrun", Port->PORTCONTROL.L2URUNC); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "RX Overruns", Port->PORTCONTROL.L2ORUNC); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "RX CRC Errors", Port->PORTCONTROL.RXERRORS); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "Frames abandoned", Port->PORTCONTROL.L1DISCARD); + } + else if ((Protocol == KISS && Port->PORTCONTROL.KISSFLAGS) || Protocol == NETROM) + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "Poll Timeout", Port->PORTCONTROL.L2URUNC); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "RX CRC Errors", Port->PORTCONTROL.RXERRORS); + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "FRMRs Sent", Port->PORTCONTROL.L2FRMRTX); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortStatsLine, "FRMRs Received", Port->PORTCONTROL.L2FRMRRX); + + // DB 'Link Active % ' + // DD AVSENDING + + } + + if (_stricmp(NodeURL, "/Node/Ports.html") == 0) + { + struct _EXTPORTDATA * ExtPort; + struct PORTCONTROL * Port; + + int count; + char DLL[20]; + char StatsURL[64]; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", PortsHddr); + + for (count = 1; count <= NUMBEROFPORTS; count++) + { + Port = GetPortTableEntryFromSlot(count); + ExtPort = (struct _EXTPORTDATA *)Port; + + // see if has a stats page + + if (Port->AVACTIVE) + sprintf(StatsURL, " Stats Graph", Port->PORTNUMBER); + else + StatsURL[0] = 0; + + if (Port->PORTTYPE == 0x10) + { + strcpy(DLL, ExtPort->PORT_DLL_NAME); + strlop(DLL, '.'); + } + else if (Port->PORTTYPE == 0) + strcpy(DLL, "ASYNC"); + + else if (Port->PORTTYPE == 22) + strcpy(DLL, "I2C"); + + else if (Port->PORTTYPE == 14) + strcpy(DLL, "INTERNAL"); + + else if (Port->PORTTYPE > 0 && Port->PORTTYPE < 14) + strcpy(DLL, "HDLC"); + + + if (Port->TNC && Port->TNC->WebWindowProc) // Has a Window + { + if (Port->UICAPABLE) + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithBeaconAndDriver, Port->PORTNUMBER, DLL, + Port->PORTDESCRIPTION, Port->PORTNUMBER, Port->PORTNUMBER, Port->TNC->WebWinX, Port->TNC->WebWinY, 200, 200, StatsURL); + else + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithDriver, Port->PORTNUMBER, DLL, + Port->PORTDESCRIPTION, Port->PORTNUMBER, Port->TNC->WebWinX, Port->TNC->WebWinY, 200, 200, StatsURL); + + continue; + } + + if (Port->PORTTYPE == 16 && Port->PROTOCOL == 10 && Port->UICAPABLE == 0) // EXTERNAL, Pactor/WINMO + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], SessionPortLine, Port->PORTNUMBER, DLL, + Port->PORTDESCRIPTION, Port->PORTNUMBER, StatsURL); + else + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], PortLineWithBeacon, Port->PORTNUMBER, Port->PORTNUMBER, + DLL, DLL, Port->PORTDESCRIPTION, Port->PORTNUMBER, StatsURL); + } + + if (RigActive) + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RigControlLine, 64, "Rig Control", "Rig Control", 600, 350, 200, 200); + + } + + if (_stricmp(NodeURL, "/Node/Nodes.html") == 0) + { + struct DEST_LIST * Dests = DESTS; + int count, i; + char Normcall[10]; + char Alias[10]; + int Width = 5; + int x = 0, n = 0; + struct DEST_LIST * List[1000]; + char Param = 0; + + if (Context) + { + _strupr(Context); + Param = Context[0]; + } + + for (count = 0; count < MAXDESTS; count++) + { + if (Dests->DEST_CALL[0] != 0) + { + if (Param != 'T' || Dests->DEST_COUNT) + List[n++] = Dests; + + if (n > 999) + break; + } + + Dests++; + } + + if (n > 1) + { + if (Param == 'C') + qsort(List, n, sizeof(void *), CompareNode); + else + qsort(List, n, sizeof(void *), CompareAlias); + } + + Alias[6] = 0; + + if (Param == 'T') + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], NodeHddr, "with traffic"); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], ""); + } + else if (Param == 'C') + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], NodeHddr, "sorted by Call"); + else + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], NodeHddr, "sorted by Alias"); + + for (i = 0; i < n; i++) + { + int len = ConvFromAX25(List[i]->DEST_CALL, Normcall); + Normcall[len]=0; + + memcpy(Alias, List[i]->DEST_ALIAS, 6); + strlop(Alias, ' '); + + if (Param == 'T') + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + Normcall, Alias, List[i]->DEST_COUNT, List[i]->DEST_RTT /16, + (List[i]->DEST_STATE & 0x40)? 'B':' ', (List[i]->DEST_STATE & 63)); + + } + else + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], NodeLine, Normcall, Alias, Normcall); + + if (++x == Width) + { + x = 0; + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], ""); + } + } + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], ""); + } + + if (_stricmp(NodeURL, "/Node/NodeDetail") == 0) + { + UCHAR AXCall[8]; + struct DEST_LIST * Dest = DESTS; + struct NR_DEST_ROUTE_ENTRY * NRRoute; + struct ROUTE * Neighbour; + char Normcall[10]; + int i, len, count, Active; + char Alias[7]; + + Alias[6] = 0; + + _strupr(Context); + + ConvToAX25(Context, AXCall); + + for (count = 0; count < MAXDESTS; count++) + { + if (CompareCalls(Dest->DEST_CALL, AXCall)) + { + break; + } + Dest++; + } + + if (count == MAXDESTS) + { + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "

Call %s not found

", Context); + goto SendResp; + } + + memcpy(Alias, Dest->DEST_ALIAS, 6); + strlop(Alias, ' '); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], + "

Info for Node %s:%s

", Alias, Context); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "

PortDriverIDBeaconsDriver WindowStats Graph
%d %s%s
%d %s%s Beacons %s
%d%s%s %s
%d%s%s Driver Window%s
%d%s%s BeaconsDriver Window%s
%d%s%s Rig Control
%s%s
%s%s
%s%d%d
%s%d%d%d%d%d
%s%d%d
%s%d%d
%s%d%d%d%d
%s%d
CallFramesRTTBPQ?Hops
%s:%s%d%d%c%.0d
"); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
FramesRTTBPQ?Hops
%d%d%c%.0d
", + Dest->DEST_COUNT, Dest->DEST_RTT /16, + (Dest->DEST_STATE & 0x40)? 'B':' ', (Dest->DEST_STATE & 63)); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "

Neighbours

"); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], + "" + ""); + + 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; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "", + (Active == i)?'>':' ',NRRoute->ROUT_QUALITY, NRRoute->ROUT_OBSCOUNT, Neighbour->NEIGHBOUR_PORT, Normcall); + } + NRRoute++; + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "
Qual Obs Port Call
%c %d%d%d%s
"); + + goto SendResp; + + } + /* + + MOV ESI,OFFSET32 NODEROUTEHDDR + MOV ECX,11 + REP MOVSB + + LEA ESI,DEST_CALL[EBX] + CALL DECODENODENAME ; CONVERT TO ALIAS:CALL + REP MOVSB + + CMP DEST_RTT[EBX],0 + JE SHORT @f ; TIMER NOT SET - DEST PROBABLY NOT USED + + MOVSB ; ADD SPACE + CALL DORTT + + @@: + MOV AL,CR + STOSB + + MOV ECX,3 + MOV DH,DEST_ROUTE[EBX] ; CURRENT ACTIVE ROUTE + MOV DL,1 + + push ebx + + PUBLIC CMDN110 + CMDN110: + + MOV ESI,ROUT1_NEIGHBOUR[EBX] + CMP ESI,0 + JE CMDN199 + + + MOV AX,' ' + CMP DH,DL + JNE SHORT CMDN112 ; NOT CURRENT DEST + MOV AX,' >' + + CMDN112: + + STOSW + + PUSH ECX + + MOV AL,ROUT1_QUALITY[EBX] + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + MOV AL,ROUT1_OBSCOUNT[EBX] + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + MOV AL,NEIGHBOUR_PORT[ESI] ; GET PORT + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + + PUSH EDI + CALL CONVFROMAX25 ; CONVERT TO CALL + POP EDI + + MOV ESI,OFFSET32 NORMCALL + REP MOVSB + + MOV AL,CR + STOSB + + ADD EBX,ROUTEVECLEN + INC DL ; ROUTE NUMBER + + POP ECX + DEC ECX + JNZ CMDN110 + + PUBLIC CMDN199 + CMDN199: + + POP EBX + + ; DISPLAY INP3 ROUTES + + MOV ECX,3 + MOV DL,4 + + PUBLIC CMDNINP3 + CMDNINP3: + + MOV ESI,INPROUT1_NEIGHBOUR[EBX] + CMP ESI,0 + JE CMDNINPEND + + MOV AX,' ' + CMP DH,DL + JNE SHORT @F ; NOT CURRENT DEST + MOV AX,' >' + + @@: + + STOSW + + PUSH ECX + + MOV AL, Hops1[EBX] + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + MOVZX EAX, SRTT1[EBX] + + MOV EDX,0 + MOV ECX, 100 + DIV ECX + CALL CONV_5DIGITS + MOV AL,'.' + STOSB + MOV EAX, EDX + CALL PRINTNUM + MOV AL,'s' + STOSB + MOV AL,' ' + STOSB + + MOV AL,NEIGHBOUR_PORT[ESI] ; GET PORT + CALL CONV_DIGITS ; CONVERT AL TO DECIMAL DIGITS + + mov AL,' ' + stosb + + PUSH EDI + CALL CONVFROMAX25 ; CONVERT TO CALL + POP EDI + + MOV ESI,OFFSET32 NORMCALL + REP MOVSB + + + MOV AL,CR + STOSB + + ADD EBX,INPROUTEVECLEN + INC DL ; ROUTE NUMBER + + POP ECX + LOOP CMDNINP3 + + CMDNINPEND: + + ret + + */ + + // AXIP Partners + + if (_stricmp(NodeURL, "/Node/AXIP.html") == 0) + { + int i; + char Normcall[10]; + int Width = 5; + int x = 0, n = 0, nd = 0; + struct arp_table_entry * List[1000]; + struct arp_table_entry * ListD[1000]; + char AXIPList[10000] = ""; + int ListLen = 0; + + struct AXIPPORTINFO * AXPORT = Portlist[0]; + struct PORTCONTROL * PORT = PORTTABLE; + struct arp_table_entry * arp; + time_t NOW = time(NULL); + + char AXIPHeader[] = + "" + "
AXIP UpAXIP Down
%s"; + + char DownList[] = + "%s"; + + char Tail[] = + "
"; + + + while (PORT) + { + AXPORT = Portlist[PORT->PORTNUMBER]; + + if (AXPORT) + { + // Get ARP entries + + for (i = 0; i < AXPORT->arp_table_len; i++) + { + arp = &AXPORT->arp_table[i]; + + if (arp->LastHeard == 0 || (NOW - arp->LastHeard) > 3600) // Considered down + ListD[nd++] = arp; + else + List[n++] = arp; + } + } + PORT = PORT->PORTPOINTER; + } + + if (n > 1) + qsort(List, n, sizeof(void *), CompareNode); + if (nd > 1) + qsort(ListD, nd, sizeof(void *), CompareNode); + + for (i = 0; i < n; i++) + { + int len = ConvFromAX25(List[i]->callsign, Normcall); + Normcall[len]=0; + + ListLen += sprintf(&AXIPList[ListLen], "%02d - %s %d
", i + 1, Normcall, (List[i]->LastHeard)?(NOW - List[i]->LastHeard):0); + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], AXIPHeader, AXIPList); + + ListLen = 0; + + for (i = 0; i < nd; i++) + { + int len = ConvFromAX25(ListD[i]->callsign, Normcall); + Normcall[len]=0; + ListLen += sprintf(&AXIPList[ListLen], "%02d - %s %d
", i + 1, Normcall, (ListD[i]->LastHeard)?(NOW - ListD[i]->LastHeard):0); + } + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], DownList, AXIPList); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], Tail); + } + + if (_stricmp(NodeURL, "/Node/Routes.html") == 0) + { + struct ROUTE * Routes = NEIGHBOURS; + int MaxRoutes = MAXNEIGHBOURS; + int count, i; + char Normcall[10]; + char locked[4] = " "; + int NodeCount; + int Percent = 0; + int Iframes, Retries; + char Active[10]; + int Queued; + + int x = 0, n = 0; + struct ROUTE * List[1000]; + + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", RouteHddr); + + // 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]; + { + int len = ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall); + Normcall[len]=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) + Queued = COUNT_AT_L2(Routes->NEIGHBOUR_LINK); + else + Queued = 0; + + Iframes = Routes->NBOUR_IFRAMES; + Retries = Routes->NBOUR_RETRIES; + + if (Routes->NEIGHBOUR_LINK && Routes->NEIGHBOUR_LINK->L2STATE >= 5) + strcpy(Active, ">"); + else + strcpy(Active, " "); + + if (Iframes) + Percent = (Retries * 100) / Iframes; + else + Percent = 0; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RouteLine, Active, Routes->NEIGHBOUR_PORT, Normcall, locked, + Routes->NEIGHBOUR_QUAL, NodeCount, Iframes, Retries, Percent, Routes->NBOUR_MAXFRAME, Routes->NBOUR_FRACK, + Routes->NEIGHBOUR_TIME >> 8, Routes->NEIGHBOUR_TIME & 0xff, Queued, Routes->OtherendsRouteQual); + } + Routes+=1; + } + } + + if (_stricmp(NodeURL, "/Node/Links.html") == 0) + { + struct _LINKTABLE * Links = LINKS; + int MaxLinks = MAXLINKS; + int count; + char Normcall1[10]; + char Normcall2[10]; + char State[12] = "", Type[12] = "Uplink"; + int axState; + int cctType; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", LinkHddr); + + for (count=0; countLINKCALL[0] != 0) + { + int len = ConvFromAX25(Links->LINKCALL, Normcall1); + Normcall1[len] = 0; + + len = ConvFromAX25(Links->OURCALL, Normcall2); + Normcall2[len] = 0; + + axState = Links->L2STATE; + + if (axState == 2) + strcpy(State, "Connecting"); + else if (axState == 3) + strcpy(State, "FRMR"); + else if (axState == 4) + strcpy(State, "Closing"); + else if (axState == 5) + strcpy(State, "Active"); + else if (axState == 6) + strcpy(State, "REJ Sent"); + + cctType = Links->LINKTYPE; + + if (cctType == 1) + strcpy(Type, "Uplink"); + else if (cctType == 2) + strcpy(Type, "Downlink"); + else if (cctType == 3) + strcpy(Type, "Node-Node"); + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], LinkLine, Normcall1, Normcall2, Links->LINKPORT->PORTNUMBER, + State, Type, 2 - Links->VER1FLAG ); + + Links+=1; + } + } + } + + if (_stricmp(NodeURL, "/Node/Users.html") == 0) + { + TRANSPORTENTRY * L4 = L4TABLE; + TRANSPORTENTRY * Partner; + int MaxLinks = MAXLINKS; + int count; + char State[12] = "", Type[12] = "Uplink"; + char LHS[50] = "", MID[10] = "", RHS[50] = ""; + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", UserHddr); + + for (count=0; count < MAXCIRCUITS; count++) + { + 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: + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], UserLine, LHS, MID, RHS); + } +CMDS60: + L4++; + } + } + /* + PUBLIC CMDUXX_1 + CMDUXX_1: + push EBX + push ESI + PUSH ECX + push EDI + + call _FINDDESTINATION + pop EDI + + jz SHORT NODE_FOUND + + push EDI ; NET/ROM not found + call CONVFROMAX25 ; CONVERT TO CALL + pop EDI + mov ESI,OFFSET32 NORMCALL + rep movsb + + jmp SHORT END_CMDUXX + + PUBLIC NODE_FOUND + NODE_FOUND: + + lea ESI,DEST_CALL[EBX] + call DECODENODENAME + + REP MOVSB + + PUBLIC END_CMDUXX + END_CMDUXX: + + POP ECX + pop ESI + pop EBX + ret + + }}} + */ + + else if (_stricmp(NodeURL, "/Node/Terminal.html") == 0) + { + if (COOKIE && Session) + { + // Already signed in as sysop + + struct UserRec * USER = Session->USER; + + struct HTTPConnectionInfo * NewSession = AllocateSession(sock, 'T'); + + if (NewSession) + { + char AXCall[10]; + ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, NewSession->Key, NewSession->Key, NewSession->Key); + strcpy(NewSession->HTTPCall, USER->Callsign); + ConvToAX25(NewSession->HTTPCall, AXCall); + ChangeSessionCallsign(NewSession->Stream, AXCall); + BPQHOSTVECTOR[NewSession->Stream -1].HOSTSESSION->Secure_Session = USER->Secure; + Session->USER = USER; + NewSession->TNC = conn->TNC; + + + // if (Appl[0]) + // { + // strcat(Appl, "\r"); + // SendMsg(Session->Stream, Appl, strlen(Appl)); + // } + + } + else + { + ReplyLen = SetupNodeMenu(_REPLYBUFFER, LOCAL); + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", BusyError); + } + } + else if (LOCAL) + { + // connected to 127.0.0.1 so sign in using node call + + struct HTTPConnectionInfo * NewSession = AllocateSession(sock, 'T'); + + if (NewSession) + { + ReplyLen = sprintf(_REPLYBUFFER, TermPage, Mycall, Mycall, NewSession->Key, NewSession->Key, NewSession->Key); + strcpy(NewSession->HTTPCall, MYNODECALL); + ChangeSessionCallsign(NewSession->Stream, MYCALL); + BPQHOSTVECTOR[NewSession->Stream -1].HOSTSESSION->Secure_Session = TRUE; + NewSession->TNC = conn->TNC; + } + } + else + ReplyLen = sprintf(_REPLYBUFFER, TermSignon, Mycall, Mycall, Context); + } + + else if (_stricmp(NodeURL, "/Node/Signon.html") == 0) + { + ReplyLen = sprintf(_REPLYBUFFER, NodeSignon, Mycall, Mycall, Context); + } + + else if (_stricmp(NodeURL, "/Node/Drivers") == 0) + { + int Bufferlen = SendMessageFile(sock, "/Drivers.htm", TRUE, allowDeflate); // return -1 if not found + + if (Bufferlen != -1) + return 0; // We've sent it + } + + else if (_stricmp(NodeURL, "/Node/OutputScreen.html") == 0) + { + struct HTTPConnectionInfo * Session = FindSession(Context); + + if (Session == NULL) + { + ReplyLen = sprintf(_REPLYBUFFER, "%s", LostSession); + } + else + { + Session->sock = sock; // socket to reply on + ReplyLen = RefreshTermWindow(TCP, Session, _REPLYBUFFER); + + if (ReplyLen == 0) // Nothing new + { + // Debugprintf("GET with no data avail - response held"); + Session->ResponseTimer = 1200; // Delay response for up to a minute + } + else + { + // Debugprintf("GET - outpur sent, timer was %d, set to zero", Session->ResponseTimer); + Session->ResponseTimer = 0; + } + + Session->KillTimer = 0; + return 0; // Refresh has sent any available output + } + } + + else if (_stricmp(NodeURL, "/Node/InputLine.html") == 0) + { + struct TNCINFO * TNC = conn->TNC; + struct TCPINFO * TCP = 0; + + if (TNC) + TCP = TNC->TCPInfo; + + if (TCP && TCP->WebTermCSS) + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Context, TCP->WebTermCSS); + else + ReplyLen = sprintf(_REPLYBUFFER, InputLine, Context, ""); + + } + + else if (_stricmp(NodeURL, "/Node/PTT") == 0) + { + struct TNCINFO * TNC = conn->TNC; + int x = atoi(Context); + } + + +SendResp: + + FormatTime3(TimeString, time(NULL)); + + strcpy(&_REPLYBUFFER[ReplyLen], Tail); + ReplyLen += (int)strlen(Tail); + + + if (allowDeflate) + { + Compressed = Compressit(_REPLYBUFFER, ReplyLen, &ReplyLen); + } + else + { + Encoding[0] = 0; + Compressed = _REPLYBUFFER; + } + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "Date: %s\r\n%s\r\n", ReplyLen, TimeString, Encoding); + sendandcheck(sock, Header, HeaderLen); + sendandcheck(sock, Compressed, ReplyLen); + + if (allowDeflate) + free (Compressed); + } + return 0; + +#ifdef WIN32xx + } +#include "StdExcept.c" +} +return 0; +#endif +} + +void ProcessHTTPMessage(void * conn) +{ + // conn is a malloc'ed copy to handle reused connections, so need to free it + + InnerProcessHTTPMessage((struct ConnectionInfo *)conn); + free(conn); + return; +} + +static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + + +VOID FormatTime3(char * Time, time_t cTime) +{ + struct tm * TM; + TM = gmtime(&cTime); + + sprintf(Time, "%s, %02d %s %3d %02d:%02d:%02d GMT", dat[TM->tm_wday], TM->tm_mday, month[TM->tm_mon], + TM->tm_year + 1900, TM->tm_hour, TM->tm_min, TM->tm_sec); + +} + +// Sun, 06 Nov 1994 08:49:37 GMT + +int StatusProc(char * Buff) +{ + int i; + char callsign[12] = ""; + char flag[3]; + UINT Mask, MaskCopy; + int Flags; + int AppNumber; + int OneBits; + int Len = sprintf(Buff, "" + "Stream Status"); + + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + Len += sprintf(&Buff[Len], ""); + + for (i=1;i<65; i++) + { + callsign[0]=0; + + if (GetAllocationState(i)) + + strcpy(flag,"*"); + else + strcpy(flag," "); + + GetCallsign(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) + Len += sprintf(&Buff[Len], "" + "", + i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName); + + else + Len += sprintf(&Buff[Len], "" + "", + i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName); + + if ((i & 1) == 0) + Len += sprintf(&Buff[Len], ""); + + } + + Len += sprintf(&Buff[Len], "
    RX   TX   MON  App  Flg Callsign  Program    RX   TX   MON  App  Flg Callsign  Program
%d%s%d%d%d%x%x%s%s%d%s%d%d%d%d%x%s%s
"); + return Len; +} + +int ProcessNodeSignon(SOCKET sock, struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + char Header[256]; + int HeaderLen; + struct HTTPConnectionInfo *Sess; + + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "\r\n", (int)(ReplyLen + strlen(Tail))); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + } + user = strtok_s(&input[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if (strcmp(password, USER->Password) == 0 && USER->Secure) + { + // ok + + Sess = *Session = AllocateSession(sock, 'N'); + Sess->USER = USER; + + ReplyLen = SetupNodeMenu(Reply, LOCAL); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n" + "Set-Cookie: BPQSessionCookie=%s; Path = /\r\n\r\n", (int)(ReplyLen + strlen(Tail)), Sess->Key); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return ReplyLen; + } + } + } + } + + ReplyLen = sprintf(Reply, NodeSignon, Mycall, Mycall); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); + + HeaderLen = sprintf(Header, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n", (int)(ReplyLen + strlen(Tail))); + send(sock, Header, HeaderLen, 0); + send(sock, Reply, ReplyLen, 0); + send(sock, Tail, (int)strlen(Tail), 0); + + return 0; + + + return ReplyLen; +} + +int ProcessMailAPISignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password; + struct HTTPConnectionInfo * NewSession; + int i; + struct UserRec * USER; + + if (strchr(MsgPtr, '?')) + { + // Check Password + + user = strlop(MsgPtr, '?'); + password = strlop(user, '&'); + strlop(password, ' '); + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if ((strcmp(password, USER->Password) == 0) && (USER->Secure || WebMail)) + { + // ok + + NewSession = AllocateSession(Appl[0], 'M'); + + *Session = NewSession; + + if (NewSession) + { + ReplyLen = 0; + strcpy(NewSession->Callsign, USER->Callsign); + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + return ReplyLen; + + } + } + } + + // Pass failed attempt to BBS code so it can try a bbs user login + + // Need to put url back together + + if (user && user[0] && password && password[0]) + { + sprintf(&MsgPtr[strlen(MsgPtr)], "?%s&%s", user, password); + } + } + + NewSession = AllocateSession(Appl[0], 'M'); + + *Session = NewSession; + + if (NewSession) + ReplyLen = 0; + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + + return ReplyLen; +} + + + + +int ProcessMailSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, BOOL WebMail, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + struct HTTPConnectionInfo * NewSession; + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + return ReplyLen; + } + user = strtok_s(&input[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if (strcmp(password, USER->Password) == 0 && (USER->Secure || WebMail)) + { + // ok + + NewSession = AllocateSession(Appl[0], 'M'); + + *Session = NewSession; + + if (NewSession) + { + + ReplyLen = 0; + strcpy(NewSession->Callsign, USER->Callsign); + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + return ReplyLen; + } + } + } + } + + ReplyLen = sprintf(Reply, MailSignon, Mycall, Mycall); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); + + return ReplyLen; +} + + +int ProcessChatSignon(struct TCPINFO * TCP, char * MsgPtr, char * Appl, char * Reply, struct HTTPConnectionInfo ** Session, int LOCAL) +{ + int ReplyLen = 0; + char * input = strstr(MsgPtr, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + + if (input) + { + int i; + struct UserRec * USER; + + UndoTransparency(input); + + if (strstr(input, "Cancel=Cancel")) + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + return ReplyLen; + } + + user = strtok_s(&input[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + + for (i = 0; i < TCP->NumberofUsers; i++) + { + USER = TCP->UserRecPtr[i]; + + if (user && _stricmp(user, USER->UserName) == 0) + { + if (strcmp(password, USER->Password) == 0 && USER->Secure) + { + // ok + + *Session = AllocateSession(Appl[0], 'C'); + + if (Session) + { + ReplyLen = 0; + } + else + { + ReplyLen = SetupNodeMenu(Reply, LOCAL); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", BusyError); + } + return ReplyLen; + } + } + } + } + + ReplyLen = sprintf(Reply, ChatSignon, Mycall, Mycall); + ReplyLen += sprintf(&Reply[ReplyLen], "%s", PassError); + + return ReplyLen; + +} + +#define SHA1_HASH_LEN 20 + +/* + +Copyright (C) 1998, 2009 +Paul E. Jones + +Freeware Public License (FPL) + +This software is licensed as "freeware." Permission to distribute +this software in source and binary forms, including incorporation +into other products, is hereby granted without a fee. THIS SOFTWARE +IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHOR SHALL NOT BE HELD +LIABLE FOR ANY DAMAGES RESULTING FROM THE USE OF THIS SOFTWARE, EITHER +DIRECTLY OR INDIRECTLY, INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA +OR DATA BEING RENDERED INACCURATE. +*/ + +/* sha1.h + * + * Copyright (C) 1998, 2009 + * Paul E. Jones + * All Rights Reserved + * + ***************************************************************************** + * $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $ + ***************************************************************************** + * + * Description: + * This class implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * Many of the variable names in the SHA1Context, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +/* + * This structure will hold context information for the hashing + * operation + */ +typedef struct SHA1Context +{ + unsigned Message_Digest[5]; /* Message Digest (output) */ + + unsigned Length_Low; /* Message length in bits */ + unsigned Length_High; /* Message length in bits */ + + unsigned char Message_Block[64]; /* 512-bit message blocks */ + int Message_Block_Index; /* Index into message block array */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corruped? */ +} SHA1Context; + +/* + * Function Prototypes + */ +void SHA1Reset(SHA1Context *); +int SHA1Result(SHA1Context *); +void SHA1Input( SHA1Context *, const unsigned char *, unsigned); + +#endif + +BOOL SHA1PasswordHash(char * lpszPassword, char * Hash) +{ + SHA1Context sha; + int i; + + SHA1Reset(&sha); + SHA1Input(&sha, lpszPassword, strlen(lpszPassword)); + SHA1Result(&sha); + + // swap byte order if little endian + + for (i = 0; i < 5; i++) + sha.Message_Digest[i] = htonl(sha.Message_Digest[i]); + + memcpy(Hash, &sha.Message_Digest[0], 20); + + return TRUE; +} + +int BuildRigCtlPage(char * _REPLYBUFFER) +{ + int ReplyLen; + + struct RIGPORTINFO * PORT; + struct RIGINFO * RIG; + int p, i; + + char Page[] = + "\r\n" + // "\r\n" + "Rigcontrol\r\n" + "" + "

Rigcontrol

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + ""; + char RigLine[] = + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n"; + char Tail[] = + "
RadioFreqModeSTPorts
%s%s%s/1%c%c%s
\r\n" + "\r\n"; + + ReplyLen = sprintf(_REPLYBUFFER, "%s", Page); + + for (p = 0; p < NumberofPorts; p++) + { + PORT = PORTInfo[p]; + + for (i=0; i< PORT->ConfiguredRigs; i++) + { + RIG = &PORT->Rigs[i]; + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], RigLine, RIG->WEB_Label, RIG->WEB_FREQ, RIG->WEB_MODE, RIG->WEB_SCAN, RIG->WEB_PTT, RIG->WEB_PORTS, RIG->Interlock); + } + } + + ReplyLen += sprintf(&_REPLYBUFFER[ReplyLen], "%s", Tail); + return ReplyLen; +} + + +void SendRigWebPage() +{ + int i, n; + struct ConnectionInfo * sockptr; + struct TNCINFO * TNC; + struct TCPINFO * TCP; + + for (i = 0; i < 33; i++) + { + TNC = TNCInfo[i]; + + if (TNC && TNC->Hardware == H_TELNET) + { + TCP = TNC->TCPInfo; + + if (TCP) + { + for (n = 0; n <= TCP->MaxSessions; n++) + { + sockptr = TNC->Streams[n].ConnectionInfo; + + if (sockptr->SocketActive) + { + if (sockptr->HTTPMode && sockptr->WebSocks && strcmp(sockptr->WebURL, "RIGCTL") == 0) + { + char RigMsg[8192]; + int RigMsgLen = strlen(RigWebPage); + char* ptr; + + RigMsg[0] = 0x81; // Fin, Data + RigMsg[1] = 126; // Unmasked, Extended Len + RigMsg[2] = RigMsgLen >> 8; + RigMsg[3] = RigMsgLen & 0xff; + strcpy(&RigMsg[4], RigWebPage); + + // If secure session enable PTT button + + if (sockptr->WebSecure) + { + while (ptr = strstr(RigMsg, "hidden")) + memcpy(ptr, " ", 6); + } + + send(sockptr->socket, RigMsg, RigMsgLen + 4, 0); + } + } + } + } + } + } +} + +// Webmail web socket code + +int ProcessWebmailWebSock(char * MsgPtr, char * OutBuffer); + +void ProcessWebmailWebSockThread(void * conn) +{ + // conn is a malloc'ed copy to handle reused connections, so need to free it + + struct ConnectionInfo * sockptr = (struct ConnectionInfo *)conn; + char * URL = sockptr->WebURL; + int Loops = 0; + int Sent; + struct HTTPConnectionInfo Dummy = {0}; + int ReplyLen = 0; + int InputLen = 0; + +#ifdef LINBPQ + + char _REPLYBUFFER[250000]; + + ReplyLen = ProcessWebmailWebSock(URL, _REPLYBUFFER); + + // Send may block + + Sent = send(sockptr->socket, _REPLYBUFFER, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + if (Sent > 0) // something sent + { + ReplyLen -= Sent; + memmove(_REPLYBUFFER, &_REPLYBUFFER[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sockptr->socket, _REPLYBUFFER, ReplyLen, 0); + } + +#else + // Send URL to BPQMail via Pipe. Just need a dummy session, as URL contains session key + + HANDLE hPipe; + char Reply[250000]; + + + + hPipe = CreateFile(MAILPipeFileName, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hPipe == (HANDLE)-1) + { + free(conn); + return; + } + + WriteFile(hPipe, &Dummy, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + WriteFile(hPipe, URL, strlen(URL), &InputLen, NULL); + + ReadFile(hPipe, &Dummy, sizeof (struct HTTPConnectionInfo), &InputLen, NULL); + ReadFile(hPipe, Reply, 250000, &ReplyLen, NULL); + + if (ReplyLen <= 0) + { + InputLen = GetLastError(); + } + + CloseHandle(hPipe); + + // ?? do we need a thread to handle write which may block + + Sent = send(sockptr->socket, Reply, ReplyLen, 0); + + while (Sent != ReplyLen && Loops++ < 3000) // 100 secs max + { + // Debugprintf("%d out of %d sent %d Loops", Sent, InputLen, Loops); + + if (Sent > 0) // something sent + { + InputLen -= Sent; + memmove(Reply, &Reply[Sent], ReplyLen); + } + + Sleep(30); + Sent = send(sockptr->socket, Reply, ReplyLen, 0); + } +#endif + free(conn); + return; +} + +/* + * sha1.c + * + * Copyright (C) 1998, 2009 + * Paul E. Jones + * All Rights Reserved + * + ***************************************************************************** + * $Id: sha1.c 12 2009-06-22 19:34:25Z paulej $ + ***************************************************************************** + * + * Description: + * This file implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * The Secure Hashing Standard, which uses the Secure Hashing + * Algorithm (SHA), produces a 160-bit message digest for a + * given data stream. In theory, it is highly improbable that + * two messages will produce the same message digest. Therefore, + * this algorithm can serve as a means of providing a "fingerprint" + * for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code was + * written with the expectation that the processor has at least + * a 32-bit machine word size. If the machine word size is larger, + * the code should still function properly. One caveat to that + * is that the input functions taking characters and character + * arrays assume that only 8 bits of information are stored in each + * character. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated for + * messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is a + * multiple of the size of an 8-bit character. + * + */ + +/* + * Define the circular shift macro + */ +#define SHA1CircularShift(bits,word) \ + ((((word) << (bits)) & 0xFFFFFFFF) | \ + ((word) >> (32-(bits)))) + +/* Function prototypes */ +void SHA1ProcessMessageBlock(SHA1Context *); +void SHA1PadMessage(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Reset(SHA1Context *context) +{ + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Message_Digest[0] = 0x67452301; + context->Message_Digest[1] = 0xEFCDAB89; + context->Message_Digest[2] = 0x98BADCFE; + context->Message_Digest[3] = 0x10325476; + context->Message_Digest[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array within the SHA1Context provided + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * + * Returns: + * 1 if successful, 0 if it failed. + * + * Comments: + * + */ +int SHA1Result(SHA1Context *context) +{ + + if (context->Corrupted) + { + return 0; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + context->Computed = 1; + } + + return 1; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion of + * the message. + * + * Parameters: + * context: [in/out] + * The SHA-1 context to update + * message_array: [in] + * An array of characters representing the next portion of the + * message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Input( SHA1Context *context, + const unsigned char *message_array, + unsigned length) +{ + if (!length) + { + return; + } + + if (context->Computed || context->Corrupted) + { + context->Corrupted = 1; + return; + } + + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + /* Force it to 32 bits */ + context->Length_Low &= 0xFFFFFFFF; + if (context->Length_Low == 0) + { + context->Length_High++; + /* Force it to 32 bits */ + context->Length_High &= 0xFFFFFFFF; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in the SHAContext, especially the + * single character names, were used because those were the names + * used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const unsigned K[] = /* Constants defined in SHA-1 */ + { + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + unsigned temp; /* Temporary word value */ + unsigned W[80]; /* Word sequence */ + unsigned A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = ((unsigned) context->Message_Block[t * 4]) << 24; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]); + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Message_Digest[0]; + B = context->Message_Digest[1]; + C = context->Message_Digest[2]; + D = context->Message_Digest[3]; + E = context->Message_Digest[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Message_Digest[0] = + (context->Message_Digest[0] + A) & 0xFFFFFFFF; + context->Message_Digest[1] = + (context->Message_Digest[1] + B) & 0xFFFFFFFF; + context->Message_Digest[2] = + (context->Message_Digest[2] + C) & 0xFFFFFFFF; + context->Message_Digest[3] = + (context->Message_Digest[3] + D) & 0xFFFFFFFF; + context->Message_Digest[4] = + (context->Message_Digest[4] + E) & 0xFFFFFFFF; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call SHA1ProcessMessageBlock() + * appropriately. When it returns, it can be assumed that the + * message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (context->Length_High >> 24) & 0xFF; + context->Message_Block[57] = (context->Length_High >> 16) & 0xFF; + context->Message_Block[58] = (context->Length_High >> 8) & 0xFF; + context->Message_Block[59] = (context->Length_High) & 0xFF; + context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF; + context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF; + context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF; + context->Message_Block[63] = (context->Length_Low) & 0xFF; + + SHA1ProcessMessageBlock(context); +} + + diff --git a/.svn/pristine/67/671a160ebd6632c27b5dcd2ba34d2c53608ca949.svn-base b/.svn/pristine/67/671a160ebd6632c27b5dcd2ba34d2c53608ca949.svn-base new file mode 100644 index 0000000..0de546b --- /dev/null +++ b/.svn/pristine/67/671a160ebd6632c27b5dcd2ba34d2c53608ca949.svn-base @@ -0,0 +1,49 @@ +/* $Id: igd_desc_parse.h,v 1.12 2014/11/17 17:19:13 nanard Exp $ */ +/* Project : miniupnp + * http://miniupnp.free.fr/ + * Author : Thomas Bernard + * Copyright (c) 2005-2014 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#ifndef IGD_DESC_PARSE_H_INCLUDED +#define IGD_DESC_PARSE_H_INCLUDED + +/* Structure to store the result of the parsing of UPnP + * descriptions of Internet Gateway Devices */ +#define MINIUPNPC_URL_MAXSIZE (128) +struct IGDdatas_service { + char controlurl[MINIUPNPC_URL_MAXSIZE]; + char eventsuburl[MINIUPNPC_URL_MAXSIZE]; + char scpdurl[MINIUPNPC_URL_MAXSIZE]; + char servicetype[MINIUPNPC_URL_MAXSIZE]; + /*char devicetype[MINIUPNPC_URL_MAXSIZE];*/ +}; + +struct IGDdatas { + char cureltname[MINIUPNPC_URL_MAXSIZE]; + char urlbase[MINIUPNPC_URL_MAXSIZE]; + char presentationurl[MINIUPNPC_URL_MAXSIZE]; + int level; + /*int state;*/ + /* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */ + struct IGDdatas_service CIF; + /* "urn:schemas-upnp-org:service:WANIPConnection:1" + * "urn:schemas-upnp-org:service:WANPPPConnection:1" */ + struct IGDdatas_service first; + /* if both WANIPConnection and WANPPPConnection are present */ + struct IGDdatas_service second; + /* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */ + struct IGDdatas_service IPv6FC; + /* tmp */ + struct IGDdatas_service tmp; +}; + +void IGDstartelt(void *, const char *, int); +void IGDendelt(void *, const char *, int); +void IGDdata(void *, const char *, int); +#ifdef DEBUG +void printIGD(struct IGDdatas *); +#endif /* DEBUG */ + +#endif /* IGD_DESC_PARSE_H_INCLUDED */ diff --git a/.svn/pristine/67/677ce2b8e06a4c3de0cb96747f6f9ce945341ae3.svn-base b/.svn/pristine/67/677ce2b8e06a4c3de0cb96747f6f9ce945341ae3.svn-base new file mode 100644 index 0000000..9857b69 --- /dev/null +++ b/.svn/pristine/67/677ce2b8e06a4c3de0cb96747f6f9ce945341ae3.svn-base @@ -0,0 +1,107 @@ +/* LzFind.h -- Match finder for LZ algorithms + 2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZFIND_H +#define __LZFIND_H + +#include "types.h" + +typedef LZ_UInt32 CLzRef; + +typedef struct _CMatchFinder +{ + Byte *buffer; + LZ_UInt32 pos; + LZ_UInt32 posLimit; + LZ_UInt32 streamPos; + LZ_UInt32 lenLimit; + + LZ_UInt32 cyclicBufferPos; + LZ_UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ + + LZ_UInt32 matchMaxLen; + CLzRef *hash; + CLzRef *son; + LZ_UInt32 hashMask; + LZ_UInt32 cutValue; + + Byte *bufferBase; + ISeqInStream *stream; + int streamEndWasReached; + + LZ_UInt32 blockSize; + LZ_UInt32 keepSizeBefore; + LZ_UInt32 keepSizeAfter; + + LZ_UInt32 numHashBytes; + int directInput; + int btMode; + /* int skipModeBits; */ + int bigHash; + LZ_UInt32 historySize; + LZ_UInt32 fixedHashSize; + LZ_UInt32 hashSizeSum; + LZ_UInt32 numSons; + SRes result; + LZ_UInt32 crc[256]; +} CMatchFinder; + +#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) +#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(LZ_Int32)(index)]) + +#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) + +int MatchFinder_NeedMove(CMatchFinder *p); +Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); +void MatchFinder_MoveBlock(CMatchFinder *p); +void MatchFinder_ReadIfRequired(CMatchFinder *p); + +void MatchFinder_Construct(CMatchFinder *p); + +/* Conditions: + historySize <= 3 GB + keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB + */ +int MatchFinder_Create(CMatchFinder *p, LZ_UInt32 historySize, + LZ_UInt32 keepAddBufferBefore, LZ_UInt32 matchMaxLen, LZ_UInt32 keepAddBufferAfter, + ISzAlloc *alloc); +void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); +void MatchFinder_Normalize3(LZ_UInt32 subValue, CLzRef *items, LZ_UInt32 numItems); +void MatchFinder_ReduceOffsets(CMatchFinder *p, LZ_UInt32 subValue); + +LZ_UInt32 * GetMatchesSpec1(LZ_UInt32 lenLimit, LZ_UInt32 curMatch, LZ_UInt32 pos, const Byte *buffer, CLzRef *son, + LZ_UInt32 _cyclicBufferPos, LZ_UInt32 _cyclicBufferSize, LZ_UInt32 _cutValue, + LZ_UInt32 *distances, LZ_UInt32 maxLen); + +/* + Conditions: + Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. + Mf_GetPointerToCurrentPos_Func's result must be used only before any other function + */ + +typedef void (*Mf_Init_Func)(void *object); +typedef Byte (*Mf_GetIndexByte_Func)(void *object, LZ_Int32 index); +typedef LZ_UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); +typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); +typedef LZ_UInt32 (*Mf_GetMatches_Func)(void *object, LZ_UInt32 *distances); +typedef void (*Mf_Skip_Func)(void *object, LZ_UInt32); + +typedef struct _IMatchFinder +{ + Mf_Init_Func Init; + Mf_GetIndexByte_Func GetIndexByte; + Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; + Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; + Mf_GetMatches_Func GetMatches; + Mf_Skip_Func Skip; +} IMatchFinder; + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); + +void MatchFinder_Init(CMatchFinder *p); +LZ_UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, LZ_UInt32 *distances); +LZ_UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, LZ_UInt32 *distances); +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, LZ_UInt32 num); +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, LZ_UInt32 num); + +#endif diff --git a/.svn/pristine/68/68188320cb1d456d26f9f37cbf63053f47bebbcd.svn-base b/.svn/pristine/68/68188320cb1d456d26f9f37cbf63053f47bebbcd.svn-base new file mode 100644 index 0000000..dee4ded Binary files /dev/null and b/.svn/pristine/68/68188320cb1d456d26f9f37cbf63053f47bebbcd.svn-base differ diff --git a/.svn/pristine/69/692fbf79b78754181ecbe72395a1461c981a146a.svn-base b/.svn/pristine/69/692fbf79b78754181ecbe72395a1461c981a146a.svn-base new file mode 100644 index 0000000..6108cac --- /dev/null +++ b/.svn/pristine/69/692fbf79b78754181ecbe72395a1461c981a146a.svn-base @@ -0,0 +1,3965 @@ +/* +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 +*/ + +// +// FLARQ Emulator/FLDIGI Interface for BPQ32 +// + +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); +extern int (WINAPI FAR *EnumProcessesPtr)(); + + +#include +#include + +#include "tncinfo.h" + +#include "bpq32.h" + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 0 + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#define DLE 0x10 +#define SOH 1 +#define STX 2 +#define EOT 4 + +#define FEND 0xC0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +#define TIMESTAMP 352 + +#define CONTIMEOUT 1200 + +#define AGWHDDRLEN sizeof(struct AGWHEADER) + +extern int (WINAPI FAR *GetModuleFileNameExPtr)(); + +//int ResetExtDriver(int num); +; +int SemHeldByAPI; + +void ConnecttoFLDigiThread(void * portptr); + +void CreateMHWindow(); +int Update_MH_List(struct in_addr ipad, char * call, char proto); + +int ConnecttoFLDigi(int port); +static int ProcessReceivedData(int bpqport); +static int ProcessLine(char * buf, int Port); +int KillTNC(struct TNCINFO * TNC); +static int RestartTNC(struct TNCINFO * TNC); +VOID ProcessFLDigiPacket(struct TNCINFO * TNC, char * Message, int Len); +VOID ProcessFLDigiKISSPacket(struct TNCINFO * TNC, char * Message, int Len); +struct TNCINFO * GetSessionKey(char * key, struct TNCINFO * TNC); +VOID SendARQData(struct TNCINFO * TNC, PMSGWITHLEN Buffer); +static VOID DoMonitorHddr(struct TNCINFO * TNC, struct AGWHEADER * RXHeader, UCHAR * Msg); +VOID SendRPBeacon(struct TNCINFO * TNC); +VOID FLReleaseTNC(struct TNCINFO * TNC); +unsigned int CalcCRC(UCHAR * ptr, int Len); +VOID ARQTimer(struct TNCINFO * TNC); +VOID QueueAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen); +VOID SaveAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen); +VOID ProcessARQStatus(struct TNCINFO * TNC, struct ARQINFO * ARQ, char *Input); +VOID SendXMLPoll(struct TNCINFO * TNC); +static int ProcessXMLData(int port); +VOID CheckFLDigiData(struct TNCINFO * TNC); +VOID SendPacket(struct TNCINFO * TNC, UCHAR * Msg, int MsgLen); +int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +VOID SendXMLCommand(struct TNCINFO * TNC, char * Command, char * Value, char ParamType); +VOID SendXMLCommandInt(struct TNCINFO * TNC, char * Command, int Value, char ParamType); +VOID FLSlowTimer(struct TNCINFO * TNC); +VOID SendKISSCommand(struct TNCINFO * TNC, char * Msg); + +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +extern UCHAR BPQDirectory[]; + +#define MAXMPSKPORTS 16 + +//LOGFONT LFTTYFONT ; + +//HFONT hFont ; + +static int MPSKChannel[MAXBPQPORTS+1]; // BPQ Port to MPSK Port +static int BPQPort[MAXMPSKPORTS][MAXBPQPORTS+1]; // MPSK Port and Connection to BPQ Port +//static int MPSKtoBPQ_Q[MAXBPQPORTS+1]; // Frames for BPQ, indexed by BPQ Port +//static int BPQtoMPSK_Q[MAXBPQPORTS+1]; // Frames for MPSK. indexed by MPSK port. Only used it TCP session is blocked + +// Each port may be on a different machine. We only open one connection to each MPSK instance + +static char * MPSKSignon[MAXBPQPORTS+1]; // Pointer to message for secure signin + +static unsigned int MPSKInst = 0; +static int AttachedProcesses=0; + +static HWND hResWnd,hMHWnd; +static BOOL GotMsg; + +static HANDLE STDOUT=0; + +//SOCKET sock; + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; +static SOCKADDR_IN destaddr[MAXBPQPORTS+1]; + +static int addrlen=sizeof(sinx); + +//static short MPSKPort=0; + +static time_t ltime,lasttime[MAXBPQPORTS+1]; + +static BOOL CONNECTING[MAXBPQPORTS+1]; +static BOOL CONNECTED[MAXBPQPORTS+1]; + +//HANDLE hInstance; + +static char WindowTitle[] = "FLDIGI"; +static char ClassName[] = "FLDIGISTATUS"; +static int RigControlRow = 165; + +static fd_set readfs; +static fd_set writefs; +static fd_set errorfs; +static struct timeval timeout; + +int Blocksizes[10] = {0,2,4,8,16,32,64,128,256,512}; + + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + PMSGWITHLEN buffptr; + char txbuff[500]; + unsigned int txlen=0; + struct TNCINFO * TNC = TNCInfo[port]; + int Stream = 0; + struct STREAMINFO * STREAM; + int TNCOK; + size_t Param; + + if (TNC == NULL) + return 0; // Port not defined + + // Look for attach on any call + +// for (Stream = 0; Stream <= 1; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) + { + char Cmd[80]; + + // New Attach + + int calllen; + STREAM->Attached = TRUE; + + TNC->FLInfo->RAW = FALSE; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); + STREAM->MyCall[calllen] = 0; + STREAM->FramesOutstanding = 0; + + SuspendOtherPorts(TNC); // Dont allow connects on interlocked ports + + // Stop Scanning + + sprintf(Cmd, "%d SCANSTOP", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Cmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +/* len = sprintf(Cmd, "%cSTOP_BEACON_ARQ_FAE\x1b", '\x1a'); + + if (TNC->MPSKInfo->TX) + TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting + else + SendPacket(TNC->TCPDataSock, Cmd, len, 0); +*/ + } + } + + switch (fn) + { + case 7: + + // 100 mS Timer. + + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + + // See if waiting for busy to clear before sending a connect + + if (TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send connect + + struct ARQINFO * ARQ = TNC->ARQInfo; + int SendLen; + char Reply[80]; + + SendLen = sprintf(Reply, "c%s:42 %s:24 %c 7 T60R5W10", + STREAM->MyCall, STREAM->RemoteCall, ARQ->OurStream); + + strcpy(TNC->WEB_PROTOSTATE, "Connecting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + ARQ->ARQState = ARQ_ACTIVE; + + ARQ->ARQTimerState = ARQ_CONNECTING; + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + + STREAM->Connecting = TRUE; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Connecting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len=39; + memcpy(buffptr+2,"Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + TidyClose(TNC, 0); + } + } + + ARQTimer(TNC); + SendXMLPoll(TNC); + + TNC->SlowTimer--; + + if (TNC->SlowTimer < 0) + { + TNC->SlowTimer = 100; + FLSlowTimer(TNC); // 10 Secs + } + + return 0; + + case 1: // poll + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE && TNC->FLInfo->KISSMODE == FALSE) + { + // See if time to reconnect + + time( <ime ); + if (ltime-lasttime[port] >9 ) + { + ConnecttoFLDigi(port); + lasttime[port]=ltime; + } + } +pollloop: + FD_ZERO(&readfs); + + if (TNC->CONNECTED) + if (TNC->TCPSock) + FD_SET(TNC->TCPSock,&readfs); + + if (TNC->CONNECTED || TNC->FLInfo->KISSMODE) + FD_SET(TNC->TCPDataSock,&readfs); + + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + FD_ZERO(&errorfs); + + if (TNC->CONNECTED) + if (TNC->TCPSock) + FD_SET(TNC->TCPSock,&errorfs); + + if (TNC->CONNECTED || TNC->FLInfo->KISSMODE) + FD_SET(TNC->TCPDataSock,&errorfs); + + + if (select((int)TNC->TCPDataSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPDataSock,&readfs)) + { + // data available + + ProcessReceivedData(port); + goto pollloop; + } + + if (FD_ISSET(TNC->TCPSock,&readfs)) + { + // data available + + ProcessXMLData(port); + } + + + if (FD_ISSET(TNC->TCPDataSock,&writefs)) + { + // Connect success + + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // If required, send signon + +// SendPacket(TNC->TCPDataSock,"\x1a", 1, 0); +// SendPacket(TNC->TCPDataSock,"DIGITAL MODE ?", 14, 0); +// SendPacket(TNC->TCPDataSock,"\x1b", 1, 0); + +// EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); + } + + if (FD_ISSET(TNC->TCPDataSock,&errorfs) || FD_ISSET(TNC->TCPSock,&errorfs)) + { + // if connecting, then failed, if connected then has just disconnected + +// if (CONNECTED[port]) +// if (!CONNECTING[port]) +// { +// i=sprintf(ErrMsg, "MPSK Connection lost for BPQ Port %d\r\n", port); +// WritetoConsole(ErrMsg); +// } + + CONNECTING[port]=FALSE; + CONNECTED[port]=FALSE; + + } + + } + + + + // See if any frames for this port + + for (Stream = 0; Stream <= 1; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + if (STREAM->ReportDISC) + { + STREAM->ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + + // if Busy, send buffer status poll + + if (STREAM->PACTORtoBPQ_Q == 0) + { + if (STREAM->DiscWhenAllSent) + { + STREAM->DiscWhenAllSent--; + if (STREAM->DiscWhenAllSent == 0) + STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion + } + } + else + { + int datalen; + + buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += (MSGHDDRLEN + 1); + + PutLengthinBuffer(buff, datalen); + + WritetoTrace(TNC, &buffptr->Data[0], datalen - (MSGHDDRLEN + 1)); + ReleaseBuffer(buffptr); + + return (1); + } + } + + if (TNC->PortRecord->UI_Q) + { + struct _MESSAGE * buffptr; + int SendLen; + char Reply[256]; + int UILen; + char * UIMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + UILen = buffptr->LENGTH; + UILen -= 23; + UIMsg = buffptr->L2DATA; + + UIMsg[UILen] = 0; + + if (UILen < 129 && TNC->Streams[0].Attached == FALSE) // Be sensible! + { + // >00uG8BPQ:72 TestA + SendLen = sprintf(Reply, "u%s:72 %s", TNC->NodeCall, UIMsg); + SendPacket(TNC, Reply, SendLen); + } + ReleaseBuffer(buffptr); + } + + return (0); + + case 2: // send + + + if (!TNC->CONNECTED) return 0; // Don't try if not connected to TNC + + Stream = buff->PORT; + + STREAM = &TNC->Streams[Stream]; + +// txlen=(buff[6]<<8) + buff[5] - 8; + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - (MSGHDDRLEN + 1); // 1 as no PID; + + if (STREAM->Connected) + { + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = txlen; + memcpy(buffptr->Data, buff->L2DATA, txlen); + + WritetoTrace(TNC, buffptr->Data, txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + + return (0); + } + else + { + buff->L2DATA[txlen] = 0; + _strupr(&buff->L2DATA[0]); + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0) + { + if (STREAM->Connected) + TidyClose(TNC, buff->PORT); + + STREAM->ReportDISC = TRUE; // Tell Node + + TNC->FLInfo->MCASTMODE = FALSE; + + return 0; + } + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + } + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "MODEM ", 6) == 0) + { + buff->L2DATA[txlen -1] = 0; + _strupr(&buff->L2DATA[0]); + + // If in KISS mode, send as a KISS command Frame + + if (TNC->FLInfo->KISSMODE) + { + sprintf(txbuff, "MODEM:%s MODEM:", &buff->L2DATA[6]); + SendKISSCommand(TNC, txbuff); + } + else + { + SendXMLCommand(TNC, "modem.set_by_name", &buff->L2DATA[6], 'S'); + } + + TNC->InternalCmd = TRUE; + return 1; + } + + if (_memicmp(buff->L2DATA, "FREQ ", 5) == 0) + { + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + // If in KISS mode, send as a KISS command Frame + + if (TNC->FLInfo->KISSMODE) + { + sprintf(txbuff, "WFF:%s WFF:", &buff->L2DATA[5]); + SendKISSCommand(TNC, txbuff); + } + else + { + SendXMLCommandInt(TNC, "modem.set_carrier", atoi(&buff->L2DATA[5]), 'I'); + } + + TNC->InternalCmd = TRUE; + return 1; + } + + if (_memicmp(buff->L2DATA, "SQUELCH ", 8) == 0) + { + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + // Only works in KISS + + if (TNC->FLInfo->KISSMODE) + { + if (_memicmp(&buff->L2DATA[8], "ON", 2) == 0) + sprintf(txbuff, "KPSQL:ON KPSQL:"); + + else if (_memicmp(&buff->L2DATA[8], "OFF", 3) == 0) + sprintf(txbuff, "KPSQL:OFF KPSQL:"); + else + txlen = sprintf(txbuff, "KPSQLS:%s KPSQLS:", &buff->L2DATA[8]); + + SendKISSCommand(TNC, txbuff); + TNC->InternalCmd = TRUE; + } + return 1; + } + + if (_memicmp(buff->L2DATA, "KPSATT ", 7) == 0) + { + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + // If in KISS mode, send as a KISS command Frame + + if (TNC->FLInfo->KISSMODE) + { + sprintf(txbuff, "KPSATT:%s KPSATT:", &buff->L2DATA[7]); + + SendKISSCommand(TNC, txbuff); + TNC->InternalCmd = TRUE; + } + + return 1; + } + + if (STREAM->Connecting && _memicmp(buff->L2DATA, "ABORT", 5) == 0) + { +// len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a'); + +// if (TNC->MPSKInfo->TX) +// TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting +// else +// SendPacket(TNC->TCPDataSock, Command, len, 0); + +// TNC->InternalCmd = TRUE; + return (0); + } + + if (_memicmp(&buff->L2DATA[0], "MODE", 4) == 0) + { + PMSGWITHLEN buffptr = GetBuff(); + + buff->L2DATA[txlen - 1] = 0; // Remove CR + + if (strstr(&buff->L2DATA[0], "RAW")) + TNC->FLInfo->RAW = TRUE; + else if (strstr(&buff->L2DATA[0], "KISS")) + TNC->FLInfo->RAW = FALSE; + else + { + buffptr->Len = sprintf(&buffptr->Data[0], "FLDigi} Error - Invalid Mode\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return 1; + } + + buffptr->Len = sprintf(&buffptr->Data[0], "FLDigi} Ok - Mode is %s\r", + (TNC->FLInfo->RAW)?"RAW":"KISS"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "MCAST", 5) == 0) + { + PMSGWITHLEN buffptr = GetBuff(); + + TNC->FLInfo->MCASTMODE = TRUE; + + buffptr->Len = sprintf(buffptr->Data, "FLDigi} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + if (_memicmp(&buff->L2DATA[0], "INUSE?", 6) == 0) + { + // Return Error if in use, OK if not + + PMSGWITHLEN buffptr = GetBuff(); + int s = 0; + + while(s <= 1) + { + if (s != Stream) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[s]) + { + buffptr->Len = sprintf(buffptr->Data, "FLDig} Error - In use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return 1; // Busy + } + } + s++; + } + buffptr->Len = sprintf(buffptr->Data, "FLDigi} Ok - Not in use\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + return 1; + } + + // See if a Connect Command. + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char * ptr; + char * context; + struct ARQINFO * ARQ = TNC->ARQInfo; + int SendLen; + char Reply[80]; + + buff->L2DATA[txlen] = 0; + _strupr(&buff->L2DATA[0]); + + memset(ARQ, 0, sizeof(struct ARQINFO)); // Reset ARQ State + ARQ->TXSeq = ARQ->TXLastACK = 63; // Last Sent + ARQ->RXHighest = ARQ->RXNoGaps = 63; // Last Received + ARQ->OurStream = (rand() % 78) + 49; // To give some protection against other stuff on channel + ARQ->FarStream = 48; // Not yet defined + TNC->FLInfo->FLARQ = FALSE; + + memset(STREAM->RemoteCall, 0, 10); + + ptr = strtok_s(&buff->L2DATA[2], " ,\r", &context); + + if (ptr == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, + "FLDigi} Error - Call missing from C command\r"); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + STREAM->DiscWhenAllSent = 10; + return 0; + } + + strcpy(STREAM->RemoteCall, ptr); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + +//00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 + + SendLen = sprintf(Reply, "c%s:42 %s:24 %c 7 T60R5W10", + STREAM->MyCall, STREAM->RemoteCall, ARQ->OurStream); + + strcpy(TNC->WEB_PROTOSTATE, "Connecting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + ARQ->ARQState = ARQ_ACTIVE; + + ARQ->ARQTimerState = ARQ_CONNECTING; + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + + Debugprintf("FLDIGI Connection %s", Reply); + + STREAM->Connecting = TRUE; + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Connecting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return 0; + } + + // Send any other command to FLDIGI + + buff->L2DATA[txlen - 1] = 0; + _strupr(&buff->L2DATA[0]); + + // If in KISS mode, send as a KISS command Frame + + if (TNC->FLInfo->KISSMODE) + { + char outbuff[1000]; + int newlen; + + buff->L2DATA[-1] = 6; // KISS Control + + newlen = KissEncode(&buff->L2DATA[-1], outbuff, txlen); + sendto(TNC->TCPDataSock, outbuff, newlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr)); + } + else + { + SendXMLCommand(TNC, "modem.set_by_name", &buff->L2DATA[0], 'S'); + } + + TNC->InternalCmd = TRUE; + } + + return (0); + + case 3: + + Stream = (int)(size_t)buff; + + TNCOK = TNC->CONNECTED; + + STREAM = &TNC->Streams[Stream]; + { + // Busy if TX Window reached + + struct ARQINFO * ARQ = TNC->ARQInfo; + int Outstanding; + + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&TNC->Streams[0].BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (Outstanding > ARQ->TXWindow) + return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); // 3rd Nibble is frames unacked + else + return TNCOK << 8 | STREAM->Disconnecting << 15; + + } + return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting + + case 4: // reinit + + shutdown(TNC->TCPSock, SD_BOTH); + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->CONNECTED = FALSE; + + if (TNC->WeStartedTNC) + { + KillTNC(TNC); + RestartTNC(TNC); + } + + return (0); + + case 5: // Close + + shutdown(TNC->TCPSock, SD_BOTH); + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + if (TNC->WeStartedTNC) + { + KillTNC(TNC); + } + + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (shouldn't happen) + { + Debugprintf("Scan Check Permission called on FLDIGI"); + return 1; // OK to change + } + + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + + if (Param == 1) // Request Permission + { + if (TNC->ConnectPending == 0) + { + TNC->FLInfo->CONOK = FALSE; + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + if (!TNC->ConnectPending) + return 0; // OK to Change + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->FLInfo->CONOK = TRUE; + TNC->GavePermission = FALSE; + } + return 0; + } + } + + return 0; +} + +#ifndef LINBPQ + +int FindFLDIGI(char * Path) +{ + HANDLE hProc; + char ExeName[256] = ""; + char FLDIGIName[256]; + DWORD Pid = 0; + DWORD Processes[1024], Needed, Count; + unsigned int i; + + if (EnumProcessesPtr == NULL) + return 0; // Cant get PID + + if (!EnumProcessesPtr(Processes, sizeof(Processes), &Needed)) + return TRUE; + + // Path is to .bat, so need to strip extension of both names + + strcpy(FLDIGIName, Path); + strlop(FLDIGIName, '.'); + + // Calculate how many process identifiers were returned. + + Count = Needed / sizeof(DWORD); + + for (i = 0; i < Count; i++) + { + if (Processes[i] != 0) + { + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Processes[i]); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + strlop(ExeName, '.'); + + if (_stricmp(ExeName, FLDIGIName) == 0) + return Processes[i]; + + } + } + } + return 0; +} + + +static KillTNC(struct TNCINFO * TNC) +{ + HANDLE hProc; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->ProgramPath) + TNC->PID = FindFLDIGI(TNC->ProgramPath); + + if (TNC->PID == 0) return 0; + + hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + TerminateProcess(hProc, 0); + CloseHandle(hProc); + } + + TNC->WeStartedTNC = 0; // So we don't try again + + return 0; +} + +#endif + +static int RestartTNC(struct TNCINFO * TNC) +{ + if (TNC->ProgramPath == NULL) + return 0; + + _strlwr(TNC->ProgramPath); + + if (_memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0) + { + int n; + + // Try to start TNC on a remote host + + SOCKET sock = socket(AF_INET,SOCK_DGRAM,0); + struct sockaddr_in destaddr; + + Debugprintf("trying to restart FLDIGI %s", TNC->ProgramPath); + + if (sock == INVALID_SOCKET) + return 0; + + destaddr.sin_family = AF_INET; + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_port = htons(8500); + + if (destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + struct hostent * HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + return 0; // Resolve failed + + memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + n = sendto(sock, TNC->ProgramPath, (int)strlen(TNC->ProgramPath), 0, (struct sockaddr *)&destaddr, sizeof(destaddr)); + + Debugprintf("Restart FLDIGI - sento returned %d", n); + + Sleep(100); + closesocket(sock); + + return 1; // Cant tell if it worked, but assume ok + } +#ifndef LINBPQ + { + STARTUPINFO SInfo; // pointer to STARTUPINFO + PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION + char HomeDir[MAX_PATH]; + int i, ret; + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + if (TNC->ProgramPath) + { + strcpy(HomeDir, TNC->ProgramPath); + i = strlen(HomeDir); + + while(--i) + { + if (HomeDir[i] == '/' || HomeDir[i] == '\\') + { + HomeDir[i] = 0; + break; + } + } + + // for some reason the program name must be lower case + + _strlwr(TNC->ProgramPath); + + ret = CreateProcess(TNC->ProgramPath, NULL, NULL, NULL, FALSE,0 ,NULL , NULL, &SInfo, &PInfo); + return ret; + } + } +#endif + return 0; +} + + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, "" + "" + "FLDigi Status" + "

FLDIGI Status

"); + + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Channel State%s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + +VOID FLDIGISuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ + TNC->FLInfo->CONOK = FALSE; +} + +VOID FLDIGIReleasePort(struct TNCINFO * TNC) +{ + TNC->FLInfo->CONOK = TRUE; +} + +VOID SendKISSCommand(struct TNCINFO * TNC, char * Msg) +{ + int txlen, rc; + char txbuff[256]; + char outbuff[256]; + + txlen = sprintf(txbuff, "%c%s", 6, Msg); + txlen = KissEncode(txbuff, outbuff, txlen); + rc = sendto(TNC->TCPDataSock, outbuff, txlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr)); +} + +VOID * FLDigiExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + struct TNCINFO * TNC; + char * ptr; + + // + // The Socket to connect to is in IOBASE + // + + srand((unsigned int)time(NULL)); + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->PortRecord = PortEntry; + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + PortEntry->PORTCONTROL.UICAPABLE = 1; // Can send beacons + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = SIMPLE; + + // Scan Control - None + + TNC->FLInfo->CONOK = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 250; + + TNC->SuspendPortProc = FLDIGISuspendPort; + TNC->ReleasePortProc = FLDIGIReleasePort; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_FLDIGI; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + MPSKChannel[port] = PortEntry->PORTCONTROL.CHANNELNUM-65; + + PortEntry->MAXHOSTMODESESSIONS = 1; + + i=sprintf(Msg,"FLDigi Host %s Port %d \n", + TNC->HostName, TNC->TCPPort); + + WritetoConsole(Msg); + +#ifndef LINBPQ + + if (TNC->ProgramPath) + TNC->PID = FindFLDIGI(TNC->ProgramPath); + + if (TNC->PID == 0) // Not running +#endif + TNC->WeStartedTNC = RestartTNC(TNC); // Always try if Linux + + if (TNC->FLInfo->KISSMODE) + { + // Open Datagram port + + SOCKET sock; + u_long param=1; + BOOL bcopt=TRUE; + struct sockaddr_in sinx; + struct hostent * HostEnt = NULL; + + TNC->FLInfo->CmdControl = 5; //Send params immediately + + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->Datadestaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (HostEnt) + { + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + } + + TNC->TCPDataSock = sock = socket(AF_INET,SOCK_DGRAM,0); + + ioctl(sock, FIONBIO, ¶m); + + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt,4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = htons(TNC->TCPPort + 1); + + if (bind(sock, (struct sockaddr *) &sinx, sizeof(sinx)) != 0 ) + { + // Bind Failed + + int err = WSAGetLastError(); + Consoleprintf("Bind Failed for UDP port %d - error code = %d", TNC->TCPPort, err); + } + + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(TNC->TCPPort); + } + else + ConnecttoFLDigi(port); + + time(&lasttime[port]); // Get initial time value + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(50); + TNC->WEB_TRAFFIC = zalloc(100); + + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode/CF", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0 Resent 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill FLDigi"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart FLDigi"); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + MoveWindows(TNC); +#endif + + return ExtProc; + +} + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + struct ARQINFO * ARQ; + struct FLINFO * FL; + + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (_stricmp(buf, "ADDR")) + return FALSE; // Must start with ADDR + + ptr = strtok(NULL, " \t\n\r"); + + BPQport = Port; + p_ipad = ptr; + + TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO)); + + ARQ = TNC->ARQInfo = zalloc(sizeof(struct ARQINFO)); + FL = TNC->FLInfo = zalloc(sizeof(struct FLINFO)); + + TNC->Timeout = 50; // Default retry = 5 seconds + TNC->Retries = 6; // Default Retries + TNC->Window = 16; + + TNC->FLInfo->KISSMODE = TRUE; // Default to KISS + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->TCPPort = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(TNC->TCPPort + 40); // Defaults XML 7362 ARQ 7322 + + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(TNC->TCPPort); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "TIMEOUT", 7) == 0) + TNC->Timeout = atoi(&buf[8]) * 10; + else + if (_memicmp(buf, "RETRIES", 7) == 0) + TNC->Retries = atoi(&buf[8]); + else + if (_memicmp(buf, "WINDOW", 6) == 0) + TNC->Window = atoi(&buf[7]); + else + if (_memicmp(buf, "ARQMODE", 7) == 0) + TNC->FLInfo->KISSMODE = FALSE; + else + if (_memicmp(buf, "DEFAULTMODEM", 12) == 0) // Send Beacon after each session + { + // Check that freq is also specified + + char * Freq = strchr(&buf[13], '/'); + + if (Freq) + { + *(Freq++) = 0; + strcpy(TNC->FLInfo->DefaultMode, &buf[13]); + TNC->FLInfo->DefaultFreq = atoi(Freq); + } + } + else + + strcat (TNC->InitScript, buf); + } + + + return (TRUE); +} + +int ConnecttoFLDigi(int port) +{ + _beginthread(ConnecttoFLDigiThread, 0, (void *)(size_t)port); + + return 0; +} + +VOID ConnecttoFLDigiThread(void * portptr) +{ + int port = (int)(size_t)portptr; + char Msg[255]; + int err,i; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt = NULL; + struct TNCINFO * TNC = TNCInfo[port]; + + Sleep(5000); // Allow init to complete + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) return; // Resolve failed + + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + if (TNC->TCPSock) + { + Debugprintf("FLDIGI Closing Sock %d", TNC->TCPSock); + closesocket(TNC->TCPSock); + } + + TNC->TCPSock = 0; + + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FLDigi Control socket - error code = %d\n", WSAGetLastError()); + WritetoConsole(Msg); + return; + } + + setsockopt (TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + TNC->CONNECTING = TRUE; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for FLDigi Control socket - error code = %d\n", err); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + TNC->LastFreq = 0; + + if (TNC->TCPDataSock) + closesocket(TNC->TCPDataSock); + + TNC->TCPDataSock = 0; + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + setsockopt (TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for FLDigi socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + + return; + } + + if (bind(TNC->TCPDataSock, (LPSOCKADDR) &sinx, addrlen) != 0 ) + { + // + // Bind Failed + // + + i=sprintf(Msg, "Bind Failed for FLDigi Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + ioctlsocket (TNC->TCPDataSock,FIONBIO,¶m); // Set nonblocking + TNC->CONNECTED = TRUE; + TNC->CONNECTING = FALSE; + + TNC->Alerted = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + else + { + sprintf(Msg, "Connect Failed for FLDigi Data socket Port %d - error code = %d\r\n", port, WSAGetLastError()); + WritetoConsole(Msg); + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + } + + return; +} + +VOID UpdateStatsLine(struct TNCINFO * TNC, struct STREAMINFO * STREAM) +{ + sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d Resent %d Queued %d", + STREAM->bytesRXed, STREAM->bytesTXed, STREAM->BytesAcked, STREAM->BytesResent, STREAM->BytesOutstanding); + SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); +} + +VOID SendPacket(struct TNCINFO * TNC, UCHAR * Msg, int MsgLen) +{ + if (TNC->FLInfo->KISSMODE) + { + char KissMsg[1000]; + char outbuff[1000]; + int newlen; + + if (TNC->FLInfo->RAW) + { + // KISS RAW + + // Add CRC and Send + + unsigned short CRC; + char crcstring[6]; + + KissMsg[0] = 7; // KISS Raw + KissMsg[1] = 1; // SOH + KissMsg[2] = '0'; // Version + KissMsg[3] = TNC->ARQInfo->FarStream; + + Msg[MsgLen] = 0; + + memcpy(&KissMsg[4], Msg, MsgLen +1 ); // Get terminating NULL + + CRC = CalcCRC(KissMsg + 1, MsgLen + 3); + + sprintf(crcstring, "%04X%c", CRC, 4); + + strcat(KissMsg, crcstring); + MsgLen += 9; + } + else + { + // Normal KISS + + KissMsg[0] = 0; // KISS Control + KissMsg[1] = TNC->ARQInfo->FarStream; + memcpy(&KissMsg[2], Msg, MsgLen); + MsgLen += 2; + } + + newlen = KissEncode(KissMsg, outbuff, MsgLen); + sendto(TNC->TCPDataSock, outbuff, newlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr)); + + SendKISSCommand(TNC, "TXBUF:"); + + } + else + { + // ARQ Scoket + + // Add Header, CRC and Send + + unsigned short CRC; + char crcstring[6]; + char outbuff[1000]; + + outbuff[0] = 1; // SOH + outbuff[1] = '0'; // Version + outbuff[2] = TNC->ARQInfo->FarStream; + + Msg[MsgLen] = 0; + + memcpy(&outbuff[3], Msg, MsgLen + 1); + + CRC = CalcCRC(outbuff , MsgLen + 3); + + sprintf(crcstring, "%04X%c", CRC, 4); + + strcat(outbuff, crcstring); + MsgLen += 8; + + send(TNC->TCPDataSock, outbuff, MsgLen, 0); + } +} + +VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, char Channel, BOOL RAW); + +static int ProcessReceivedData(int port) +{ + int bytes, used, bytesleft; + int i; + char ErrMsg[255]; + unsigned char MessageBuff[1500]; + unsigned char * Message = MessageBuff; + unsigned char * MessageBase = MessageBuff; + + struct TNCINFO * TNC = TNCInfo[port]; + struct FLINFO * FL = TNC->FLInfo; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + // If using KISS/UDP interface use recvfrom + + if (FL->KISSMODE) + { + struct sockaddr_in rxaddr; + int addrlen = sizeof(struct sockaddr_in); + unsigned char * KissEnd; + + bytesleft = recvfrom(TNC->TCPDataSock, Message, 1500, 0, (struct sockaddr *)&rxaddr, &addrlen); + + if (bytesleft < 0) + { + int err = WSAGetLastError(); + // if (err != 11) + // printf("KISS Error %d %d\n", nLength, err); + bytes = 0; + } + + while (bytesleft > 0) + { + unsigned char * in; + unsigned char * out; + unsigned char c; + + if (bytesleft < 3) + return 0; + + if (Message[0] != FEND) + return 0; // Duff + + Message = MessageBase; + in = out = &Message[2]; + + // We may have more than one KISS message in a packet + + KissEnd = memchr(&Message[2], FEND, bytesleft ); + + if (KissEnd == 0) + return 0; // Duff + + *(KissEnd) = 0; + + used = (int)(KissEnd - Message + 1); + + bytesleft -= used; + bytes = used; + + MessageBase += used; + + if (Message[1] == 6) // KISS Command + { + UCHAR * ptr = strchr(&Message[2], FEND); + + if (ptr) *ptr = 0; // Null Terminate + + if (bytes > 250) + Message[250] = 0; + + FL->Responding = 5; + + if (TNC->TNCOK == 0) + { + TNC->TNCOK = TRUE; + TNC->CONNECTED = TRUE; + + sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + + // Trap BUSY fiest - there are lots of them, and they are likely to be confused + // with tesponses to Interactive commands + + if (memcmp(&Message[2], "BUSY", 4) == 0) + { + BOOL Changed = FALSE; + + if (Message[7] == 'T' && FL->Busy == FALSE) + { + TNC->Busy = FL->Busy = TRUE; + Changed = TRUE; + } + else + { + if (Message[7] == 'F' && FL->Busy == TRUE) + { + TNC->Busy = FL->Busy = FALSE; + Changed = TRUE; + } + } + + if (Changed) + { + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + + continue; + } + + if (TNC->InternalCmd) + { + PMSGWITHLEN buffptr = GetBuff(); + + TNC->InternalCmd = FALSE; + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "FLDIGI} Ok %s\r", &Message[2]); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + + // Drop through in case need to extract info from command + } + + // Auto Command + +// Debugprintf("%d %s", TNC->PortRecord->PORTCONTROL.PORTNUMBER, &Message[2]); + + if (memcmp(&Message[2], "FLSTAT", 4) == 0) + { + if (strstr(&Message[2], "FLSTAT:INIT")) + { + // FLDIGI Reloaded - set parmas + SendKISSCommand(TNC, "RSIDBCAST:ON TRXSBCAST:ON TXBEBCAST:ON KISSRAW:ON"); + } + continue; + } + + if (memcmp(&Message[2], "TRXS", 4) == 0) + { + char * ptr1, * context; + BOOL Changed = FALSE; + + ptr1 = strtok_s(&Message[7], ",", &context); + + if (strstr(ptr1, "TX")) + { + if (TNC->FLInfo->TX == FALSE) + { + TNC->FLInfo->TX = TRUE; + Changed = TRUE; + } + } + else + { + if (TNC->FLInfo->TX) + { + TNC->FLInfo->TX = FALSE; + Changed = TRUE; + } + } + + if (Changed) + { + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + + continue; + } + + if (memcmp(&Message[2], "TXBUF:", 6) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[8], ",", &context); + STREAM->BytesOutstanding = atoi(ptr1); + UpdateStatsLine(TNC, STREAM); + continue; + } + + if (memcmp(&Message[2], "TXBE:", 5) == 0) + { + STREAM->BytesOutstanding = 0; + UpdateStatsLine(TNC, STREAM); + continue; + } + + if (memcmp(&Message[2], "RSIDN:", 6) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[8], ",", &context); + + TNC->FLInfo->CenterFreq = atoi(ptr1); + ptr1 = strtok_s(NULL, ",", &context); + if (strlen(ptr1) > 19) + ptr1[19] = 0; + + strcpy(TNC->FLInfo->CurrentMode, ptr1); + + TNC->ConnectPending = 3; // Lock Scan for 3 + } + + if (memcmp(&Message[2], "MODEM:", 6) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[8], ",", &context); + if (strlen(ptr1) > 19) + ptr1[19] = 0; + + strcpy(TNC->FLInfo->CurrentMode, ptr1); + } + + if (memcmp(&Message[2], "WFF:", 4) == 0) + { + char * ptr1, * context; + + ptr1 = strtok_s(&Message[6], ",", &context); + TNC->FLInfo->CenterFreq = atoi(ptr1); + } + + sprintf(TNC->WEB_MODE, "%s/%d", TNC->FLInfo->CurrentMode, TNC->FLInfo->CenterFreq); + SetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE); + + continue; + } + + if (Message[1] == 7) // Not Normal Data + { + // "RAW" Mode. Just process as if received from TCP Socket Interface + +// Debugprintf("7 %d %s", TNC->PortRecord->PORTCONTROL.PORTNUMBER, &Message[2]); + ProcessFLDigiPacket(TNC, &Message[2] , bytes - 3); // Data may be for another port + continue; + } + + bytes -= 3; // Two FEND and Control + +// Debugprintf("0 %d %s", TNC->PortRecord->PORTCONTROL.PORTNUMBER, &Message[2]); + + // Undo KISS + + while (bytes) + { + bytes--; + + c = *(in++); + + if (c == FESC) + { + c = *(in++); + bytes--; + + if (c == TFESC) + c = FESC; + else if (c == TFEND) + c = FEND; + } + *(out++) = c; + } + ProcessFLDigiData(TNC, &Message[3], (int)(out - &Message[3]), Message[2], FALSE); // KISS not RAW + } + + return 0; + } + + // Need to extract messages from byte stream + + bytes = recv(TNC->TCPDataSock, Message, 500, 0); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for MPSK socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPDataSock); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "FlDigi Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + // Have some data + + ProcessFLDigiPacket(TNC, Message, bytes); // Data may be for another port + + return (0); + +} + + +VOID ProcessFLDigiPacket(struct TNCINFO * TNC, char * Message, int Len) +{ + char * MPTR = Message; + char c; + struct FLINFO * FL = TNC->FLInfo; + + + if (TNC->FLInfo->MCASTMODE) + { + if (TNC->Streams[0].Attached == 0) + return; + + while(Len) + { + c = *(MPTR++); + + if (TNC->InPacket) + { + TNC->DataBuffer[TNC->DataLen++] = c; + + // Sanity Check + + if (TNC->DataLen == 6) + { + char * ptr = &TNC->DataBuffer[1]; + + if (memcmp(ptr, "DATA ", 5) == 0 || + memcmp(ptr, "PROG ", 5) == 0 || + memcmp(ptr, "FILE ", 5) == 0 || + memcmp(ptr, "SIZE ", 5) == 0 || + memcmp(ptr, "DESC ", 5) == 0 || + memcmp(ptr, "CNTL ", 5) == 0 || + memcmp(ptr, "ID ", 3) == 0) + + { + } + else + { + // False Trigger, try again + + TNC->InPacket = FALSE; + } + + } + else + { + if (TNC->InData) + { + if (--TNC->MCASTLen == 0) + { + // Got a packet + + PMSGWITHLEN buffptr; + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + buffptr = GetBuff(); + + if (buffptr) + { + TNC->DataBuffer[TNC->DataLen++] = 13; // Keep Tidy + + buffptr->Len = TNC->DataLen; + memcpy(&buffptr[2], &TNC->DataBuffer[0], TNC->DataLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + TNC->InPacket = FALSE; + } + } + else + { + // Looking for > + + if (TNC->DataLen == 16) + { + // Not found it + + TNC->InPacket = FALSE; + } + else + { + if (c == '>') + { + // Got Header - extract Length + + char * ptr; + int len; + + ptr = strchr(TNC->DataBuffer, ' '); + + if (ptr) + { + len = atoi(ptr); + + if (len) + { + TNC->InData = TRUE; + TNC->MCASTLen = len; + } + } + } + } + } + } + + if (TNC->DataLen > 520) + TNC->DataLen--; // Protect Buffer + } + else + { + // Look for '<' + + if (c == '<') + { + TNC->DataBuffer[0] = c; + TNC->DataLen = 1; + TNC->InPacket = TRUE; + TNC->InData = FALSE; + } + } + Len--; + } + return; + } + // Look for SOH/EOT delimiters. May Have several SOH before EOTTNC->FL + + while(Len) + { + c = *(MPTR++); + + switch (c) + { + case 01: // New Packet + + if (TNC->InPacket) + CheckFLDigiData(TNC); + + TNC->DataBuffer[0] = 1; + TNC->DataLen = 1; + TNC->InPacket = TRUE; + break; + + case 04: + + if (TNC->InPacket) + CheckFLDigiData(TNC); + TNC->DataLen = 0; + TNC->InPacket = FALSE; + + break; + + default: + + if (TNC->InPacket) + { + if (TNC->DataLen == 1) + { + if (c != '0' && c != '1') + { + // Drop if not Protocol '0' or '1' - this should eliminate almost all noise packets + + TNC->InPacket = 0; + break; + } + } + TNC->DataBuffer[TNC->DataLen++] = c; + } + + if (TNC->DataLen > 520) + TNC->DataLen--; // Protect Buffer + + } + Len--; + } +} +VOID CheckFLDigiData(struct TNCINFO * TNC) +{ + UCHAR * Input = &TNC->DataBuffer[0]; + int Len = TNC->DataLen - 4; // Not including CRC + unsigned short CRC; + char crcstring[6]; + + if (Len < 0) + return; + + TNC->DataBuffer[TNC->DataLen] = 0; + + // RAW format message, either from ARQ Scoket or RAW KISS + + // Check Checksum + + CRC = CalcCRC(Input , Len); + + sprintf(crcstring, "%04X", CRC); + + if (memcmp(&Input[Len], crcstring, 4) !=0) + { + // CRC Error - could just be noise + +// Debugprintf("%s %s", crcstring, Input); + return; + } + ProcessFLDigiData(TNC, &Input[3], Len - 3, Input[2], TRUE); // From RAW +} +/* +VOID ProcessARQPacket(struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + // ARQ Packet from KISS-Like Hardware + + struct TNCINFO * TNC = TNCInfo[PORT->PORTNUMBER]; + UCHAR * Input; + int Len; + + if (TNC == NULL) + { + // Set up TNC info + + TNC = TNCInfo[PORT->PORTNUMBER] = zalloc(sizeof(struct TNCINFO)); + TNC->ARQInfo = zalloc(sizeof(struct ARQINFO)); + TNC->FLInfo = zalloc(sizeof(struct FLINFO)); + + TNC->Timeout = 50; // Default retry = 10 seconds + TNC->Retries = 6; // Default Retries + TNC->Window = 16; + } + + Input = &Buffer->DEST[0]; + Len = Buffer->LENGTH - 7; // Not including CRC + + // Look for attach on any call + + ProcessFLDigiData(TNC, Input, Len); +} +*/ +static int Stuff(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + UCHAR * ptr = inbuff; + + // DLE Escape DLE, SOH, EOT + + for (i = 0; i < len; i++) + { + c = *(ptr++); + +// if (c == 0 || c == DLE || c == SOH || c == EOT) + if (c < 32 && c != 10 && c != 13 && c != 8) + { + outbuff[txptr++] = DLE; + + // if between 0 and 0x1F, Add 40, + // if > x80 and less than 0xa0 subtract 20 + + c += 0x40; + } + outbuff[txptr++]=c; + } + + return txptr; +} + + +static int UnStuff(UCHAR * inbuff, int len) +{ + int i, txptr = 0; + UCHAR c; + UCHAR * outbuff = inbuff; + UCHAR * ptr = inbuff; + + // This unstuffs into the input buffer + + for (i = 0; i < len; i++) + { + c = *(ptr++); + + if (c == DLE) + { + c = *(ptr++); + i++; + + // if between 0x40 and 0x5F, subtract 0x40, + // else add 0x20 (so we can send chars 80-9f without a double DLE) + + if (c < 0x60) + c -= 0x40; + else + c += 0x20; + } + outbuff[txptr++] = c; + } + + return txptr; +} + +unsigned int crcval = 0xFFFF; + +void update(char c) +{ + int i; + + crcval ^= c & 255; + for (i = 0; i < 8; ++i) + { + if (crcval & 1) + crcval = (crcval >> 1) ^ 0xA001; + else + crcval = (crcval >> 1); + } +} + +unsigned int CalcCRC(UCHAR * ptr, int Len) +{ + int i; + + crcval = 0xFFFF; + for (i = 0; i < Len; i++) + { + update(*ptr++); + } + return crcval; +} +/* + +00cG8BPQ:1025 G8BPQ:24 0 8 T60R6W108E06 +00kG8BPQ:24 G8BPQ 4 85F9B + +00cG8BPQ:1025 GM8BPQ:24 0 7 T60R5W1051D5 (128, 5) + +,00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 +00kG8BPQ:24 G8BPQ 5 89FCA + +First no sees to be a connection counter. Next may be stream + + +08s___ABFC +08tG8BPQ:73 xxx 33FA +00tG8BPQ:73 yyy 99A3 +08dG8BPQ:90986C +00bG8BPQ:911207 + +call:90 for dis 91 for dis ack 73 for chat) + +08pG8BPQ?__645E +00s_??4235 + +08pG8BPQ?__645E +00s_??4235 + +i Ident +c Connect +k Connect Ack +r Connect NAK +d Disconnect req +s Data Ack/ Retransmit Req )status) +p Poll +f Format Fail +b dis ack +t talk + +a Abort +o Abort ACK + + +00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36 +00kG8BPQ:24 G8BPQ 6 49A3A +08s___ABFC +08 ARQ:FILE::flarqmail-1.eml +ARQ:EMAIL:: +ARQ:SIZE::90 +ARQ::STX +//FLARQ COMPOSER +Date: 09/01/2014 23:24:42 +To: gm8bpq +From: +SubjectA0E0 +08!: Test + +Test Message + +ARQ::ETX +F0F2 +08pG8BPQ!__623E +08pG8BPQ!__623E +08pG8BPQ!__623E + + + + +*/ +VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, char Channel, BOOL RAW) +{ + PMSGWITHLEN buffptr; + int Stream = 0; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + char CTRL = Input[0]; + struct ARQINFO * ARQ = TNC->ARQInfo; + struct FLINFO * FL = TNC->FLInfo; + + int SendLen; + char Reply[80]; + + + // Process Message + + // This processes eitrher message from the KISS or RAW interfaces. + // Headers and RAW checksum have been removed, so packet starts with Control Byte + + // Only a connect request is allowed with no session, so check first + + if (CTRL == 'c') + { + // Connect Request + + char * call1; + char * call2; + char * port1; + char * port2; + char * ptr; + char * context; + char FarStream = 0; + int BlockSize = 6; // 64 default + int Window = TNC->Window; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + TRANSPORTENTRY * SESS; + + TNC->ConnectPending = 0; + + if (FL->CONOK == FALSE) + return; + + call1 = strtok_s(&Input[1], " ", &context); + call2 = strtok_s(NULL, " ", &context); + + port1 = strlop(call1, ':'); + port2 = strlop(call2, ':'); + + + // See if for us + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) *ptr = 0; + + if (_stricmp(call2, Appl) == 0) + break; + } + + if (App > 31) + if (strcmp(TNC->NodeCall, call2) !=0) + return; // Not Appl or Port/Node Call + + ptr = strtok_s(NULL, " ", &context); + FarStream = *ptr; + ptr = strtok_s(NULL, " ", &context); + BlockSize = atoi(ptr); + + if (ARQ->ARQState) + { + // We have already received a connect request - just ACK it + + goto AckConnectRequest; + } + + // Get a Session + + SuspendOtherPorts(TNC); + + ProcessIncommingConnect(TNC, call1, 0, TRUE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + strcpy(STREAM->MyCall, call2); + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, call2, TNC->RIG->Valchar); + SESS->Frequency = (atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + SESS->Mode = TNC->WL2KMode; + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, call2); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + SESS->Mode = WL2K->mode; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Connect Pending"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + memset(ARQ, 0, sizeof(struct ARQINFO)); // Reset ARQ State + ARQ->FarStream = FarStream; + ARQ->TXSeq = ARQ->TXLastACK = 63; // Last Sent + ARQ->RXHighest = ARQ->RXNoGaps = 63; // Last Received + ARQ->ARQState = ARQ_ACTIVE; + ARQ->OurStream = (rand() % 78) + 49; // To give some protection against other stuff on channel + ARQ->FarStream = FarStream; // Not Yet defined + if (strcmp(port1, "1025") == 0) + { + FL->FLARQ = TRUE; // From FLARQ + ARQ->OurStream = '8'; // FLARQ Ignores what we send + } + else + FL->FLARQ = FALSE; // From other app (eg BPQ) + + FL->RAW = RAW; + + STREAM->NeedDisc = 0; + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + char Buffer[32]; + int MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(buffptr->Data, Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + STREAM->NeedDisc = 50; // 1 sec + } + } + + ARQ->TXWindow = Window; + + if (BlockSize < 4) BlockSize = 4; + if (BlockSize < 9) BlockSize = 9; + + ARQ->MaxBlock = Blocksizes[BlockSize]; + + + ARQ->ARQTimer = 10; // To force CTEXT to be Queued + + if (STREAM->NeedDisc) + { + // Send Not Avail + + buffptr = GetBuff(); + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "Application Not Available\n"); + SendARQData(TNC, buffptr); + } + } + +AckConnectRequest: + + SendLen = sprintf(Reply, "k%s:24 %s %c 7", call2, call1, ARQ->OurStream); + + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->ARQTimerState = ARQ_CONNECTACK; + + return; + } + + // All others need a session + +// if (!STREAM->Connected && !STREAM->Connecting) +// return; + + if (CTRL == 'k') + { + // Connect ACK + + char * call1; + char * call2; + char * port1; + char * port2; + char * ptr; + char * context; + char FarStream = 0; + int BlockSize = 6; // 64 default + int Window = 16; + + char Reply[80]; + int ReplyLen; + + call1 = strtok_s(&Input[1], " ", &context); + call2 = strtok_s(NULL, " ", &context); + + port1 = strlop(call1, ':'); + port2 = strlop(call2, ':'); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ptr = strtok_s(NULL, " ", &context); + if (ptr) + FarStream = *ptr; + ptr = strtok_s(NULL, " ", &context); + if (ptr) + BlockSize = atoi(ptr); + + if (STREAM->Connected) + goto SendKReply; // Repeated ACK + + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0; + STREAM->Connected = TRUE; + + ARQ->ARQTimerState = 0; + ARQ->ARQTimer = 0; + + if (TNC->RIG) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", STREAM->MyCall, STREAM->RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", STREAM->MyCall, STREAM->RemoteCall); + + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, STREAM->RemoteCall, '+', 'Z'); + + ARQ->ARQTimerState = 0; + ARQ->FarStream = FarStream; + ARQ->TXWindow = TNC->Window; + ARQ->MaxBlock = Blocksizes[BlockSize]; + + ARQ->ARQState = ARQ_ACTIVE; + + STREAM->NeedDisc = 0; + + buffptr = GetBuff(); + + if (buffptr) + { + ReplyLen = sprintf(Reply, "*** Connected to %s\r", STREAM->RemoteCall); + + buffptr->Len = ReplyLen; + memcpy(buffptr->Data, Reply, ReplyLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + +SendKReply: + + // Reply with status + + SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + return; + } + + // All others need a session + + //if (!STREAM->Connected) + // return; + + + if (CTRL == 's') + { + // Status + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ARQ->ARQTimer = 0; // Stop retry timer + Input[Len] = 0; + ProcessARQStatus(TNC, ARQ, &Input[1]); + + return; + } + + if (CTRL == 'p') + { + // Poll + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " \x1A", &context); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + else + ARQ->TurnroundTimer = 15; // Allow us to send it all acked + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + + return; + } + + + if (CTRL == 'a') + { + // Abort. Send Abort ACK - same as + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " :", &context); + + if (strcmp(call1, STREAM->RemoteCall) != 0) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + SendLen = sprintf(Reply, "o%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32); + + if (ARQ->RXHighest != ARQ->RXNoGaps) + { + int n = ARQ->RXNoGaps + 1; + n &= 63; + + while (n != ARQ->RXHighest) + { + if (ARQ->RXHOLDQ[n] == 0) // Dont have it + SendLen += sprintf(&Reply[SendLen], "%c", n + 32); + + n++; + n &= 63; + } + } + + QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + return; + } + + if (CTRL == 'i') + { + // Ident + + return; + } + + if (CTRL == 't') + { + // Talk - pass to node + + char * call1; + char * context; + char * ptr; + + PMSGWITHLEN buffptr; + + Input[Len] = 0; // Removes checksum + + call1 = strtok_s(&Input[1], " ", &context); + strlop(call1, ':'); + + if (strcmp(STREAM->RemoteCall, call1)) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + while (ptr = strchr(context, 10)) + *ptr = 13; + + buffptr->Len = strlen(context); + strcpy(buffptr->Data, context); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + } + + if (CTRL == 'd') + { + // Disconnect Request + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " ", &context); + strlop(call1, ':'); + + if (strcmp(STREAM->RemoteCall, call1)) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + + // As the Disc ACK isn't repeated, we have to clear session now + + STREAM->Connected = FALSE; + STREAM->Connecting = FALSE; + STREAM->ReportDISC = TRUE; + + strcpy(TNC->WEB_PROTOSTATE, "Disconnected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + ARQ->ARQState = 0; + + SendLen = sprintf(Reply, "b%s:91", STREAM->MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->Retries = 2; + return; + } + + if (CTRL == 'b') + { + // Disconnect ACK + + char * call1; + char * context; + + call1 = strtok_s(&Input[1], " ", &context); + strlop(call1, ':'); + + if (strcmp(STREAM->RemoteCall, call1)) + return; + + if (Channel != ARQ->OurStream) + return; // Wrong Session + + ARQ->ARQTimer = 0; + ARQ->ARQTimerState = 0; + ARQ->ARQState = 0; + + if (STREAM->Connected) + { + hookL4SessionDeleted(TNC, STREAM); + } + + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; // Tell Node + + if (STREAM->Disconnecting) // + FLReleaseTNC(TNC); + + STREAM->Disconnecting = FALSE; + + strcpy(TNC->WEB_PROTOSTATE, "Disconnected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; + } + + if (CTRL == 'u') + { + // Beacon + + //>00uGM8BPQ:72 GM8BPQ TestingAD67 + + char * Call = &Input[1]; + strlop(Call, ':'); + + UpdateMH(TNC, Call, '!', 0); + return; + } + + if (STREAM->Connected) + { + if (Channel != ARQ->OurStream) + return; // Wrong Session + + if (CTRL >= ' ' && CTRL < 96) + { + // ARQ Data + + int Seq = CTRL - 32; + int Work; + +// if (rand() % 5 == 2) +// { +// Debugprintf("Dropping %d", Seq); +// return; +// } + + buffptr = GetBuff(); + + if (buffptr == NULL) + return; // Should never run out, but cant do much else + + // Remove any DLE transparency + + if (TNC->FLInfo->KISSMODE) + Len -= 1; + else + Len = UnStuff(&Input[1], Len - 1); + + buffptr->Len = Len; + memcpy(buffptr->Data, &Input[1], Len); + STREAM->bytesRXed += Len; + + UpdateStatsLine(TNC, STREAM); + + // Safest always to save, then see what we can process + + if (ARQ->RXHOLDQ[Seq]) + { + // Wot! Shouldn't happen + + ReleaseBuffer(ARQ->RXHOLDQ[Seq]); +// Debugprintf("ARQ Seq %d Duplicate"); + } + + ARQ->RXHOLDQ[Seq] = buffptr; +// Debugprintf("ARQ saving %d", Seq); + + // If this is higher that highest received, save. But beware of wrap' + + // Hi = 2, Seq = 60 dont save s=h = 58 + // Hi = 10 Seq = 12 save s-h = 2 + // Hi = 14 Seq = 10 dont save s-h = -4 + // Hi = 60 Seq = 2 save s-h = -58 + + Work = Seq - ARQ->RXHighest; + + if ((Work > 0 && Work < 32) || Work < -32) + ARQ->RXHighest = Seq; + + // We may now be able to process some + + Work = (ARQ->RXNoGaps + 1) & 63; // The next one we need + + while (ARQ->RXHOLDQ[Work]) + { + // We have it + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, ARQ->RXHOLDQ[Work]); + + ARQ->RXHOLDQ[Work] = NULL; +// Debugprintf("Processing %d from Q", Work); + + ARQ->RXNoGaps = Work; + Work = (Work + 1) & 63; // The next one we need + } + + ARQ->TurnroundTimer = 200; // Delay before allowing reply. Will normally be reset by the poll following data + return; + } + } +} + + +VOID SendARQData(struct TNCINFO * TNC, PMSGWITHLEN Buffer) +{ + // Send Data, saving a copy until acked. + + struct ARQINFO * ARQ = TNC->ARQInfo; + struct FLINFO * FL = TNC->FLInfo; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + + UCHAR TXBuffer[300]; + SOCKET sock = TNC->TCPDataSock; + int SendLen; + UCHAR * ptr; + int Origlen = Buffer->Len; + int Stuffedlen; + + ARQ->TXSeq++; + ARQ->TXSeq &= 63; + + SendLen = sprintf(TXBuffer, "%c", ARQ->TXSeq + 32); + + ptr = (UCHAR *)&Buffer->Data; // Start of data; + + ptr[Buffer->Len] = 0; + + if (memcmp(ptr, "ARQ:", 4) == 0) + { + // FLARQ Mail/FIle transfer. Turn off CR > LF translate (used for terminal mode) + + FL->FLARQ = FALSE; + } + + if (FL->FLARQ) + { + // Terminal Mode. Need to convert CR to LF so it displays in FLARQ Window + + ptr = strchr(ptr, 13); + + while (ptr) + { + *(ptr++) = 10; // Replace CR with LF + ptr = strchr(ptr, 13); + } + } + + if (TNC->FLInfo->KISSMODE) + { + memcpy(&TXBuffer[SendLen], (UCHAR *)&Buffer->Data, Origlen); + SendLen += Origlen; + } + else + { + Stuffedlen = Stuff((UCHAR *)&Buffer->Data, &TXBuffer[SendLen], Origlen); + SendLen += Stuffedlen; + } + + TXBuffer[SendLen] = 0; + +// if (rand() % 5 == 2) +// Debugprintf("Dropping %d", ARQ->TXSeq); +// else + + ARQ->TXHOLDQ[ARQ->TXSeq] = Buffer; + + STREAM->bytesTXed += Origlen; + + UpdateStatsLine(TNC, STREAM); + + // if waiting for ack, don't send, just queue. Will be sent when ack received + + if (ARQ->ARQTimer == 0 || ARQ->ARQTimerState == ARQ_WAITDATA) + { + SendPacket(TNC, TXBuffer, SendLen); + ARQ->ARQTimer = 15; // wait up to 1.5 sec for more data before polling + ARQ->Retries = 1; + ARQ->ARQTimerState = ARQ_WAITDATA; + } + else + STREAM->BytesResent -= Origlen; // So wont be included in resent bytes +} + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + char Reply[80]; + int SendLen; + + struct ARQINFO * ARQ = TNC->ARQInfo; + + SendLen = sprintf(Reply, "d%s:90", TNC->Streams[0].MyCall); + + SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen); + ARQ->ARQTimerState = ARQ_DISC; + + strcpy(TNC->WEB_PROTOSTATE, "Disconnecting"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // I don't think Hostmode has a DD +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + FLReleaseTNC(TNC); +} + +VOID FLReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + strcpy(TNC->WEB_TNCSTATE, "Free"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // if a default Modem is defined, select it + + if (TNC->FLInfo->DefaultMode[0]) + { + char txbuff[80]; + + if (TNC->FLInfo->KISSMODE) + { + sprintf(txbuff, "WFF:%d MODEM:%s MODEM: WFF:", TNC->FLInfo->DefaultFreq, TNC->FLInfo->DefaultMode); + SendKISSCommand(TNC, txbuff); + } + else + { + SendXMLCommand(TNC, "modem.set_by_name", TNC->FLInfo->DefaultMode, 'S'); + SendXMLCommandInt(TNC, "modem.set_carrier", TNC->FLInfo->DefaultFreq, 'I'); + } + } + // Start Scanner + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + ReleaseOtherPorts(TNC); + +} +VOID QueueAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen) +{ + // Queue to be sent after TXDELAY + + memcpy(ARQ->TXMsg, Msg, MsgLen + 1); + ARQ->TXLen = MsgLen; + ARQ->TXDelay = 15; // Try 1500 ms +} + +VOID SaveAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen) +{ + // Used for Messages that need a reply. Save, send and set timeout + + memcpy(ARQ->LastMsg, Msg, MsgLen + 1); // Include Null + ARQ->LastLen = MsgLen; + + // Delay the send for a short while Just use the timeout code + +// SendPacket(sock, Msg, MsgLen, 0); + ARQ->ARQTimer = 1; // Try 500 ms + ARQ->Retries = TNC->Retries + 1; // First timout is rthe real send + + return; +} + + +VOID ARQTimer(struct TNCINFO * TNC) +{ + struct ARQINFO * ARQ = TNC->ARQInfo; + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + int SendLen; + char Reply[80]; + struct FLINFO * FL = TNC->FLInfo; + + //Send frames, unless held by TurnroundTimer or Window + + int Outstanding; + + // Use new BUSY: poll to detect busy state + + if (FL->TX == FALSE) + if (TNC->FLInfo->KISSMODE) + SendKISSCommand(TNC, "BUSY:"); // Send every poll for now - may need to optimize later + + +/* +// Use Received chars as a rough channel active indicator + + FL->BusyTimer++; + + if (FL->BusyTimer > 4) + { + FL->BusyTimer = 0; + + if (FL->BusyCounter > 2) // 2 chars in last .3 secs + FL->Busy = TRUE; + else + FL->Busy = FALSE; + + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + FL->BusyCounter = 0; + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + +*/ // TXDelay is used as a turn round delay for frames that don't have to be retried. It doesn't + // need to check for busy (or anything else (I think!) + + if (ARQ->TXDelay) + { + ARQ->TXDelay--; + + if (ARQ->TXDelay) + return; + + SendPacket(TNC, ARQ->TXMsg, ARQ->TXLen); + } + + // if We are alredy sending (State = ARQ_WAITDATA) we should allow it to send more (and the Poll at end) + + if (ARQ->ARQTimerState == ARQ_WAITDATA) + { + while (STREAM->BPQtoPACTOR_Q) + { + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (Outstanding > ARQ->TXWindow) + break; + + buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + SendARQData(TNC, buffptr); + } + + ARQ->ARQTimer--; + + if (ARQ->ARQTimer > 0) + return; // Timer Still Running + + // No more data available - send poll + + SendLen = sprintf(Reply, "p%s", TNC->Streams[0].MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + + // This is one message that should not be queued so it is sent straiget after data + +// Debugprintf("Sending Poll"); + + memcpy(ARQ->LastMsg, Reply, SendLen + 1); + ARQ->LastLen = SendLen; + + SendPacket(TNC, Reply, SendLen); + + ARQ->ARQTimer = TNC->Timeout; + ARQ->Retries = TNC->Retries; + + strcpy(TNC->WEB_PROTOSTATE, "Wait ACK"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; + + } + + // TrunroundTimer is used to allow time for far end to revert to RX + + if (ARQ->TurnroundTimer && !FL->Busy) + ARQ->TurnroundTimer--; + + if (ARQ->TurnroundTimer == 0) + { + while (STREAM->BPQtoPACTOR_Q) + { + if (ARQ->ARQState != ARQ_ACTIVE) + break; + + if (ARQ->ARQTimerState == ARQ_CONNECTACK) + break; + + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q) + 1; // Make sure busy is reported to BBS + + if (Outstanding > ARQ->TXWindow) + break; + + buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + SendARQData(TNC, buffptr); + } + } + + if (ARQ->ARQTimer) + { + if (FL->TX || FL->Busy) + { + // Only decrement if running send poll timer + + if (ARQ->ARQTimerState != ARQ_WAITDATA) + return; + } + + ARQ->ARQTimer--; + { + if (ARQ->ARQTimer) + return; // Timer Still Running + } + + ARQ->Retries--; + + if (ARQ->Retries) + { + // Retry Current Message + + SendPacket(TNC, ARQ->LastMsg, ARQ->LastLen); + ARQ->ARQTimer = TNC->Timeout + (rand() % 30); + + return; + } + + // Retried out. + + switch (ARQ->ARQTimerState) + { + case ARQ_WAITDATA: + + // No more data available - send poll + + SendLen = sprintf(Reply, "p%s", TNC->Streams[0].MyCall); + + ARQ->ARQTimerState = ARQ_WAITACK; + + // This is one message that should not be queued so it is sent straiget after data + + memcpy(ARQ->LastMsg, Reply, SendLen + 1); + ARQ->LastLen = SendLen; + + SendPacket(TNC, Reply, SendLen); + + ARQ->ARQTimer = TNC->Timeout; + ARQ->Retries = TNC->Retries; + + strcpy(TNC->WEB_PROTOSTATE, "Wait ACK"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; + + case ARQ_CONNECTING: + + // Report Connect Failed, and drop back to command mode + + buffptr = GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "FLDigi} Failure with %s\r", STREAM->RemoteCall); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + } + + // Send Disc to TNC in case it got the Connects, but we missed the ACKs + + TidyClose(TNC, 0); + ARQ->Retries = 2; // First timout is the real send, only send once + STREAM->Connecting = FALSE; // Back to Command Mode + ARQ->ARQState = FALSE; + + break; + + case ARQ_WAITACK: + case ARQ_CONNECTACK: + case ARQ_DISC: + + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->ReportDISC = TRUE; + ARQ->ARQState = FALSE; + + while (STREAM->PACTORtoBPQ_Q) + ReleaseBuffer(Q_REM(&STREAM->PACTORtoBPQ_Q)); + + while (STREAM->BPQtoPACTOR_Q) + ReleaseBuffer(Q_REM(&STREAM->BPQtoPACTOR_Q)); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + strcpy(TNC->WEB_PROTOSTATE, "Disconncted"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + break; + + } + } +} + +VOID ProcessARQStatus(struct TNCINFO * TNC, struct ARQINFO * ARQ, char * Input) +{ + // Release any acked frames and resend any outstanding + + int LastInSeq = Input[1] - 32; + int LastRXed = Input[2] - 32; + int FirstUnAcked = ARQ->TXLastACK; + int n = (int)strlen(Input) - 3; + char * ptr; + int NexttoResend; + int First, Last, Outstanding; + PMSGWITHLEN Buffer; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + int Acked = 0; + + // First status is an ack of Connect ACK + + if (ARQ->ARQTimerState == ARQ_CONNECTACK) + { + ARQ->Retries = 0; + ARQ->ARQTimer = 0; + ARQ->ARQTimerState = 0; + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + } + + // Release all up to LastInSeq + + while (FirstUnAcked != LastInSeq) + { + FirstUnAcked++; + FirstUnAcked &= 63; + + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { +// Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer->Len; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + } + + ARQ->TXLastACK = FirstUnAcked; + + Outstanding = ARQ->TXSeq - ARQ->TXLastACK; + + if (Outstanding < 0) + Outstanding += 64; + + TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames + + if (FirstUnAcked == ARQ->TXSeq) + { + UpdateStatsLine(TNC, STREAM); + ARQ->NoAckRetries = 0; + + strcpy(TNC->WEB_PROTOSTATE, "Connected"); + SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE); + + return; // All Acked + } + // Release any not in retry list up to LastRXed. + + ptr = &Input[3]; + + while (n) + { + NexttoResend = *(ptr++) - 32; + + FirstUnAcked++; + FirstUnAcked &= 63; + + while (FirstUnAcked != NexttoResend) + { + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { +// Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer->Len; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + + FirstUnAcked++; + FirstUnAcked &= 63; + } + + // We don't ACK this one. Process any more resend values, then release up to LastRXed. + + n--; + } + + // Release rest up to LastRXed + + while (FirstUnAcked != LastRXed) + { + FirstUnAcked++; + FirstUnAcked &= 63; + + Buffer = ARQ->TXHOLDQ[FirstUnAcked]; + + if (Buffer) + { +// Debugprintf("Acked %d", FirstUnAcked); + STREAM->BytesAcked += Buffer->Len; + ReleaseBuffer(Buffer); + ARQ->TXHOLDQ[FirstUnAcked] = NULL; + Acked++; + } + } + + // Resend anything in TX Buffer (From LastACK to TXSeq + + Last = ARQ->TXSeq + 1; + Last &= 63; + + First = LastInSeq; + + while (First != Last) + { + First++; + First &= 63; + + if(ARQ->TXHOLDQ[First]) + { + PMSGWITHLEN Buffer = ARQ->TXHOLDQ[First]; + UCHAR TXBuffer[300]; + SOCKET sock = TNC->TCPDataSock; + int SendLen; + +// Debugprintf("Resend %d", First); + + STREAM->BytesResent += Buffer->Len; + + SendLen = sprintf(TXBuffer, "%c", First + 32); + + if (TNC->FLInfo->KISSMODE) + { + memcpy(&TXBuffer[SendLen], (UCHAR *)&Buffer->Data, Buffer->Len); + SendLen += Buffer->Len; + } + else + SendLen += Stuff((UCHAR *)&Buffer->Data, &TXBuffer[SendLen], Buffer->Len); + + TXBuffer[SendLen] = 0; + + SendPacket(TNC, TXBuffer, SendLen); + + ARQ->ARQTimer = 10; // wait up to 1 sec for more data before polling + ARQ->Retries = 1; + ARQ->ARQTimerState = ARQ_WAITDATA; + + if (Acked == 0) + { + // Nothing acked by this statis message + + Acked = 0; // Dont count more thna once + ARQ->NoAckRetries++; + if (ARQ->NoAckRetries > TNC->Retries) + { + // Too many retries - just disconnect + + TidyClose(TNC, 0); + return; + } + } + } + } + + UpdateStatsLine(TNC, STREAM); +} + +VOID FLSlowTimer(struct TNCINFO * TNC) +{ + struct FLINFO * FL = TNC->FLInfo; + + // Entered every 10 secs + + // if in MCAST mode, clear KILL timer (MCAST RX can run for a long time + + if (TNC->FLInfo->MCASTMODE) + { + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + SESS->L4KILLTIMER = 0; + } + + if (FL->KISSMODE) + { + if (FL->Responding) + FL->Responding--; + + if (FL->Responding == 0) + { + TNC->TNCOK = FALSE; + TNC->CONNECTED = FALSE; + + sprintf(TNC->WEB_COMMSSTATE, "Connection to FLDIGI lost"); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // Set basic params till it responds + } + + FL->CmdControl++; + + if (FL->CmdControl > 5) // Every Minute + { + FL->CmdControl = 0; + + SendKISSCommand(TNC, "FLSTAT: MODEM: WFF:"); + } + + SendKISSCommand(TNC, "TRXS: TXBUF:"); // In case TX/RX report is missed + } +} + +static int ProcessXMLData(int port) +{ + unsigned int bytes; + int i; + char ErrMsg[255]; + char Message[500]; + struct TNCINFO * TNC = TNCInfo[port]; + struct FLINFO * FL = TNC->FLInfo; + char * ptr1, * ptr2, *ptr3; + + // Need to extract messages from byte stream + + bytes = recv(TNC->TCPSock,(char *)&Message, 500, 0); + + if (bytes == SOCKET_ERROR) + { +// i=sprintf(ErrMsg, "Read Failed for FLDigi socket - error code = %d\r\n", WSAGetLastError()); +// WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + if (bytes == 0) + { + // zero bytes means connection closed + + i=sprintf(ErrMsg, "FlDigi Connection closed for BPQ Port %d\n", port); + WritetoConsole(ErrMsg); + + TNC->CONNECTED = FALSE; + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + return (0); + } + + // Have some data. Assume for now we get a whole packet + + if (TNC->InternalCmd) + { + PMSGWITHLEN buffptr = GetBuff(); + + TNC->InternalCmd = FALSE; + + ptr1 = strstr(Message, ""); + + if (ptr1) + { + ptr1 += 7; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + + ptr3 = strstr(ptr1, ""); + + if (ptr3) + { + ptr1 = ptr3 + 4; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + } + + if (buffptr) + { + buffptr->Len = sprintf(buffptr->Data, "FLDIGI} Ok Was %s\r", ptr1); + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + } + + return 0; + } + + + ptr1 = strstr(Message, ""); + + if (ptr1) + { + ptr1 += 7; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + + ptr2 = strstr(ptr1, ""); + + if (ptr2) + { + ptr2 += 8; + ptr1 = ptr2; + ptr2 = strstr(ptr1, ""); + if (ptr2) *ptr2 = 0; + } + + if (strcmp(FL->LastXML, "modem.get_name") == 0) + { + strcpy(TNC->WEB_MODE, ptr1); + SetWindowText(TNC->xIDC_MODE, ptr1); + } + else if (strcmp(FL->LastXML, "main.get_trx_state") == 0) + { + if (strcmp(ptr1, "TX") == 0) + FL->TX = TRUE; + else + FL->TX = FALSE; + + + if (FL->TX) + strcpy(TNC->WEB_CHANSTATE, "TX"); + else + if (FL->Busy) + strcpy(TNC->WEB_CHANSTATE, "Busy"); + else + strcpy(TNC->WEB_CHANSTATE, "Idle"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + } + else if (strcmp(FL->LastXML, "main.get_squelch") == 0) + { +/* + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + SetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); +*/ + return 0; + } +/* + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->BusyHold) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } +*/ + + } + + return (0); + +} + + + +char MsgHddr[] = "POST /RPC2 HTTP/1.1\r\n" + "User-Agent: XMLRPC++ 0.8\r\n" + "Host: 127.0.0.1:7362\r\n" + "Content-Type: text/xml\r\n" + "Content-length: %d\r\n" + "\r\n%s"; + +char Req[] = "\r\n" + "%s\r\n" + "%s" + "\r\n"; + + +VOID SendXMLCommand(struct TNCINFO * TNC, char * Command, char * Value, char ParamType) +{ + int Len; + char ReqBuf[512]; + char SendBuff[512]; + struct FLINFO * FL = TNC->FLInfo; + struct ARQINFO * ARQ = TNC->ARQInfo; + char ValueString[256] =""; + + if (!TNC->CONNECTED || TNC->FLInfo->KISSMODE) + return; + + if (Value) + if (ParamType == 'S') + sprintf(ValueString, "%s", Value); + else + sprintf(ValueString, "%d", Value); + + strcpy(FL->LastXML, Command); + Len = sprintf(ReqBuf, Req, FL->LastXML, ValueString); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(TNC->TCPSock, SendBuff, Len, 0); + return; +} + +VOID SendXMLCommandInt(struct TNCINFO * TNC, char * Command, int Value, char ParamType) +{ + int Len; + char ReqBuf[512]; + char SendBuff[512]; + struct FLINFO * FL = TNC->FLInfo; + struct ARQINFO * ARQ = TNC->ARQInfo; + char ValueString[256] =""; + + if (!TNC->CONNECTED || TNC->FLInfo->KISSMODE) + return; + + sprintf(ValueString, "%d", Value); + + strcpy(FL->LastXML, Command); + Len = sprintf(ReqBuf, Req, FL->LastXML, ValueString); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(TNC->TCPSock, SendBuff, Len, 0); + return; +} + +VOID SendXMLPoll(struct TNCINFO * TNC) +{ + int Len; + char ReqBuf[256]; + char SendBuff[256]; + struct FLINFO * FL = TNC->FLInfo; + struct ARQINFO * ARQ = TNC->ARQInfo; + + if (!TNC->CONNECTED) + return; + + if (TNC->FLInfo->KISSMODE) + return; + + if (ARQ->ARQTimer) + { + // if timer is running, poll fot TX State + + strcpy(FL->LastXML, "main.get_trx_state"); + Len = sprintf(ReqBuf, Req, FL->LastXML, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(TNC->TCPSock, SendBuff, Len, 0); + return; + } + + FL->XMLControl++; + + + if (FL->XMLControl > 9) + { + FL->XMLControl = 0; + strcpy(FL->LastXML, "modem.get_name"); + } + else + { + if (FL->XMLControl == 5) + strcpy(FL->LastXML, "main.get_trx_state"); + else + return; + } + + Len = sprintf(ReqBuf, Req, FL->LastXML, ""); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + send(TNC->TCPSock, SendBuff, Len, 0); +} + +// sudo add-apt-repository ppa:kamalmostafa/fldigi + + diff --git a/.svn/pristine/6c/6cb9bbf9bebe4a04aa92870a68fe7cc4b148007b.svn-base b/.svn/pristine/6c/6cb9bbf9bebe4a04aa92870a68fe7cc4b148007b.svn-base new file mode 100644 index 0000000..60f670e --- /dev/null +++ b/.svn/pristine/6c/6cb9bbf9bebe4a04aa92870a68fe7cc4b148007b.svn-base @@ -0,0 +1,2154 @@ +/* +Copyright 2001-2018 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 +*/ + +// Control Routine for LinBPQ + +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" +#include "bpqmail.h" +#ifdef WIN32 +#include +//#include "C:\Program Files (x86)\GnuWin32\include\iconv.h" +#else +#include +#include +#ifndef MACBPQ +#ifndef FREEBSD +#include +#endif +#endif +#endif + +#include "time.h" + +#define Connect(stream) SessionControl(stream,1,0) +#define Disconnect(stream) SessionControl(stream,2,0) +#define ReturntoNode(stream) SessionControl(stream,3,0) +#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) + +BOOL APIENTRY Rig_Init(); + + + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) + +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); +void FreeSemaphore(struct SEM * Semaphore); +VOID CopyConfigFile(char * ConfigName); +VOID SendMailForThread(VOID * Param); +VOID GetUIConfig(); +Dll BOOL APIENTRY Init_IP(); +VOID OpenReportingSockets(); +VOID SetupNTSAliases(char * FN); +int DeleteRedundantMessages(); +BOOL InitializeTNCEmulator(); +VOID FindLostBuffers(); +VOID IPClose(); +DllExport BOOL APIENTRY Rig_Close(); +Dll BOOL APIENTRY Poll_IP(); +BOOL Rig_Poll(); +BOOL Rig_Poll(); +VOID CheckWL2KReportTimer(); +VOID TNCTimer(); +VOID SendLocation(); +int ChatPollStreams(); +void ChatTrytoSend(); +VOID BBSSlowTimer(); +int GetHTMLForms(); +char * AddUser(char * Call, char * password, BOOL BBSFlag); +VOID SaveChatConfigFile(char * ConfigName); +VOID SaveMH(); +int upnpClose(); +void SaveAIS(); +void initAIS(); +void DRATSPoll(); +void RHPPoll(); + +VOID GetPGConfig(); +void SendBBSDataToPktMap(); + +extern uint64_t timeLoadedMS; + +BOOL IncludesMail = FALSE; +BOOL IncludesChat = FALSE; + +BOOL RunMail = FALSE; +BOOL RunChat = FALSE; +BOOL needAIS= FALSE; +BOOL needADSB = FALSE; + +int CloseOnError = 0; + +VOID Poll_AGW(); +BOOL AGWAPIInit(); +int AGWAPITerminate(); + +BOOL AGWActive = FALSE; + +extern int AGWPort; + +BOOL RigActive = FALSE; + +extern ULONG ChatApplMask; +extern char Verstring[]; + +extern char SignoffMsg[]; +extern char AbortedMsg[]; +extern char InfoBoxText[]; // Text to display in Config Info Popup + +extern int LastVer[4]; // In case we need to do somthing the first time a version is run + +extern HWND MainWnd; +extern char BaseDir[]; +extern char BaseDirRaw[]; +extern char MailDir[]; +extern char WPDatabasePath[]; +extern char RlineVer[50]; + +extern BOOL LogBBS; +extern BOOL LogCHAT; +extern BOOL LogTCP; +extern BOOL ForwardToMe; + +extern int LatestMsg; +extern char BBSName[]; +extern char SYSOPCall[]; +extern char BBSSID[]; +extern char NewUserPrompt[]; + +extern int NumberofStreams; +extern int MaxStreams; +extern ULONG BBSApplMask; +extern int BBSApplNum; +extern int ChatApplNum; +extern int MaxChatStreams; + +extern int NUMBEROFTNCPORTS; + +extern int EnableUI; + +extern BOOL AUTOSAVEMH; + +extern FILE * LogHandle[4]; + +#define MaxSockets 64 + +extern ConnectionInfo Connections[MaxSockets+1]; + +time_t LastTrafficTime; +extern int MaintTime; + +#define LOG_BBS 0 +#define LOG_CHAT 1 +#define LOG_TCP 2 +#define LOG_DEBUG_X 3 + +int _MYTIMEZONE = 0; + +// flags equates + +#define F_Excluded 0x0001 +#define F_LOC 0x0002 +#define F_Expert 0x0004 +#define F_SYSOP 0x0008 +#define F_BBS 0x0010 +#define F_PAG 0x0020 +#define F_GST 0x0040 +#define F_MOD 0x0080 +#define F_PRV 0x0100 +#define F_UNP 0x0200 +#define F_NEW 0x0400 +#define F_PMS 0x0800 +#define F_EMAIL 0x1000 +#define F_HOLDMAIL 0x2000 +#define F_POLLRMS 0x4000 +#define F_SYSOP_IN_LM 0x8000 +#define F_Temp_B2_BBS 0x00010000 + +/* #define F_PWD 0x1000 */ + + +extern UCHAR BPQDirectory[260]; +extern UCHAR LogDirectory[260]; +extern UCHAR ConfigDirectory[260]; + +// overrides from params +UCHAR LogDir[260] = ""; +UCHAR ConfigDir[260] = ""; +UCHAR DataDir[260] = ""; + + +BOOL GetConfig(char * ConfigName); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); +int EncryptPass(char * Pass, char * Encrypt); +int APIENTRY FindFreeStream(); +int PollStreams(); +int APIENTRY SetAppl(int stream, int flags, int mask); +int APIENTRY SessionState(int stream, int * state, int * change); +int APIENTRY SessionControl(int stream, int command, int Mask); + +BOOL ChatInit(); +VOID CloseChat(); +VOID CloseTNCEmulator(); + +static config_t cfg; +static config_setting_t * group; + +BOOL MonBBS = TRUE; +BOOL MonCHAT = TRUE; +BOOL MonTCP = TRUE; + +BOOL LogBBS = TRUE; +BOOL LogCHAT = TRUE; +BOOL LogTCP = TRUE; + +extern BOOL LogAPRSIS; + +BOOL UIEnabled[33]; +BOOL UINull[33]; +char * UIDigi[33]; + +extern struct UserInfo ** UserRecPtr; +extern int NumberofUsers; + +extern struct UserInfo * BBSChain; // Chain of users that are BBSes + +extern struct MsgInfo ** MsgHddrPtr; +extern int NumberofMessages; + +extern int FirstMessageIndextoForward; // Lowest Message wirh a forward bit set - limits search + +extern char UserDatabaseName[MAX_PATH]; +extern char UserDatabasePath[MAX_PATH]; + +extern char MsgDatabasePath[MAX_PATH]; +extern char MsgDatabaseName[MAX_PATH]; + +extern char BIDDatabasePath[MAX_PATH]; +extern char BIDDatabaseName[MAX_PATH]; + +extern char WPDatabasePath[MAX_PATH]; +extern char WPDatabaseName[MAX_PATH]; + +extern char BadWordsPath[MAX_PATH]; +extern char BadWordsName[MAX_PATH]; + +extern char NTSAliasesPath[MAX_PATH]; +extern char NTSAliasesName[MAX_PATH]; + +extern char BaseDir[MAX_PATH]; +extern char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% +extern char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat + +extern char MailDir[MAX_PATH]; + +extern time_t MaintClock; // Time to run housekeeping + +#ifdef WIN32 +BOOL KEEPGOING = 30; // 5 secs to shut down +#else +BOOL KEEPGOING = 50; // 5 secs to shut down +#endif +BOOL Restarting = FALSE; +BOOL CLOSING = FALSE; + +int ProgramErrors; +int Slowtimer = 0; + +#define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer +int ReportTimer = 0; + +// Console Terminal Support + +struct ConTermS +{ + int BPQStream; + BOOL Active; + int Incoming; + + char kbbuf[INPUTLEN]; + int kbptr; + + char * KbdStack[MAXSTACK]; + int StackIndex; + + BOOL CONNECTED; + int SlowTimer; +}; + +struct ConTermS ConTerm = {0, 0}; + + +VOID CheckProgramErrors() +{ + if (Restarting) + exit(0); // Make sure can't loop in restarting + + ProgramErrors++; + + if (ProgramErrors > 25) + { + Restarting = TRUE; + + Logprintf(LOG_DEBUG_X, NULL, '!', "Too Many Program Errors - Closing"); + +/* + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + GetModuleFileName(NULL, ProgName, 256); + + Debugprintf("Attempting to Restart %s", ProgName); + + CreateProcess(ProgName, "MailChat.exe WAIT", NULL, NULL, FALSE, 0, NULL, NULL, &SInfo, &PInfo); +*/ + exit(0); + } +} + +#ifdef WIN32 + +BOOL CtrlHandler(DWORD fdwCtrlType) +{ + switch( fdwCtrlType ) + { + // Handle the CTRL-C signal. + case CTRL_C_EVENT: + printf( "Ctrl-C event\n\n" ); + CLOSING = TRUE; + Beep( 750, 300 ); + return( TRUE ); + + // CTRL-CLOSE: confirm that the user wants to exit. + case CTRL_CLOSE_EVENT: + + CLOSING = TRUE; + printf( "Ctrl-Close event\n\n" ); + Sleep(20000); + Beep( 750, 300 ); + return( TRUE ); + + // Pass other signals to the next handler. + case CTRL_BREAK_EVENT: + Beep( 900, 200 ); + printf( "Ctrl-Break event\n\n" ); + CLOSING = TRUE; + Beep( 750, 300 ); + return FALSE; + + case CTRL_LOGOFF_EVENT: + Beep( 1000, 200 ); + printf( "Ctrl-Logoff event\n\n" ); + return FALSE; + + case CTRL_SHUTDOWN_EVENT: + Beep( 750, 500 ); + printf( "Ctrl-Shutdown event\n\n" ); + CLOSING = TRUE; + Beep( 750, 300 ); + return FALSE; + + default: + return FALSE; + } +} + +#else + +#include +#include + +// Linux Signal Handlers + +static void segvhandler(int sig) +{ + void *array[10]; + size_t size; + char msg[] = "SIGSEGV Received\n"; + + write(STDERR_FILENO, msg, strlen(msg)); + + // get void*'s for all entries on the stack + size = backtrace(array, 10); + + // print out all the frames to stderr + + backtrace_symbols_fd(array, size, STDERR_FILENO); + + exit(1); +} + +static void abrthandler(int sig) +{ + void *array[10]; + size_t size; + char msg[] = "SIGABRT Received\n"; + + write(STDERR_FILENO, msg, strlen(msg)); + + // get void*'s for all entries on the stack + + size = backtrace(array, 10); + backtrace_symbols_fd(array, size, STDERR_FILENO); + + exit(1); +} + +static void sigterm_handler(int sig) +{ + syslog(LOG_INFO, "terminating on SIGTERM\n"); + CLOSING = TRUE; +} + +static void sigint_handler(int sig) +{ + printf("terminating on SIGINT\n"); + CLOSING = TRUE; +} + + +static void sigusr1_handler(int sig) +{ + signal(SIGUSR1, sigusr1_handler); +} + +#endif + + +#ifndef WIN32 + +BOOL CopyFile(char * In, char * Out, BOOL Failifexists) +{ + FILE * Handle; + DWORD FileSize; + char * Buffer; + struct stat STAT; + + if (stat(In, &STAT) == -1) + return FALSE; + + FileSize = STAT.st_size; + + Handle = fopen(In, "rb"); + + if (Handle == NULL) + return FALSE; + + Buffer = malloc(FileSize+1); + + FileSize = fread(Buffer, 1, STAT.st_size, Handle); + + fclose(Handle); + + if (FileSize != STAT.st_size) + { + free(Buffer); + return FALSE; + } + + Handle = fopen(Out, "wb"); + + if (Handle == NULL) + { + free(Buffer); + return FALSE; + } + + FileSize = fwrite(Buffer, 1, STAT.st_size, Handle); + + fclose(Handle); + free(Buffer); + + return TRUE; +} +#endif + +int RefreshMainWindow() +{ + return 0; +} + +int LastSemGets = 0; + +extern int SemHeldByAPI; + +VOID MonitorThread(void * x) +{ + // Thread to detect stuck semaphore + + do + { + if ((Semaphore.Gets == LastSemGets) && Semaphore.Flag) + { + // It is stuck - try to release + + Debugprintf ("Semaphore locked - Process ID = %d, Held By %d from %s Line %d", + Semaphore.SemProcessID, SemHeldByAPI, Semaphore.File, Semaphore.Line); + + + Semaphore.Flag = 0; + } + + LastSemGets = Semaphore.Gets; + + Sleep(30000); +// Debugprintf("Monitor Thread Still going %d %d %d %x %d", LastSemGets, Semaphore.Gets, Semaphore.Flag, Semaphore.SemThreadID, SemHeldByAPI); + + } + while (TRUE); +} + + + + +VOID TIMERINTERRUPT(); + +BOOL Start(); +VOID INITIALISEPORTS(); +Dll BOOL APIENTRY Init_APRS(); +VOID APRSClose(); +Dll VOID APIENTRY Poll_APRS(); +VOID HTTPTimer(); + + +#define CKernel +#include "Versions.h" + +extern struct SEM Semaphore; + +int SemHeldByAPI = 0; +BOOL IGateEnabled = TRUE; +BOOL APRSActive = FALSE; +BOOL ReconfigFlag = FALSE; +BOOL APRSReconfigFlag = FALSE; +BOOL RigReconfigFlag = FALSE; + +BOOL IPActive = FALSE; +extern BOOL IPRequired; + +extern struct WL2KInfo * WL2KReports; + +int InitDone; +char pgm[256] = "LINBPQ"; + +char SESSIONHDDR[80] = ""; +int SESSHDDRLEN = 0; + + +// Next 3 should be uninitialised so they are local to each process + +UCHAR MCOM; +UCHAR MUIONLY; +UCHAR MTX; +uint64_t MMASK; + + +UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list + +int SAVEPORT = 0; + +VOID SetApplPorts(); + +char VersionString[50] = Verstring; +char VersionStringWithBuild[50]=Verstring; +int Ver[4] = {Vers}; +char TextVerstring[50] = Verstring; + +extern UCHAR PWLen; +extern char PWTEXT[]; +extern int ISPort; + +extern char ChatConfigName[250]; + +BOOL EventsEnabled = 0; + +UCHAR * GetBPQDirectory() +{ + return BPQDirectory; +} +UCHAR * GetLogDirectory() +{ + return LogDirectory; +} +extern int POP3Timer; + +// Console Terminal Stuff + +#ifndef WIN32 + + + +#define _getch getchar + +/** + Linux (POSIX) implementation of _kbhit(). + Morgan McGuire, morgan@cs.brown.edu + */ + +#include +#include +#include + +int _kbhit() { + static const int STDIN = 0; + static int initialized = 0; + + if (! initialized) { + // Use termios to turn off line buffering + struct termios term; + tcgetattr(STDIN, &term); + term.c_lflag &= ~ICANON; + + tcsetattr(STDIN, TCSANOW, &term); + setbuf(stdin, NULL); + initialized = 1; + } + + int bytesWaiting; + ioctl(STDIN, FIONREAD, &bytesWaiting); + return bytesWaiting; +} + +#endif + +void ConTermInput(char * Msg) +{ + int i; + + if (ConTerm.BPQStream == 0) + { + ConTerm.BPQStream = FindFreeStream(); + + if (ConTerm.BPQStream == 255) + { + ConTerm.BPQStream = 0; + printf("No Free Streams\n"); + return; + } + } + + if (!ConTerm.CONNECTED) + SessionControl(ConTerm.BPQStream, 1, 0); + + ConTerm.StackIndex = 0; + + // Stack it + + if (ConTerm.KbdStack[19]) + free(ConTerm.KbdStack[19]); + + for (i = 18; i >= 0; i--) + { + ConTerm.KbdStack[i+1] = ConTerm.KbdStack[i]; + } + + ConTerm.KbdStack[0] = _strdup(ConTerm.kbbuf); + + ConTerm.kbbuf[ConTerm.kbptr]=13; + + SendMsg(ConTerm.BPQStream, ConTerm.kbbuf, ConTerm.kbptr+1); +} + +void ConTermPoll() +{ + int port, sesstype, paclen, maxframe, l4window, len; + int state, change, InputLen, count; + char callsign[11] = ""; + char Msg[300]; + + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + SessionState(ConTerm.BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) + { + // Connected + + ConTerm.CONNECTED = TRUE; + ConTerm.SlowTimer = 0; + } + else + { + ConTerm.CONNECTED = FALSE; + printf("*** Disconnected\n"); + } + } + + GetMsg(ConTerm.BPQStream, Msg, &InputLen, &count); + + if (InputLen) + { + char * ptr = Msg; + char * ptr2 = ptr; + Msg[InputLen] = 0; + + while (ptr) + { + ptr2 = strlop(ptr, 13); + + // Replace CR with CRLF + + printf("%s", ptr); + + if (ptr2) + printf("\r\n"); + + ptr = ptr2; + } + } + + if (_kbhit()) + { + unsigned char c = _getch(); + + if (c == 0xe0) + { + // Cursor control + + c = _getch(); + + if (c == 75) // cursor left + c = 8; + } + +#ifdef WIN32 + printf("%c", c); +#endif + if (c == 8) + { + if (ConTerm.kbptr) + ConTerm.kbptr--; + printf(" \b"); // Already echoed bs - clear typed char from screen + return; + } + + if (c == 13 || c == 10) + { + ConTermInput(ConTerm.kbbuf); + ConTerm.kbptr = 0; + return; + } + + ConTerm.kbbuf[ConTerm.kbptr++] = c; + fflush(NULL); + + } + + return; + +} + +#include + +static struct option long_options[] = +{ + {"logdir", required_argument, 0 , 'l'}, + {"configdir", required_argument, 0 , 'c'}, + {"datadir", required_argument, 0 , 'd'}, + {"help", no_argument, 0 , 'h'}, + { NULL , no_argument , NULL , no_argument } +}; + +char HelpScreen[] = + "Usage:\n" + "Optional Paramters\n" + "-l path or --logdir path Path for log files\n" + "-c path or --configdir path Path to Config file bpq32.cfg\n" + "-d path or --datadir path Path to Data Files\n" + "-v Show version and exit\n"; + +int Redirected = 0; + +static void segvhandler(int sig); +static void abrthandler(int sig); + + +int main(int argc, char * argv[]) +{ + int i; + struct UserInfo * user = NULL; + ConnectionInfo * conn; + struct stat STAT; + PEXTPORTDATA PORTVEC; + +#ifdef WIN32 + + WSADATA WsaData; // receives data from WSAStartup + HWND hWnd = GetForegroundWindow(); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); + + // disable the [x] button. + + if (hWnd != NULL) + { + HMENU hMenu = GetSystemMenu(hWnd, 0); + if (hMenu != NULL) + { + DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); + DrawMenuBar(hWnd); + } + } + +#else + + signal(SIGSEGV, segvhandler); + signal(SIGABRT, abrthandler); + + setlinebuf(stdout); + struct sigaction act; + openlog("LINBPQ", LOG_PID, LOG_DAEMON); +#ifndef MACBPQ +#ifndef FREEBSD + prctl(PR_SET_DUMPABLE, 1); // Enable Core Dumps even with setcap +#endif +#endif + + // Disable Console Terminal if stdout redirected + +// printf("STDOUT %d\n",isatty(STDOUT_FILENO)); +// printf("STDIN %d\n",isatty(STDIN_FILENO)); + + if (!isatty(STDOUT_FILENO) || !isatty(STDIN_FILENO)) + Redirected = 1; + + timeLoadedMS = GetTickCount(); + +#endif + + printf("G8BPQ AX25 Packet Switch System Version %s %s\n", TextVerstring, Datestring); + printf("%s\n", VerCopyright); + + + // look for optarg format parameters + + { + int val; + UCHAR * ptr1; + UCHAR * ptr2; + int c; + + while (1) + { + int option_index = 0; + + c = getopt_long(argc, argv, "l:c:d:hv", long_options, &option_index); + + // Check for end of operation or error + + if (c == -1) + break; + + // Handle options + switch (c) + { + case 'h': + + printf("%s", HelpScreen); + exit (0); + + case 'l': + strcpy(LogDir, optarg); + printf("cc %s\n", LogDir); + break; + + case 'c': + strcpy(ConfigDir, optarg); + break; + + case 'd': + strcpy(DataDir, optarg); + break; + + + case '?': + /* getopt_long already printed an error message. */ + break; + + case 'v': + return 0; + } + } + } + + sprintf(RlineVer, "LinBPQ%d.%d.%d", Ver[0], Ver[1], Ver[2]); + + + Debugprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + +#ifndef MACBPQ + _MYTIMEZONE = _timezone; +#endif + + if (_MYTIMEZONE < -86400 || _MYTIMEZONE > 86400) + _MYTIMEZONE = 0; + +#ifdef WIN32 + GetCurrentDirectory(256, BPQDirectory); +#else + getcwd(BPQDirectory, 256); +#endif + + strcpy(ConfigDirectory, BPQDirectory); + strcpy(LogDirectory, BPQDirectory); + + Consoleprintf("Current Directory is %s", BPQDirectory); + + if (LogDir[0]) + { + strcpy(LogDirectory, LogDir); + } + if (DataDir[0]) + { + strcpy(BPQDirectory, DataDir); + Consoleprintf("Working Directory is %s", BPQDirectory); + } + if (ConfigDir[0]) + { + strcpy(ConfigDirectory, ConfigDir); + Consoleprintf("Config Directory is %s", ConfigDirectory); + } + + for (i = optind; i < argc; i++) + { + if (_memicmp(argv[i], "logdir=", 7) == 0) + { + strcpy(LogDirectory, &argv[i][7]); + Consoleprintf("Log Directory is %s\n", LogDirectory); + break; + } + } + + Consoleprintf("Log Directory is %s", LogDirectory); + + // Make sure logs directory exists + + sprintf(LogDir, "%s/logs", LogDirectory); + +#ifdef WIN32 + CreateDirectory(LogDir, NULL); +#else + printf("Making Directory %s\n", LogDir); + i = mkdir(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); + if (i == -1 && errno != EEXIST) + { + perror("Couldn't create log directory\n"); + return 0; + } + chmod(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); +#endif + + if (!ProcessConfig()) + { + WritetoConsoleLocal("Configuration File Error\n"); + return (0); + } + + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for Linux (", TextVerstring); + +#ifdef MACBPQ + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for MAC (", TextVerstring); +#endif +#ifdef FREEBSD + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for FreeBSD (", TextVerstring); +#endif + + + GetSemaphore(&Semaphore, 0); + + if (Start() != 0) + { + FreeSemaphore(&Semaphore); + return (0); + } + + for (i=0;PWTEXT[i] > 0x20;i++); //Scan for cr or null + + PWLen=i; + + SetApplPorts(); + + GetUIConfig(); + + INITIALISEPORTS(); + + if (IPRequired) IPActive = Init_IP(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + if (needAIS) + initAIS(); + + RigActive = Rig_Init(); + + FreeSemaphore(&Semaphore); + + OpenReportingSockets(); + + initUTF8(); + + InitDone = TRUE; + + Debugprintf("Monitor Thread ID %x", _beginthread(MonitorThread, 0, 0)); + + +#ifdef WIN32 +#else + openlog("LINBPQ", LOG_PID, LOG_DAEMON); + + memset (&act, '\0', sizeof(act)); + + act.sa_handler = &sigint_handler; + if (sigaction(SIGINT, &act, NULL) < 0) + perror ("SIGINT"); + + act.sa_handler = &sigterm_handler; + if (sigaction(SIGTERM, &act, NULL) < 0) + perror ("sigaction"); + + act.sa_handler = SIG_IGN; + if (sigaction(SIGHUP, &act, NULL) < 0) + perror ("SIGHUP"); + + if (sigaction(SIGPIPE, &act, NULL) < 0) + perror ("SIGPIPE"); + +#endif + + for (i = optind; i < argc; i++) + { + if (_stricmp(argv[i], "chat") == 0) + IncludesChat = TRUE; + } + + if (IncludesChat) + { + RunChat = TRUE; + + printf("Starting Chat\n"); + + sprintf (ChatConfigName, "%s/chatconfig.cfg", BPQDirectory); + printf("Config File is %s\n", ChatConfigName); + + if (stat(ChatConfigName, &STAT) == -1) + { + printf("Chat Config File not found - creating a default config\n"); + ChatApplNum = 2; + MaxChatStreams = 10; + SaveChatConfigFile(ChatConfigName); + } + + if (GetChatConfig(ChatConfigName) == EXIT_FAILURE) + { + printf("Chat Config File seems corrupt - check before continuing\n"); + return -1; + } + + if (ChatApplNum) + { + if (ChatInit() == 0) + { + printf("Chat Init Failed\n"); + RunChat = 0; + } + else + { + printf("Chat Started\n"); + } + } + else + { + printf("Chat APPLNUM not defined\n"); + RunChat = 0; + } + CopyConfigFile(ChatConfigName); + } + + // Start Mail if requested by command line or config + + for (i = optind; i < argc; i++) + { + if (_stricmp(argv[i], "mail") == 0) + IncludesMail = TRUE; + } + + + if (IncludesMail) + { + RunMail = TRUE; + + printf("Starting Mail\n"); + + sprintf (ConfigName, "%s/linmail.cfg", BPQDirectory); + printf("Config File is %s\n", ConfigName); + + if (stat(ConfigName, &STAT) == -1) + { + printf("Config File not found - creating a default config\n"); + strcpy(BBSName, MYNODECALL); + strlop(BBSName, '-'); + strlop(BBSName, ' '); + BBSApplNum = 1; + MaxStreams = 10; + SaveConfig(ConfigName); + } + + if (GetConfig(ConfigName) == EXIT_FAILURE) + { + printf("BBS Config File seems corrupt - check before continuing\n"); + return -1; + } + + printf("Config Processed\n"); + + BBSApplMask = 1<<(BBSApplNum-1); + + // See if we need to warn of possible problem with BaseDir moved by installer + + sprintf(BaseDir, "%s", BPQDirectory); + + + // Set up file and directory names + + strcpy(UserDatabasePath, BaseDir); + strcat(UserDatabasePath, "/"); + strcat(UserDatabasePath, UserDatabaseName); + + strcpy(MsgDatabasePath, BaseDir); + strcat(MsgDatabasePath, "/"); + strcat(MsgDatabasePath, MsgDatabaseName); + + strcpy(BIDDatabasePath, BaseDir); + strcat(BIDDatabasePath, "/"); + strcat(BIDDatabasePath, BIDDatabaseName); + + strcpy(WPDatabasePath, BaseDir); + strcat(WPDatabasePath, "/"); + strcat(WPDatabasePath, WPDatabaseName); + + strcpy(BadWordsPath, BaseDir); + strcat(BadWordsPath, "/"); + strcat(BadWordsPath, BadWordsName); + + strcpy(NTSAliasesPath, BaseDir); + strcat(NTSAliasesPath, "/"); + strcat(NTSAliasesPath, NTSAliasesName); + + strcpy(MailDir, BaseDir); + strcat(MailDir, "/"); + strcat(MailDir, "Mail"); + +#ifdef WIN32 + CreateDirectory(MailDir, NULL); // Just in case +#else + mkdir(MailDir, S_IRWXU | S_IRWXG | S_IRWXO); + chmod(MailDir, S_IRWXU | S_IRWXG | S_IRWXO); +#endif + + // Make backup copies of Databases + + // CopyConfigFile(ConfigName); + + CopyBIDDatabase(); + CopyMessageDatabase(); + CopyUserDatabase(); + CopyWPDatabase(); + + SetupMyHA(); + SetupFwdAliases(); + SetupNTSAliases(NTSAliasesPath); + + GetWPDatabase(); + + GetMessageDatabase(); + GetUserDatabase(); + GetBIDDatabase(); + GetBadWordFile(); + GetHTMLForms(); + GetPGConfig(); + + // Make sure there is a user record for the BBS, with BBS bit set. + + user = LookupCall(BBSName); + + if (user == NULL) + { + user = AllocateUserRecord(BBSName); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if(SetupNewBBS(user)) + user->flags |= F_BBS; + } + + // if forwarding AMPR mail make sure User/BBS AMPR exists + + if (SendAMPRDirect) + { + BOOL NeedSave = FALSE; + + user = LookupCall("AMPR"); + + if (user == NULL) + { + user = AllocateUserRecord("AMPR"); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + NeedSave = TRUE; + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if (SetupNewBBS(user)) + user->flags |= F_BBS; + NeedSave = TRUE; + } + + if (NeedSave) + SaveUserDatabase(); + } + + + // Make sure SYSOPCALL is set + + if (SYSOPCall[0] == 0) + strcpy(SYSOPCall, BBSName); + + // See if just want to add user (mainly for setup scripts) + + if (argc == 5 && _stricmp(argv[1], "--adduser") == 0) + { + BOOL isBBS = FALSE; + char * response; + + if (_stricmp(argv[4], "TRUE") == 0) + isBBS = TRUE; + + printf("Adding User %s\r\n", argv[2]); + response = AddUser(argv[2], argv[3], isBBS); + printf("%s", response); + exit(0); + } + // Allocate Streams + + strcpy(pgm, "BBS"); + + for (i=0; i < MaxStreams; i++) + { + conn = &Connections[i]; + conn->BPQStream = FindFreeStream(); + + if (conn->BPQStream == 255) break; + + NumberofStreams++; + + // BPQSetHandle(conn->BPQStream, hWnd); + + SetAppl(conn->BPQStream, (i == 0 && EnableUI) ? 0x82 : 2, BBSApplMask); + Disconnect(conn->BPQStream); + } + + strcpy(pgm, "LINBPQ"); + + InitialiseTCP(); + InitialiseNNTP(); + + SetupListenSet(); // Master set of listening sockets + + if (EnableUI || MailForInterval) + SetupUIInterface(); + + if (MailForInterval) + _beginthread(SendMailForThread, 0, 0); + + + // Calulate time to run Housekeeping + { + struct tm *tm; + time_t now; + + now = time(NULL); + + tm = gmtime(&now); + + tm->tm_hour = MaintTime / 100; + tm->tm_min = MaintTime % 100; + tm->tm_sec = 0; + + MaintClock = mktime(tm) - (time_t)_MYTIMEZONE; + + while (MaintClock < now) + MaintClock += MaintInterval * 3600; + + Debugprintf("Maint Clock %lld NOW %lld Time to HouseKeeping %lld", (long long)MaintClock, (long long)now, (long long)(MaintClock - now)); + + if (LastHouseKeepingTime) + { + if ((now - LastHouseKeepingTime) > MaintInterval * 3600) + { + DoHouseKeeping(FALSE); + } + } + for (i = optind; i < argc; i++) + + { + if (_stricmp(argv[i], "tidymail") == 0) + DeleteRedundantMessages(); + + if (_stricmp(argv[i], "nohomebbs") == 0) + DontNeedHomeBBS = TRUE; + } + + printf("Mail Started\n"); + Logprintf(LOG_BBS, NULL, '!', "Mail Starting"); + + APIClock = 0; + + SendBBSDataToPktMap(); + + } + } + + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + + if (Redirected == 0) + ConTerm.BPQStream = FindFreeStream(); + + +#ifndef WIN32 + + for (i = 1; i < argc; i++) + { + if (_stricmp(argv[i], "daemon") == 0) + { + + // Convert to daemon + + pid_t pid, sid; + + /* Fork off the parent process */ + pid = fork(); + + if (pid < 0) + exit(EXIT_FAILURE); + + if (pid > 0) + exit(EXIT_SUCCESS); + + /* Change the file mode mask */ + + umask(0); + + /* Create a new SID for the child process */ + + sid = setsid(); + + if (sid < 0) + exit(EXIT_FAILURE); + + /* Change the current working directory */ + + if ((chdir("/")) < 0) + exit(EXIT_FAILURE); + + /* Close out the standard file descriptors */ + + printf("Entering daemon mode\n"); + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + break; + } + } +#endif + + while (KEEPGOING) + { + Sleep(100); + GetSemaphore(&Semaphore, 2); + + if (QCOUNT < 10) + { + if (CLOSING == FALSE) + FindLostBuffers(); + CLOSING = TRUE; + } + + if (CLOSING) + { + if (RunChat) + { + CloseChat(); + RunChat = FALSE; + } + + if (RunMail) + { + int BPQStream, n; + + RunMail = FALSE; + + for (n = 0; n < NumberofStreams; n++) + { + BPQStream = Connections[n].BPQStream; + + if (BPQStream) + { + SetAppl(BPQStream, 0, 0); + Disconnect(BPQStream); + DeallocateStream(BPQStream); + } + } + +// SaveUserDatabase(); + SaveMessageDatabase(); + SaveBIDDatabase(); + SaveConfig(ConfigName); + } + + KEEPGOING--; // Give time for links to close + setbuf(stdout, NULL); + printf("Closing... %d \r", KEEPGOING); + } + + + if (RigReconfigFlag) + { + RigReconfigFlag = FALSE; + Rig_Close(); + Sleep(2000); // Allow CATPTT threads to close + RigActive = Rig_Init(); + + Consoleprintf("Rigcontrol Reconfiguration Complete"); + } + + if (APRSReconfigFlag) + { + APRSReconfigFlag = FALSE; + APRSClose(); + APRSActive = Init_APRS(); + + Consoleprintf("APRS Reconfiguration Complete"); + } + + if (ReconfigFlag) + { + int i; + BPQVECSTRUC * HOSTVEC; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + ReconfigFlag = FALSE; + +// SetupBPQDirectory(); + + WritetoConsoleLocal("Reconfiguring ...\n\n"); + OutputDebugString("BPQ32 Reconfiguring ...\n"); + + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { +// SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); +// SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); +// CloseDriverWindow(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + IPClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + + if (AGWActive) + AGWAPITerminate(); + + WL2KReports = NULL; + +// Sleep(2000); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + Start(); + + INITIALISEPORTS(); + + SetApplPorts(); + + GetUIConfig(); + + FreeConfig(); + + for (i=1; i<68; i++) // Include Telnet, APRS, IP Vec + { + HOSTVEC=&BPQHOSTVECTOR[i-1]; + + HOSTVEC->HOSTTRACEQ=0; + + if (HOSTVEC->HOSTSESSION !=0) + { + // Had a connection + + HOSTVEC->HOSTSESSION=0; + HOSTVEC->HOSTFLAGS |=3; // Disconnected + +// PostMessage(HOSTVEC->HOSTHANDLE, BPQMsg, i, 4); + } + } + + OpenReportingSockets(); + + WritetoConsoleLocal("\n\nReconfiguration Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + RigActive = Rig_Init(); + + if (NUMBEROFTNCPORTS) + { + FreeSemaphore(&Semaphore); + InitializeTNCEmulator(); + GetSemaphore(&Semaphore, 2); + } + + FreeSemaphore(&Semaphore); + AGWActive = AGWAPIInit(); + GetSemaphore(&Semaphore, 2); + + OutputDebugString("BPQ32 Reconfiguration Complete\n"); + } + + if (IPActive) Poll_IP(); + if (RigActive) Rig_Poll(); + if (APRSActive) Poll_APRS(); + CheckWL2KReportTimer(); + + TIMERINTERRUPT(); + + FreeSemaphore(&Semaphore); + + if (Redirected == 0) + ConTermPoll(); + + if (NUMBEROFTNCPORTS) + TNCTimer(); + + if (AGWActive) + Poll_AGW(); + + DRATSPoll(); + RHPPoll(); + + HTTPTimer(); + + if (ReportTimer) + { + ReportTimer--; + + if (ReportTimer == 0) + { + ReportTimer = REPORTINTERVAL; + SendLocation(); + } + } + + Slowtimer++; + + if (RunChat) + { + ChatPollStreams(); + ChatTrytoSend(); + + if (Slowtimer > 100) // 10 secs + { + ChatTimer(); + } + } + + if (RunMail) + { + PollStreams(); + + if ((Slowtimer % 20) == 0) + FWDTimerProc(); + + if (Slowtimer > 100) // 10 secs + { + time_t NOW = time(NULL); + struct tm * tm; + + TCPTimer(); + BBSSlowTimer(); + + if (MaintClock < NOW) + { + while (MaintClock < NOW) // in case large time step + MaintClock += MaintInterval * 3600; + + Debugprintf("|Enter HouseKeeping"); + DoHouseKeeping(FALSE); + } + + if (APIClock < NOW) + { + SendBBSDataToPktMap(); + APIClock = NOW + 7200; // Every 2 hours + } + + + tm = gmtime(&NOW); + + if (tm->tm_wday == 0) // Sunday + { + if (GenerateTrafficReport && (LastTrafficTime + 86400) < NOW) + { + CreateBBSTrafficReport(); + LastTrafficTime = NOW; + } + } + } + TCPFastTimer(); + TrytoSend(); + } + + if (Slowtimer > 100) + Slowtimer = 0; + } + + printf("Closing Ports\n"); + + CloseTNCEmulator(); + + if (AGWActive) + AGWAPITerminate(); + + if (needAIS) + SaveAIS(); + + // Close Ports + + PORTVEC=(PEXTPORTDATA)PORTTABLE; + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + PORTVEC->PORT_EXT_ADDR(5, PORTVEC->PORTCONTROL.PORTNUMBER, NULL); + } + } + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + if (AUTOSAVE) + SaveNodes(); + + if (AUTOSAVEMH) + SaveMH(); + + if (IPActive) + IPClose(); + + if (RunMail) + FreeWebMailMallocs(); + + upnpClose(); + + // Close any open logs + + for (i = 0; i < 4; i++) + { + if (LogHandle[i]) + fclose(LogHandle[i]); + } + + return 0; +} + +int APIENTRY WritetoConsole(char * buff) +{ + return WritetoConsoleLocal(buff); +} + +int WritetoConsoleLocal(char * buff) +{ + return printf("%s", buff); +} + +#ifdef WIN32 +void * VCOMExtInit(struct PORTCONTROL * PortEntry); +void * V4ExtInit(EXTPORTDATA * PortEntry); +#endif +//UINT SoundModemExtInit(EXTPORTDATA * PortEntry); +//UINT BaycomExtInit(EXTPORTDATA * PortEntry); + +void * AEAExtInit(struct PORTCONTROL * PortEntry); +void * MPSKExtInit(EXTPORTDATA * PortEntry); +void * HALExtInit(struct PORTCONTROL * PortEntry); + +void * AGWExtInit(struct PORTCONTROL * PortEntry); +void * KAMExtInit(struct PORTCONTROL * PortEntry); +void * WinmorExtInit(EXTPORTDATA * PortEntry); +void * SCSExtInit(struct PORTCONTROL * PortEntry); +void * TrackerExtInit(EXTPORTDATA * PortEntry); +void * TrackerMExtInit(EXTPORTDATA * PortEntry); + +void * TelnetExtInit(EXTPORTDATA * PortEntry); +void * UZ7HOExtInit(EXTPORTDATA * PortEntry); +void * FLDigiExtInit(EXTPORTDATA * PortEntry); +void * ETHERExtInit(struct PORTCONTROL * PortEntry); +void * AXIPExtInit(struct PORTCONTROL * PortEntry); +void * ARDOPExtInit(EXTPORTDATA * PortEntry); +void * VARAExtInit(EXTPORTDATA * PortEntry); +void * SerialExtInit(EXTPORTDATA * PortEntry); +void * WinRPRExtInit(EXTPORTDATA * PortEntry); +void * HSMODEMExtInit(EXTPORTDATA * PortEntry); +void * FreeDataExtInit(EXTPORTDATA * PortEntry); +void * KISSHFExtInit(EXTPORTDATA * PortEntry); + +void * InitializeExtDriver(PEXTPORTDATA PORTVEC) +{ + // Only works with built in drivers + + UCHAR Value[20]; + + strcpy(Value,PORTVEC->PORT_DLL_NAME); + + _strupr(Value); + +#ifndef FREEBSD +#ifndef MACBPQ + if (strstr(Value, "BPQETHER")) + return ETHERExtInit; +#endif +#endif + if (strstr(Value, "BPQAXIP")) + return AXIPExtInit; + + if (strstr(Value, "BPQTOAGW")) + return AGWExtInit; + + if (strstr(Value, "AEAPACTOR")) + return AEAExtInit; + + if (strstr(Value, "HALDRIVER")) + return HALExtInit; + +#ifdef WIN32 + + if (strstr(Value, "BPQVKISS")) + return VCOMExtInit; + + if (strstr(Value, "V4")) + return V4ExtInit; + +#endif +/* + if (strstr(Value, "SOUNDMODEM")) + return (UINT) SoundModemExtInit; + + if (strstr(Value, "BAYCOM")) + return (UINT) BaycomExtInit; +*/ + if (strstr(Value, "MULTIPSK")) + return MPSKExtInit; + + if (strstr(Value, "KAMPACTOR")) + return KAMExtInit; + + if (strstr(Value, "WINMOR")) + return WinmorExtInit; + + if (strstr(Value, "SCSPACTOR")) + return SCSExtInit; + + if (strstr(Value, "SCSTRACKER")) + return TrackerExtInit; + + if (strstr(Value, "TRKMULTI")) + return TrackerMExtInit; + + if (strstr(Value, "UZ7HO")) + return UZ7HOExtInit; + + if (strstr(Value, "FLDIGI")) + return FLDigiExtInit; + + if (strstr(Value, "TELNET")) + return TelnetExtInit; + + if (strstr(Value, "ARDOP")) + return ARDOPExtInit; + + if (strstr(Value, "VARA")) + return VARAExtInit; + + if (strstr(Value, "KISSHF")) + return KISSHFExtInit; + + if (strstr(Value, "SERIAL")) + return SerialExtInit; + + if (strstr(Value, "WINRPR")) + return WinRPRExtInit; + + if (strstr(Value, "HSMODEM")) + return HSMODEMExtInit; + + if (strstr(Value, "FREEDATA")) + return FreeDataExtInit; + + return(0); +} + +int APIENTRY Restart() +{ + CLOSING = TRUE; + return TRUE; +} + +int APIENTRY Reboot() +{ + // Run sudo shutdown -r -f +#ifdef WIN32 + STARTUPINFO SInfo; + PROCESS_INFORMATION PInfo; + char Cmd[] = "shutdown -r -f"; + + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + return CreateProcess(NULL, Cmd, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo); + return 0; +#else + + char * arg_list[] = {NULL, NULL, NULL, NULL, NULL}; + pid_t child_pid; + char * Context; + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + arg_list[0] = "sudo"; + arg_list[1] = "shutdown"; + arg_list[2] = "now"; + arg_list[3] = "-r"; + + // Fork and Exec shutdown + + // Duplicate this process. + + child_pid = fork(); + + if (child_pid == -1) + { + printf ("Reboot fork() Failed\n"); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + /* The execvp function returns only if an error occurs. */ + + printf ("Failed to run shutdown\n"); + exit(0); // Kill the new process + } + return TRUE; +#endif + +} + +int APIENTRY Reconfig() +{ + if (!ProcessConfig()) + { + return (0); + } + SaveNodes(); + WritetoConsoleLocal("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsoleLocal("Reconfig requested ... Waiting for Timer Poll\n"); + return 1; +} + +int APRSWriteLog(char * msg); + +VOID MonitorAPRSIS(char * Msg, size_t MsgLen, BOOL TX) +{ + char Line[300]; + char Copy[300]; + int Len; + struct tm * TM; + time_t NOW; + + if (LogAPRSIS == 0) + return; + + if (MsgLen > 250) + return; + + // Mustn't change Msg + + memcpy(Copy, Msg, MsgLen); + Copy[MsgLen] = 0; + + NOW = time(NULL); + TM = gmtime(&NOW); + + Len = sprintf_s(Line, 299, "%02d:%02d:%02d%c %s", TM->tm_hour, TM->tm_min, TM->tm_sec, (TX)? 'T': 'R', Copy); + + APRSWriteLog(Line); + +} + +struct TNCINFO * TNC; + +#ifndef WIN32 + +#include +#include + +#ifndef MACBPQ +#ifdef __MACH__ + +#include + +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 0 + + + +int clock_gettime(int clk_id, struct timespec *t){ + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + uint64_t time; + time = mach_absolute_time(); + double nseconds = ((double)time * (double)timebase.numer)/((double)timebase.denom); + double seconds = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e9); + t->tv_sec = seconds; + t->tv_nsec = nseconds; + return 0; +} +#endif +#endif + + +uint64_t GetTickCount() +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000); +} + + + +void SetWindowText(HWND hWnd, char * lpString) +{ + return; +}; + +BOOL SetDlgItemText(HWND hWnd, int item, char * lpString) +{ + return 0; +}; + +#endif + +int GetListeningPortsPID(int Port) +{ +#ifdef WIN32 + + MIB_TCPTABLE_OWNER_PID * TcpTable = NULL; + PMIB_TCPROW_OWNER_PID Row; + int dwSize = 0; + unsigned int n; + + // Get PID of process for this TCP Port + + // Get Length of table + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + TcpTable = malloc(dwSize); + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + for (n = 0; n < TcpTable->dwNumEntries; n++) + { + Row = &TcpTable->table[n]; + + if (Row->dwLocalPort == Port && Row->dwState == MIB_TCP_STATE_LISTEN) + { + return Row->dwOwningPid; + break; + } + } +#endif + return 0; // Not found +} + + + +VOID Check_Timer() +{ +} + +VOID POSTDATAAVAIL(){}; + +COLORREF Colours[256] = {0, + RGB(0,0,0), RGB(0,0,128), RGB(0,0,192), RGB(0,0,255), // 1 - 4 + RGB(0,64,0), RGB(0,64,128), RGB(0,64,192), RGB(0,64,255), // 5 - 8 + RGB(0,128,0), RGB(0,128,128), RGB(0,128,192), RGB(0,128,255), // 9 - 12 + RGB(0,192,0), RGB(0,192,128), RGB(0,192,192), RGB(0,192,255), // 13 - 16 + RGB(0,255,0), RGB(0,255,128), RGB(0,255,192), RGB(0,255,255), // 17 - 20 + + RGB(6425,0,0), RGB(64,0,128), RGB(64,0,192), RGB(0,0,255), // 21 + RGB(64,64,0), RGB(64,64,128), RGB(64,64,192), RGB(64,64,255), + RGB(64,128,0), RGB(64,128,128), RGB(64,128,192), RGB(64,128,255), + RGB(64,192,0), RGB(64,192,128), RGB(64,192,192), RGB(64,192,255), + RGB(64,255,0), RGB(64,255,128), RGB(64,255,192), RGB(64,255,255), + + RGB(128,0,0), RGB(128,0,128), RGB(128,0,192), RGB(128,0,255), // 41 + RGB(128,64,0), RGB(128,64,128), RGB(128,64,192), RGB(128,64,255), + RGB(128,128,0), RGB(128,128,128), RGB(128,128,192), RGB(128,128,255), + RGB(128,192,0), RGB(128,192,128), RGB(128,192,192), RGB(128,192,255), + RGB(128,255,0), RGB(128,255,128), RGB(128,255,192), RGB(128,255,255), + + RGB(192,0,0), RGB(192,0,128), RGB(192,0,192), RGB(192,0,255), // 61 + RGB(192,64,0), RGB(192,64,128), RGB(192,64,192), RGB(192,64,255), + RGB(192,128,0), RGB(192,128,128), RGB(192,128,192), RGB(192,128,255), + RGB(192,192,0), RGB(192,192,128), RGB(192,192,192), RGB(192,192,255), + RGB(192,255,0), RGB(192,255,128), RGB(192,255,192), RGB(192,255,255), + + RGB(255,0,0), RGB(255,0,128), RGB(255,0,192), RGB(255,0,255), // 81 + RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,64,255), + RGB(255,128,0), RGB(255,128,128), RGB(255,128,192), RGB(255,128,255), + RGB(255,192,0), RGB(255,192,128), RGB(255,192,192), RGB(255,192,255), + RGB(255,255,0), RGB(255,255,128), RGB(255,255,192), RGB(255,255,255) +}; + + +//VOID SendRPBeacon(struct TNCINFO * TNC) +//{ +//} + +int PollStreams() +{ + int state,change; + ConnectionInfo * conn; + int n; + struct UserInfo * user = NULL; + char ConnectedMsg[] = "*** CONNECTED "; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + DoReceivedData(conn->BPQStream); + DoBBSMonitorData(conn->BPQStream); + + SessionState(conn->BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) // Connected + { + GetSemaphore(&ConSemaphore, 0); + Connected(conn->BPQStream); + FreeSemaphore(&ConSemaphore); + } + else + { + GetSemaphore(&ConSemaphore, 0); + Disconnected(conn->BPQStream); + FreeSemaphore(&ConSemaphore); + } + } + } + + return 0; +} + + +VOID CloseConsole(int Stream) +{ +} + +#ifndef WIN32 + +int V4ProcessReceivedData(struct TNCINFO * TNC) +{ + return 0; +} +#endif + +#ifdef FREEBSD + +char * gcvt(double _Val, int _NumOfDigits, char * _DstBuf) +{ + sprintf(_DstBuf, "%f", _Val); + return _DstBuf; +} + +#endif + + + + + diff --git a/.svn/pristine/6c/6cd45a8108fa7fe05fa63e7648cbea6e1b656b1e.svn-base b/.svn/pristine/6c/6cd45a8108fa7fe05fa63e7648cbea6e1b656b1e.svn-base new file mode 100644 index 0000000..4580ca4 --- /dev/null +++ b/.svn/pristine/6c/6cd45a8108fa7fe05fa63e7648cbea6e1b656b1e.svn-base @@ -0,0 +1,450 @@ +// +// Prototypes for BPQ32 Node Functions +// + +#define DllImport + +#define EXCLUDEBITS + +#define _WINSOCK_DEPRECATED_NO_WARNINGS + +#include "compatbits.h" + +#include "asmstrucs.h" + +BOOL CheckExcludeList(UCHAR * Call); + +Dll int ConvFromAX25(unsigned char * incall,unsigned char * outcall); +Dll BOOL ConvToAX25(unsigned char * callsign, unsigned char * ax25call); +DllExport BOOL ConvToAX25Ex(unsigned char * callsign, unsigned char * ax25call); +int WritetoConsoleLocal(char * buff); +VOID Consoleprintf(const char * format, ...); +VOID FreeConfig(); +int GetListeningPortsPID(int Port); + +void * InitializeExtDriver(PEXTPORTDATA PORTVEC); + +VOID PutLengthinBuffer(PDATAMESSAGE buff, USHORT datalen); // Needed for arm5 portability +int GetLengthfromBuffer(PDATAMESSAGE buff); +int IntDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, uint64_t Mask, BOOL APRS, BOOL MCTL); +int IntSetTraceOptionsEx(uint64_t mask, int mtxparam, int mcomparam, int monUIOnly); +int CountBits64(uint64_t in); + + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) + +#define GetBuff() _GetBuff(__FILE__, __LINE__) +#define ReleaseBuffer(s) _ReleaseBuffer(s, __FILE__, __LINE__) +#define CheckGuardZone() _CheckGuardZone(__FILE__, __LINE__) + +#define Q_REM(s) _Q_REM(s, __FILE__, __LINE__) +#define Q_REM_NP(s) _Q_REM_NP(s, __FILE__, __LINE__) + +#define C_Q_ADD(s, b) _C_Q_ADD(s, b, __FILE__, __LINE__) + +void _CheckGuardZone(char * File, int Line); + +VOID * _Q_REM(VOID **Q, char * File, int Line); +VOID * _Q_REM_NP(VOID *Q, char * File, int Line); + +int _C_Q_ADD(VOID *Q, VOID *BUFF, char * File, int Line); + +UINT _ReleaseBuffer(VOID *BUFF, char * File, int Line); + +VOID * _GetBuff(char * File, int Line); +int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line); + +int C_Q_COUNT(VOID *Q); + +DllExport char * APIENTRY GetApplCall(int Appl); +DllExport char * APIENTRY GetApplAlias(int Appl); +DllExport int APIENTRY FindFreeStream(); +DllExport int APIENTRY DeallocateStream(int stream); +DllExport int APIENTRY SessionState(int stream, int * state, int * change); +DllExport int APIENTRY SetAppl(int stream, int flags, int mask); +DllExport int APIENTRY GetMsg(int stream, char * msg, int * len, int * count ); +DllExport int APIENTRY GetConnectionInfo(int stream, char * callsign, + int * port, int * sesstype, int * paclen, + int * maxframe, int * l4window); + +#define LIBCONFIG_STATIC +#include "libconfig.h" + +int GetIntValue(config_setting_t * group, char * name); +BOOL GetStringValue(config_setting_t * group, char * name, char * value, int maxlen); +VOID SaveIntValue(config_setting_t * group, char * name, int value); +VOID SaveStringValue(config_setting_t * group, char * name, char * value); + +int EncryptPass(char * Pass, char * Encrypt); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); +Dll VOID APIENTRY CreateOneTimePassword(char * Password, char * KeyPhrase, int TimeOffset); +Dll BOOL APIENTRY CheckOneTimePassword(char * Password, char * KeyPhrase); + +DllExport int APIENTRY TXCount(int stream); +DllExport int APIENTRY RXCount(int stream); +DllExport int APIENTRY MONCount(int stream); + +VOID ReadNodes(); +int BPQTRACE(MESSAGE * Msg, BOOL APRS); + +VOID CommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer); + +VOID PostStateChange(TRANSPORTENTRY * Session); + +VOID InnerCommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer); +VOID DoTheCommand(TRANSPORTENTRY * Session); +char * MOVEANDCHECK(TRANSPORTENTRY * Session, char * Bufferptr, char * Source, int Len); +VOID DISPLAYCIRCUIT(TRANSPORTENTRY * L4, char * Buffer); +char * strlop(char * buf, char delim); +BOOL CompareCalls(UCHAR * c1, UCHAR * c2); + +VOID PostDataAvailable(TRANSPORTENTRY * Session); +int WritetoConsoleLocal(char * buff); +char * CHECKBUFFER(TRANSPORTENTRY * Session, char * Bufferptr); +VOID CLOSECURRENTSESSION(TRANSPORTENTRY * Session); + +VOID SendCommandReply(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer, int Len); + +struct PORTCONTROL * APIENTRY GetPortTableEntryFromPortNum(int portnum); + +int cCOUNT_AT_L2(struct _LINKTABLE * LINK); +VOID SENDL4CONNECT(TRANSPORTENTRY * Session); + +VOID CloseSessionPartner(TRANSPORTENTRY * Session); +int COUNTNODES(struct ROUTE * ROUTE); +int DecodeNodeName(char * NodeName, char * ptr);; +VOID DISPLAYCIRCUIT(TRANSPORTENTRY * L4, char * Buffer); +int cCOUNT_AT_L2(struct _LINKTABLE * LINK); +void * zalloc(int len); +BOOL FindDestination(UCHAR * Call, struct DEST_LIST ** REQDEST); + +BOOL ProcessConfig(); + +VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer); +VOID CLEAROUTLINK(struct _LINKTABLE * LINK); +VOID TellINP3LinkGone(struct ROUTE * Route); +VOID CLEARACTIVEROUTE(struct ROUTE * ROUTE, int Reason); + +// Reason Equates + +#define NORMALCLOSE 0 +#define RETRIEDOUT 1 +#define SETUPFAILED 2 +#define LINKLOST 3 +#define LINKSTUCK 4 + +int COUNT_AT_L2(struct _LINKTABLE * LINK); +VOID SENDIDMSG(); +VOID SENDBTMSG(); +VOID INP3TIMER(); +VOID REMOVENODE(dest_list * DEST); +BOOL ACTIVATE_DEST(struct DEST_LIST * DEST); +VOID TellINP3LinkSetupFailed(struct ROUTE * Route); +BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE); +VOID PROCROUTES(struct DEST_LIST * DEST, struct ROUTE * ROUTE, int Qual); +BOOL L2SETUPCROSSLINK(PROUTE ROUTE); +VOID REMOVENODE(dest_list * DEST); +char * SetupNodeHeader(struct DATAMESSAGE * Buffer); +VOID L4CONNECTFAILED(TRANSPORTENTRY * L4); +int CountFramesQueuedOnSession(TRANSPORTENTRY * Session); +VOID CLEARSESSIONENTRY(TRANSPORTENTRY * Session); +VOID __cdecl Debugprintf(const char * format, ...); + +int APIENTRY Restart(); +int APIENTRY Reboot(); +int APIENTRY Reconfig(); +Dll int APIENTRY SaveNodes (); + + +struct SEM; + +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); +void FreeSemaphore(struct SEM * Semaphore); + +void MySetWindowText(HWND hWnd, char * Msg); + +Dll int APIENTRY SessionControl(int stream, int command, int Mask); + +HANDLE OpenCOMPort(VOID * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits); +int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength); +BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite); +VOID CloseCOMPort(HANDLE fd); + +VOID initUTF8(); +int Is8Bit(unsigned char *cpt, int len); +int WebIsUTF8(unsigned char *ptr, int len); +int IsUTF8(unsigned char *ptr, int len); +int Convert437toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1251toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int Convert1252toUTF8(unsigned char * MsgPtr, int len, unsigned char * UTF); +int TrytoGuessCode(unsigned char * Char, int Len); + + +#define CMD_TO_APPL 1 // PASS COMMAND TO APPLICATION +#define MSG_TO_USER 2 // SEND 'CONNECTED' TO USER +#define MSG_TO_APPL 4 // SEND 'CONECTED' TO APPL +#define CHECK_FOR_ESC 8 // Look for ^d (^D) to disconnect session) + +#define UI 3 +#define SABM 0x2F +#define DISC 0x43 +#define DM 0x0F +#define UA 0x63 +#define FRMR 0x87 +#define RR 1 +#define RNR 5 +#define REJ 9 + +// V2.2 Types + +#define SREJ 0x0D +#define SABME 0x6F +#define XID 0xAF +#define TEST 0xE3 + +#define SUPPORT2point2 1 + +// XID Optional Functions + +#define OPMustHave 0x02A080 // Sync TEST 16 bit FCS Extended Address +#define OPSREJ 4 +#define OPSREJMult 0x200000 +#define OPREJ 2 +#define OPMod8 0x400 +#define OPMod128 0x800 + +#define BPQHOSTSTREAMS 64 + +extern TRANSPORTENTRY * L4TABLE; +extern unsigned char NEXTID; +extern int MAXCIRCUITS; +extern int L4DEFAULTWINDOW; +extern int L4T1; +extern APPLCALLS APPLCALLTABLE[]; +extern char * APPLS; +extern int NEEDMH; +extern int RFOnly; + +extern char SESSIONHDDR[]; + +extern UCHAR NEXTID; + +extern struct ROUTE * NEIGHBOURS; +extern int MAXNEIGHBOURS; + +extern struct ROUTE * NEIGHBOURS; +extern int ROUTE_LEN; +extern int MAXNEIGHBOURS; + +extern struct DEST_LIST * DESTS; // NODE LIST +extern struct DEST_LIST * ENDDESTLIST; +extern int DEST_LIST_LEN; +extern int MAXDESTS; // MAX NODES IN SYSTEM + +extern struct _LINKTABLE * LINKS; +extern int LINK_TABLE_LEN; +extern int MAXLINKS; + + + +extern char MYCALL[]; // DB 7 DUP (0) ; NODE CALLSIGN (BIT SHIFTED) +extern char MYALIASTEXT[]; // {" " ; NODE ALIAS (KEEP TOGETHER) + +extern UCHAR MYCALLWITHALIAS[13]; +extern APPLCALLS APPLCALLTABLE[NumberofAppls]; + +extern UCHAR MYNODECALL[]; // NODE CALLSIGN (ASCII) +extern char NODECALLLOPPED[]; // NODE CALLSIGN (ASCII). Null terminated +extern UCHAR MYNETROMCALL[]; // NETROM CALLSIGN (ASCII) + +extern UCHAR NETROMCALL[]; // NETORM CALL (AX25) + +extern VOID * FREE_Q; + +extern struct PORTCONTROL * PORTTABLE; +extern int NUMBEROFPORTS; + + +extern int OBSINIT; // INITIAL OBSOLESCENCE VALUE +extern int OBSMIN; // MINIMUM TO BROADCAST +extern int L3INTERVAL; // "NODES" INTERVAL IN MINS +extern int IDINTERVAL; // "ID" BROADCAST INTERVAL +extern int BTINTERVAL; // "BT" BROADCAST INTERVAL +extern int MINQUAL; // MIN QUALITY FOR AUTOUPDATES +extern int HIDENODES; // N * COMMAND SWITCH +extern int BBSQUAL; // QUALITY OF BBS RELATIVE TO NODE + +extern int NUMBEROFBUFFERS; // PACKET BUFFERS +extern int PACLEN; //MAX PACKET SIZE + +// L2 SYSTEM TIMER RUNS AT 3 HZ + +extern int T3; // LINK VALIDATION TIMER (3 MINS) (+ a bit to reduce RR collisions) + +extern int L2KILLTIME; // IDLE LINK TIMER (16 MINS) +extern int L3LIVES; // MAX L3 HOPS +extern int L4N2; // LEVEL 4 RETRY COUNT +extern int L4LIMIT; // IDLE SESSION LIMIT - 15 MINS +extern int L4DELAY; // L4 DELAYED ACK TIMER + +extern int BBS; // INCLUDE BBS SUPPORT +extern int NODE; // INCLUDE SWITCH SUPPORT + +extern int FULL_CTEXT; // CTEXT ON ALL CONNECTS IF NZ + + +// Although externally streams are numbered 1 to 64, internally offsets are 0 - 63 + +extern BPQVECSTRUC DUMMYVEC; // Needed to force correct order of following + +extern BPQVECSTRUC BPQHOSTVECTOR[BPQHOSTSTREAMS + 5]; + +extern int NODEORDER; +extern UCHAR LINKEDFLAG; + +extern UCHAR UNPROTOCALL[80]; + + +extern char * INFOMSG; + +extern char * CTEXTMSG; +extern int CTEXTLEN; + +extern UCHAR MYALIAS[7]; // ALIAS IN AX25 FORM +extern UCHAR BBSALIAS[7]; + +extern VOID * TRACE_Q; // TRANSMITTED FRAMES TO BE TRACED + +extern char HEADERCHAR; // CHAR FOR _NODE HEADER MSGS + +extern int AUTOSAVE; // AUTO SAVE NODES ON EXIT FLAG +extern int L4APPL; // Application for BBSCALL/ALIAS connects +extern int CFLAG; // C =HOST Command + +extern VOID * IDMSG_Q; // ID/BEACONS WAITING TO BE SENT + +extern struct DATAMESSAGE BTHDDR; +extern struct _MESSAGE IDHDDR; + +extern VOID * IDMSG; + +extern int L3TIMER; // TIMER FOR 'NODES' MESSAGE +extern int IDTIMER; // TIMER FOR ID MESSAGE +extern int BTTIMER; // TIMER FOR BT MESSAGE + +extern int STATSTIME; + + +extern BOOL IPRequired; +extern int MaxHops; +extern int MAXRTT; +extern USHORT CWTABLE[]; +extern TRANSPORTENTRY * L4TABLE; +extern UCHAR ROUTEQUAL; +extern UINT BPQMsg; +extern UCHAR ExcludeList[]; + + +extern APPLCALLS APPLCALLTABLE[]; + +extern char VersionStringWithBuild[]; +extern char VersionString[]; + +extern int MAXHEARDENTRIES; +extern int MHLEN; + +extern int APPL1; +extern int PASSCMD; +extern int NUMBEROFCOMMANDS; + +extern char * ConfigBuffer; + +extern char * WL2KReportLine[]; + +extern struct CMDX COMMANDS[]; + +extern int QCOUNT, MAXBUFFS, MAXCIRCUITS, L4DEFAULTWINDOW, L4T1, CMDXLEN; +extern char CMDALIAS[ALIASLEN][NumberofAppls]; + +extern int SEMGETS; +extern int SEMRELEASES; +extern int SEMCLASHES; +extern int MINBUFFCOUNT; + +extern UCHAR BPQDirectory[]; +extern UCHAR BPQProgramDirectory[]; + +extern UCHAR WINMOR[]; +extern UCHAR PACTORCALL[]; + +extern UCHAR MCOM; +extern UCHAR MUIONLY; +extern UCHAR MTX; +extern uint64_t MMASK; + +extern UCHAR NODECALL[]; // NODES in ax.25 + +extern int L4CONNECTSOUT; +extern int L4CONNECTSIN; +extern int L4FRAMESTX; +extern int L4FRAMESRX; +extern int L4FRAMESRETRIED; +extern int OLDFRAMES; +extern int L3FRAMES; + +extern char * PortConfig[]; +extern struct SEM Semaphore; +extern UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list + +extern int REALTIMETICKS; + +extern time_t CurrentSecs; +extern time_t lastSlowSecs; +extern time_t lastSaveSecs; + +// SNMP Variables + +extern int InOctets[64]; +extern int OutOctets[64]; + +extern BOOL CloseAllNeeded; +extern int CloseOnError; + +extern char * PortConfig[70]; +extern struct TNCINFO * TNCInfo[71]; // Records are Malloc'd + +#define MaxBPQPortNo 63 // Port 64 reserved for BBS Mon +#define MAXBPQPORTS 63 + +// IP, APRS use port ocnfig slots above the real port range + +#define IPConfigSlot MaxBPQPortNo + 1 +#define PortMapConfigSlot MaxBPQPortNo + 2 +#define APRSConfigSlot MaxBPQPortNo + 3 + + +extern char * UIUIDigi[MaxBPQPortNo + 1]; +extern char UIUIDEST[MaxBPQPortNo + 1][11]; // Dest for Beacons +extern UCHAR FN[MaxBPQPortNo + 1][256]; // Filename +extern int Interval[MaxBPQPortNo + 1]; // Beacon Interval (Mins) +extern char Message[MaxBPQPortNo + 1][1000]; // Beacon Text + +extern int MinCounter[MaxBPQPortNo + 1]; // Interval Countdown +extern BOOL SendFromFile[MaxBPQPortNo + 1]; + +extern BOOL MQTT; +extern char MQTT_HOST[80]; +extern int MQTT_PORT; +extern char MQTT_USER[80]; +extern char MQTT_PASS[80]; + +DllExport uint64_t APIENTRY GetPortFrequency(int PortNo, char * FreqStringMhz); + + +void hookL2SessionAccepted(int Port, char * remotecall, char * ourcall, struct _LINKTABLE * LINK); +void hookL2SessionDeleted(struct _LINKTABLE * LINK); +void hookL2SessionAttempt(int Port, char * ourcall, char * remotecall, struct _LINKTABLE * LINK); + +void hookL4SessionAttempt(void * STREAM, char * remotecall, char * ourcall); +void hookL4SessionAccepted(void * STREAM, char * remotecall, char * ourcall); +void hookL4SessionDeleted(struct TNCINFO * TNC, void * STREAM); diff --git a/.svn/pristine/6e/6e4d66082b027c59133143d65476478c5087911a.svn-base b/.svn/pristine/6e/6e4d66082b027c59133143d65476478c5087911a.svn-base new file mode 100644 index 0000000..cc79fea --- /dev/null +++ b/.svn/pristine/6e/6e4d66082b027c59133143d65476478c5087911a.svn-base @@ -0,0 +1,1627 @@ +/* +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 +*/ + +// +// DLL to inteface AEA/Timewave TNC in Pactor Mode to BPQ32 switch +// +// Uses BPQ EXTERNAL interface +// + +// Version 1.1.1.2 Sept 2010 + +// Fix CTEXT +// Turn round link when all acked (not all sent) + +// Version 1.1.1.3 Sept 2010 + +// Turn round link if too long in receive + +// Version 1.1.1.4 September 2010 + +// Fix Freq Display after Node reconfig +// Only use AutoConnect APPL for Pactor Connects + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +#include "time.h" + +//#include +//#include + +#include "cheaders.h" +#include "tncinfo.h" + +#include "bpq32.h" + + +static char ClassName[]="AEAPACTORSTATUS"; + +static char WindowTitle[] = "AEA Pactor"; +static int RigControlRow = 165; + +#define SOH 0x01 // CONTROL CODES +#define ETB 0x17 +#define DLE 0x10 + +#define MaxStreams 26 + +#define PTOVER 0x1a // Pactor Turnround Char + +char OverMsg[3] = " \x1a"; + +static RECT Rect; + +static char status[8][8] = {"STANDBY", "PHASING", "CHGOVER", "IDLE", "TRAFFIC", "ERROR", "RQ", "XXXX"}; + +struct TNCINFO * CreateTTYInfo(int port, int speed); +BOOL SetupConnection(int); +BOOL CloseConnection(struct TNCINFO * conn); +static BOOL WriteCommBlock(struct TNCINFO * TNC); +BOOL DestroyTTYInfo(int port); +static void CheckRX(struct TNCINFO * TNC); +VOID AEAPoll(int Port); +static VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); +static VOID ProcessTermModeResponse(struct TNCINFO * TNC); +static VOID DoTNCReinit(struct TNCINFO * TNC); +static VOID DoTermModeTimeout(struct TNCINFO * TNC); + +VOID ProcessPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +VOID ProcessKPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); +static VOID ProcessAEAPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, size_t Len); +VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); +static VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, size_t Len); + +// Note that AEA host Mode uses SOH/ETB delimiters, with DLE stuffing + +static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); +static int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, size_t len); +static int DLEDecode(UCHAR * inbuff, UCHAR * outbuff, size_t len); + +int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + ptr = strtok(NULL, " \t\n\r"); + + if (_stricmp(buf, "ADDR") == 0) // Winmor Using BPQ32 COnfig + { + BPQport = Port; + p_ipad = ptr; + } + else + if (_stricmp(buf, "APPL") == 0) // Using BPQ32 COnfig + { + BPQport = Port; + p_cmd = ptr; + } + else + if (_stricmp(buf, "PORT") != 0) // Using Old Config + { + // New config without a PORT or APPL - this is a Config Command + + strcpy(buf, errbuf); + strcat(buf, "\r"); + + BPQport = Port; + + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + goto ConfigLine; + } + else + + { + + // Old Config from file + + BPQport=0; + BPQport = atoi(ptr); + + p_cmd = strtok(NULL, " \t\n\r"); + + if (Port && Port != BPQport) + { + // Want a particular port, and this isn't it + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + } + } + } + if(BPQport > 0 && BPQport < 33) + { + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + if (p_cmd != NULL) + { + if (p_cmd[0] != ';' && p_cmd[0] != '#') + TNC->ApplCmd=_strdup(p_cmd); + } + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; +ConfigLine: + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if (_memicmp(buf, "WL2KREPORT", 10) == 0) + TNC->WL2K = DecodeWL2KReportLine(buf); + else + strcat (TNC->InitScript, buf); + } + } + + return (TRUE); +} + + + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int txlen = 0; + PMSGWITHLEN buffptr; + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM; + int Stream; + + if (TNC == NULL || TNC->hDevice == 0) + return 0; // Port not open + + switch (fn) + { + case 1: // poll + + while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q + { + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].ReportDISC) + { + TNC->Streams[Stream].ReportDISC = FALSE; + buff->PORT = Stream; + + return -1; + } + } + + CheckRX(TNC); + AEAPoll(port); + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->Streams[Stream].PACTORtoBPQ_Q !=0) + { + size_t datalen; + + buffptr=Q_REM(&TNC->Streams[Stream].PACTORtoBPQ_Q); + + datalen = buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, (int)datalen); + + + ReleaseBuffer(buffptr); + + return (1); + } + } + + return 0; + + case 2: // send + + buffptr = GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + // Find TNC Record + + Stream = buff->PORT; + + if (!TNC->TNCOK) + { + // Send Error Response + + buffptr->Len = sprintf(buffptr->Data, "No Connection to PACTOR TNC\r"); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; + } + + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - 8; + + buffptr->Len = txlen; + memcpy(buffptr->Data, buff->L2DATA, txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + + if(TNC->Streams[Stream].Connected) + { + TNC->Streams[Stream].FramesQueued++; + } + return (0); + + + case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding + + Stream = (int)(size_t)buff; + STREAM = &TNC->Streams[Stream]; + + if (STREAM->FramesQueued > 4) + return 1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15; // Busy + + return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting; // OK + + case 4: // reinit + + return (0); + + case 5: // Close + + EncodeAndSend(TNC, "OHON", 4); // HOST N + Sleep(50); + + SaveWindowPos(port); + + CloseCOMPort(TNC->hDevice); + + return (0); + + case 6: // Scan Control + + return 0; // None Yet + + } + return 0; + +} + + +VOID * AEAExtInit(EXTPORTDATA * PortEntry) +{ + char msg[500]; + struct TNCINFO * TNC; + int port; + char * TempScript; + char * ptr; + + // + // Will be called once for each Pactor Port + // The COM port number is in IOBASE + // + + sprintf(msg,"AEA Pactor %s", PortEntry->PORTCONTROL.SerialPortName); + WritetoConsole(msg); + + port=PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(msg); + + return ExtProc; + } + + TNC->Port = port; + + TNC->PortRecord = PortEntry; + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_AEA; + + TNC->TEXTMODE = FALSE; + + PortEntry->MAXHOSTMODESESSIONS = 11; // Default + + TNC->InitScript = _strupr(TNC->InitScript); + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + { + memcpy(TNC->NodeCall, MYNODECALL, 10); + } + else + { + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + } + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + PortEntry->SCANCAPABILITIES = NONE; // No Scan Interlock + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 100; + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + TempScript = malloc(4000); + + strcpy(TempScript, "RESTART\r"); + strcat(TempScript, "EXPERT ON\r"); + strcat(TempScript, "PTHUFF 0\r"); + strcat(TempScript, "PT200 ON\r"); + strcat(TempScript, "WIDESHFT OFF\r"); + strcat(TempScript, "ARQT 30\r"); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Others go on end so they can't be overriden + + strcat(TNC->InitScript, "XMITOK ON\r"); + strcat(TNC->InitScript, "XFLOW OFF\r"); + strcat(TNC->InitScript, "RXREV OFF\r"); + strcat(TNC->InitScript, "FLOW OFF\r"); + strcat(TNC->InitScript, "AWLEN 8\r"); + strcat(TNC->InitScript, "AUTOBAUD OFF\r"); + strcat(TNC->InitScript, "8BITCONV ON\r"); + strcat(TNC->InitScript, "ALFPAC OFF\r"); + strcat(TNC->InitScript, "ALFDISP OFF\r"); + strcat(TNC->InitScript, "ACRRTTY 0\r"); + strcat(TNC->InitScript, "HPOLL ON\r"); + strcat(TNC->InitScript, "EAS ON\r\r"); + strcat(TNC->InitScript, "CONMODE TRANS\r"); + strcat(TNC->InitScript, "PTOVER $1A\r\r"); + + // Set the ax.25 MYCALL + + sprintf(msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, msg); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 0, 0, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Buffers", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_BUFFERS = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,138,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0", WS_CHILD | WS_VISIBLE,116,138,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 213; + TNC->ClientWidth = 500; + + TNC->hMenu = CreateMenu(); + TNC->hWndMenu = CreatePopupMenu(); + + MoveWindows(TNC); +#endif + + OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); + + WritetoConsole("\n"); + + return ExtProc; +} + +static void CheckRX(struct TNCINFO * TNC) +{ + int Length, Len; + + // only try to read number of bytes in queue + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + // AEA uses SOH ETX Framing + + if (TNC->RXBuffer[0] != SOH) + { + // Char Mode Frame I think we need to see cmd: on end + + // If we think we are in host mode, then to could be noise - just discard. + + if (TNC->HostMode) + { + if (TNC->ReinitState == 2) + { + // Just entered host mode, so could be text command on front + + unsigned char * ptr = strchr(TNC->RXBuffer, SOH); + + if (ptr) + { + int textlen = ptr - TNC->RXBuffer; + + TNC->RXLen -= textlen; + Length = TNC->RXLen; + + Debugprintf("%s", TNC->RXBuffer); + Debugprintf("AEA Entered Host Mode"); + TNC->ReinitState =0; + + memmove(TNC->RXBuffer, ptr, Length); + goto hostFrame; + } + } + + Debugprintf("AEA Bad Host Frame"); + TNC->RXLen = 0; // Ready for next frame + return; + } + + TNC->RXBuffer[TNC->RXLen] = 0; + +// if (TNC->RXBuffer[TNC->RXLen-2] != ':') + if (strstr(TNC->RXBuffer, "cmd:") == 0) + { + if (strlen(TNC->RXBuffer) < TNC->RXLen) + TNC->RXLen = 0; // NULL in text mode message + + return; // Wait for rest of frame + } + + // Complete Char Mode Frame + + TNC->RXLen = 0; // Ready for next frame + + ProcessTermModeResponse(TNC); + return; + } + + // Receiving a Host Mode frame + + if (TNC->HostMode == 0) // If we are in Term Mode, discard it. Probably in recovery + { + TNC->RXLen = 0; // Ready for next frame + return; + } + +hostFrame: + + if (Length < 3) // Minimum Frame Sise + return; + + if (TNC->RXBuffer[Length-1] != ETB) + return; // Wait till we have a full frame + + if (TNC->RXBuffer[Length-2] == DLE && TNC->RXBuffer[Length-3] != DLE) + return; // ??? DLE ETB isn't end of frame, but DLE DLE ETB is + + ProcessHostFrame(TNC, TNC->RXBuffer, Length); // Could have multiple packets in buffer + + TNC->RXLen = 0; // Ready for next frame + + + return; + +} + +static VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, size_t Len) +{ + UCHAR * FendPtr; + size_t NewLen; + + // Split into Packets. By far the most likely is a single packet, so treat as special case + // Beware of DLE ETB and DLE DLE ETB! + + FendPtr = memchr(&rxbuffer[1], ETB, Len-1); + +FENDLoop: + + if (*(FendPtr - 1) == DLE && *(FendPtr - 2) != DLE) + { + FendPtr++; + FendPtr = memchr(FendPtr, ETB, Len-1); + goto FENDLoop; + } + + if (FendPtr == &rxbuffer[Len-1]) + { + ProcessAEAPacket(TNC, &rxbuffer[1], Len - 2); + return; + } + + // Process the first Packet in the buffer + + NewLen = FendPtr - rxbuffer -1; + + ProcessAEAPacket(TNC, &rxbuffer[1], NewLen); + + // Loop Back + + ProcessHostFrame(TNC, FendPtr+1, Len - NewLen - 2); + return; + +} + +static BOOL WriteCommBlock(struct TNCINFO * TNC) +{ + WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); + TNC->Timeout = 50; + return TRUE; +} + +VOID AEAPoll(int Port) +{ + struct TNCINFO * TNC = TNCInfo[Port]; + struct STREAMINFO * STREAM; + UCHAR * Poll = TNC->TXBuffer; + char Status[80]; + int Stream; + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + // Timed Out + + if (TNC->HostMode == 0) + { + DoTermModeTimeout(TNC); + return; + } + + // Timed out in host mode - Clear any connection and reinit the TNC + + TNC->TNCOK = FALSE; + TNC->HostMode = 0; + TNC->ReinitState = 0; + + sprintf(Status,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, Status); + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected + { + TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Nod + } + } + } + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (!TNC->HostMode) + { + DoTNCReinit(TNC); + return; + } + + if (TNC->CommandBusy) + goto Poll; + + // We don't check for a new attach unless Timeout and CommandBusy are both zero, as we need to send a command. + + // If Pactor Session has just been attached, set Pactor Call to the connecting user's callsign + + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] && TNC->Streams[0].Attached == 0) + { + // New Attach + + int calllen; + UCHAR TXMsg[1000]; + int datalen; + char Msg[80]; + + TNC->Streams[0].Attached = TRUE; + TNC->Streams[0].TimeInRX = 0; + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); + TNC->Streams[0].MyCall[calllen] = 0; + + datalen = sprintf(TXMsg, "OMf%s", TNC->Streams[0].MyCall); + EncodeAndSend(TNC, TXMsg, datalen); + TNC->InternalCmd = 'M'; + TNC->CommandBusy = TRUE; + + sprintf(Status, "In Use by %s", TNC->Streams[0].MyCall); + SetWindowText(TNC->xIDC_TNCSTATE, Status); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + + // Shouldn't we also take out of standby mode?? PN is Pactor Listen, for monitoring + + return; + + } + + //If sending internal command list, send next element + + if (TNC->CmdSet) + { + char * start, * end; + size_t len; + + start = TNC->CmdSet; + + if (*(start) == 0) // End of Script + { + free(TNC->CmdSave); + TNC->CmdSet = NULL; + } + else + { + end = strchr(start, 13); + len = ++end - start -1; // exclude cr + TNC->CmdSet = end; + + EncodeAndSend(TNC, start, (int)len); + TNC->InternalCmd = 'X'; + TNC->CommandBusy = TRUE; + + return; + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + } + + if (TNC->NeedPACTOR) + { + TNC->NeedPACTOR--; + + if (TNC->NeedPACTOR == 0) + { + EncodeAndSend(TNC, "OPA", 3); // ??Return to packet mode?? + TNC->CommandBusy = TRUE; + + TNC->CmdSet = TNC->CmdSave = malloc(100); + + sprintf(TNC->CmdSet, "OMf%s\rOPt\rOCETRANS\r", TNC->NodeCall); // Queue Back to Pactor Standby + TNC->InternalCmd = 'T'; + TNC->TEXTMODE = FALSE; + TNC->IntCmdDelay--; + + // Restart Scanning + + sprintf(Status, "%d SCANSTART 15", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Status); + + return; + } + } + + for (Stream = 0; Stream <= MaxStreams; Stream++) + { + if (TNC->TNCOK && TNC->Streams[Stream].BPQtoPACTOR_Q && TNC->DataBusy == FALSE) + { + int datalen; + UCHAR TXMsg[1000]; + PMSGWITHLEN buffptr; + UCHAR * MsgPtr; + char Status[80]; + + if (TNC->Streams[Stream].Connected) + { + if (Stream == 0) + { + // Limit amount in TX + + if (TNC->Streams[0].bytesTXed - TNC->Streams[0].BytesAcked > 200) + continue; + + // If in IRS state for too long, force turnround + + if (TNC->TXRXState == 'R') + { + if (TNC->Streams[0].TimeInRX++ > 15) + { + EncodeAndSend(TNC, "OAG", 3); + TNC->InternalCmd = 'A'; + TNC->CommandBusy = TRUE; + } + else + goto GetStatus; + } + TNC->Streams[0].TimeInRX = 0; + } + + buffptr=Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + TNC->Streams[Stream].FramesQueued--; + datalen=buffptr->Len; + MsgPtr = buffptr->Data; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; + if (strstr(MsgPtr, "Connected")) // Discard *** connected + { + ReleaseBuffer(buffptr); + return; + } + } + + // If in CONV, and data looks binary, switch to TRAN + + TNC->NeedTurnRound = TRUE; // Sending data, so need turnround at end + + if (TNC->TEXTMODE) + { + int i; + UCHAR j; + + for (i = 0; i < datalen; i++) + { + j = MsgPtr[i]; + + if (j > 127 || j == 26 || j < 10) + { + TNC->TEXTMODE = FALSE; + EncodeAndSend(TNC, "OCETRANS", 8); + Debugprintf("Switching to TRANS"); + TNC->CommandBusy = TRUE; + TNC->InternalCmd = 'A'; + break; + } + } + } + + sprintf(TXMsg, "%c", Stream + ' '); + + memcpy(&TXMsg[1], buffptr->Data, datalen); + + EncodeAndSend(TNC, TXMsg, datalen + 1); + ReleaseBuffer(buffptr); + TNC->Streams[Stream].bytesTXed += datalen; + Debugprintf("Stream %d Sending %d, BytesTXED now %d", Stream, datalen, TNC->Streams[Stream].bytesTXed); + TNC->Timeout = 0; + TNC->DataBusy = TRUE; + + if (Stream == 0) + ShowTraffic(TNC); + + if (STREAM->Disconnecting && TNC->Streams[Stream].BPQtoPACTOR_Q == 0) + TidyClose(TNC, 0); + + return; + } + else + { + buffptr=Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + datalen=buffptr->Len; + MsgPtr = buffptr->Data; + + // Command. Do some sanity checking and look for things to process locally + + datalen--; // Exclude CR + MsgPtr[datalen] = 0; // Null Terminate + _strupr(MsgPtr); + + if ((Stream == 0) && memcmp(MsgPtr, "RADIO ", 6) == 0) + { + sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]); + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &MsgPtr[40])) + { + ReleaseBuffer(buffptr); + } + else + { + buffptr->Len = sprintf(buffptr->Data, "%s", &MsgPtr[40]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return; + } + + if (memcmp(MsgPtr, "MODE CONV", 9) == 0) + { + TNC->TEXTMODE = TRUE; + buffptr->Len = sprintf((UCHAR *)buffptr->Data,"AEA} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + EncodeAndSend(TNC, "OCECONV", 7); + TNC->CommandBusy = TRUE; + + return; + } + + if (memcmp(MsgPtr, "MODE TRANS", 9) == 0) + { + TNC->TEXTMODE = FALSE; + buffptr->Len = sprintf(buffptr->Data,"AEA} Ok\r"); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + EncodeAndSend(TNC, "OCETRANS", 8); + TNC->CommandBusy = TRUE; + + return; + } + + if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect + { + memcpy(TNC->Streams[Stream].RemoteCall, &MsgPtr[2], 9); + TNC->Streams[Stream].Connecting = TRUE; + + // If Stream 0, Convert C CALL to PACTOR CALL + + if (Stream == 0) + { + datalen = sprintf(TXMsg, "%cPG%s", Stream + '@', TNC->Streams[0].RemoteCall); + sprintf(Status, "%s Connecting to %s", + TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, Status); + } + else + datalen = sprintf(TXMsg, "%cCO %s", Stream + '@', TNC->Streams[Stream].RemoteCall); + + EncodeAndSend(TNC, TXMsg, datalen); + TNC->InternalCmd = 'C'; // So we dont send the reply to the user. + ReleaseBuffer(buffptr); + TNC->Streams[Stream].Connecting = TRUE; + + return; + } + + if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect + { + if (Stream == 0) + { + EncodeAndSend(TNC, "ODI", 3); // ??Return to packet mode?? + TNC->NeedPACTOR = 50; + TNC->CommandBusy = TRUE; + } + else + { + sprintf(TXMsg, "%cDI", Stream + '@'); + EncodeAndSend(TNC, TXMsg, 3); + TNC->CmdStream = Stream; + } + + TNC->Streams[Stream].Connecting = FALSE; + TNC->Streams[Stream].ReportDISC = TRUE; + ReleaseBuffer(buffptr); + + return; + } + + // Other Command ?? + + if (Stream > 0) + datalen = sprintf(TXMsg, "C20%s", MsgPtr); + else + datalen = sprintf(TXMsg, "O%s", MsgPtr); + + EncodeAndSend(TNC, TXMsg, datalen); + ReleaseBuffer(buffptr); + TNC->InternalCmd = 0; + TNC->CommandBusy = TRUE; + TNC->CmdStream = Stream; + } + } + } + +GetStatus: + + // Need to poll data and control channel (for responses to commands) + + // Also check status if we have data buffered (for flow control) + + if (TNC->TNCOK) + { + if (TNC->IntCmdDelay <= 0) + { + EncodeAndSend(TNC, "OOP", 3); + TNC->InternalCmd = 'S'; + TNC->CommandBusy = TRUE; + TNC->IntCmdDelay = 9; // Every second + return; + } + else + TNC->IntCmdDelay--; + } + +Poll: + // Nothing doing - send Poll (but not too often) + + TNC->PollDelay++; + + if (TNC->PollDelay < 3) + return; + + TNC->PollDelay = 0; + + EncodeAndSend(TNC, "OGG", 3); // Poll + + return; +} + +static VOID DoTNCReinit(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 1) // Forcing back to Term + TNC->ReinitState = 0; // Got Response, so must be back in term mode + + if (TNC->ReinitState == 0) + { + // Just Starting - Send a CR to see if in Terminal or Host Mode + + char Status[80]; + + sprintf(Status,"%s Initialising TNC", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, Status); + + Poll[0] = 13; + TNC->TXLen = 1; + TNC->RXLen = 0; + + WriteCommBlock(TNC); + + return; + } + + if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands + { + char * start, * end; + size_t len; + + start = TNC->InitPtr; + + if (*(start) == 0) // End of Script + { + // Put into Host Mode + + + Poll[0] = 0x11; // XON + Poll[1] = 0x18; // CAN + Poll[2] = 0x03; // COM + + memcpy(&Poll[3], "HOST Y\r", 7); + + TNC->TXLen = 10; + WriteCommBlock(TNC); + TNC->Timeout = 0; + TNC->CommandBusy = FALSE; + TNC->DataBusy = FALSE; + + TNC->HostMode = TRUE; // Should now be in Host Mode + TNC->NeedPACTOR = 50; // Need to Send PACTOR command after 5 secs + + return; + } + + end = strchr(start, 13); + len = ++end - start; + TNC->InitPtr = end; + memcpy(Poll, start, len); + + TNC->TXLen = (int)len; + WriteCommBlock(TNC); + + + return; + + } +} + +static VOID DoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + + EncodeAndSend(TNC, "OHON", 4); // HOST N + TNC->Timeout = 20; + return; + } + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + DoTNCReinit(TNC); // See if worked + return; + } +} + +static VOID ProcessTermModeResponse(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + Debugprintf("AEA Initstate %d Response %s", TNC->ReinitState, TNC->RXBuffer); + + if (TNC->ReinitState == 0 || TNC->ReinitState == 1) + { + // Testing if in Term Mode. It is, so can now send Init Commands + + TNC->InitPtr = TNC->InitScript; + TNC->ReinitState = 2; + DoTNCReinit(TNC); // Send First Command + return; + } + if (TNC->ReinitState == 2) + { + // Sending Init Commands + + DoTNCReinit(TNC); // Send Next Command + return; + } +} + +static VOID ProcessAEAPacket(struct TNCINFO * TNC, UCHAR * Msg, size_t Len) +{ + PMSGWITHLEN buffptr; + char * Buffer = &Msg[1]; // Data portion of frame + char * Call; + char Status[80]; + int Stream = 0; + int Opcode; + struct STREAMINFO * STREAM; + + // Any valid frame is an ACK + + TNC->TNCOK = TRUE; + TNC->Timeout = 0; + + Len = DLEDecode(Msg, Msg, Len); // Remove KISS transparency + + Stream = Msg[0] & 15; + Opcode = Msg[0] >> 4; + + STREAM = &TNC->Streams[Stream]; + + if (Msg[0] == 'O' && Msg[1] == 'G' && Msg[2] == 'G') + { + if (Msg[3] == 0) + { + // OK Response + + sprintf(Status,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, Status); + + return; + } + } + + if (Msg[0] == '/') // 2F + { + char * Eptr; + + // Echoed Data + + Len--; + + Eptr = memchr(Buffer, 0x1c, Len); + + if (Eptr) + Debugprintf("Echoed 1c followed by %x", *(++Eptr)); + + TNC->Streams[0].BytesAcked += (int)Len; + + Debugprintf("Ack for %d, BytesAcked now %d", Len, TNC->Streams[0].BytesAcked); + ShowTraffic(TNC); + + // If nothing more to send, turn round link + + if ((TNC->Streams[0].BPQtoPACTOR_Q == 0) && TNC->NeedTurnRound && + (TNC->Streams[0].BytesAcked >= TNC->Streams[0].bytesTXed)) // Nothing following and all acked + { + Debugprintf("AEA Sent = Acked - sending Turnround"); + + if (TNC->TEXTMODE == 0) + { + // In Trans - switch back + + TNC->TEXTMODE = TRUE; + EncodeAndSend(TNC, "OCECONV", 7); + Debugprintf("Switching to CONV"); + TNC->CommandBusy = TRUE; + TNC->InternalCmd = 'A'; + TNC->NeedTRANS = TRUE; + } + + TNC->NeedTurnRound = FALSE; + EncodeAndSend(TNC, OverMsg, 2); + TNC->Timeout = 0; // No response to data + } + return; + } + + if (Opcode == 3) + { + // Received Data + + // Pass to Appl + + buffptr = GetBuff(); + if (buffptr == NULL) return; // No buffers, so ignore + + Len--; // Remove Header + + buffptr->Len = Len; // Length + TNC->Streams[Stream].bytesRXed += (int)Len; + memcpy(&buffptr->Data[0], Buffer, Len); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + if (Stream == 0) + ShowTraffic(TNC); + + return; + } + + + if (Opcode == 4) + { + // Link Status or Command Response + + TNC->CommandBusy = FALSE; + + if (TNC->InternalCmd) + { + // Process it + + if (TNC->InternalCmd == 'S') // Status + { +// if (Msg[3] == 'P' && Msg[4] == 'G') + { + SetWindowText(TNC->xIDC_STATE, status[Msg[5] - 0x30]); + + TNC->TXRXState = Msg[6]; + + if (Msg[6] == 'S') + SetWindowText(TNC->xIDC_TXRX, "Sender"); + else + SetWindowText(TNC->xIDC_TXRX, "Receiver"); + + Msg[12] = 0; + SetWindowText(TNC->xIDC_MODE, Msg); + + // Testing.. I think ZF returns buffers + + EncodeAndSend(TNC, "OZF", 3); + TNC->InternalCmd = 'Z'; + TNC->CommandBusy = TRUE; + } + + return; + } + + if (TNC->InternalCmd == 'Z') // Buffers? + { + Msg[Len] = 0; + SetWindowText(TNC->xIDC_BUFFERS, &Msg[3]); + return; + } + + return; + } + + // Reply to Manual command - Pass to Appl + + Stream = TNC->CmdStream; + STREAM = &TNC->Streams[Stream]; + + + buffptr = GetBuff(); + + if (buffptr == NULL) return; // No buffers, so ignore + + Buffer[Len - 1] = 13; + Buffer[Len] = 0; + + buffptr->Len = sprintf(&buffptr->Data[0],"AEA} %s", Buffer); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return; + + } + + if (Opcode == 5) + { + // Link Messages (Connect, etc) + + if (Stream == 15) + { + // SOH $5F X X $00 ETB data acknowledgement + + if (Msg[1] == 'X' && Msg[2] == 'X' && Msg[3] == 0) + { + TNC->DataBusy = FALSE; + + if (TNC->NeedTRANS) // Sent CTRL/Z in conv mode - switch back to trans + { + TNC->NeedTRANS = FALSE; + TNC->TEXTMODE = FALSE; + EncodeAndSend(TNC, "OCETRANS", 8); + Debugprintf("Switching to TRANS"); + TNC->CommandBusy = TRUE; + TNC->InternalCmd = 'A'; + } + else + EncodeAndSend(TNC, "OGG", 3); // Send another Poll + } + return; + } + + if (strstr(Buffer, "DISCONNECTED") || strstr(Buffer, "Timeout")) + { + if ((TNC->Streams[Stream].Connecting | TNC->Streams[Stream].Connected) == 0) + { + // Not connected or Connecting. Probably response to going into Pactor Listen Mode + + return; + } + + if (STREAM->Connecting && STREAM->Disconnecting == FALSE) + { + // Connect Failed + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "*** Failure with %s\r", TNC->Streams[Stream].RemoteCall); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + TNC->Streams[Stream].Connecting = FALSE; + TNC->Streams[Stream].Connected = FALSE; // In case! + TNC->Streams[Stream].FramesQueued = 0; + + return; + } + + // Connected, or Disconnecting - Release Session + + TNC->Streams[Stream].Connecting = FALSE; + TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode + TNC->Streams[Stream].FramesQueued = 0; + + if (STREAM->Disconnecting == FALSE) + STREAM->ReportDISC = TRUE; // Tell Node + + STREAM->Disconnecting = FALSE; + + // Need to reset Pactor Call in case it was changed + + TNC->NeedPACTOR = 20; + + if (Stream == 0) + { + // Claar any data from buffers + + EncodeAndSend(TNC, "OTC", 3); // Clear buffers + TNC->NeedPACTOR = 20; + TNC->InternalCmd = 'T'; + TNC->CommandBusy = TRUE; + } + + return; + } + + Call = strstr(Buffer, "NNECTED to"); + + if (Call) + { + Call+=11; // To Callsign + + if (Stream == 0) + { + Buffer[Len-2] = 0; + } + + TNC->Streams[Stream].bytesRXed = TNC->Streams[Stream].bytesTXed = TNC->Streams[Stream].BytesAcked = 0; + TNC->Streams[Stream].ConnectTime = time(NULL); + + if (Stream == 0) + { + // Stop Scanner + + char Msg[80]; + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + + ShowTraffic(TNC); + + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) + { + // Incoming Connect + + ProcessIncommingConnect(TNC, Call, Stream, TRUE); + + if (Stream == 0) + { + struct WL2KInfo * WL2K = TNC->WL2K; + char FreqAppl[10] = ""; // Frequecy-specific application + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig) + { + // If Scan Entry has a Appl, save it + + if (TNC->RIG->FreqPtr && TNC->RIG->FreqPtr[0]->APPL[0]) + strcpy(FreqAppl, &TNC->RIG->FreqPtr[0]->APPL[0]); + } + + // We are going to Send something, so turn link round + + EncodeAndSend(TNC, "OAG", 3); + TNC->InternalCmd = 'A'; + TNC->CommandBusy = TRUE; + + sprintf(Status, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->NodeCall); + SetWindowText(TNC->xIDC_TNCSTATE, Status); + + // If an autoconnect APPL is defined, send it + + if (FreqAppl[0]) // Frequency specific APPL overrides TNC APPL + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "%s\r", FreqAppl); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + TNC->SwallowSignon = TRUE; + return; + } + + if (TNC->ApplCmd) + { + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "%s\r", TNC->ApplCmd); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + return; + } + } + + if (FULL_CTEXT && HFCTEXTLEN == 0) + { + char CTBuff[300]; + int Len = CTEXTLEN, CTPaclen = 50; + int Next = 0; + + CTBuff[0] = Stream + ' '; + + while (Len > CTPaclen) // CTEXT Paclen + { + memcpy(&CTBuff[1], &CTEXTMSG[Next], CTPaclen); + EncodeAndSend(TNC, CTBuff, CTPaclen + 1); + Next += CTPaclen; + Len -= CTPaclen; + } + + memcpy(&CTBuff[1], &CTEXTMSG[Next], Len); + EncodeAndSend(TNC, CTBuff, Len + 1); + } + return; + + } + else + { + // Connect Complete + + buffptr = GetBuff(); + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "*** Connected to %s\r", Call);; + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + TNC->Streams[Stream].Connecting = FALSE; + TNC->Streams[Stream].Connected = TRUE; // Subsequent data to data channel + + if (Stream == 0) + { + sprintf(Status, "%s Connected to %s Outbound", TNC->NodeCall, TNC->Streams[0].RemoteCall); + SetWindowText(TNC->xIDC_TNCSTATE, Status); + UpdateMH(TNC, Call, '+', 'O'); + } + + return; + } + } + } +} + + +int DLEDecode(UCHAR * inbuff, UCHAR * outbuff, size_t len) +{ + unsigned int i, txptr = 0; + UCHAR c; + + for (i=0; iTXLen = DLEEncode(txbuffer, TNC->TXBuffer, Len); + + WriteCommBlock(TNC); +} + +static int DLEEncode(UCHAR * inbuff, UCHAR * outbuff, size_t len) +{ + unsigned int i, txptr = 0; + UCHAR c; + + outbuff[0] = SOH; + txptr=1; + + for (i=0; iInternalCmd = 'P'; + TNC->CommandBusy = TRUE; + } + else + { + UCHAR TXMsg[10]; + + sprintf(TXMsg, "%cDI", Stream + '@'); + EncodeAndSend(TNC, TXMsg, 4); // Send twice - must force a disconnect + } +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + TidyClose(TNC, Stream); // Send DI again - can't do much else +} + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + TNC->NeedPACTOR = 50; +} diff --git a/.svn/pristine/6f/6f21d906b78020e00b54e3e16146bf66c1df728c.svn-base b/.svn/pristine/6f/6f21d906b78020e00b54e3e16146bf66c1df728c.svn-base new file mode 100644 index 0000000..9df59a2 --- /dev/null +++ b/.svn/pristine/6f/6f21d906b78020e00b54e3e16146bf66c1df728c.svn-base @@ -0,0 +1,4551 @@ +/* +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 +*/ + +// +// C replacement for L2Code.asm +// +#define Kernel + +#define _CRT_SECURE_NO_DEPRECATE + + +#pragma data_seg("_BPQDATA") + +#include "time.h" +#include "stdio.h" + +#include "cheaders.h" +#include "tncinfo.h" + +// This is needed to link with a lib built from source + +#ifdef WIN32 +#define ZEXPORT __stdcall +#endif + +#include + + +#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE + +#define REJSENT 1 // SET WHEN FIRST REJ IS SENT IN REPLY + // TO AN I(P) +#define RNRSET 0x2 // RNR RECEIVED FROM OTHER END +#define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED +#define RNRSENT 0x10 // WE HAVE SEND RNR +#define POLLSENT 0x20 // POLL BIT OUTSTANDING + +#define ONEMINUTE 60*3 +#define TENSECS 10*3 +#define THREESECS 3*3 + + +VOID L2Routine(struct PORTCONTROL * PORT, PMESSAGE Buffer); +MESSAGE * SETUPL2MESSAGE(struct _LINKTABLE * LINK, UCHAR CMD); +VOID SendSupervisCmd(struct _LINKTABLE * LINK); +void SEND_RR_RESP(struct _LINKTABLE * LINK, UCHAR PF); +VOID L2SENDRESPONSE(struct _LINKTABLE * LINK, int CMD); +VOID L2SENDCOMMAND(struct _LINKTABLE * LINK, int CMD); +VOID ACKMSG(struct _LINKTABLE * LINK); +VOID InformPartner(struct _LINKTABLE * LINK, int Reason); +UINT RR_OR_RNR(struct _LINKTABLE * LINK); +VOID L2TIMEOUT(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT); +VOID CLEAROUTLINK(struct _LINKTABLE * LINK); +VOID SENDFRMR(struct _LINKTABLE * LINK); +char * SetupNodeHeader(struct DATAMESSAGE * Buffer); +VOID CLEARSESSIONENTRY(TRANSPORTENTRY * Session); +VOID SDFRMR(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT); +VOID SDNRCHK(struct _LINKTABLE * LINK, UCHAR CTL); +VOID RESETNS(struct _LINKTABLE * LINK, UCHAR NS); +VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer); +VOID RESET2X(struct _LINKTABLE * LINK); +VOID RESET2(struct _LINKTABLE * LINK); +VOID CONNECTREFUSED(struct _LINKTABLE * LINK); +VOID SDUFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL); +VOID SFRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, UCHAR CTL, UCHAR MSGFLAG); +VOID SDIFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG); +VOID SENDCONNECTREPLY(struct _LINKTABLE * LINK); +VOID SETUPNEWL2SESSION(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR MSGFLAG); +BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE); +VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR MSGFLAG); +VOID L2SENDUA(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER); +VOID L2SENDDM(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER); +VOID L2SENDRESP(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL); +int COUNTLINKS(int Port); +VOID L2_PROCESS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG); +TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK); +BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions); +VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer); +VOID L2SWAPADDRESSES(MESSAGE * Buffer); +BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK); +VOID SENDSABM(struct _LINKTABLE * LINK); +VOID L2SENDXID(struct _LINKTABLE * LINK); +VOID __cdecl Debugprintf(const char * format, ...); +VOID Q_IP_MSG(MESSAGE * Buffer); +VOID PROCESSNODEMESSAGE(MESSAGE * Msg, struct PORTCONTROL * PORT); +VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); +BOOL CompareAliases(UCHAR * c1, UCHAR * c2); +VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); +VOID Digipeat(struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR * OurCall, int toPort, int UIOnly); +VOID DigiToMultiplePorts(struct PORTCONTROL * PORTVEC, PMESSAGE Msg); +VOID MHPROC(struct PORTCONTROL * PORT, MESSAGE * Buffer); +BOOL CheckForListeningSession(struct PORTCONTROL * PORT, MESSAGE * Msg); +VOID L2SENDINVALIDCTRL(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL); +UCHAR * SETUPADDRESSES(struct _LINKTABLE * LINK, PMESSAGE Msg); +VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); +int CountBits(uint32_t in); +void AttachKISSHF(struct PORTCONTROL * PORT, MESSAGE * Buffer); +void DetachKISSHF(struct PORTCONTROL * PORT); +void KISSHFConnected(struct PORTCONTROL * PORT, struct _LINKTABLE * LINK); +void WriteConnectLog(char * fromcall, char * tocall, UCHAR * Mode); +int seeifInterlockneeded(struct PORTCONTROL * PORT); +int seeifUnlockneeded(struct _LINKTABLE * LINK); +int CheckKissInterlock(struct PORTCONTROL * MYPORT, int Exclusive); +void hookL2SessionAccepted(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK); +void hookL2SessionDeleted(struct _LINKTABLE * LINK); +void hookL2SessionAttempt(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK); +int L2Compressit(unsigned char * Out, int OutSize, unsigned char * In, int Len); + +extern int REALTIMETICKS; + +// MSGFLAG contains CMD/RESPONSE BITS + +#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND +#define RESP 2 // CURRENT MSG IS RESPONSE +#define VER1 1 // CURRENT MSG IS VERSION 1 + +// FRMR REJECT FLAGS + +#define SDINVC 1 // INVALID COMMAND +#define SDNRER 8 // INVALID N(R) + +extern int L2Compress; +extern int L2CompMaxframe; +extern int L2CompPaclen; + +UCHAR NO_CTEXT = 0; +UCHAR ALIASMSG = 0; + +static UCHAR ISNETROMMSG = 0; +UCHAR MSGFLAG = 0; +extern char * ALIASPTR; + +UCHAR QSTCALL[7] = {'Q'+'Q','S'+'S','T'+'T',0x40,0x40,0x40,0xe0}; // QST IN AX25 +UCHAR NODECALL[7] = {0x9C, 0x9E, 0x88, 0x8A, 0xA6, 0x40, 0xE0}; // 'NODES' IN AX25 FORMAT + +extern BOOL LogAllConnects; + +APPLCALLS * APPL; + + +void SendL2ToMonMap(struct PORTCONTROL * PORT, char * ReportCall, char Mode, char Direction) +{ + // if Port Freq < 30Mhz send to Node Map + + if (PORT->PortFreq && PORT->PortFreq < 30000000) + { + char ReportMode[16]; + char ReportFreq[350] = ""; + + ReportMode[0] = '@'; + ReportMode[1] = Mode; + ReportMode[2] = '?'; + ReportMode[3] = Direction; + ReportMode[4] = 0; + + // If no position see if we have an APRS posn + + _gcvt(PORT->PortFreq, 9, ReportFreq); + + SendMH(0, ReportCall, ReportFreq, 0, ReportMode); + } +} + +VOID L2Routine(struct PORTCONTROL * PORT, PMESSAGE Buffer) +{ + // LEVEL 2 PROCESSING + + MESSAGE * ADJBUFFER; + struct _LINKTABLE * LINK; + UCHAR * ptr; + int n; + UCHAR CTL; + uintptr_t Work; + UCHAR c; + unsigned int APPLMASK = 0; + + // Check for invalid length (< 22 7Header + 7Addr + 7Addr + CTL + + if (Buffer->LENGTH < (18 + sizeof(void *))) + { + Debugprintf("BPQ32 Bad L2 Msg Port %d Len %d", PORT->PORTNUMBER, Buffer->LENGTH); + ReleaseBuffer(Buffer); + return; + } + + PORT->L2FRAMES++; + + ALIASMSG = 0; + ISNETROMMSG = 0; + + MSGFLAG = 0; // CMD/RESP UNDEFINED + + // Check for Corrupted Callsign in Origin (to keep MH list clean) + + ptr = &Buffer->ORIGIN[0]; + n = 6; + + c = *(ptr) >> 1; + + if (c == ' ') // Blank Call + { + Debugprintf("BPQ32 Blank Call Port %d", PORT->PORTNUMBER); + ReleaseBuffer(Buffer); + return; + } + + while(n--) + { + // Try a bit harder to detect corruption + + c = *(ptr++); + + if (c & 1) + { + ReleaseBuffer(Buffer); + return; + } + + c = c >> 1; + + if (!isalnum(c) && !(c == '#') && !(c == ' ')) + { + ReleaseBuffer(Buffer); + return; + } + } + + // Check Digis if present + + if ((Buffer->ORIGIN[6] & 1) == 0) // Digis + { + ptr = &Buffer->CTL; + n = 6; + + while(n--) + { + c = *(ptr++); + + if (c & 1) + { + ReleaseBuffer(Buffer); + return; + } + + c = c >> 1; + + if (!isalnum(c) && !(c == '#') && !(c == ' ')) + { + ReleaseBuffer(Buffer); + return; + } + } + } + + BPQTRACE(Buffer, TRUE); // TRACE - RX frames to APRS + + if (PORT->PORTMHEARD) + MHPROC(PORT, Buffer); + + + /// TAJ added 07/12/2020 for 'all RX traffic as IfinOctects + + InOctets[PORT->PORTNUMBER] += Buffer->LENGTH - MSGHDDRLEN; + + // CHECK THAT ALL DIGIS HAVE BEEN ACTIONED, + // AND ADJUST FOR DIGIPEATERS IF PRESENT + + n = 8; // MAX DIGIS + ptr = &Buffer->ORIGIN[6]; // End of Address bit + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + ptr += 7; + + if ((*ptr & 0x80) == 0) // Digi'd bit + { + // FRAME HAS NOT BEEN REPEATED THROUGH CURRENT DIGI - + // SEE IF WE ARE MEANT TO DIGI IT + + struct XDIGI * XDigi = PORT->XDIGIS; // Cross port digi setup + + ptr -= 6; // To start of Call + + if (CompareCalls(ptr, MYCALL) || CompareAliases(ptr, MYALIAS) || + CompareCalls(ptr, PORT->PORTALIAS) || CompareCalls(ptr, PORT->PORTALIAS2)) + { + Digipeat(PORT, Buffer, ptr, 0, 0); // Digi it (if enabled) + return; + } + + while (XDigi) + { + if (CompareCalls(ptr, XDigi->Call)) + { + Digipeat(PORT, Buffer, ptr, XDigi->Port, XDigi->UIOnly); // Digi it (if enabled) + return; + } + XDigi = XDigi->Next; + } + + ReleaseBuffer(Buffer); + return; // not complete and not for us + } + n--; + + if (n == 0) + { + ReleaseBuffer(Buffer); + return; // Corrupt - no end of address bit + } + } + + // Reached End of digis, and all actioned, so can process it + + Work = (uintptr_t)&Buffer->ORIGIN[6]; + ptr -= Work; // ptr is now length of digis + + Work = (uintptr_t)Buffer; + ptr += Work; + + ADJBUFFER = (MESSAGE * )ptr; // ADJBUFFER points to CTL, etc. allowing for digis + + // GET CMD/RESP BITS + + if (Buffer->DEST[6] & 0x80) + { + if (Buffer->ORIGIN[6] & 0x80) // Both set, assume V1 + MSGFLAG |= VER1; + else + MSGFLAG |= CMDBIT; + } + else + { + if (Buffer->ORIGIN[6] & 0x80) // Only Dest Set + MSGFLAG |= RESP; + else + MSGFLAG |= VER1; // Neither, assume V1 + } + + // SEE IF FOR AN ACTIVE LINK SESSION + + CTL = ADJBUFFER->CTL; + + // IF A UI, THERE IS NO SESSION + + if (FindLink(Buffer->ORIGIN, Buffer->DEST, PORT->PORTNUMBER, &LINK)) + { + L2LINKACTIVE(LINK, PORT, Buffer,ADJBUFFER, CTL, MSGFLAG); + return; + } + + // NOT FOR ACTIVE LINK - SEE IF ADDRESSED TO OUR ADDRESSES + + // FIRST TRY PORT ADDR/ALIAS + + if(PORT->PORTBBSFLAG == 1) + goto PORTCALLISBBS; // PORT CALL/ALIAS ARE FOR BBS + + if (NODE) + goto USING_NODE; + +PORTCALLISBBS: + + // NODE IS NOT ACTIVE, SO PASS CALLS TO PORTCALL/ALIAS TO BBS + + APPLMASK = 1; + + if (CompareCalls(Buffer->DEST, NETROMCALL)) + { + ISNETROMMSG = 1; + goto FORUS; + } + if (PORT->PORTL3FLAG) // L3 Only Port? + goto NOTFORUS; // If L3ONLY, only accept calls to NETROMCALL + + ISNETROMMSG = 0; + +USING_NODE: + + if (CompareCalls(Buffer->DEST, PORT->PORTCALL)) + goto FORUS; + + ALIASMSG = 1; + + if (CompareAliases(Buffer->DEST, PORT->PORTALIAS)) // only compare 6 bits - allow any ssid + goto FORUS; + + if (NODE == 0) + goto TRYBBS; // NOT USING NODE SYSTEM + + ALIASMSG = 0; + + if (CompareCalls(Buffer->DEST, MYCALL)) + goto FORUS; + + ALIASMSG = 1; + + if (CompareAliases(Buffer->DEST, MYALIAS)) // only compare 6 bits - allow any ssid + goto FORUS; + +TRYBBS: + + if (BBS == 0) + goto NOWTRY_NODES; // NOT USING BBS CALLS + + // TRY APPLICATION CALLSIGNS/ALIASES + + + APPLMASK = 1; + ALIASPTR = &CMDALIAS[0][0]; + + n = NumberofAppls; + + APPL = APPLCALLTABLE; + + while (n--) + { + if (APPL->APPLCALL[0] > 0x40) // Valid ax.25 addr + { + // WE MAY NOT BE ALLOWED TO USE THE BBS CALL ON SOME BANDS DUE TO + // THE RATHER ODD UK LICENCING RULES! + // For backward compatibility only apply to appl 1 + + if ((PORT->PERMITTEDAPPLS & APPLMASK) != 0) + { + ALIASMSG = 0; + + if (CompareCalls(Buffer->DEST, APPL->APPLCALL)) + goto FORUS; + + ALIASMSG = 1; + + if (CompareAliases(Buffer->DEST, APPL->APPLALIAS)) // only compare 6 bits - allow any ssid + goto FORUS; + + if (CompareAliases(Buffer->DEST, APPL->L2ALIAS)) // only compare 6 bits - allow any ssid + goto FORUS; + } + } + APPLMASK <<= 1; + ALIASPTR += ALIASLEN; + APPL++; + } + + // NOT FOR US - SEE IF 'NODES' OR IP/ARP BROADCAST MESSAGE + +NOWTRY_NODES: + + if (CompareCalls(Buffer->DEST, QSTCALL)) + { + Q_IP_MSG(Buffer); // IP BROADCAST + return; + } + + if (ADJBUFFER->PID != 0xCF) // NETROM MSG? + goto NOTFORUS; // NO + + if (CompareCalls(Buffer->DEST, NODECALL)) + { + if (Buffer->L2DATA[0] == 0xff) // Valid NODES Broadcast + { + PROCESSNODEMESSAGE(Buffer, PORT); + } + } + + ReleaseBuffer(Buffer); + return; + +NOTFORUS: + // + // MAY JUST BE A REPLY TO A 'PRIMED' CQ CALL + // + if ((CTL & ~PFBIT) == SABM) + if (CheckForListeningSession(PORT, Buffer)) + return; // Used buffer to send UA + + ReleaseBuffer(Buffer); + return; + +FORUS: + + // if a UI frame and UIHook Specified, call it + + if (PORT->UIHook && CTL == 3) + PORT->UIHook(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); + + LINK->APPLMASK = APPLMASK; + + L2FORUS(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); +} + + +VOID MHPROC(struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + PMHSTRUC MH = PORT->PORTMHEARD; + PMHSTRUC MHBASE = MH; + int i; + int OldCount = 0; + char Freq[64] = ""; + char DIGI = '*'; + double ReportFreq = 0; + + // if port has a freq associated with it use it + + GetPortFrequency(PORT->PORTNUMBER, Freq); + + // if (Buffer->ORIGIN[6] & 1) + DIGI = 0; // DOn't think we want to do this + + // See if in list + + for (i = 0; i < MHENTRIES; i++) + { + if ((MH->MHCALL[0] == 0) || (CompareCalls(Buffer->ORIGIN, MH->MHCALL) && MH->MHDIGI == DIGI)) // Spare or our entry + { + OldCount = MH->MHCOUNT; + goto DoMove; + } + MH++; + } + + // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP + + i = MHENTRIES - 1; + + // Move others down and add at front +DoMove: + if (i != 0) // First + memmove(MHBASE + 1, MHBASE, i * sizeof(MHSTRUC)); + + memcpy (MHBASE->MHCALL, Buffer->ORIGIN, 7 * 9); // Save Digis + MHBASE->MHDIGI = DIGI; + MHBASE->MHTIME = time(NULL); + MHBASE->MHCOUNT = ++OldCount; + strcpy(MHBASE->MHFreq, Freq); + MHBASE->MHLocator[0] = 0; + + return; +} + + +int CountFramesQueuedOnSession(TRANSPORTENTRY * Session) +{ + // COUNT NUMBER OF FRAMES QUEUED ON A SESSION + + if (Session == 0) + return 0; + + if (Session->L4CIRCUITTYPE & BPQHOST) + { + return C_Q_COUNT(&Session->L4TX_Q); + } + + if (Session->L4CIRCUITTYPE & SESSION) + { + // L4 SESSION - GET NUMBER UNACKED, AND ADD NUMBER ON TX QUEUE + + int Count = C_Q_COUNT(&Session->L4TX_Q); + UCHAR Unacked = Session->TXSEQNO - Session->L4WS; + + return Count + Unacked; + } + + if (Session->L4CIRCUITTYPE & PACTOR) + { + // PACTOR Type - Frames are queued on the Port Entry + + struct PORTCONTROL * PORT = Session->L4TARGET.PORT; + EXTPORTDATA * EXT = (EXTPORTDATA *)PORT; + + int ret = EXT->FramesQueued; + + // Check L4 Queue as messages can stay there briefly + + ret += C_Q_COUNT(&Session->L4RX_Q); + + return ret + C_Q_COUNT(&PORT->PORTTX_Q); + } + + // L2 CIRCUIT + + { + int SessCount = C_Q_COUNT(&Session->L4TX_Q); + struct _LINKTABLE * LINK = Session->L4TARGET.LINK; + int L2 = COUNT_AT_L2(LINK); + + return SessCount + L2; + } +} + +int CHECKIFBUSYL2(TRANSPORTENTRY * Session) +{ + // RETURN TOP BIT OF AL SET IF SESSION PARTNER IS BUSY + + if (Session->L4CROSSLINK) // CONNECTED? + { + Session = Session->L4CROSSLINK; + + if (CountFramesQueuedOnSession(Session) > 10) + return L4BUSY;; + } + return 0; +} + +VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) +{ + // MESSAGE ADDRESSED TO OUR CALL OR ALIAS, BUT NOT FOR AN ACTIVE SESSION + + // LINK points to an empty link table entry + + struct ROUTE * ROUTE; + int CTLlessPF = CTL & ~PFBIT; + + PORT->L2FRAMESFORUS++; + + NO_CTEXT = 0; + + // ONLY SABM or UI ALLOWED IF NO SESSION + // Plus XID/TEST/SABME if V2.2 support enabled + + if (CTLlessPF == 3) // UI + { + // A UI ADDRESSED TO US - SHOULD ONLY BE FOR IP, or possibly addressed NODES + + switch(ADJBUFFER->PID) + { + case 0xcf: // Netrom + + if (Buffer->L2DATA[0] == 0xff) // NODES + PROCESSNODEMESSAGE(Buffer, PORT); + + break; + + case 0xcc: // TCP + case 0xcd: // ARP + case 0x08: // NOS FRAGMENTED AX25 TCP/IP + + Q_IP_MSG( Buffer); + return; + } + + ReleaseBuffer(Buffer); + return; + } + + if (PORT->PortUIONLY) // Port is for UI only + { + ReleaseBuffer(Buffer); + return; + } + + if (CTLlessPF == SABME) + { + // Although some say V2.2 requires SABME I don't agree! + + // Reject until we support Mod 128 + + L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + return; + } + + if (CTLlessPF == SREJ) // Used to see if other end supports SREJ on 2.0 + { + // Send FRMR if dont support SREJ + // Send DM if we do + + if (SUPPORT2point2) + L2SENDRESP(PORT, Buffer, ADJBUFFER, DM); + else + L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + + return; + } + + if (CTLlessPF == XID) + { + // Send FRMR if we only support V 2.0 + + if (SUPPORT2point2 == FALSE) + { + L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + return; + } + // if Support 2.2 drop through + } + + if (CTLlessPF == TEST) + { + // I can't see amy harm in replying to TEST + + L2SENDRESP(PORT, Buffer, ADJBUFFER, TEST); + return; + } + + +// if (CTLlessPF != SABM && CTLlessPF != SABME) + if (CTLlessPF != SABM && CTLlessPF != XID) + { + if ((MSGFLAG & CMDBIT) && (CTL & PFBIT)) // Command with P? + L2SENDDM(PORT, Buffer, ADJBUFFER); + else + ReleaseBuffer(Buffer); // Ignore if not + + return; + } + + // Exclude and limit tests are done for XID and SABM + + if (NODE == 0 && BBS == 0) // Don't want any calls + { + ReleaseBuffer(Buffer); + return; + } + +#ifdef EXCLUDEBITS + + // CHECK ExcludeList + + if (CheckExcludeList(Buffer->ORIGIN) == 0) + { + ReleaseBuffer(Buffer); + return; + } +#endif + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (PORT->PERMITTEDCALLS) + { + UCHAR * ptr = PORT->PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(Buffer->ORIGIN, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + ReleaseBuffer(Buffer); + return; + } + } + } + + // IF CALL REQUEST IS FROM A LOCKED NODE WITH QUALITY ZERO, IGNORE IT + + if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE)) + { + // From a known node + + NO_CTEXT = 1; + + if (ROUTE->NEIGHBOUR_FLAG && ROUTE->NEIGHBOUR_QUAL == 0) // Locked, qual 0 + { + ReleaseBuffer(Buffer); + return; + } + } + + // CHECK PORT CONNECT LIMITS + + if (PORT->USERS) + { + if (COUNTLINKS(PORT->PORTNUMBER) >= PORT->USERS) + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + } + + // if KISSHF, check if attached. If so, reject. If not, attach. + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + { + struct TNCINFO * TNC = PORT->TNC; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0]) + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + } + + // OK to accept SABM or XID + + if (CTLlessPF == XID) + { + ProcessXIDCommand(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); + return; + } + + // Not XID, so must be SABM + + L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM +} + + +VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) +{ + // I think it is fairly safe to accept XID as soon as we + // can process SREJ, but only accept Mod 8 and 256 Byte frames + + // I think the only way to run 2.2 Mod 8 is to preceed a + // SABM with XID, but others don't seem to agree! + + // Run through XID fields, changing any we don't like, + // then return an XID response + + // Decode and process XID + + UCHAR * ptr = &ADJBUFFER->PID; + UCHAR * ptr1, * ptr2; + UCHAR TEMPDIGI[57]; + int n; + + // Check Interlock - should we also check exclude etc?. No, checked in L2FORUS + + if (CheckKissInterlock(PORT, TRUE)) // Interlock with ARDOP/VARA etc + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + if (*ptr++ == 0x82 && *ptr++ == 0x80) + { + int Type; + int Len; + unsigned int value; + int xidlen = *(ptr++) << 8; + xidlen += *ptr++; + + // XID is set of Type, Len, Value n-tuples + + while (xidlen > 0) + { + unsigned char * typeptr = ptr; + Type = *ptr++; + Len = *ptr++; + + value = 0; + xidlen -= (Len + 2); + + while (Len--) + { + value <<=8; + value += *ptr++; + } + switch(Type) + { + case 2: //Bin fields + + break; + + case 3: + + if ((value & OPMustHave) != OPMustHave) + goto BadXID; + + if ((value & OPMod8) == 0) + goto BadXID; + + if ((value & OPSREJMult) == 0) + goto BadXID; + + + // Reply Mod 8 SREJMULTI + + value = OPMustHave | OPSREJMult | OPMod8; + ptr -=3; + *ptr++ = value >> 16; + *ptr++ = value >> 8; + *ptr++ = value; + + + break; + + case 6: //RX Size + + break; + + case 8: //RX Window + + break; + + case 16: + + // Compression + + if (L2Compress) + { + LINK->AllowCompress = 1; + // return as 17 + *typeptr = 17; + } + else + { + ptr = &ADJBUFFER->PID; + ptr[3] -= 2; // Length field - remove compress option + Buffer->LENGTH -=2; + } + } + } + + // Send back as XID response + + LINK->L2STATE = 1; // XID received + LINK->Ver2point2 = TRUE; // Must support 2.2 if sent XID + LINK->L2TIME = PORT->PORTT1; + + LINK->LINKPORT = PORT; + + LINK->KILLTIMER = L2KILLTIME - 60*3; // Time out after 60 secs if SABM not received + + // save calls so we can match up SABM when it comes + + memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7); + LINK->LINKCALL[6] &= 0x1e; // Mask SSID + + memcpy(LINK->OURCALL, Buffer->DEST, 7); + + LINK->OURCALL[6] &= 0x1e; // Mask SSID + + memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT + + if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address + { + // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK + + memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT + + ptr1 = &Buffer->ORIGIN[6]; // End of add + ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi + + while((*ptr1 & 1) == 0) // End of address bit + { + ptr1++; + memcpy(ptr2, ptr1, 7); + ptr2[6] &= 0x1e; // Mask Repeated and Last bits + ptr2 -= 7; + ptr1 += 6; + } + + // LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY + + n = PORT->PORTMAXDIGIS; + + ptr1 = ptr2 + 7; // First in TEMPDIGIS + ptr2 = &LINK->DIGIS[0]; + + while (*ptr1) + { + if (n == 0) + { + // Too many for us + + CLEAROUTLINK(LINK); + ReleaseBuffer(Buffer); + return; + } + + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + n--; + } + } + + ADJBUFFER->CTL = CTL | PFBIT; + + // Buffer->LENGTH = (UCHAR *)ADJBUFFER - (UCHAR *)Buffer + MSGHDDRLEN + 15; // SET UP BYTE COUNT + + L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS + + // We need to save APPLMASK and ALIASPTR so following SABM connects to application + + // LINK->APPLMASK now set in L2FORUS + LINK->ALIASPTR = ALIASPTR; + + PUT_ON_PORT_Q(PORT, Buffer); + return; + } +BadXID: + L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL); + return; +} + + + +int COUNTLINKS(int Port) +{ + //COUNT LINKS ON PORT + + int i = MAXLINKS, n = 0; + struct _LINKTABLE * LINK = LINKS; + + while (i--) + { + if (LINK->LINKPORT && LINK->LINKPORT->PORTNUMBER == Port) + n++; + + LINK++; + } + + return n; +} + + +VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG) +{ + // MESSAGE ON AN ACTIVE LINK + + int CTLlessPF = CTL & ~PFBIT; + unsigned char * ptr; + + PORT->L2FRAMESFORUS++; + + // ONLY SABM or UI ALLOWED IF NO SESSION + + if (CTLlessPF == 3) // UI + { + // A UI ADDRESSED TO US - SHOULD ONLY BE FOR IP, or possibly addressed NODES + + switch(ADJBUFFER->PID) + { + case 0xcf: // Netrom + + if (Buffer->L2DATA[0] == 0xff) // NODES + PROCESSNODEMESSAGE(Buffer, PORT); + + break; + + case 0xcc: // TCP + case 0xcd: // ARP + case 0x08: // NOS FRAGMENTED AX25 TCP/IP + + Q_IP_MSG( Buffer); + return; + } + + ReleaseBuffer(Buffer); + return; + } + + if (CTLlessPF == DISC) + { + InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END + CLEAROUTLINK(LINK); + L2SENDUA(PORT, Buffer, ADJBUFFER); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + return; + } + + + if (LINK->L2STATE == 1) + { + // XID State. Should be XID response if 2.2 ok or DM/FRMR if not + + if (MSGFLAG & RESP) + { + if (CTLlessPF == DM || CTLlessPF == FRMR) + { + // Doesn't support XID - Send SABM + + LINK->L2STATE = 2; + LINK->Ver2point2 = FALSE; + LINK->L2TIMER = 1; // Use retry to send SABM + } + else if (CTLlessPF == XID) + { + // Process response to make sure ok, Send SABM or DISC + + LINK->L2STATE = 2; + LINK->Ver2point2 = TRUE;// Must support 2.2 if responded to XID + + // if Compress enabled set it + + ptr = &ADJBUFFER->PID; + + if (*ptr++ == 0x82 && *ptr++ == 0x80) + { + int Type; + int Len; + unsigned int value; + int xidlen = *(ptr++) << 8; + xidlen += *ptr++; + + // XID is set of Type, Len, Value n-tuples + + while (xidlen > 0) + { + Type = *ptr++; + Len = *ptr++; + + value = 0; + xidlen -= (Len + 2); + + while (Len--) + { + value <<=8; + value += *ptr++; + } + switch(Type) + { + case 17: + + // Compression + + if (L2Compress) + LINK->AllowCompress = 1; + + } + } + + } + + LINK->L2TIMER = 1; // Use retry to send SABM + } + + ReleaseBuffer(Buffer); + return; + } + + // Command on existing session. Could be due to other end missing + // the XID response, so if XID just resend response + + } + + if (CTLlessPF == XID && (MSGFLAG & CMDBIT)) + { + // XID Command on active session. Other end may be restarting. Send Response + + ProcessXIDCommand(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG); + return; + } + + + if (CTLlessPF == SABM) + { + // SABM ON EXISTING SESSION - IF DISCONNECTING, REJECT + + if (LINK->L2STATE == 1) // Sent XID? + { + LINK->APPLMASK; + ALIASPTR = LINK->ALIASPTR; + + L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM + return; + } + + if (LINK->L2STATE == 4) // DISCONNECTING? + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + // THIS IS A SABM ON AN EXISTING SESSION + + // THERE ARE SEVERAL POSSIBILITIES: + + // 1. RECONNECT COMMAND TO TNC + // 2. OTHER END THINKS LINK HAS DIED + // 3. RECOVERY FROM FRMR CONDITION + // 4. REPEAT OF ORIGINAL SABM COS OTHER END MISSED UA + + // FOR 1-3 IT IS REASONABLE TO FULLY RESET THE CIRCUIT, BUT IN 4 + // SUCH ACTION WILL LOSE THE INITIAL SIGNON MSG IF CONNECTING TO A + // BBS. THE PROBLEM IS TELLING THE DIFFERENCE. I'M GOING TO SET A FLAG + // WHEN FIRST INFO RECEIVED - IF SABM REPEATED BEFORE THIS, I'LL ASSUME + // CONDITION 4, AND JUST RESEND THE UA + + + if (LINK->SESSACTIVE == 0) // RESET OF ACTIVE CIRCUIT? + { + L2SENDUA(PORT, Buffer, ADJBUFFER); // No, so repeat UA + return; + } + + InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END + LINK->CIRCUITPOINTER = 0; + + L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM + return; + } + + L2_PROCESS(LINK, PORT, Buffer, CTL, MSGFLAG); +} + + +VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR MSGFLAG) +{ + // SET UP NEW SESSION (OR RESET EXISTING ONE) + + TRANSPORTENTRY * Session; + int CONERROR; + + char toCall[12], fromCall[12]; + + + if (LINK == 0) // NO LINK ENTRIES - SEND DM RESPONSE + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + if (CheckKissInterlock(PORT, TRUE)) // Interlock with ARDOP/VARA etc + { + L2SENDDM(PORT, Buffer, ADJBUFFER); + return; + } + + toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; + fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; + + SETUPNEWL2SESSION(LINK, PORT, Buffer, MSGFLAG); + + if (LINK->L2STATE != 5) // Setup OK? + { + L2SENDDM(PORT, Buffer, ADJBUFFER); // Failed + return; + } + + // See if need to Interlock non-sharable modes, eg ARDOP and VARA + + seeifInterlockneeded(PORT); + + // IF CONNECT TO APPL ADDRESS, SET UP APPL SESSION + + if (LINK->APPLMASK == 0) + { + // Not ATTACH TO APPL + + // Send CTEXT if connect to NODE/Port Alias, or NODE/Port Call, and FULL_CTEXT set + // Dont sent to known NODEs, or appl connects + + struct DATAMESSAGE * Msg; + int Totallen = 0; + int Paclen= PORT->PORTPACLEN; + UCHAR * ptr; + + if (LogAllConnects) + WriteConnectLog(fromCall, toCall, "AX.25"); + + hookL2SessionAccepted(PORT->PORTNUMBER, fromCall, toCall, LINK); + + SendL2ToMonMap(PORT, fromCall, '+', 'I'); + + L2SENDUA(PORT, Buffer, ADJBUFFER); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + AttachKISSHF(PORT, Buffer); + + if (NO_CTEXT == 1) + return; + + if (FULL_CTEXT == 0 && !ALIASMSG) // Any connect, or call to alias + return; + + // if Port CTEXT defined, use it + + if (PORT->CTEXT) + { + Totallen = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (CTEXTLEN) + { + Totallen = CTEXTLEN; + ptr = CTEXTMSG; + } + else + return; + + if (Paclen == 0) + Paclen = PACLEN; + + while(Totallen) + { + Msg = GetBuff(); + + if (Msg == NULL) + break; // No Buffers + + Msg->PID = 0xf0; + + if (Paclen > Totallen) + Paclen = Totallen; + + memcpy(Msg->L2DATA, ptr, Paclen); + Msg->LENGTH = Paclen + MSGHDDRLEN + 1; + + C_Q_ADD(&LINK->TX_Q, Msg); + + ptr += Paclen; + Totallen -= Paclen; + } + return; + } + + + // Connnect to APPL + + if (LINK->LINKTYPE != 1) + { + L2SENDUA(PORT, Buffer, ADJBUFFER); // RESET OF DOWN/CROSSLINK + return; + } + + if (LINK->CIRCUITPOINTER) + { + L2SENDUA(PORT, Buffer, ADJBUFFER); // ALREADY SET UP - MUST BE REPEAT OF SABM OR LINK RESET + return; + } + + // IF RUNNING ONLY BBS (NODE=0), THIS MAY BE EITHER A USER OR NODE + // TRYING TO SET UP A L4 CIRCUIT - WE DONT WANT TO ATTACH A NODE TO + // THE BBS! + + if (NODE == 0) + { + // NOW THINGS GET DIFICULT - WE MUST EITHER WAIT TO SEE IF A PID CF MSG + // ARRIVES, OR ASSUME ALL NODES ARE IN NEIGHBOURS - I'LL TRY THE LATTER + // AND SEE HOW IT GOES. tHIS MEANS THAT YOU MUST DEFINE ALL ROUTES + // IN CONFIG FILE + + struct ROUTE * ROUTE; + + if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE)) + { + // It's a node + + L2SENDUA(PORT, Buffer, ADJBUFFER); // ALREADY SET UP - MUST BE REPEAT OF SABM OR LINK RESET + return; + } + } + + + Session = SetupSessionForL2(LINK); // CREATE INCOMING L4 SESSION + + if (Session == NULL) + { + CLEAROUTLINK(LINK); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + L2SENDDM(PORT, Buffer, ADJBUFFER); + + return; + } + + // NOW TRY A BBS CONNECT + // IF APPL CONNECT, SEE IF APPL HAS AN ALIAS + + if (ALIASPTR[0] > ' ') + { + struct DATAMESSAGE * Msg; + + // ACCEPT THE CONNECT, THEN INVOKE THE ALIAS + + L2SENDUA(PORT, Buffer, ADJBUFFER); + + hookL2SessionAccepted(PORT->PORTNUMBER, fromCall, toCall, LINK); + + SendL2ToMonMap(PORT, fromCall, '+', 'I'); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + { + struct DATAMESSAGE * Msg; + int Totallen = 0; + int Paclen= PORT->PORTPACLEN; + UCHAR * ptr; + + AttachKISSHF(PORT, Buffer); + + // if Port CTEXT defined, use it + + if (PORT->CTEXT) + { + Totallen = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (HFCTEXTLEN) + { + Totallen = HFCTEXTLEN; + ptr = HFCTEXT; + } + + if (Paclen == 0) + Paclen = PACLEN; + + while(Totallen) + { + Msg = GetBuff(); + + if (Msg == NULL) + break; // No Buffers + + Msg->PID = 0xf0; + + if (Paclen > Totallen) + Paclen = Totallen; + + memcpy(Msg->L2DATA, ptr, Paclen); + Msg->LENGTH = Paclen + MSGHDDRLEN + 1; + + C_Q_ADD(&LINK->TX_Q, Msg); + + ptr += Paclen; + Totallen -= Paclen; + } + + } + + if (LogAllConnects) + { + char toCall[12], fromCall[12]; + toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; + fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; + WriteConnectLog(fromCall, toCall, "AX.25"); + } + + Msg = GetBuff(); + + if (Msg) + { + Msg->PID = 0xf0; + + memcpy(Msg->L2DATA, ALIASPTR, 12); + Msg->L2DATA[12] = 13; + + Msg->LENGTH = MSGHDDRLEN + 12 + 2; // 2 for PID and CR + + C_Q_ADD(&LINK->RX_Q, Msg); + } + + return; + } + + if (cATTACHTOBBS(Session, LINK->APPLMASK, PORT->PORTPACLEN, &CONERROR) == 0) + { + // NO BBS AVAILABLE + + CLEARSESSIONENTRY(Session); + CLEAROUTLINK(LINK); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + L2SENDDM(PORT, Buffer, ADJBUFFER); + + return; + } + + if (LogAllConnects) + { + char toCall[12], fromCall[12]; + toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0; + fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0; + WriteConnectLog(fromCall, toCall, "AX.25"); + } + + L2SENDUA(PORT, Buffer, ADJBUFFER); + + hookL2SessionAccepted(PORT->PORTNUMBER, fromCall, toCall, LINK); + + SendL2ToMonMap(PORT, fromCall, '+', 'I'); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + { + struct DATAMESSAGE * Msg; + int Totallen = 0; + int Paclen= PORT->PORTPACLEN; + UCHAR * ptr; + + AttachKISSHF(PORT, Buffer); + + // if Port CTEXT defined, use it + + if (PORT->CTEXT) + { + Totallen = strlen(PORT->CTEXT); + ptr = PORT->CTEXT; + } + else if (HFCTEXTLEN) + { + Totallen = HFCTEXTLEN; + ptr = HFCTEXT; + } + else + return; + + if (Paclen == 0) + Paclen = PACLEN; + + while(Totallen) + { + Msg = GetBuff(); + + if (Msg == NULL) + break; // No Buffers + + Msg->PID = 0xf0; + + if (Paclen > Totallen) + Paclen = Totallen; + + memcpy(Msg->L2DATA, ptr, Paclen); + Msg->LENGTH = Paclen + MSGHDDRLEN + 1; + + C_Q_ADD(&LINK->TX_Q, Msg); + + ptr += Paclen; + Totallen -= Paclen; + } + return; + } +} + +VOID SETUPNEWL2SESSION(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR MSGFLAG) +{ + // COPY ADDRESS INFO TO LINK TABLE + + UCHAR * ptr1, * ptr2; + UCHAR TEMPDIGI[57]; + int n; + + memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7); + LINK->LINKCALL[6] &= 0x1e; // Mask SSID + + memcpy(LINK->OURCALL, Buffer->DEST, 7); + LINK->OURCALL[6] &= 0x1e; // Mask SSID + + memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT + + LINK->L2TIME = PORT->PORTT1; // Set tomeoiut for no digis + + if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address + { + // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK + + memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT + + ptr1 = &Buffer->ORIGIN[6]; // End of add + ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi + + while((*ptr1 & 1) == 0) // End of address bit + { + ptr1++; + memcpy(ptr2, ptr1, 7); + ptr2[6] &= 0x1e; // Mask Repeated and Last bits + ptr2 -= 7; + ptr1 += 6; + } + + // LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY + + n = PORT->PORTMAXDIGIS; + + ptr1 = ptr2 + 7; // First in TEMPDIGIS + ptr2 = &LINK->DIGIS[0]; + + while (*ptr1) + { + if (n == 0) + { + // Too many for us + + CLEAROUTLINK(LINK); + return; + } + + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + n--; + + LINK->L2TIME += PORT->PORTT1; // Adjust timeout for digis + } + } + + // THIS MAY BE RESETTING A LINK - BEWARE OF CONVERTING A CROSSLINK TO + // AN UPLINK AND CONFUSING EVERYTHING + + LINK->LINKPORT = PORT; + + if (LINK->LINKTYPE == 0) + { + if (ISNETROMMSG && NODE == 0) // Only allow crosslink if node = 0 + LINK->LINKTYPE = 3; // Crosslink + else + LINK->LINKTYPE = 1; // Uplink + } + LINK->L2TIMER = 0; // CANCEL TIMER + + LINK->L2SLOTIM = T3; // SET FRAME SENT RECENTLY + + LINK->LINKWINDOW = PORT->PORTWINDOW; + + RESET2(LINK); // RESET ALL FLAGS + + LINK->L2STATE = 5; + + // IF VERSION 1 MSG, SET FLAG + + if (MSGFLAG & VER1) + LINK->VER1FLAG |= 1; + +} + +VOID L2SENDUA(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER) +{ + L2SENDRESP(PORT, Buffer, ADJBUFFER, UA); +} + +VOID L2SENDDM(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER) +{ + if (CheckExcludeList(Buffer->ORIGIN) == 0) // if in exclude, don't send DM + { + ReleaseBuffer(Buffer); // not sure that this is the right place for releasing? + return; + } + + L2SENDRESP(PORT, Buffer, ADJBUFFER, DM); +} + +VOID L2SENDRESP(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL) +{ + // QUEUE RESPONSE TO PORT CONTROL - MAY NOT HAVE A LINK ENTRY + + // SET APPROPRIATE P/F BIT + + ADJBUFFER->CTL = CTL | PFBIT; + + Buffer->LENGTH = (int)((UCHAR *)ADJBUFFER - (UCHAR *)Buffer) + MSGHDDRLEN + 15; // SET UP BYTE COUNT + + L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS + + PUT_ON_PORT_Q(PORT, Buffer); + + return; +} + + +VOID L2SENDINVALIDCTRL(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL) +{ + // Send FRMR Invalid Control field + + // QUEUE RESPONSE TO PORT CONTROL - MAY NOT HAVE A LINK ENTRY + + // SET APPROPRIATE P/F BIT + + UCHAR * ptr; + + ADJBUFFER->CTL = FRMR | PFBIT; + + ptr = &ADJBUFFER->PID; + + *(ptr++) = CTL; // MOVE REJECT C-BYTE + *(ptr++) = 0; + *(ptr++) = SDINVC; // MOVE REJECT FLAGS + + Buffer->LENGTH = (int)((UCHAR *)ADJBUFFER - (UCHAR *)Buffer) + MSGHDDRLEN + 18; // SET UP BYTE COUNT + + L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS + + PUT_ON_PORT_Q(PORT, Buffer); + + return; +} + +VOID L2SWAPADDRESSES(MESSAGE * Buffer) +{ + // EXCHANGE ORIGIN AND DEST, AND REVERSE DIGIS (IF PRESENT) + + char TEMPFIELD[7]; + UCHAR * ptr1, * ptr2; + UCHAR TEMPDIGI[57]; + + memcpy(TEMPFIELD, Buffer->ORIGIN, 7); + memcpy(Buffer->ORIGIN, Buffer->DEST, 7); + memcpy(Buffer->DEST, TEMPFIELD, 7); + + Buffer->ORIGIN[6] &= 0x1e; // Mask SSID + Buffer->ORIGIN[6] |= 0xe0; // Reserved and Response + + Buffer->DEST[6] &= 0x1e; // Mask SSID + Buffer->DEST[6] |= 0x60; // Reserved + + if ((TEMPFIELD[6] & 1) == 0) + { + // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK + + memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT + + ptr1 = &Buffer->ORIGIN[6]; // End of add + ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi + + while((*ptr1 & 1) == 0) // End of address bit + { + ptr1++; + memcpy(ptr2, ptr1, 7); + ptr2[6] &= 0x1e; // Mask Repeated and Last bits + ptr2 -= 7; + ptr1 += 6; + } + + // LIST OF DIGI CALLS COMPLETE - copy back + + ptr1 = ptr2 + 7; // First in TEMPDIGIS + ptr2 = &Buffer->CTL; + + while (*ptr1) + { + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + } + + *(ptr2 - 1) |= 1; // End of addresses + } + else + { + Buffer->ORIGIN[6] |= 1; // End of address + } +} + +BOOL InternalL2SETUPCROSSLINK(PROUTE ROUTE, int Retries) +{ + // ROUTE POINTS TO A NEIGHBOUR - FIND AN L2 SESSION FROM US TO IT, OR INITIATE A NEW ONE + + struct _LINKTABLE * LINK; + struct PORTCONTROL * PORT; + int FRACK; + + if (FindLink(ROUTE->NEIGHBOUR_CALL, NETROMCALL, ROUTE->NEIGHBOUR_PORT, &LINK)) + { + // SESSION ALREADY EXISTS + + LINK->LINKTYPE = 3; // MAKE SURE IT KNOWS ITS A CROSSLINK + ROUTE->NEIGHBOUR_LINK = LINK; + LINK->NEIGHBOUR = ROUTE; + + return TRUE; + } + + // SET UP NEW SESSION (OR RESET EXISTING ONE) + + if (LINK == NULL) + return FALSE; // No free links + + + ROUTE->NEIGHBOUR_LINK = LINK; + LINK->NEIGHBOUR = ROUTE; + + LINK->LINKPORT = PORT = GetPortTableEntryFromPortNum(ROUTE->NEIGHBOUR_PORT); + + if (PORT == NULL) + return FALSE; // maybe port has been deleted + + // IF ROUTE HAS A FRACK, SET IT + + if (ROUTE->NBOUR_FRACK) + FRACK = ROUTE->NBOUR_FRACK; + else + FRACK = PORT->PORTT1; + + LINK->L2TIME = FRACK; // SET TIMER VALUE + + // IF ROUTE HAS A WINDOW, SET IT + + if (ROUTE->NBOUR_MAXFRAME) + LINK->LINKWINDOW = ROUTE->NBOUR_MAXFRAME; + else + LINK->LINKWINDOW = PORT->PORTWINDOW; + + if (SUPPORT2point2) + LINK->L2STATE = 1; // Send XID + else + LINK->L2STATE = 2; + + memcpy(LINK->LINKCALL, ROUTE->NEIGHBOUR_CALL, 7); + memcpy(LINK->OURCALL, NETROMCALL, 7); + + if (ROUTE->NEIGHBOUR_DIGI1[0]) + { + memcpy(LINK->DIGIS, ROUTE->NEIGHBOUR_DIGI1, 7); + LINK->L2TIME += FRACK; + } + + if (ROUTE->NEIGHBOUR_DIGI2[0]) + { + memcpy(&LINK->DIGIS[7], ROUTE->NEIGHBOUR_DIGI1, 7); + LINK->L2TIME += FRACK; + } + + LINK->LINKTYPE = 3; // CROSSLINK + + if (Retries) + LINK->L2RETRIES = PORT->PORTN2 - Retries; + + if (LINK->L2STATE == 1) + L2SENDXID(LINK); + else + SENDSABM(LINK); + + return TRUE; +} + + + +BOOL L2SETUPCROSSLINKEX(PROUTE ROUTE, int Retries) +{ + // Allows caller to specify number of times SABM should be sent + + return InternalL2SETUPCROSSLINK(ROUTE, Retries); +} + +BOOL L2SETUPCROSSLINK(PROUTE ROUTE) +{ + return InternalL2SETUPCROSSLINK(ROUTE, 0); +} + +VOID L2_PROCESS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG) +{ + // PROCESS LEVEL 2 PROTOCOL STUFF + + // SEE IF COMMAND OR RESPONSE + + if ((MSGFLAG & CMDBIT) == 0) + { + + // RESPONSE OR VERSION 1 + + // IF RETRYING, MUST ONLY ACCEPT RESPONSES WITH F SET (UNLESS RUNNING V1) + + if ((CTL & PFBIT) || LINK->VER1FLAG == 1) + { + // F SET or V1 - CAN CANCEL TIMER + + LINK->L2TIMER = 0; // CANCEL LINK TIMER + } + } + + if (LINK->L2STATE == 3) + { + + // FRMR STATE - IF C(P) SEND FRMR, ELSE IGNORE + + if (CTL & PFBIT) + { + if (CTL == (FRMR | PFBIT)) // if both ends in FRMR state, reset link + { + RESET2(LINK); + + LINK->L2STATE = 2; // INITIALISING + LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE + LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM + + L2SENDCOMMAND(LINK, SABM | PFBIT); + } + } + + if (MSGFLAG & CMDBIT) + { + // SEND FRMR AGAIN + + SENDFRMR(LINK); + } + + ReleaseBuffer(Buffer); + return; + } + + if (LINK->L2STATE >= 5) + { + // LINK IN STATE 5 OR ABOVE - LINK RUNNING + + if ((CTL & 1) == 0) // I frame + { + SDIFRM(LINK, PORT, Buffer, CTL, MSGFLAG); // consumes buffer + return; + } + + if ((CTL & 2)) // U frame + { + SDUFRM(LINK, PORT, Buffer, CTL); //consumes buffer + return; + } + + // ELSE SUPERVISORY, MASK OFF N(R) AND P-BIT + + switch (CTL & 0x0f) + { + // is there any harm in accepting SREJ even if we don't + // otherwise support 2.2? + + case REJ: + case SREJ: + + PORT->L2REJCOUNT++; + + case RR: + case RNR: + + SFRAME(LINK, PORT, CTL, MSGFLAG); + break; + + default: + + // UNRECOGNISABLE COMMAND + + LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE + LINK->SDREJF |= SDINVC; // SET INVALID COMMAND REJECT + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + } + + ReleaseBuffer(Buffer); + return; + } + + // NORMAL DISCONNECT MODE + + // COULD BE UA, DM - SABM AND DISC HANDLED ABOVE + + switch (CTL & ~PFBIT) + { + case UA: + + // UA RECEIVED + + if (LINK->L2STATE == 2) + { + // RESPONSE TO SABM - SET LINK UP + + char fromCall[12]; + + fromCall[ConvFromAX25(Buffer->ORIGIN, fromCall)] = 0; + + RESET2X(LINK); // LEAVE QUEUED STUFF + + SendL2ToMonMap(PORT, fromCall, '+', 'O'); + + LINK->L2STATE = 5; + LINK->L2TIMER = 0; // CANCEL TIMER + LINK->L2RETRIES = 0; + LINK->L2SLOTIM, T3; // SET FRAME SENT RECENTLY + + // IF VERSION 1 MSG, SET FLAG + + if (MSGFLAG & VER1) + LINK->VER1FLAG |= 1; + + // TELL PARTNER CONNECTION IS ESTABLISHED + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + KISSHFConnected(PORT, LINK); + + SENDCONNECTREPLY(LINK); + ReleaseBuffer(Buffer); + return; + } + + if (LINK->L2STATE == 4) // DISCONNECTING? + { + InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END + CLEAROUTLINK(LINK); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + } + + // UA, BUT NOT IN STATE 2 OR 4 - IGNORE + + ReleaseBuffer(Buffer); + return; + + case DM: + + // DM RESPONSE - IF TO SABM, SEND BUSY MSG + + if (LINK->L2STATE == 2) + { + CONNECTREFUSED(LINK); // SEND MESSAGE IF DOWNLINK + return; + } + + // DM RESP TO DISC RECEIVED - OTHER END HAS LOST SESSION + + // CLEAR OUT TABLE ENTRY - IF INTERNAL TNC, SHOULD SEND *** DISCONNECTED + + InformPartner(LINK, LINKLOST); // SEND DISC TO OTHER END + CLEAROUTLINK(LINK); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + ReleaseBuffer(Buffer); + return; + + case FRMR: + + // FRAME REJECT RECEIVED - LOG IT AND RESET LINK + + RESET2(LINK); + + LINK->L2STATE = 2; // INITIALISING + LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE + LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM + + PORT->L2FRMRRX++; + + L2SENDCOMMAND(LINK, SABM | PFBIT); + return; + + default: + + // ANY OTHER - IGNORE + + ReleaseBuffer(Buffer); + } +} + +VOID SDUFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL) +{ + // PROCESS AN UNSEQUENCED COMMAND (IN LINK UP STATES) + + switch (CTL & ~PFBIT) + { + case UA: + + // DISCARD - PROBABLY REPEAT OF ACK OF SABM + + break; + + case FRMR: + + // FRAME REJECT RECEIVED - LOG IT AND RESET LINK + + RESET2(LINK); + + LINK->L2STATE = 2; // INITIALISING + LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE + LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM + + PORT->L2FRMRRX++; + + L2SENDCOMMAND(LINK, SABM | PFBIT); + break; + + case DM: + + // DM RESPONSE - SESSION MUST HAVE GONE + + // SEE IF CROSSLINK ACTIVE + + InformPartner(LINK, LINKLOST); // SEND DISC TO OTHER END + CLEAROUTLINK(LINK); + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + break; + + default: + + // UNDEFINED COMMAND + + LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE + LINK->SDREJF |= SDINVC; + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + + } + ReleaseBuffer(Buffer); +} + + +VOID SFRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, UCHAR CTL, UCHAR MSGFLAG) +{ + // CHECK COUNTS, AND IF RNR INDICATE _BUFFER SHORTAGE AT OTHER END + + if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET? + { + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + return; + } + + SDNRCHK(LINK, CTL); // CHECK RECEIVED N(R) + + if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET NOW? + { + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + return; + } + + if ((CTL & 0xf) == SREJ) + { + // Probably safer to handle SREJ completely separately + + // Can we get SREJ Command with P??(Yes) + + // Can we just resend missing frame ?? (Think so!) + + // We support MultiSREJ (can gave additional missing frame + // numbers in the Info field + + // I don't see the point of Multi unless we wait fot an F bit, + // bur maybe not safe to assume others do the same + + // So if I get SREJ(F) I can send missing frame(s) + + if (MSGFLAG & RESP) + { + // SREJ Response + + if (CTL & PFBIT) + { + // SREJ(F). Send Frames() + + UCHAR NS = (CTL >> 5) & 7; // Frame to resend + + struct PORTCONTROL * PORT; + UCHAR * ptr1, * ptr2; + UCHAR CTL; + int count; + MESSAGE * Msg; + MESSAGE * Buffer; + + Msg = LINK->FRAMES[NS]; // is frame available? + + if (Msg == NULL) + return; // Wot!! + + // send the frame + + // GET BUFFER FOR COPY OF MESSAGE - HAVE TO KEEP ORIGINAL FOR RETRIES + + Buffer = GetBuff(); + + if (Buffer == NULL) + return; + + ptr2 = SETUPADDRESSES(LINK, Buffer); // copy addresses + + // ptr2 NOW POINTS TO COMMAND BYTE + + // GOING TO SEND I FRAME - WILL ACK ANY RECEIVED FRAMES + + LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY + LINK->KILLTIMER = 0; // RESET IDLE CIRCUIT TIMER + + CTL = LINK->LINKNR << 5; // GET CURRENT N(R), SHIFT IT TO TOP 3 BITS + CTL |= NS << 1; // BITS 1-3 OF CONTROL BYTE + + // SET P BIT IF NO MORE TO SEND (only more if Multi SREJ) + + if (LINK->VER1FLAG == 0) // NO POLL BIT IF V1 + { + CTL |= PFBIT; + LINK->L2FLAGS |= POLLSENT; + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND (or ACK if ACKMODE) + + Buffer->Linkptr = LINK; + } + + *(ptr2++) = CTL; // TO DATA (STARTING WITH PID) + + count = Msg->LENGTH - MSGHDDRLEN; + + if (count > 0) // SHOULD ALWAYS BE A PID, BUT BETTER SAFE THAN SORRY + { + ptr1 = (UCHAR *)Msg; + ptr1 += MSGHDDRLEN; + memcpy(ptr2, ptr1, count); + } + + Buffer->DEST[6] |= 0x80; // SET COMMAND + + Buffer->LENGTH = (int)(ptr2 - (UCHAR *)Buffer) + count; // SET NEW LENGTH + + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + PORT = LINK->LINKPORT; + + if (PORT) + { + Buffer->PORT = PORT->PORTNUMBER; + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + Buffer->Linkptr = 0; + ReleaseBuffer(Buffer); + } + } + } + + return; + } + + // VALID RR/RNR RECEIVED + + LINK->L2FLAGS &= ~RNRSET; //CLEAR RNR + + if ((CTL & 0xf) == RNR) + LINK->L2FLAGS |= RNRSET; //Set RNR + + if (MSGFLAG & CMDBIT) + { + // ALWAYS REPLY TO RR/RNR/REJ COMMAND (even if no P bit ??) + + // FIRST PROCESS RESEQ QUEUE + + //; CALL PROCESS_RESEQ + + // IGNORE IF AN 'F' HAS BEEN SENT RECENTLY + + if (LINK->LAST_F_TIME + 15 > REALTIMETICKS) + return; // DISCARD + + CTL = RR_OR_RNR(LINK); + + CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS + CTL |= PFBIT; + + L2SENDRESPONSE(LINK, CTL); + + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY + + LINK->L2ACKREQ = 0; // CANCEL DELAYED ACKL2 + + // SAVE TIME IF 'F' SENT' + + LINK->LAST_F_TIME = REALTIMETICKS; + + return; + } + + // Response + + if ((CTL & PFBIT) == 0 && LINK->VER1FLAG == 0) + { + // RESPONSE WITHOUT P/F DONT RESET N(S) (UNLESS V1) + + return; + + } + + // RESPONSE WITH P/F - MUST BE REPLY TO POLL FOLLOWING TIMEOUT OR I(P) + + // THERE IS A PROBLEM WITH REPEATED RR(F), SAY CAUSED BY DELAY AT L1 + + // AS FAR AS I CAN SEE, WE SHOULD ONLY RESET N(S) IF AN RR(F) FOLLOWS + // AN RR(P) AFTER A TIMEOUT - AN RR(F) FOLLOWING AN I(P) CANT POSSIBLY + // INDICATE A LOST FRAME. ON THE OTHER HAND, A REJ(F) MUST INDICATE + // A LOST FRAME. So dont reset NS if not retrying, unless REJ + + + // someone (probably WLE KISS Driver) is sending REJ followed by RR(F) + // after lost frame and i(p) + +/* +1:Fm W4DHW-10 To W4DHW [17:08:03R] [+++] +úJƒÑZKÀ)x@DÖBÉrNôÝ4XÔ;i‹#CäM³,ïнҼüÕrÞùOË N¿XæâïÀÄ5Ð(È|©¸ì#íÿÈUþïÒcYÞÍl—çûž)Àú璘oÑȼö>©Ï9¨*ÎG²£ëðû(6À5C‹!áL±Ÿîßì÷³ÙQð»pƒËIH”Š;ØÚi¯Ò>â9p¶B¬õ<ÌcŠEPž«<ŸÊ{0aŽ(’­YÕ–´M¢†—N£+<ÇIÐ[–áÛPw–[^]6ƒ2\ù¿9äÆov{‹¥Å¸mm [17:08:03T] +1:Fm W4DHW To W4DHW-10 [17:08:03T] +1:Fm W4DHW To W4DHW-10 [17:08:03T] + + is there a problem with restting on RR(F) following I(P)? + + I think the problem is restting NS twice if you get delayed responses to + I or RR (P). So lets try only resetting NS once for each P sent + +*/ +// if ((CTL & 0xf) == REJ || LINK->L2RETRIES) + if ((LINK->L2FLAGS & POLLSENT)) + { + RESETNS(LINK, (CTL >> 5) & 7); // RESET N(S) AND COUNT RETRIED FRAMES + + LINK->L2RETRIES = 0; + LINK->L2TIMER = 0; // WILL RESTART TIMER WHEN RETRY SENT + } + + LINK->L2FLAGS &= ~POLLSENT; // CLEAR I(P) or RR(P) SET + + if ((CTL & 0xf) == RNR) + { + // Dont Clear timer on receipt of RNR(F), spec says should poll for clearing of busy, + // and loss of subsequent RR will cause hang. Perhaps should set slightly longer time?? + // Timer may have been cleared earlier, so restart it + + LINK->L2TIMER = LINK->L2TIME; + } +} + +//*** PROCESS AN INFORMATION FRAME + +VOID SDIFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG) +{ + int NS; + + if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET? + { + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + ReleaseBuffer(Buffer); + return; + } + + SDNRCHK(LINK, CTL); // CHECK RECEIVED N(R) + + if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET NOW? + { + SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION + ReleaseBuffer(Buffer); + return; + } + + LINK->SESSACTIVE = 1; // SESSION IS DEFINITELY SET UP + + NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S) + + // IPOLL (sending an I(P) frame following timeout instead of RR(P)) + // is a problem. We need to send REJ(F), but shouldn't add to collector. + // We also need to handle repeated I(P), so shouldn't set REJSENT in + // this state. + + if ((((NS + 1) & 7) == LINK->LINKNR) && (CTL & PFBIT)) + { + // Previous Frame and P set - Assume IPOLL + + PORT->L2OUTOFSEQ++; + LINK->L2STATE = 6; + + LINK->L2ACKREQ = 0; // CANCEL RR NEEDED + + // We need to protect against sending multiple REJ(F) if channel + // delays mean we get two I(P) close together (how close is close ??) + // SM has default IPOLL limit of 30 bytes or about a second at 300 + // ACKMODE should avoid this anyway, and resptime of under 3 secs + // is unlikely so say 2.5 secs ?? + + if (LINK->LAST_F_TIME + 25 > REALTIMETICKS) + { + ReleaseBuffer(Buffer); + return; + } + + SEND_RR_RESP(LINK, PFBIT); + LINK->LAST_F_TIME = REALTIMETICKS; + + ReleaseBuffer(Buffer); + return; + } + +CheckNSLoop: + + if (NS != LINK->LINKNR) // EQUAL TO OUR N(R)? + { + // There is a frame missing. + // if we have just sent a REJ we have at least one out + // of sequence frame in RXFRAMES + + // so if we have frame LINK->LINKNR we can process it + // and remove it from RXFRAMES. If we are then back + // in sequence we just carry on. + + if (LINK->RXFRAMES[LINK->LINKNR]) + { + // We have the first missing frame. Process it. + + MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]); + + Debugprintf("L2 process saved Frame %d", LINK->LINKNR); + PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer + + // NR has been updated. + + goto CheckNSLoop; // See if OK or we have another saved frame + } + + // BAD FRAME, SEND REJ (AFTER RESPTIME - OR WE MAY SEND LOTS!) + + // ALSO SAVE THE FRAME - NEXT TIME WE MAY GET A DIFFERENT SUBSET + // AND SOON WE WILL HANDLE SREJ + + PORT->L2OUTOFSEQ++; + + LINK->L2STATE = 6; + + // IF RUNNING VER1, AND OTHER END MISSES THIS REJ, LINK WILL FAIL + // SO TIME OUT REJ SENT STATE (MUST KEEP IT FOR A WHILE TO AVOID + // 'MULTIPLE REJ' PROBLEM) + + if (LINK->VER1FLAG == 1) + LINK->REJTIMER = TENSECS; + + // SET ACK REQUIRED TIMER - REJ WILL BE SENT WHEN IT EXPIRES + + // if configured RESPTIME is longer than 3 secs use it (may be longer on HF) + + if (PORT->PORTT2 > THREESECS) + LINK->L2ACKREQ = PORT->PORTT2; + else + LINK->L2ACKREQ = THREESECS; // EXTRA LONG RESPTIME, AS SENDING TOO MANY REJ'S IS SERIOUS + + if (LINK->RXFRAMES[NS]) + { + // Already have a copy, so discard old and keep this + + Debugprintf ("Frame %d out of seq but already have copy - release it", NS); + ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS])); + } + else + { + Debugprintf ("Frame %d out of seq - save", NS); + } + + Buffer->CHAIN = 0; + LINK->RXFRAMES[NS] = Buffer; + goto CheckPF; + } + + // IN SEQUENCE FRAME + + // Remove any stored frame with this seq + + if (LINK->RXFRAMES[NS]) + ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS])); + + if (LINK->L2STATE == 6) // REJ? + { + // If using REJ we can cancel REJ state. + // If using SREJ we only cancel REJ if we have no stored frames + + if (LINK->Ver2point2) + { + // see if any frames saved. + + int i; + + for (i = 0; i < 8; i++) + { + if (LINK->RXFRAMES[i]) + goto stayinREJ; + } + // Drop through if no stored frames + } + + // CANCEL REJ + + LINK->L2STATE = 5; + LINK->L2FLAGS &= ~REJSENT; + } + +stayinREJ: + + PROC_I_FRAME(LINK, PORT, Buffer); // Passes on or releases Buffer + + +CheckPF: + + if (LINK->Ver2point2 == 0) // Unless using SREJ + { + if (LINK->L2FLAGS & REJSENT) + { + return; // DONT SEND ANOTHER TILL REJ IS CANCELLED + } + } + + if (CTL & PFBIT) + { + if (LINK->L2STATE == 6) + LINK->L2FLAGS |= REJSENT; // Set "REJ Sent" + else + { + // we have all frames. Clear anything in RXFRAMES + + int n = 0; + + while (n < 8) + { + if (LINK->RXFRAMES[n]) + ReleaseBuffer(Q_REM(&LINK->RXFRAMES[n])); + + n++; + } + } + LINK->L2ACKREQ = 0; // CANCEL RR NEEDED + + SEND_RR_RESP(LINK, PFBIT); + + // RECORD TIME + + LINK->LAST_F_TIME = REALTIMETICKS; + } + else + if (LINK->L2ACKREQ == 0) // Resptime is zero so send RR now + SEND_RR_RESP(LINK, 0); + +} + +int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen); + + +VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + int Length; + char * Info; + UCHAR PID; + struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)Buffer; + UCHAR * EOA; + int n = 8; // Max Digis + + LINK->LINKNR++; // INCREMENT OUR N(R) + LINK->LINKNR &= 7; // MODULO 8 + + // ATTACH I FRAMES TO LINK TABLE RX QUEUE - ONLY DATA IS ADDED (NOT ADDRESSES) + + // IF DISC PENDING SET, IGNORE FRAME + + if (LINK->L2FLAGS & DISCPENDING) + { + ReleaseBuffer(Buffer); + return; + } + + // Copy data down the buffer so PID comes after Header (DATAMESSAGE format) + + Length = Buffer->LENGTH - (MSGHDDRLEN + 15); // Buffer Header + addrs + CTL + Info = &Buffer->PID; + + LINK->bytesRXed += Length; + LINK->Received += Length - 1; // Exclude PID + + // Adjust for DIGIS + + EOA = &Buffer->ORIGIN[6]; // End of address Bit + + while (((*EOA & 1) == 0) && n--) + { + Length -= 7; + Info += 7; + EOA += 7; + } + + PID = EOA[2]; + + switch(PID) + { + case 0xf2: + + // Intermediate fragment of compressed. Save + + // Length and Info include pid + + Length--; + Info++; + + if (LINK->unCompress == 0) + LINK->unCompress = malloc(8192); + + // Save data + + memcpy(&LINK->unCompress[LINK->unCompressLen], Info, Length); + LINK->unCompressLen += Length; + + ReleaseBuffer(Buffer); + + LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED + LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER + return; + + + case 0xf1: + + // Compressed last or only + + { + char exBuffer[8192]; + int Len; + int outLen; + int sendLen; + char * sendptr = exBuffer; + + Length--; + Info++; + + // we may have previous fragments + + if (LINK->unCompressLen) + { + memcpy(&LINK->unCompress[LINK->unCompressLen], Info, Length); + LINK->unCompressLen += Length; + Len = doinflate(LINK->unCompress, exBuffer, LINK->unCompressLen, 8192, &outLen); + LINK->ReceivedAfterExpansion += outLen - 1; + + LINK->unCompressLen = 0; + } + else + { + Len = doinflate(Info, exBuffer, Length, 8192, &outLen); + LINK->ReceivedAfterExpansion += outLen - 1; + } + sendLen = outLen; + + // Send first bit in input buffer. If still some left get new buffers for it + + if (sendLen > 257) + sendLen = 257; + + // First byte is original PID + + memcpy(&Msg->PID, exBuffer, sendLen); + Msg->LENGTH = sendLen + MSGHDDRLEN; + + C_Q_ADD(&LINK->RX_Q, Msg); + + outLen -= sendLen; + sendptr += sendLen; + + while (outLen > 0) + { + sendLen = outLen; + + if (sendLen > 236) + sendLen = 236; + + Msg = GetBuff(); + + if (Msg) + { + // Just ignore if no buffers - shouldn't happen + + Msg->PID = exBuffer[0]; + Msg->PORT = LINK->LINKPORT->PORTNUMBER; + + memcpy(Msg->L2DATA, sendptr, sendLen); + Length = sendLen + 1; + + Msg->LENGTH = Length + MSGHDDRLEN; + C_Q_ADD(&LINK->RX_Q, Msg); + } + + outLen -= sendLen; + sendptr += sendLen; + } + + LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED + LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER + + return; + } + + + case 0xcc: + case 0xcd: + + // IP Message + + if (n < 8) // If digis, move data back down buffer + { + memmove(&Buffer->PID, &EOA[2], Length); + Buffer->LENGTH -= (int)(&EOA[2] - &Buffer->PID); + } + + Q_IP_MSG( Buffer); + break; + + case 8: + + // NOS FRAGMENTED IP + + if (n < 8) // If digis, move data back down buffer + { + memmove(&Buffer->PID, &EOA[2], Length); + Buffer->LENGTH -= (int)(&EOA[2] - &Buffer->PID); + } + + C_Q_ADD(&LINK->L2FRAG_Q, Buffer); + + if (Buffer->L2DATA[0] == 0) + { + // THERE IS A WHOLE MESSAGE ON FRAG_Q - PASS TO IP + + while(LINK->L2FRAG_Q) + { + Buffer = Q_REM(&LINK->L2FRAG_Q); + Q_IP_MSG( Buffer); + } + } + break; + + default: + + if (Length < 1 || Length > 257) + { + ReleaseBuffer(Buffer); + return; + } + + // Copy Data back over + + memmove(&Msg->PID, Info, Length); + LINK->ReceivedAfterExpansion += Length - 1; + + Buffer->LENGTH = Length + MSGHDDRLEN; + + C_Q_ADD(&LINK->RX_Q, Buffer); + } + + LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED + LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER +} + +//*** CHECK RECEIVED N(R) COUNT + +VOID SDNRCHK(struct _LINKTABLE * LINK, UCHAR CTL) +{ + UCHAR NR = (CTL >> 5) & 7; + + if (NR >= LINK->LINKWS) // N(R) >= WINDOW START? + { + // N(R) ABOVE OR EQUAL TO WINDOW START - OK IF NOT ABOVE N(S), OR N(S) BELOW WS + + if (NR > LINK->LINKNS) // N(R) <= WINDOW END? + { + // N(R) ABOVE N(S) - DOES COUNT WRAP? + + if (LINK->LINKNS >= LINK->LINKWS) // Doesnt wrap + goto BadNR; + } + +GoodNR: + + if ((CTL & 0x0f) == SREJ) + if ((CTL & PFBIT) == 0) + return; // SREJ without F doesn't ACK anything + + LINK->LINKWS = NR; // NEW WINDOW START = RECEIVED N(R) + ACKMSG(LINK); // Remove any acked messages + return; + } + + // N(R) LESS THAN WINDOW START - ONLY OK IF WINDOW WRAPS + + if (NR <= LINK->LINKNS) // N(R) <= WINDOW END? + goto GoodNR; + +BadNR: + + // RECEIVED N(R) IS INVALID + + LINK->SDREJF |= SDNRER; // FLAG A REJECT CONDITION + LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE +} + +VOID RESETNS(struct _LINKTABLE * LINK, UCHAR NS) +{ + int Resent = (LINK->LINKNS - NS) & 7; // FRAMES TO RESEND + + LINK->LINKNS = NS; // RESET N(S) + + if (LINK->LINKTYPE == 3) // mode-Node + { + if (LINK->NEIGHBOUR) + LINK->NEIGHBOUR->NBOUR_RETRIES += Resent; + } +} + +int COUNT_AT_L2(struct _LINKTABLE * LINK) +{ + // COUNTS FRAMES QUEUED ON AN L2 SESSION (IN BX) + + int count = 0, abovelink = 0; + int n = 0; + + if (LINK == NULL) + return 0; + + abovelink = C_Q_COUNT((UINT *)&LINK->TX_Q); + + // COUNT FRAMES IN TSLOTS + + while (n < 8) + { + if (LINK->FRAMES[n]) + count++; + n++; + } + +// ADD AL,AH ; TOTAL IN AL, NUMBER ABOVE LINK IN AH + + return abovelink + count; +} + +//*** RESET HDLC AND PURGE ALL QUEUES ETC. + +VOID RESET2X(struct _LINKTABLE * LINK) +{ + LINK->SDREJF = 0; // CLEAR FRAME REJECT FLAGS + LINK->LINKWS = 0; // CLEAR WINDOW POINTERS + LINK->LINKOWS = 0; + LINK->LINKNR = 0; // CLEAR N(R) + LINK->LINKNS = 0; // CLEAR N(S) + LINK->SDTSLOT= 0; + LINK->L2STATE = 5; // RESET STATE + LINK->L2FLAGS = 0; +} + + +VOID CLEARL2QUEUES(struct _LINKTABLE * LINK) +{ + // GET RID OF ALL FRAMES THAT ARE QUEUED + + int n = 0; + + while (n < 8) + { + while (LINK->FRAMES[n]) + ReleaseBuffer(Q_REM(&LINK->FRAMES[n])); + while (LINK->RXFRAMES[n]) + ReleaseBuffer(Q_REM(&LINK->RXFRAMES[n])); + n++; + } + + // GET RID OF ALL FRAMES THAT ARE + // QUEUED ON THE TX HOLDING QUEUE, RX QUEUE AND LEVEL 3 QUEUE + + + while (LINK->TX_Q) + ReleaseBuffer(Q_REM(&LINK->TX_Q)); + + while (LINK->RX_Q) + ReleaseBuffer(Q_REM(&LINK->RX_Q)); + +} + +VOID RESET2(struct _LINKTABLE * LINK) +{ + CLEARL2QUEUES(LINK); + RESET2X(LINK); +} + +VOID SENDSABM(struct _LINKTABLE * LINK) +{ + char toCall[10]; + char fromCall[10]; + + toCall[ConvFromAX25(LINK->LINKCALL, toCall)] = 0; + fromCall[ConvFromAX25(LINK->OURCALL, fromCall)] = 0; + hookL2SessionAttempt(LINK->LINKPORT->PORTNUMBER, fromCall, toCall, LINK); + + L2SENDCOMMAND(LINK, SABM | PFBIT); +} + + +VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer) +{ + // TIME STAMP IT + + time(&Buffer->Timestamp); + + if (PORT->TXPORT) + { + Buffer->PORT = PORT->TXPORT; // update port no in header + + PORT = GetPortTableEntryFromPortNum(PORT->TXPORT); + + if (PORT == NULL) + { + ReleaseBuffer(Buffer); + return; + } + } + C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer); +} + + +UCHAR * SETUPADDRESSES(struct _LINKTABLE * LINK, PMESSAGE Msg) +{ + // COPY ADDRESSES FROM LINK TABLE TO MESSAGE _BUFFER + + UCHAR * ptr1 = &LINK->DIGIS[0]; + UCHAR * ptr2 = &Msg->CTL; + int Digis = 8; + + memcpy(&Msg->DEST[0], &LINK->LINKCALL[0], 14); // COPY DEST AND ORIGIN + + Msg->DEST[6] |= 0x60; + Msg->ORIGIN[6] |= 0x60; + + while (Digis) + { + if (*(ptr1)) // any more to copy? + { + memcpy(ptr2, ptr1, 7); + ptr1 += 7; + ptr2 += 7; + Digis--; + } + else + break; + } + + *(ptr2 - 1) |= 1; // SET END OF ADDRESSES + + return ptr2; // Pointer to CTL +} + +VOID SDETX(struct _LINKTABLE * LINK) +{ + // Start sending frsmes if possible + + struct PORTCONTROL * PORT; + int Outstanding; + UCHAR * ptr1, * ptr2; + UCHAR CTL; + int count; + struct DATAMESSAGE * Msg; + MESSAGE * Buffer; + + // DONT SEND IF RESEQUENCING RECEIVED FRAMES - CAN CAUSE FRMR PROBLEMS + +// if (LINK->L2RESEQ_Q) +// return; + + Outstanding = LINK->LINKNS - LINK->LINKOWS; // Was WS not NS + + if (Outstanding < 0) + Outstanding += 8; // allow for wrap + + if (Outstanding >= LINK->LINKWINDOW) // LIMIT + return; + + // See if we can load any more frames into the frame holding q + + while (LINK->TX_Q && LINK->FRAMES[LINK->SDTSLOT] == NULL) + { + // Try compressing here. Only Compress PID 0xF0 frames - NETROM doesn't treat L2 session as a byte stream + + Msg = Q_REM(&LINK->TX_Q); + Msg->CHAIN = NULL; + + if (LINK->AllowCompress && Msg->LENGTH > 20 && LINK->TX_Q && Msg->PID == 240) // if short and no more not worth trying compression + { + int complen = 0; + int dataLen; + int savePort = Msg->PORT; + int savePID = Msg->PID; + unsigned char Compressed[8192]; + unsigned char toCompress[8192]; + int toCompressLen = 0; + + int slots = 0; + int n = LINK->SDTSLOT; + int maxcompsize; + int PACLEN = LINK->LINKPORT->PORTPACLEN; + unsigned char * compdata; + int sendLen = complen; + int uncompressed = 0; + + if (PACLEN == 0) + PACLEN = 256; + + // I think I need to know how many slots are available, so I don't compress too much + // Then collect data, compressing after each frame to make sure will fit in available space + + while (LINK->FRAMES[n] == NULL && slots < 8) + { + slots++; + n++; + n &= 7; + } + + maxcompsize = slots * PACLEN; + + // Save first packet, then see if more on TX_Q + + toCompressLen = 0; + + dataLen = Msg->LENGTH - MSGHDDRLEN; + + LINK->Sent += dataLen; + + memcpy(&toCompress[toCompressLen], &Msg->PID, dataLen); + toCompressLen += dataLen; + + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + + ReleaseBuffer(Msg); + + while (LINK->TX_Q) + { + Msg = LINK->TX_Q; // Leave on queue until sure it will fit + dataLen = Msg->LENGTH - MSGHDDRLEN -1; // PID only on 1st fragment + + memcpy(&toCompress[toCompressLen], &Msg->L2DATA, dataLen); + toCompressLen += dataLen; + + // Need to make sure we don't go over maxcompsize + + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + + if (complen > maxcompsize) + { + // Remove last fragment and compress again + + toCompressLen -= dataLen; + complen = L2Compressit(Compressed, 8192, toCompress, toCompressLen); + break; + } + else + { + LINK->Sent += dataLen; + Msg = Q_REM(&LINK->TX_Q); + Msg->CHAIN = NULL; + + ReleaseBuffer(Msg); + } + } + + if (complen >= toCompressLen) + { + // Won't compress, so just send original data + // May still need to fragment + + memcpy(Compressed, toCompress, toCompressLen); + complen = toCompressLen - 1; // Remove leading PID + uncompressed = 1; + compdata = &Compressed[1]; + } + else + compdata = Compressed; + + // We now need to packetize and add to FRAMES + + LINK->SentAfterCompression += complen; + + sendLen = PACLEN; + + while (complen > 0) + { + int PID = 0xF1; + + if (complen > sendLen) + PID = 0xF2; // More to come + else + sendLen = complen; + + if (uncompressed) + PID = Compressed[0]; + + Msg = GetBuff(); + + if (!Msg) + return; + + Msg->PORT = savePort; + Msg->PID = PID; + + memcpy(&Msg->L2DATA, compdata, sendLen); + Msg->LENGTH = sendLen + MSGHDDRLEN + 1; + + LINK->FRAMES[LINK->SDTSLOT] = Msg; + LINK->SDTSLOT ++; + LINK->SDTSLOT &= 7; + + compdata += sendLen; + complen -= sendLen; + } + + toCompressLen = 0; + + } + else + { + LINK->FRAMES[LINK->SDTSLOT] = Msg; + LINK->SDTSLOT ++; + LINK->SDTSLOT &= 7; + } + } + + // dont send while poll outstanding + + while ((LINK->L2FLAGS & POLLSENT) == 0) + { + Msg = LINK->FRAMES[LINK->LINKNS]; // is next frame available? + + if (Msg == NULL) + return; + + // send the frame + + // GET BUFFER FOR COPY OF MESSAGE - HAVE TO KEEP ORIGINAL FOR RETRIES + + Buffer = GetBuff(); + + if (Buffer == NULL) + return; + + ptr2 = SETUPADDRESSES(LINK, Buffer); // copy addresses + + // ptr2 NOW POINTS TO COMMAND BYTE + + // GOING TO SEND I FRAME - WILL ACK ANY RECEIVED FRAMES + + LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY + LINK->KILLTIMER = 0; // RESET IDLE CIRCUIT TIMER + + CTL = LINK->LINKNR << 5; // GET CURRENT N(R), SHIFT IT TO TOP 3 BITS + CTL |= LINK->LINKNS << 1; // BITS 1-3 OF CONTROL BYTE + + LINK->LINKNS++; // INCREMENT NS + LINK->LINKNS &= 7; // mod 8 + + // SET P BIT IF END OF WINDOW OR NO MORE TO SEND + + if (LINK->VER1FLAG == 0) // NO POLL BIT IF V1 + { + Outstanding = LINK->LINKNS - LINK->LINKOWS; + + if (Outstanding < 0) + Outstanding += 8; // allow for wrap + + // if at limit, or no more to send, set P) + + if (Outstanding >= LINK->LINKWINDOW || LINK->FRAMES[LINK->LINKNS] == NULL) + { + CTL |= PFBIT; + LINK->L2FLAGS |= POLLSENT; + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND (or ACK if ACKMODE) + + Buffer->Linkptr = LINK; + } + } + + *(ptr2++) = CTL; // TO DATA (STARTING WITH PID) + + count = Msg->LENGTH - MSGHDDRLEN; + + if (count > 0) // SHOULD ALWAYS BE A PID, BUT BETTER SAFE THAN SORRY + { + ptr1 = (UCHAR *)Msg; + ptr1 += MSGHDDRLEN; + memcpy(ptr2, ptr1, count); + } + + Buffer->DEST[6] |= 0x80; // SET COMMAND + + Buffer->LENGTH = (int)(ptr2 - (UCHAR *)Buffer) + count; // SET NEW LENGTH + + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + PORT = LINK->LINKPORT; + + if (PORT) + { + Buffer->PORT = PORT->PORTNUMBER; + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + Buffer->Linkptr = 0; + ReleaseBuffer(Buffer); + } + + } +} + +VOID L2TimerProc() +{ + int i = MAXLINKS; + struct _LINKTABLE * LINK = LINKS; + struct PORTCONTROL * PORT = PORTTABLE; + + while (i--) + { + if (LINK->LINKCALL[0] == 0) + { + LINK++; + continue; + } + + // CHECK FOR TIMER EXPIRY OR BUSY CLEARED + + PORT = LINK->LINKPORT; + + if (PORT == NULL) + { + LINK++; + continue; // just ion case!! + } + + if (LINK->L2TIMER) + { + LINK->L2TIMER--; + if (LINK->L2TIMER == 0) + { + L2TIMEOUT(LINK, PORT); + LINK++; + continue; + } + } + else + { + // TIMER NOT RUNNING - MAKE SURE STATE NOT BELOW 5 - IF + // IT IS, SOMETHING HAS GONE WRONG, AND LINK WILL HANG FOREVER + + if (LINK->L2STATE < 5 && LINK->L2STATE != 2 && LINK->L2STATE != 1) // 2 = CONNECT - PROBABLY TO CQ + LINK->L2TIMER = 2; // ARBITRARY VALUE + } + + // TEST FOR RNR SENT, AND NOT STILL BUSY + + if (LINK->L2FLAGS & RNRSENT) + { + // Was busy + + if (RR_OR_RNR(LINK) != RNR) // SEE IF STILL BUSY + { + // Not still busy - tell other end + + // Just sending RR will hause a hang of RR is missed, and other end does not poll on Busy + // Try sending RR CP, so we will retry if not acked + + LINK->L2ACKREQ = 0; // CLEAR ANY DELAYED ACK TIMER + + if (LINK->L2RETRIES == 0) // IF RR(P) OUTSTANDING WILl REPORT ANYWAY + { + SendSupervisCmd(LINK); + LINK++; + continue; + } + } + } + else + { + // NOT BUSY + + if (LINK->L2ACKREQ) // DELAYED ACK TIMER + { + if (LINK->L2RETRIES == 0) // DONT SEND RR RESPONSE WHILEST RR(P) OUTSTANDING + { + LINK->L2ACKREQ--; + if (LINK->L2ACKREQ == 0) + { + SEND_RR_RESP(LINK, 0); // NO F BIT + LINK++; + continue; + } + } + } + } + + // CHECK FOR REJ TIMEOUT + + if (LINK->REJTIMER) + { + LINK->REJTIMER--; + if (LINK->REJTIMER == 0) // {REJ HAS TIMED OUT (THIS MUST BE A VERSION 1 SESSION) + { + // CANCEL REJ STATE + + if (LINK->L2STATE == 6) // REJ? + LINK->L2STATE = 5; // CLEAR REJ + } + } + + // See if time for link validation poll + + if (LINK->L2SLOTIM) + { + LINK->L2SLOTIM--; + if (LINK->L2SLOTIM == 0) // Time to poll + { + SendSupervisCmd(LINK); + LINK++; + continue; + } + } + + // See if idle too long + + LINK->KILLTIMER++; + + if (L2KILLTIME && LINK->KILLTIMER > L2KILLTIME) + { + // CIRCUIT HAS BEEN IDLE TOO LONG - SHUT IT DOWN + + // if in XID received state session was never established so don't send DISC + + if (LINK->L2STATE == 1) + { + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + CLEAROUTLINK(LINK); + } + else + { + LINK->KILLTIMER = 0; + LINK->L2TIMER = 1; // TO FORCE DISC + LINK->L2STATE = 4; // DISCONNECTING + + // TELL OTHER LEVELS + + InformPartner(LINK, NORMALCLOSE); + } + } + LINK++; + } +} + +VOID SendSupervisCmd(struct _LINKTABLE * LINK) +{ + // Send Super Command RR/RNR/REJ(P) + + UCHAR CTL; + + if (LINK->VER1FLAG == 1) + { + // VERSION 1 TIMEOUT + + // RESET TO RESEND I FRAMES + + LINK->LINKNS = LINK->LINKOWS; + + SDETX(LINK); // PREVENT FRMR (I HOPE) + } + + // SEND RR COMMAND - EITHER AS LINK VALIDATION POLL OR FOLLOWING TIMEOUT + + LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED + + CTL = RR_OR_RNR(LINK); + +// MOV L2STATE[EBX],5 ; CANCEL REJ - ACTUALLY GOING TO 'PENDING ACK' + + CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS + CTL |= PFBIT; + + LINK->L2FLAGS |= POLLSENT; + + L2SENDCOMMAND(LINK, CTL); + + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY +} + +void SEND_RR_RESP(struct _LINKTABLE * LINK, UCHAR PF) +{ + UCHAR CTL; + + CTL = RR_OR_RNR(LINK); + +// MOV L2STATE[EBX],5 ; CANCEL REJ - ACTUALLY GOING TO 'PENDING ACK' + + CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS + CTL |= PF; + + L2SENDRESPONSE(LINK, CTL); + + ACKMSG(LINK); // SEE IF STILL WAITING FOR ACK +} + +VOID ACKMSG(struct _LINKTABLE * LINK) +{ + // RELEASE ANY ACKNOWLEDGED FRAMES + + while (LINK->LINKOWS != LINK->LINKWS) // is OLD WINDOW START EQUAL TO NEW WINDOW START? + { + // No, so frames to ack + + if (LINK->FRAMES[LINK->LINKOWS]) + ReleaseBuffer(Q_REM(&LINK->FRAMES[LINK->LINKOWS])); + else + { + char Call1[12], Call2[12]; + + Call1[ConvFromAX25(LINK->LINKCALL, Call1)] = 0; + Call2[ConvFromAX25(LINK->OURCALL, Call2)] = 0; + + Debugprintf("Missing frame to ack Seq %d Calls %s %s", LINK->LINKOWS, Call1, Call2); + } + + LINK->IFrameRetryCounter = 0; + + LINK->LINKOWS++; // INCREMENT OLD WINDOW START + LINK->LINKOWS &= 7; // MODULO 8 + + // SOMETHING HAS BEEN ACKED - RESET RETRY COUNTER + + if (LINK->L2RETRIES) + LINK->L2RETRIES = 1; // MUSTN'T SET TO ZERO - COULD CAUSE PREMATURE RETRANSMIT + + } + + if (LINK->LINKWS != LINK->LINKNS) // IS N(S) = NEW WINDOW START? + { + // NOT ALL I-FRAMES HAVE BEEN ACK'ED - RESTART TIMER + + // Need to kill link if we are getting repeated RR(F) after timeout + // (Indicating other station is seeing our RR(P) but not the resent I frame) + + if (LINK->IFrameRetryCounter++ > LINK->LINKPORT->PORTN2) + { + Debugprintf("Too many repeats of same I frame - closing connection"); + LINK->L2TIMER = 1; // USE TIMER TO SEND DISC + LINK->L2STATE = 4; // DISCONNECTING + return; + } + + + LINK->L2TIMER = LINK->L2TIME; + return; + } + + // ALL FRAMES HAVE BEEN ACKED - CANCEL TIMER UNLESS RETRYING + // IF RETRYING, MUST ONLY CANCEL WHEN RR(F) RECEIVED + + if (LINK->VER1FLAG == 1 || LINK->L2RETRIES == 0) // STOP TIMER IF LEVEL 1 or not retrying + { + LINK->L2TIMER = 0; + LINK->L2FLAGS &= ~POLLSENT; // CLEAR I(P) SET (IN CASE TALKING TO OLD BPQ!) + } + + // IF DISCONNECT REQUEST OUTSTANDING, AND NO FRAMES ON TX QUEUE, SEND DISC + + if ((LINK->L2FLAGS & DISCPENDING) && LINK->TX_Q == 0) + { + LINK->L2FLAGS &= ~DISCPENDING; + + LINK->L2TIMER = 1; // USE TIMER TO SEND DISC + LINK->L2STATE = 4; // DISCONNECTING + } +} + +VOID CONNECTFAILED(struct _LINKTABLE * LINK); + +VOID L2TIMEOUT(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT) +{ + // TIMER EXPIRED + + // IF LINK UP (STATE 5 OR ABOVE) SEND RR/RNR AS REQUIRED + // IF S2, REPEAT SABM + // IF S3, REPEAT FRMR + // IF S4, REPEAT DISC + + + PORT->L2TIMEOUTS++; // FOR STATS + + if (LINK->L2STATE == 0) + return; + + if (LINK->L2STATE == 1) + { + // XID + + LINK->L2RETRIES++; + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N2 TIMES - Give up + + CONNECTFAILED(LINK); // TELL LEVEL 4 IT FAILED + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + CLEAROUTLINK(LINK); + return; + } + + L2SENDXID(LINK); + return; + } + + + if (LINK->L2STATE == 2) + { + // CONNECTING + + LINK->L2RETRIES++; + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N2 TIMES - Give up + + CONNECTFAILED(LINK); // TELL LEVEL 4 IT FAILED + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + CLEAROUTLINK(LINK); + return; + } + + SENDSABM(LINK); + return; + } + + if (LINK->L2STATE == 4) + { + // DISCONNECTING + + LINK->L2RETRIES++; + + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N2 TIMES - JUST CLEAR OUT LINK + + if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF) + DetachKISSHF(PORT); + + CLEAROUTLINK(LINK); + return; + } + + L2SENDCOMMAND(LINK, DISC | PFBIT); + return; + } + + if (LINK->L2STATE == 3) + { + // FRMR + + LINK->L2RETRIES++; + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N2 TIMES - RESET LINK + + LINK->L2RETRIES = 0; + LINK->L2STATE = 2; + SENDSABM(LINK); + return; + } + } + + // STATE 5 OR ABOVE + + // SEND RR(P) UP TO N2 TIMES + + LINK->L2RETRIES++; + + if (LINK->L2RETRIES >= PORT->PORTN2) + { + // RETRIED N TIMES SEND A COUPLE OF DISCS AND THEN CLOSE + + InformPartner(LINK, RETRIEDOUT); // TELL OTHER END ITS GONE + + LINK->L2RETRIES -= 1; // Just send one DISC + LINK->L2STATE = 4; // CLOSING + + L2SENDCOMMAND(LINK, DISC | PFBIT); + return; + } + + SendSupervisCmd(LINK); +} + +VOID SDFRMR(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT) +{ + PORT->L2FRMRTX++; + + LINK->L2STATE = 3; // ENTER FRMR STATE + + LINK->L2TIMER = LINK->L2TIME; //SET TIMER + + SENDFRMR(LINK); +} + +VOID SENDFRMR(struct _LINKTABLE * LINK) +{ + // RESEND FRMR + + struct PORTCONTROL * PORT; + MESSAGE * Buffer; + UCHAR * ptr; + + Buffer = SETUPL2MESSAGE(LINK, FRMR); + + if (Buffer == NULL) + return; + + Buffer->ORIGIN[6] |= 0x80; // SET RESPONSE + + ptr = &Buffer->PID; + + *(ptr++) = LINK->SDRBYTE; // MOVE REJECT C-BYTE + + *(ptr++) = LINK->LINKNR << 5 | LINK->LINKNS << 1; + + *(ptr++) = LINK->SDREJF; // MOVE REJECT FLAGS + + Buffer->LENGTH += 3; + + PORT = LINK->LINKPORT; + Buffer->PORT = PORT->PORTNUMBER; + + if (PORT) + PUT_ON_PORT_Q(PORT, Buffer); + else + ReleaseBuffer(Buffer); + + return; +} + +VOID CLEAROUTLINK(struct _LINKTABLE * LINK) +{ + hookL2SessionDeleted(LINK); + + seeifUnlockneeded(LINK); + + CLEARL2QUEUES(LINK); // TO RELEASE ANY BUFFERS + + if (LINK->unCompress) + free(LINK->unCompress); + + memset(LINK, 0, sizeof(struct _LINKTABLE)); +} + +VOID L2SENDXID(struct _LINKTABLE * LINK) +{ + // Set up and send XID + + struct PORTCONTROL * PORT; + UCHAR * ptr; + unsigned int xidval; + MESSAGE * Buffer; + + if (LINK->LINKPORT == 0) + return; //??? has been zapped + + Buffer = SETUPL2MESSAGE(LINK, XID | PFBIT); + + if (Buffer == NULL) + { + // NO BUFFERS - SET TIMER TO FORCE RETRY + + LINK->L2TIMER = 10*3; // SET TIMER + return; + } + + Buffer->DEST[6] |= 0x80; // SET COMMAND + + ptr = &Buffer->PID; + + // Set up default XID Mod 8 + + *ptr++ = 0x82; // FI + *ptr++ = 0x80; // GI + *ptr++ = 0x0; + + if (L2Compress) + *ptr++ = 0x12; // Length 18 + else + *ptr++ = 0x10; // Length 16 + + *ptr++ = 0x02; // Classes of Procedures + *ptr++ = 0x02; // Length + *ptr++ = 0x00; // + *ptr++ = 0x21; // ABM Half Duplex + + // We offer REJ, SREJ and SREJ Multiframe + + *ptr++ = 0x03; // Optional Functions + *ptr++ = 0x03; // Len + + // Sync TX, SREJ Multiframe 16 bit FCS, Mod 8, TEST, + // Extended Addressing, REJ, SREJ + + xidval = OPMustHave | OPSREJ | OPSREJMult | OPREJ | OPMod8; + *ptr++ = xidval >> 16; + *ptr++ = xidval >> 8; + *ptr++ = xidval; + + + *ptr++ = 0x06; // RX Packet Len + *ptr++ = 0x02; // Len + *ptr++ = 0x08; // + *ptr++ = 0x00; // 2K bits (256) Bytes + + *ptr++ = 0x08; // RX Window + *ptr++ = 0x01; // Len + *ptr++ = 0x07; // 7 + + // if L2Compress Enabled request it + + if (L2Compress) + { + *ptr++ = 0x10; // Compress + *ptr++ = 0x00; // Len + } + + Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); // SET LENGTH + + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND + + Buffer->Linkptr = LINK; + + PORT = LINK->LINKPORT; + + if (PORT) + { + Buffer->PORT = PORT->PORTNUMBER; + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + Buffer->Linkptr = 0; + ReleaseBuffer(Buffer); + } +} + + + + + + +VOID L2SENDCOMMAND(struct _LINKTABLE * LINK, int CMD) +{ + // SEND COMMAND IN CMD + + struct PORTCONTROL * PORT; + MESSAGE * Buffer; + + if (LINK->LINKPORT == 0) + return; //??? has been zapped + + Buffer = SETUPL2MESSAGE(LINK, CMD); + + if (Buffer == NULL) + { + // NO BUFFERS - SET TIMER TO FORCE RETRY + + if (CMD & PFBIT) // RESPONSE EXPECTED? + LINK->L2TIMER = 10*3; // SET TIMER + + return; + } + + Buffer->DEST[6] |= 0x80; // SET COMMAND + + if (CMD & PFBIT) // RESPONSE EXPECTED? + { + LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER + + // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND + + Buffer->Linkptr = LINK; + } + + PORT = LINK->LINKPORT; + + if (PORT) + { + Buffer->PORT = PORT->PORTNUMBER; + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + Buffer->Linkptr = 0; + ReleaseBuffer(Buffer); + } +} + + + + + + +VOID L2SENDRESPONSE(struct _LINKTABLE * LINK, int CMD) +{ + // SEND Response IN CMD + + struct PORTCONTROL * PORT; + MESSAGE * Buffer; + + Buffer = SETUPL2MESSAGE(LINK, CMD); + + if (Buffer == NULL) + { + // NO BUFFERS - SET TIMER TO FORCE RETRY + + if (CMD & PFBIT) // RESPONSE EXPECTED? + LINK->L2TIMER = 10*3; // SET TIMER + + return; + } + + Buffer->ORIGIN[6] |= 0x80; // SET RESPONSE + + LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY + + PORT = LINK->LINKPORT; + Buffer->PORT = PORT->PORTNUMBER; + + if (PORT) + PUT_ON_PORT_Q(PORT, Buffer); + else + ReleaseBuffer(Buffer); + +} + + +MESSAGE * SETUPL2MESSAGE(struct _LINKTABLE * LINK, UCHAR CMD) +{ + MESSAGE * Buffer; + UCHAR * ptr; + + Buffer = GetBuff(); + + if (Buffer == NULL) + return NULL; + + ptr = SETUPADDRESSES(LINK, Buffer); // copy addresses + + // ptr NOW POINTS TO COMMAND BYTE + + *(ptr)++ = CMD; + + Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); // SET LENGTH + + return Buffer; +} + + +VOID L3LINKCLOSED(struct _LINKTABLE * LINK, int Reason); + +VOID InformPartner(struct _LINKTABLE * LINK, int Reason) +{ + // LINK IS DISCONNECTING - IF THERE IS A CROSSLINK, SEND DISC TO IT + + if (LINK->LINKTYPE == 3) + { + L3LINKCLOSED(LINK, Reason); + return; + } + + if (LINK->CIRCUITPOINTER) + { + CloseSessionPartner(LINK->CIRCUITPOINTER); + CLEARSESSIONENTRY(LINK->CIRCUITPOINTER); + } +} + + +UINT RR_OR_RNR(struct _LINKTABLE * LINK) +{ + UCHAR Temp; + TRANSPORTENTRY * Session; + + LINK->L2FLAGS &= ~RNRSENT; + + // SET UP APPROPRIATE SUPER COMMAND + + if (LINK->LINKTYPE == 3) + + // Node to Node - only busy if short of buffers + + goto CHKBUFFS; + +// UP OR DOWN LINK - SEE IF SESSION IS BUSY + + if (LINK->CIRCUITPOINTER == 0) + goto CHKBUFFS; // NOT CONNECTED + + Session = LINK->CIRCUITPOINTER; // TO CIRCUIT ENTRY + + Temp = CHECKIFBUSYL2(Session); //TARGET SESSION BUSY? + + if (Temp & L4BUSY) + goto SENDRNR; // BUSY + +CHKBUFFS: + + if (QCOUNT < 20) + goto SENDRNR; // NOT ENOUGH + + // SEND REJ IF IN REJ STATE + + if (LINK->L2STATE == 6) + { + + // We may have the needed frame in RXFRAMES + +CheckNSLoop2: + + if (LINK->RXFRAMES[LINK->LINKNR]) + { + // We have the first missing frame. Process it. + + struct PORTCONTROL * PORT = LINK->LINKPORT; + MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]); + + Debugprintf("L2 about to send REJ - process saved Frame %d", LINK->LINKNR); + PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer + + // NR has been updated. + + // Clear REJ if we have no more saved + + if (LINK->Ver2point2) // Using SREJ? + { + // see if any frames saved. + + int i; + + for (i = 0; i < 8; i++) + { + if (LINK->RXFRAMES[i]) + goto stayinREJ2; + } + // Drop through if no stored frames + } + + LINK->L2STATE = 5; + LINK->L2FLAGS &= ~REJSENT; +stayinREJ2: + LINK->L2ACKREQ = 0; // Cancel Resptime (Set by PROC_I_FRAME) + + goto CheckNSLoop2; // See if OK or we have another saved frame + } + if (LINK->L2STATE == 6) + + // if we support SREJ send that instesd or REJ + + if (LINK->Ver2point2) // We only allow 2.2 with SREJ Multi + return SREJ; + else + return REJ; + } + return RR; + +SENDRNR: + + LINK->L2FLAGS |= RNRSENT; // REMEMBER + + return RNR; +} + + +VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg); + +VOID CONNECTFAILED(struct _LINKTABLE * LINK) +{ + ConnectFailedOrRefused(LINK, "Failure with"); +} +VOID CONNECTREFUSED(struct _LINKTABLE * LINK) +{ + ConnectFailedOrRefused(LINK, "Busy from"); +} + +VOID L3CONNECTFAILED(struct _LINKTABLE * LINK); + +VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg) +{ + // IF DOWNLINK, TELL PARTNER + // IF CROSSLINK, TELL ROUTE CONTROL + + struct DATAMESSAGE * Buffer; + UCHAR * ptr1; + char Normcall[10]; + TRANSPORTENTRY * Session; + TRANSPORTENTRY * InSession; + + if (LINK->LINKTYPE == 3) + { + L3CONNECTFAILED(LINK); // REPORT TO LEVEL 3 + return; + } + + if (LINK->CIRCUITPOINTER == 0) // No crosslink?? + return; + + Buffer = GetBuff(); + + if (Buffer == NULL) + return; + + // SET UP HEADER + + Buffer->PID = 0xf0; + + ptr1 = SetupNodeHeader(Buffer); + + Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0; + + ptr1 += sprintf(ptr1, "%s %s\r", Msg, Normcall); + + Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer); + + Session = LINK->CIRCUITPOINTER; // GET CIRCUIT TABLE ENTRY + InSession = Session->L4CROSSLINK; // TO INCOMMING SESSION + + CLEARSESSIONENTRY(Session); + + if (InSession) + { + InSession->L4CROSSLINK = NULL; // CLEAR REVERSE LINK + C_Q_ADD(&InSession->L4TX_Q, Buffer); + PostDataAvailable(InSession); + } + else + ReleaseBuffer(Buffer); +} + +VOID SENDCONNECTREPLY(struct _LINKTABLE * LINK) +{ + // LINK SETUP COMPLETE + + struct DATAMESSAGE * Buffer; + UCHAR * ptr1; + char Normcall[10]; + TRANSPORTENTRY * Session; + TRANSPORTENTRY * InSession; + + if (LINK->LINKTYPE == 3) + return; + + // UP/DOWN LINK + + if (LINK->CIRCUITPOINTER == 0) // No crosslink?? + return; + + Buffer = GetBuff(); + + if (Buffer == NULL) + return; + + // SET UP HEADER + + Buffer->PID = 0xf0; + + ptr1 = SetupNodeHeader(Buffer); + + Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0; + + ptr1 += sprintf(ptr1, "Connected to %s\r", Normcall); + + Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer); + + Session = LINK->CIRCUITPOINTER; // GET CIRCUIT TABLE ENTRY + Session->L4STATE = 5; + InSession = Session->L4CROSSLINK; // TO INCOMMONG SESSION + + if (InSession) + { + C_Q_ADD(&InSession->L4TX_Q, Buffer); + PostDataAvailable(InSession); + } +} + + +TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK) +{ + TRANSPORTENTRY * NewSess = L4TABLE; + int Index = 0; + + while (Index < MAXCIRCUITS) + { + if (NewSess->L4USER[0] == 0) + { + // Got One + + LINK->CIRCUITPOINTER = NewSess; // SETUP LINK-CIRCUIT CONNECTION + + memcpy(NewSess->L4USER, LINK->LINKCALL, 7); + memcpy(NewSess->L4MYCALL, MYCALL, 7); // ALWAYS USE _NODE CALL + + NewSess->CIRCUITINDEX = Index; //OUR INDEX + NewSess->CIRCUITID = NEXTID; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // kEEP nON-ZERO + + NewSess->L4TARGET.LINK = LINK; + + NewSess->L4CIRCUITTYPE = L2LINK | UPLINK; + + NewSess->L4STATE = 5; // SET LINK ACTIVE + + NewSess->SESSPACLEN = LINK->LINKPORT->PORTPACLEN; + + + NewSess->SESSIONT1 = L4T1; // Default + NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + + return NewSess; + } + Index++; + NewSess++; + } + + return NULL; +} + + +VOID Digipeat(struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR * OurCall, int toPort, int UIOnly) // Digi it (if enabled) +{ + // WE MAY HAVE DISABLED DIGIPEAT ALTOGETHER, (DIGIFLAG=0), + // OR ALLOW ALLOW ONLY UI FRAMES TO BE DIGIED (DIGIFLAG=-1) + + // toPort and UIOnly are used for Cross Port digi feature + + int n; + + if (PORT->DIGIFLAG == 0 && toPort == 0) + { + ReleaseBuffer(Buffer); + return; + } + + OurCall[6] |= 0x80; // SET HAS BEEN REPEATED + + // SEE IF UI FRAME - scan forward for end of address bit + + n = 8; + + while ((OurCall[6] & 1) == 0) + { + OurCall += 7; + + if ((OurCall - &Buffer->CTL) > 56) + { + // Run off end before findin end of address + + ReleaseBuffer(Buffer); + return; + } + } + + if (toPort) // Cross port digi + { + if (((OurCall[7] & ~PFBIT) == 3) || UIOnly == 0) + { + // UI or Digi all + + Buffer->PORT = toPort; // update port no in header + PORT = GetPortTableEntryFromPortNum(toPort); + + if (PORT == NULL) + ReleaseBuffer(Buffer); + else + PUT_ON_PORT_Q(PORT, Buffer); + return; + } + else + { + ReleaseBuffer(Buffer); + return; + } + } + + if ((OurCall[7] & ~PFBIT) == 3) + { + // UI + + // UI FRAME. IF DIGIMASK IS NON-ZERO, SEND TO ALL PORTS SET, OTHERWISE SEND TO DIGIPORT + + PORT->L2DIGIED++; + + if (toPort) + { + // Cross port digi + + PORT = GetPortTableEntryFromPortNum(toPort); + Buffer->PORT = PORT->DIGIPORT; // update port no in header + + if (PORT == NULL) + ReleaseBuffer(Buffer); + else + PUT_ON_PORT_Q(PORT, Buffer); + + return; + } + + if (PORT->DIGIMASK == 0) + { + if (PORT->DIGIPORT) // Cross Band Digi? + { + Buffer->PORT = PORT->DIGIPORT; // update port no in header + + PORT = GetPortTableEntryFromPortNum(PORT->DIGIPORT); + + if (PORT == NULL) + { + ReleaseBuffer(Buffer); + return; + } + } + PUT_ON_PORT_Q(PORT, Buffer); + } + else + { + DigiToMultiplePorts(PORT, Buffer); + ReleaseBuffer(Buffer); + } + return; + } + + // Not UI - Only Digi if Digiflag not -1 + + if (PORT->DIGIFLAG == -1) + { + ReleaseBuffer(Buffer); + return; + } + + PORT->L2DIGIED++; + + if (PORT->DIGIPORT) // Cross Band Digi? + { + Buffer->PORT = PORT->DIGIPORT; // update port no in header + + PORT = GetPortTableEntryFromPortNum(PORT->DIGIPORT); + + if (PORT == NULL) + { + ReleaseBuffer(Buffer); + return; + } + } + PUT_ON_PORT_Q(PORT, Buffer); +} + +BOOL CheckForListeningSession(struct PORTCONTROL * PORT, MESSAGE * Msg) +{ + TRANSPORTENTRY * L4 = L4TABLE; + struct DATAMESSAGE * Buffer; + int i = MAXCIRCUITS; + UCHAR * ptr; + + while (i--) + { + if ((CountBits64(L4->LISTEN) == 1) && ((1 << ((Msg->PORT & 0x7f) - 1) && L4->LISTEN))) + { + // See if he is calling our call + + UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted + memcpy(ourcall, L4->L4USER, 7); + ourcall[6] ^= 0x1e; // Flip SSID + + if (CompareCalls(Msg->DEST, ourcall)) + { + // Get Session Entry for Downlink + + TRANSPORTENTRY * NewSess = L4TABLE; + struct _LINKTABLE * LINK; + char Normcall[10]; + + int Index = 0; + + while (Index < MAXCIRCUITS) + { + if (NewSess->L4USER[0] == 0) + { + // Got One + + L4->L4CROSSLINK = NewSess; + NewSess->L4CROSSLINK = L4; + + memcpy(NewSess->L4USER, L4->L4USER, 7); + memcpy(NewSess->L4MYCALL, L4->L4USER, 7); + + NewSess->CIRCUITINDEX = Index; //OUR INDEX + NewSess->CIRCUITID = NEXTID; + NewSess->L4STATE = 5; + NewSess->L4CIRCUITTYPE = L2LINK+UPLINK; + + NEXTID++; + if (NEXTID == 0) + NEXTID++; // kEEP nON-ZERO + + NewSess->SESSIONT1 = L4->SESSIONT1; + NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW; + + // SET UP NEW SESSION (OR RESET EXISTING ONE) + + FindLink(Msg->ORIGIN, ourcall, PORT->PORTNUMBER, &LINK); + + if (LINK == NULL) + return FALSE; + + memcpy(LINK->LINKCALL, Msg->ORIGIN, 7); + LINK->LINKCALL[6] &= 0xFE; + 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 + + LINK->L2STATE = 5; // CONNECTED + + LINK->CIRCUITPOINTER = NewSess; + + NewSess->L4TARGET.LINK = LINK; + + if (PORT->PORTPACLEN) + NewSess->SESSPACLEN = L4->SESSPACLEN = PORT->PORTPACLEN; + + L2SENDUA(PORT, Msg, Msg); // RESET OF DOWN/CROSSLINK + + L4->LISTEN = FALSE; // Take out of listen mode + + // Tell User + + Buffer = GetBuff(); + + if (Buffer == NULL) + return TRUE; + + // SET UP HEADER + + Buffer->PID = 0xf0; + + ptr = &Buffer->L2DATA[0]; + + Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0; + + ptr += sprintf(ptr, "Incoming call from %s\r", Normcall); + + Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); + + C_Q_ADD(&L4->L4TX_Q, Buffer); + PostDataAvailable(L4); + + return TRUE; + + } + Index++; + NewSess++; + } + return FALSE; + } + } + L4++; + } + return FALSE; +} + + +int COUNTLINKS(int Port); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); + + +int CheckKissInterlock(struct PORTCONTROL * PORT, int Exclusive) +{ + // This checks for interlocked kiss and other ports. Returns 1 if attach/connect not allowed + + // If Exclusive is not set allow connects on specified port up to l2limit, + + // If Exclusive is set also don't allow any connects on specified port. + + // Generally use Exclusive if locking a port that doesn't allow shared access, eg ARDOP, VARAus + + // Maybe only Exclusive is needed, and just check session mode ports. Sharing of KISS ports is controlled by USERS + + int Interlock = PORT->PORTINTERLOCK; + + if (Interlock == 0) + return 0; // No locking + + PORT = PORTTABLE; + + if (Exclusive) + { + while(PORT) + { + if (PORT->TNC) + { + struct TNCINFO * TNC = PORT->TNC; + + if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group + { + // See if port in use + + int n; + + for (n = 0; n <= 26; n++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[n]) + { + return TNC->Port; ; // Refuse Connect + } + } + } + } + PORT = PORT->PORTPOINTER; + } + } + return 0; // ok to connect +} + +int seeifInterlockneeded(struct PORTCONTROL * PORT) +{ + // Can we just call SuspendOtherPorts - it won't do any harm if already suspended + // No, at that needs a TNC Record, so duplicate code here + + int i; + int Interlock = PORT->PORTINTERLOCK; + struct TNCINFO * TNC; + + if (Interlock == 0) + return 0; // No locking + + for (i = 1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + + if (TNC) + if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group + if (TNC->SuspendPortProc && TNC->PortRecord->PORTCONTROL.PortSuspended == FALSE) + TNC->SuspendPortProc(TNC, TNC); + } + + return 0; +} + +int seeifUnlockneeded(struct _LINKTABLE * LINK) +{ + // We need to see if any other links are active on any interlocked KISS ports. If not, release the lock + + int i; + int links = 0; + + int Interlock; + struct TNCINFO * TNC; + struct PORTCONTROL * PORT = LINK->LINKPORT; + + if (PORT == NULL) + return 0; + + // Should only be called for KISS links, but just in case + + if (PORT->PORTTYPE > 12) // INTERNAL or EXTERNAL? + return 0; // Not KISS Port + + Interlock = PORT->PORTINTERLOCK; + + if (Interlock == 0) + return 0; // No locking + + + // Count all L2 links on interlocked KISS ports + + PORT = PORTTABLE; + + while(PORT) + { + if (PORT->PORTTYPE <= 12) // INTERNAL or EXTERNAL? + if (Interlock == PORT->PORTINTERLOCK) + links += COUNTLINKS(PORT->PORTNUMBER); + + PORT = PORT->PORTPOINTER; + } + + if (links > 1) // must be the one we are closing + return 0; // Keep lock + + + for (i = 1; i <= MAXBPQPORTS; i++) + { + TNC = TNCInfo[i]; + + if (TNC) + if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group + if (TNC->ReleasePortProc && TNC->PortRecord->PORTCONTROL.PortSuspended == TRUE) + TNC->ReleasePortProc(TNC); + } + + return 0; +} + +int L2Compressit(unsigned char * Out, int OutSize, unsigned char * In, int Len) +{ + z_stream defstream; + int maxSize; + + defstream.zalloc = Z_NULL; + defstream.zfree = Z_NULL; + defstream.opaque = Z_NULL; + + defstream.avail_in = Len; // size of input + defstream.next_in = (Bytef *)In; // input char array + + deflateInit(&defstream, Z_BEST_COMPRESSION); + maxSize = deflateBound(&defstream, Len); + + if (maxSize > OutSize) + return 0; + + defstream.avail_out = maxSize; // size of output + defstream.next_out = (Bytef *)Out; // output char array + + deflate(&defstream, Z_FINISH); + deflateEnd(&defstream); + + return defstream.total_out; +} + + + + diff --git a/.svn/pristine/70/7046861379015ba0f5eb82e31ee59f95954d5d07.svn-base b/.svn/pristine/70/7046861379015ba0f5eb82e31ee59f95954d5d07.svn-base new file mode 100644 index 0000000..5903d0d --- /dev/null +++ b/.svn/pristine/70/7046861379015ba0f5eb82e31ee59f95954d5d07.svn-base @@ -0,0 +1,1559 @@ +/* +Copyright 2001-2018 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 Fvoideven 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 +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// White Pages Database Support Routines + +#include "bpqmail.h" + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); + +int CurrentWPIndex; +char CurrentWPCall[10]; + +time_t LASTWPSendTime; + + +VOID DoWPUpdate(WPRec *WP, char Type, char * Name, char * HA, char * QTH, char * ZIP, time_t WPDate); +VOID Do_Save_WPRec(HWND hDlg); +VOID SaveInt64Value(config_setting_t * group, char * name, long long value); +VOID SaveIntValue(config_setting_t * group, char * name, int value); +VOID SaveStringValue(config_setting_t * group, char * name, char * value); +void MQTTMessageEvent(void* message); + +WPRec * AllocateWPRecord() +{ + WPRec * WP = zalloc(sizeof (WPRec)); + + GetSemaphore(&AllocSemaphore, 0); + + WPRecPtr=realloc(WPRecPtr,(++NumberofWPrecs+1) * sizeof(void *)); + WPRecPtr[NumberofWPrecs]= WP; + + FreeSemaphore(&AllocSemaphore); + + return WP; +} + +int BadCall(char * Call) +{ + if (_stricmp(Call, "RMS") == 0) + return 1; + + if (_stricmp(Call, "SYSTEM") == 0) + return 1; + + if (_stricmp(Call, "SWITCH") == 0) + return 1; + + if (_stricmp(Call, "SYSOP") == 0) + return 1; + + if (_memicmp(Call, "SMTP", 4) == 0) + return 1; + + if (_memicmp(Call, "SMTP:", 5) == 0) + return 1; + + if (_stricmp(Call, "AMPR") == 0) + return 1; + + if (_stricmp(Call, "FILE") == 0) + return 1; + + if (_memicmp(Call, "MCAST", 5) == 0) + return 1; + + if (_memicmp(Call, "SYNC", 5) == 0) + return 1; + + return 0; +} + +extern config_t cfg; + +VOID GetWPDatabase() +{ + WPRec WPRec; + FILE * Handle; + int ReadLen; + WPRecP WP; + char CfgName[MAX_PATH]; + long long val; + config_t wpcfg; + config_setting_t * group, * wpgroup; + int i = 1; + struct stat STAT; + + // If WP info is in main config file, use it + + group = config_lookup (&cfg, "WP"); + + if (group) + { + // Set up control record + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = zalloc(sizeof(WPRec)); + NumberofWPrecs = 0; + + while (1) + { + char Key[16]; + char Record[1024]; + char * ptr, * ptr2; + unsigned int n; + + sprintf(Key, "R%d", i++); + + GetStringValue(group, Key, Record, 1024); + + if (Record[0] == 0) // End of List + return; + + memset(&WPRec, 0, sizeof(WPRec)); + + WP = &WPRec; + + ptr = Record; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->callsign[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->name[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) WP->Type = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) WP->changed = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) WP->seen = atoi(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->first_homebbs[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->secnd_homebbs[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->first_zip[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + if (ptr) strcpy(&WP->secnd_zip[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) continue; + + if (strlen(ptr) > 30) + ptr[30] = 0; + + strcpy(&WP->first_qth[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr == NULL) continue; + + if (strlen(ptr) > 30) + ptr[30] = 0; + + strcpy(&WP->secnd_qth[0], ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr) WP->last_modif = atol(ptr); + + ptr = ptr2; + ptr2 = strlop(ptr, '|'); + + if (ptr) + { + WP->last_seen = atol(ptr); + + // Check Call + + for (n = 1; n < strlen(WP->callsign); n++) // skip first which may also be digit + { + if (isdigit(WP->callsign[n])) + { + // Has a digit. Check Last is not digit + + if (isalpha(WP->callsign[strlen(WP->callsign) - 1])) + { + WP = LookupWP(WPRec.callsign); + if (WP == NULL) + WP = AllocateWPRecord(); + + memcpy(WP, &WPRec, sizeof(WPRec)); + goto WPOK; + } + } + } + Debugprintf("Bad WP Call %s", WP->callsign); + } +WPOK:; + } + return; + } + + // If text format exists use it + + strcpy(CfgName, WPDatabasePath); + strlop(CfgName, '.'); + strcat(CfgName, ".cfg"); + + if (stat(CfgName, &STAT) == -1) + goto tryOld; + + config_init(&wpcfg); + + if (!config_read_file(&wpcfg, CfgName)) + { + char Msg[256]; + sprintf(Msg, "Config File %s Line %d - %s\n", CfgName, + config_error_line(&wpcfg), config_error_text(&wpcfg)); + + printf("%s", Msg); + config_destroy(&wpcfg); + goto tryOld; + } + + // Set up control record + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = zalloc(sizeof(WPRec)); + NumberofWPrecs = 0; + + while (1) + { + char Key[16]; + char Temp[128]; + + sprintf(Key, "R%d", i++); + + wpgroup = config_lookup(&wpcfg, Key); + + if (wpgroup == NULL) // End of List + { + config_destroy(&wpcfg); + return; + } + + memset(&WPRec, 0, sizeof(WPRec)); + + GetStringValue(wpgroup, "c", WPRec.callsign, 6); + GetStringValue(wpgroup, "n", WPRec.name, 12); + + WPRec.Type = GetIntValue(wpgroup, "T"); + WPRec.changed = GetIntValue(wpgroup, "ch"); + WPRec.seen = GetIntValue(wpgroup, "s"); + + GetStringValue(wpgroup, "h", WPRec.first_homebbs, 40); + GetStringValue(wpgroup, "sh", WPRec.secnd_homebbs, 40); + GetStringValue(wpgroup, "z", WPRec.first_zip, 8); + GetStringValue(wpgroup, "sz", WPRec.secnd_zip, 8); + + GetStringValue(wpgroup, "q", Temp, 30); + Temp[30] = 0; + strcpy(WPRec.first_qth, Temp); + + GetStringValue(wpgroup, "sq", Temp, 30); + Temp[30] = 0; + strcpy(WPRec.secnd_qth, Temp); + + val = GetIntValue(wpgroup, "m"); + WPRec.last_modif = val; + val = GetIntValue(wpgroup, "ls"); + WPRec.last_seen = val; + + _strupr(WPRec.callsign); + + strlop(WPRec.callsign, ' '); + + if (strlen(WPRec.callsign) > 2) + { + if (strchr(WPRec.callsign, ':')) + continue; + + if (BadCall(WPRec.callsign)) + continue; + + WP = LookupWP(WPRec.callsign); + + if (WP == NULL) + WP = AllocateWPRecord(); + + memcpy(WP, &WPRec, sizeof(WPRec)); + } + } + +tryOld: + + Handle = fopen(WPDatabasePath, "rb"); + + if (Handle == NULL) + { + // Initialise a new File + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = malloc(sizeof(WPRec)); + memset(WPRecPtr[0], 0, sizeof(WPRec)); + NumberofWPrecs = 0; + + return; + } + + + // Get First Record + + ReadLen = fread(&WPRec, 1, sizeof(WPRec), Handle); + + if (ReadLen == 0) + { + // Duff file + + memset(&WPRec, 0, sizeof(WPRec)); + } + + // Set up control record + + WPRecPtr = malloc(sizeof(void *)); + WPRecPtr[0] = malloc(sizeof(WPRec)); + memcpy(WPRecPtr[0], &WPRec, sizeof(WPRec)); + + NumberofWPrecs = 0; + +Next: + + ReadLen = fread(&WPRec, 1, sizeof(WPRec), Handle); + + if (ReadLen == sizeof(WPRec)) + { + _strupr(WPRec.callsign); + + strlop(WPRec.callsign, ' '); + + if (strlen(WPRec.callsign) > 2) + { + if (strchr(WPRec.callsign, ':')) + goto Next; + + if (BadCall(WPRec.callsign)) + goto Next; + + WP = LookupWP(WPRec.callsign); + + if (WP == NULL) + WP = AllocateWPRecord(); + + memcpy(WP, &WPRec, sizeof(WPRec)); + } + goto Next; + } + + fclose(Handle); + SaveWPDatabase(); +} + +VOID CopyWPDatabase() +{ + char Backup[MAX_PATH]; + char Orig[MAX_PATH]; + + return; + + strcpy(Backup, WPDatabasePath); + strcat(Backup, ".bak"); + + CopyFile(WPDatabasePath, Backup, FALSE); + + strcpy(Backup, WPDatabasePath); + strlop(Backup, '.'); + strcat(Backup, ".cfg.bak"); + + strcpy(Orig, WPDatabasePath); + strlop(Orig, '.'); + strcat(Orig, ".cfg"); + CopyFile(Orig, Backup, FALSE); +} + +VOID SaveWPDatabase() +{ +// SaveConfig(ConfigName); // WP config is now in main config file + + int i; + config_setting_t *root, *group; + config_t cfg; + char Key[16]; + WPRec * WP; + char CfgName[MAX_PATH]; + long long val; + + memset((void *)&cfg, 0, sizeof(config_t)); + + config_init(&cfg); + + root = config_root_setting(&cfg); + + for (i = 0; i <= NumberofWPrecs; i++) + { + WP = WPRecPtr[i]; + sprintf(Key, "R%d", i); + + group = config_setting_add(root, Key, CONFIG_TYPE_GROUP); + + SaveStringValue(group, "c", &WP->callsign[0]); + SaveStringValue(group, "n", &WP->name[0]); + SaveIntValue(group, "T", WP->Type); + SaveIntValue(group, "ch", WP->changed); + SaveIntValue(group, "s", WP->seen); + SaveStringValue(group, "h", &WP->first_homebbs[0]); + SaveStringValue(group, "sh", &WP->secnd_homebbs[0]); + SaveStringValue(group, "z", &WP->first_zip[0]); + SaveStringValue(group, "sz", &WP->secnd_zip[0]); + SaveStringValue(group, "q", &WP->first_qth[0]); + SaveStringValue(group, "sq", &WP->secnd_qth[0]); + val = WP->last_modif; + SaveInt64Value(group, "m", val); + val = WP->last_seen; + SaveInt64Value(group, "ls", val); + } + + strcpy(CfgName, WPDatabasePath); + strlop(CfgName, '.'); + strcat(CfgName, ".cfg"); + + Debugprintf("Saving WP Database to %s\n", CfgName); + + config_write_file(&cfg, CfgName); + config_destroy(&cfg); + +} + +WPRec * LookupWP(char * Call) +{ + WPRec * ptr = NULL; + int i; + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + if (_stricmp(ptr->callsign, Call) == 0) return ptr; + } + + return NULL; +} + +char * FormatWPDate(time_t Datim) +{ + struct tm *tm; + static char Date[]="xx-xxx-xx "; + + tm = gmtime(&Datim); + + if (tm) + { + if (tm->tm_year >= 100) + sprintf_s(Date, sizeof(Date), "%02d-%3s-%02d", + tm->tm_mday, month[tm->tm_mon], tm->tm_year - 100); + else + sprintf_s(Date, sizeof(Date), ""); + } + return Date; +} + +#ifndef LINBPQ + +int Do_WP_Sel_Changed(HWND hDlg) +{ + // Update WP display with newly selected rec + + WPRec * WP; + int Sel = SendDlgItemMessage(hDlg, IDC_WP, CB_GETCURSEL, 0, 0); + char Type[] = " "; + + if (Sel == -1) + SendDlgItemMessage(hDlg, IDC_WP, WM_GETTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentWPCall); + else + SendDlgItemMessage(hDlg, IDC_WP, CB_GETLBTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentWPCall); + + for (CurrentWPIndex = 1; CurrentWPIndex <= NumberofWPrecs; CurrentWPIndex++) + { + WP = WPRecPtr[CurrentWPIndex]; + + if (_stricmp(WP->callsign, CurrentWPCall) == 0) + { + + SetDlgItemText(hDlg, IDC_WPNAME, WP->name); + SetDlgItemText(hDlg, IDC_HOMEBBS1, WP->first_homebbs); + SetDlgItemText(hDlg, IDC_HOMEBBS2, WP->secnd_homebbs); + SetDlgItemText(hDlg, IDC_QTH1, WP->first_qth); + SetDlgItemText(hDlg, IDC_QTH2, WP->secnd_qth); + SetDlgItemText(hDlg, IDC_ZIP1, WP->first_zip); + SetDlgItemText(hDlg, IDC_ZIP2, WP->secnd_zip); + Type[0] = WP->Type; + SetDlgItemText(hDlg, IDC_TYPE, Type); + SetDlgItemInt(hDlg, IDC_CHANGED, WP->changed, FALSE); + SetDlgItemInt(hDlg, IDC_SEEN, WP->seen, FALSE); + + SetDlgItemText(hDlg, IDC_LASTSEEN, FormatWPDate(WP->last_seen)); + SetDlgItemText(hDlg, IDC_LASTMODIFIED, FormatWPDate(WP->last_modif)); + + return 0; + } + } + + CurrentWPIndex = -1; + + return 0; +} + +INT_PTR CALLBACK InfoDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + + +VOID Do_Save_WPRec(HWND hDlg) +{ + WPRec * WP; + char Type[] = " "; + BOOL OK1; + + if (CurrentWPIndex == -1) + { + sprintf(InfoBoxText, "Please select a WP Record to save"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + WP = WPRecPtr[CurrentWPIndex]; + + if (strcmp(CurrentWPCall, WP->callsign) != 0) + { + sprintf(InfoBoxText, "Inconsistancy detected - record not saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + GetDlgItemText(hDlg, IDC_WPNAME, WP->name, 13); + GetDlgItemText(hDlg, IDC_HOMEBBS1, WP->first_homebbs, 41); + GetDlgItemText(hDlg, IDC_HOMEBBS2, WP->secnd_homebbs, 41); + GetDlgItemText(hDlg, IDC_QTH1, WP->first_qth, 31); + GetDlgItemText(hDlg, IDC_QTH2, WP->secnd_qth, 31); + GetDlgItemText(hDlg, IDC_ZIP1, WP->first_zip, 31); + GetDlgItemText(hDlg, IDC_ZIP2, WP->secnd_zip, 31); + WP->last_modif = time(NULL); + WP->seen = GetDlgItemInt(hDlg, IDC_SEEN, &OK1, FALSE); + + WP->Type = 'U'; + WP->changed = 1; + + SaveWPDatabase(); + + sprintf(InfoBoxText, "WP information saved"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); +} + +VOID Do_Delete_WPRec(HWND hDlg) +{ + WPRec * WP; + int n; + + if (CurrentWPIndex == -1) + { + sprintf(InfoBoxText, "Please select a WP Record to delete"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + WP = WPRecPtr[CurrentWPIndex]; + + if (strcmp(CurrentWPCall, WP->callsign) != 0) + { + sprintf(InfoBoxText, "Inconsistancy detected - record not deleted"); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + return; + } + + for (n = CurrentWPIndex; n < NumberofWPrecs; n++) + { + WPRecPtr[n] = WPRecPtr[n+1]; // move down all following entries + } + + NumberofWPrecs--; + + SendDlgItemMessage(hDlg, IDC_WP, CB_RESETCONTENT, 0, 0); + + for (n = 1; n <= NumberofWPrecs; n++) + { + SendDlgItemMessage(hDlg, IDC_WP, CB_ADDSTRING, 0, (LPARAM)WPRecPtr[n]->callsign); + } + + + sprintf(InfoBoxText, "WP record for %s deleted", WP->callsign); + DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); + + free(WP); + + SaveWPDatabase(); + + return; + +} + +INT_PTR CALLBACK WPEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Command, n; + + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + + case WM_INITDIALOG: + + for (n = 1; n <= NumberofWPrecs; n++) + { + SendDlgItemMessage(hDlg, IDC_WP, CB_ADDSTRING, 0, (LPARAM)WPRecPtr[n]->callsign); + } + + return (INT_PTR)TRUE; + + case WM_CTLCOLORDLG: + + return (LONG)bgBrush; + + case WM_CTLCOLORSTATIC: + { + HDC hdcStatic = (HDC)wParam; + SetTextColor(hdcStatic, RGB(0, 0, 0)); + SetBkMode(hdcStatic, TRANSPARENT); + return (LONG)bgBrush; + } + + + case WM_COMMAND: + + Command = LOWORD(wParam); + + switch (Command) + { + + case IDOK: + case IDCANCEL: + + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + + case IDC_WP: + + // Msg Selection Changed + + Do_WP_Sel_Changed(hDlg); + + return TRUE; + + case IDC_SAVEWP: + + Do_Save_WPRec(hDlg); + return TRUE; + + case IDC_DELETEWP: + + Do_Delete_WPRec(hDlg); + return TRUE; + } + break; + } + + return (INT_PTR)FALSE; +} +#endif + +VOID GetWPBBSInfo(char * Rline) +{ + // Update WP with /I records for each R: Line + + // R:111206/1636Z 29130@N9PMO.#SEWI.WI.USA.NOAM [Racine, WI] FBB7.00i + + struct tm rtime; + time_t RLineTime; + int Age; + + WPRec * WP; + char ATBBS[200]; + char Call[200]; + char QTH[200] = ""; + int RLen; + + char * ptr1; + char * ptr2; + + + memset(&rtime, 0, sizeof(struct tm)); + + if (Rline[10] == '/') + { + // Dodgy 4 char year + + sscanf(&Rline[2], "%04d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + rtime.tm_year -= 1900; + rtime.tm_mon--; + } + else if (Rline[8] == '/') + { + sscanf(&Rline[2], "%02d%02d%02d/%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + + if (rtime.tm_year < 90) + rtime.tm_year += 100; // Range 1990-2089 + rtime.tm_mon--; + } + + // Otherwise leave date as zero, which should be rejected + + if ((RLineTime = mktime(&rtime)) != (time_t)-1 ) + { + Age = (time(NULL) - RLineTime)/86400; + + if ( Age < -1) + return; // in the future + + if (Age > BidLifetime || Age > MaxAge) + return; // Too old + } + + ptr1 = strchr(Rline, '@'); + ptr2 = strchr(Rline, '\r'); + + if (!ptr1) + return; // Duff + + if (*++ptr1 == ':') + ptr1++; // Format 2 + + + if (ptr2 == NULL) + return; // No CR on end + + RLen = ptr2 - ptr1; + + if (RLen > 200) + return; + + memcpy(ATBBS, ptr1, RLen); + ATBBS[RLen] = 0; + + ptr2 = strchr(ATBBS, ' '); + + if (ptr2) + *ptr2 = 0; + + strcpy(Call, ATBBS); + strlop(Call, '.'); + + ptr2 = memchr(ptr1, '[', RLen); + + if (ptr2) + { + ptr1= memchr(ptr2, ']', RLen); + if (ptr1) + memcpy(QTH, ptr2 + 1, ptr1 - ptr2 - 1); + } + + if (BadCall(Call)) + return; + + WP = LookupWP(Call); + + if (!WP) + { + // Not Found + + WP = AllocateWPRecord(); + + strcpy(WP->callsign, Call); + strcpy(WP->first_homebbs, ATBBS); + strcpy(WP->secnd_homebbs, ATBBS); + + if (QTH[0]) + { + strcpy(WP->first_qth, QTH); + strcpy(WP->secnd_qth, QTH); + } + + WP->last_modif = RLineTime; + WP->last_seen = RLineTime; + + WP->Type = 'I'; + WP->changed = TRUE; + + return; + } + + if (WP->last_modif >= RLineTime || WP->Type != 'I') + return; + + // Update 2nd if changed + + if (strcmp(WP->secnd_homebbs , ATBBS) != 0) + { + strcpy(WP->secnd_homebbs, ATBBS); + WP->last_modif = RLineTime; + } + + if (QTH[0] && strcmp(WP->secnd_qth , QTH) != 0) + { + strcpy(WP->secnd_qth, QTH); + WP->last_modif = RLineTime; + } + + return; +} + + + + +VOID GetWPInfoFromRLine(char * From, char * FirstRLine, time_t RLineTime) +{ + /* The /G suffix denotes that the information in this line has been gathered by examining + the header of a message to GUESS at which BBS the sender is registered. The HomeBBS of the User + is assumed to be the BBS shown in the first R: header line. The date associated with this + information is the date shown on this R: header line. + */ + + // R:930101/0000 1530@KA6FUB.#NOCAL.CA.USA.NOAM + + // R:930101/0000 @:KA6FUB.#NOCAL.CA.USA.NOAM #:1530 + + // The FirstRLine pointer points to the message, so shouldnt be changed + + WPRec * WP; + char ATBBS[200]; + int RLen; + + char * ptr1 = strchr(FirstRLine, '@'); + char * ptr2 = strchr(FirstRLine, '\r'); + + if (BadCall(From)) + return; + + if (!ptr1) + return; // Duff + + if (*++ptr1 == ':') + ptr1++; // Format 2 + + RLen = ptr2 - ptr1; + + if (RLen > 200) + return; + + memcpy(ATBBS, ptr1, RLen); + ATBBS[RLen] = 0; + + ptr2 = strchr(ATBBS, ' '); + + if (ptr2) + *ptr2 = 0; + + if (strlen(ATBBS) > 40) + ATBBS[40] = 0; + + WP = LookupWP(From); + + if (!WP) + { + // Not Found + + WP = AllocateWPRecord(); + + strcpy(WP->callsign, From); + strcpy(WP->first_homebbs, ATBBS); + strcpy(WP->secnd_homebbs, ATBBS); + + WP->last_modif = RLineTime; + WP->last_seen = RLineTime; + + WP->Type = 'G'; + WP->changed = TRUE; + + return; + } + + if (WP->last_modif >= RLineTime) + return; + + // Update 2nd if changed + + if (strcmp(WP->secnd_homebbs , ATBBS) != 0) + { + strcpy(WP->secnd_homebbs, ATBBS); + WP->last_modif = RLineTime; + } + + return; +} + +VOID ProcessWPMsg(char * MailBuffer, int Size, char * FirstRLine) +{ + char * ptr1 = MailBuffer; + char * ptr2; + WPRec * WP; + char WPLine[200]; + int WPLen; + + ptr1[Size] = 0; + + ptr1 = FirstRLine; + + if (ptr1 == NULL) + return; + + while (*ptr1) + { + ptr2 = strchr(ptr1, '\r'); + + if (ptr2 == 0) // No CR in buffer + return; + + WPLen = ptr2 - ptr1; + + if (WPLen > 128) + return; + + if ((memcmp(ptr1, "On ", 3) == 0) && (WPLen < 200)) + { + char * Date; + char * Call; + char * AT; + char * HA; + char * zip; + char * ZIP; + char * Name; + char * QTH = NULL; + char * Context; + char seps[] = " \r"; + + // Make copy of string, as strtok messes with it + + memcpy(WPLine, ptr1, WPLen); + WPLine[WPLen] = 0; + + Date = strtok_s(WPLine+3, seps, &Context); + Call = strtok_s(NULL, seps, &Context); + AT = strtok_s(NULL, seps, &Context); + HA = strtok_s(NULL, seps, &Context); + zip = strtok_s(NULL, seps, &Context); + ZIP = strtok_s(NULL, seps, &Context); + Name = strtok_s(NULL, seps, &Context); + QTH = strtok_s(NULL, "\r", &Context); // QTH may have spaces + + if (Date == 0 || Call == 0 || AT == 0 || ZIP == 0 || Name == 0 || QTH == 0) + return; + + if (strlen(HA) > 40) + return; + if (strlen(ZIP) > 8) + return; + if (strlen(Name) > 12) + return; + if (strlen(QTH) > 30) + return; + + if (AT[0] == '@' && (QTH)) + { + struct tm rtime; + time_t WPDate; + char Type; + char * TypeString; + + if (memcmp(Name, "?", 2) == 0) Name = NULL; + if (memcmp(ZIP, "?", 2) == 0) ZIP = NULL; + if (memcmp(QTH, "?", 2) == 0) QTH = NULL; + + memset(&rtime, 0, sizeof(struct tm)); + + sscanf(Date, "%02d%02d%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday); + + rtime.tm_year += 100; + rtime.tm_mon--; + +/* +This process freshens the database, following receipt of the new or changed information detailed above. + +The update subroutine will first look for an entry in the database for the callsign which matches the received information. +If it does not exist then a completely new record will be created in the database and the information be used to fill what +fields it can, in both the active and the temporary components. The date will be then changed to the one associated with the +update information. + +If the record does already exist, then the unknown fields of both the temporary and active fields will be filled in, and +those fields already known in the temporary part will be replaced by the new information if the date new information is +younger than that already on file. The date will then be +adjusted such that it is consistent with the updated information. + +If the new information is of the /U category, then the current fields will be replaced by the new information in both +the primary and secondary (Active and Temporary) parts of the record, as this information has been input directly from +the user. If the information was of another category then only the secondary (Temporary) part of the record will be +updated, so the Active or primary record will remain unchanged at this time. + +If a field is changed, a flag giving the update request type is then validated. If the /U flag is already validated, +it will not be replaced. This flag will be used in case the WP update messages are validated. +*/ + if ((WPDate = mktime(&rtime)) != (time_t)-1 ) + { + WPDate -= (time_t)_MYTIMEZONE; + TypeString = strlop(Call, '/'); + + if (strlen(Call) < 3 || strlen(Call) > 6) + return; + + if (TypeString) + Type = TypeString[0]; + else + Type = 'G'; + + if (strchr(Call, ':')) + break; + + if (BadCall(Call)) + break; + + WP = LookupWP(Call); + + if (WP) + { + // Found, so update + + DoWPUpdate(WP, Type, Name, HA, QTH, ZIP, WPDate); + } + else + { + WP = AllocateWPRecord(); + + strcpy(WP->callsign, Call); + if (Name) strcpy(WP->name, Name); + strcpy(WP->first_homebbs, HA); + strcpy(WP->secnd_homebbs, HA); + + if (QTH) + { + strcpy(WP->first_qth, QTH); + strcpy(WP->secnd_qth, QTH);; + } + if (ZIP) + { + strcpy(WP->first_zip, ZIP); + strcpy(WP->secnd_zip, ZIP); + } + + WP->Type = Type; + WP->last_modif = WPDate; + WP->last_seen = WPDate; + WP->changed = TRUE; + WP->seen++; + } + } + } + } + + ptr1 = ++ptr2; + if (*ptr1 == '\n') + ptr1++; + } + + SaveWPDatabase(); + + return; +} + +VOID DoWPUpdate(WPRec * WP, char Type, char * Name, char * HA, char * QTH, char * ZIP, time_t WPDate) +{ + // First Update any unknown field + + if(Name) + if (WP->name == NULL) {strcpy(WP->name, Name); WP->last_modif = WPDate; WP->changed = TRUE;} + + if (QTH) + { + if (WP->first_qth == NULL) {strcpy(WP->first_qth, QTH); WP->last_modif = WPDate; WP->changed = TRUE;} + if (WP->secnd_qth == NULL) {strcpy(WP->secnd_qth, QTH); WP->last_modif = WPDate; WP->changed = TRUE;} + } + if (ZIP) + { + if (WP->first_zip == NULL) {strcpy(WP->first_zip, ZIP); WP->last_modif = WPDate; WP->changed = TRUE;} + if (WP->secnd_zip == NULL) {strcpy(WP->secnd_zip, ZIP); WP->last_modif = WPDate; WP->changed = TRUE;} + } + + WP->last_seen = WPDate; + WP->seen++; + + // Now Update Temp Fields if update is newer than original + + if (WP->last_modif >= WPDate) + return; + + if (Type == 'U') // Definitive Update + { + if (strcmp(WP->first_homebbs , HA) != 0) + { + strcpy(WP->first_homebbs, HA); strcpy(WP->secnd_homebbs, HA); WP->last_modif = WPDate; WP->changed = TRUE; + } + + if (Name) + { + if (strcmp(WP->name , Name) != 0) + { + strcpy(WP->name, Name); + WP->last_modif = WPDate; + WP->changed = TRUE; + } + } + + if (QTH) + { + if (strcmp(WP->first_qth , QTH) != 0) + { + strcpy(WP->first_qth, QTH); + strcpy(WP->secnd_qth, QTH); + WP->last_modif = WPDate; + WP->changed = TRUE; + } + } + + if (ZIP) + { + if (strcmp(WP->first_zip , ZIP) != 0) + { + strcpy(WP->first_zip, ZIP); + strcpy(WP->secnd_zip, ZIP); + WP->last_modif = WPDate; + WP->changed = TRUE; + } + } + + WP->Type = Type; + + return; + } + + // Non-Definitive - only update second copy + + if (strcmp(WP->secnd_homebbs , HA) != 0) {strcpy(WP->secnd_homebbs, HA); WP->last_modif = WPDate; WP->Type = Type;} + + if (Name) + if (strcmp(WP->name , Name) != 0) {strcpy(WP->name, Name); WP->last_modif = WPDate; WP->Type = Type;} + + if (QTH) + if (strcmp(WP->secnd_qth , QTH) != 0) {strcpy(WP->secnd_qth, QTH); WP->last_modif = WPDate; WP->Type = Type;} + + if (ZIP) + if (strcmp(WP->secnd_zip , ZIP) != 0) {strcpy(WP->secnd_zip, ZIP); WP->last_modif = WPDate; WP->Type = Type;} + + return; +} + +VOID UpdateWPWithUserInfo(struct UserInfo * user) +{ + WPRec * WP = LookupWP(user->Call); + + if (strchr(user->Call, ':')) + return; + + if (BadCall(user->Call)) + return; + + if (!WP) + { + WP = AllocateWPRecord(); + strcpy(WP->callsign, user->Call); + } + + // Update Record + + if (user->HomeBBS[0]) + { + strcpy(WP->first_homebbs, user->HomeBBS); + strcpy(WP->secnd_homebbs, user->HomeBBS); + } + + if (user->Address[0]) + { + char Temp[127]; + strcpy(Temp, user->Address); + Temp[30] = 0; + + strcpy(WP->first_qth, Temp); + strcpy(WP->secnd_qth, Temp); + } + + if (user->ZIP[0]) + { + strcpy(WP->first_zip, user->ZIP); + strcpy(WP->secnd_zip, user->ZIP ); + } + + if (user->Name[0]) + strcpy(WP->name, user->Name); + + WP->last_modif = WP->last_seen = time(NULL); + + WP->changed = TRUE; + WP->Type = 'U'; + + SaveWPDatabase(); + +} + +VOID DoWPLookup(ConnectionInfo * conn, struct UserInfo * user, char Type, char *Param) +{ + // Process the I call command + + WPRec * ptr = NULL; + int i; + char ATBBS[100]; + char * HA; + char * Rest; + + _strupr(Param); + Type = toupper(Type); + + switch (Type) + { + case 0: + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + if (wildcardcompare(ptr->callsign, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, + ptr->name, ptr->first_zip, ptr->first_qth); + } + } + + return; + + case'@': // AT BBS + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + strcpy(ATBBS, ptr->first_homebbs); + strlop(ATBBS,'.'); + + if (wildcardcompare(ATBBS, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); + } + } + + case'H': // Hierarchic Element + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + strcpy(ATBBS, ptr->first_homebbs); + + Rest = strlop(ATBBS,'.'); + + if (Rest == 0) + continue; + + HA = strtok_s(Rest, ".", &Rest); + + while (HA) + { + if (wildcardcompare(HA, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); + } + + HA = strtok_s(NULL, ".", &Rest); + } + } + return; + + case'Z': // ZIP + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + if (ptr->first_zip[0] == 0) + continue; + + if (wildcardcompare(ptr->first_zip, Param)) + { + nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); + } + } + return; + } + nodeprintf(conn, "Invalid I command option %c\r", Type); +} +/* +On 111120 N4ZKF/I @ N4ZKF.#NFL.FL.USA.NOAM zip 32118 Dave 32955 +On 111120 F6IQF/I @ F6IQF.FRPA.FRA.EU zip ? ? f6iqf.dyndns.org +On 111121 W9JUN/I @ W9JUN.W9JUN.#SEIN.IN.US.NOAM zip 47250 Don NORTH VERNON, IN +On 111120 KR8ZY/U @ KR8ZY zip ? john ? +On 111120 N0DEC/G @ N0ARY.#NCA.CA.USA.NOAM zip ? ? ? + +From: N0JAL +To: WP +Type/Status: B$ +Date/Time: 22-Nov 10:15Z +Bid: 95F7N0JAL +Title: WP Update + +R:111122/1500Z 35946@KD6PGI.OR.USA.NOAM BPQ1.0.4 +R:111122/1020 16295@K7ZS.OR.USA.NOAM +R:111122/1015 38391@N0JAL.OR.USA.NOAM + +On 111121 N0JAL @ N0JAL.OR.USA.NOAM zip ? Sai Damascus, Oregon CN85sj + +*/ + +VOID UpdateWP() +{ + // If 2nd copy of info has been unchanged for 30 days, copy to active fields + + WPRec * ptr = NULL; + int i; + char * via = NULL; + int MsgLen = 0; + char MailBuffer[100100]; + char * Buffptr = MailBuffer; + int WriteLen=0; + char HDest[61] = "WP"; + char WPMsgType = 'P'; + time_t NOW = time(NULL); + time_t UpdateLimit = NOW - (86400 * 30); // 30 days + LASTWPSendTime = NOW - (86400 * 5); // 5 days max + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + + // DO we have a new field, and if so is it different? + + if ((ptr->secnd_homebbs[0] && _stricmp(ptr->first_homebbs, ptr->secnd_homebbs)) + || (ptr->secnd_qth[0] && _stricmp(ptr->first_qth, ptr->secnd_qth)) + || (ptr->secnd_zip[0] && _stricmp(ptr->first_zip, ptr->secnd_zip))) + { + // Have new data + + if (ptr->last_modif < UpdateLimit) + { + // Stable for 30 days + + if (ptr->secnd_homebbs[0]) + strcpy(ptr->first_homebbs, ptr->secnd_homebbs); + if (ptr->secnd_qth[0]) + strcpy(ptr->first_qth, ptr->secnd_qth); + if (ptr->secnd_zip[0]) + strcpy(ptr->first_zip, ptr->secnd_zip); + + ptr->last_modif = NOW; + + } + } + } +} + +int CreateWPMessage() +{ + // Include all messages with Type of U whach have changed since LASTWPSendTime + + WPRec * ptr = NULL; + int i; + struct tm *tm; + struct MsgInfo * Msg; + char * via = NULL; + char BID[13]; + BIDRec * BIDRec; + int MsgLen = 0; + char MailBuffer[100100]; + char * Buffptr = MailBuffer; + char MsgFile[MAX_PATH]; + FILE * hFile; + int WriteLen=0; + char ** To = SendWPAddrs; + + LASTWPSendTime = time(NULL) - (86400 * 5); // 5 days max + + for (i=1; i <= NumberofWPrecs; i++) + { + ptr = WPRecPtr[i]; + +// if (ptr->last_modif > LASTWPSendTime && ptr->Type == 'U' && ptr->first_homebbs[0]) + if (ptr->changed && ptr->last_modif > LASTWPSendTime && ptr->first_homebbs[0]) + { + tm = gmtime((time_t *)&ptr->last_modif); + MsgLen += sprintf(Buffptr, "On %02d%02d%02d %s/%c @ %s zip %s %s %s\r\n", + tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, + ptr->callsign, ptr->Type, ptr->first_homebbs, + (ptr->first_zip[0]) ? ptr->first_zip : "?", + (ptr->name[0]) ? ptr->name : "?", + (ptr->first_qth[0]) ? ptr->first_qth : "?"); + + Buffptr = &MailBuffer[MsgLen]; + + ptr->changed = FALSE; + + if (MsgLen > 100000) + break; + } + } + + if (MsgLen == 0) + return TRUE; + + while(To[0]) + { + char TO[256]; + char * VIA; + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + Msg->length = MsgLen; + MsgnotoMsg[Msg->number] = Msg; + + strcpy(Msg->from, BBSName); + + strcpy(TO, To[0]); + + VIA = strlop(TO, '@'); + + if (VIA) + { + if (strlen(VIA) > 40) + VIA[40] = 0; + strcpy(Msg->via, VIA); + } + strcpy(Msg->to, TO); + + strcpy(Msg->title, "WP Update"); + + Msg->type = (SendWPType) ? 'P' : 'B'; + Msg->status = 'N'; + + sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName); + + strcpy(Msg->bid, BID); + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + fwrite(MailBuffer, 1, Msg->length, hFile); + fclose(hFile); + } + + MatchMessagetoBBSList(Msg, 0); + + BuildNNTPList(Msg); // Build NNTP Groups list + +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(Msg); +#endif + To++; + } + + SaveMessageDatabase(); + SaveBIDDatabase(); + + return TRUE; +} + +VOID CreateWPReport() +{ + int i; + char Line[200]; + int len; + char File[100]; + FILE * hFile; + WPRec * WP = NULL; + + sprintf_s(File, sizeof(File), "%s/wp.txt", BaseDir); + + hFile = fopen(File, "wb"); + + if (hFile == NULL) + return; + +// len = sprintf(Line, " Call Connects Connects Messages Messages Bytes Bytes Rejected Rejected\r\n"); +// WriteFile(hFile, Line, len, &written, NULL); +// len = sprintf(Line, " In Out Rxed Sent Rxed Sent In Out\r\n\r\n"); +// WriteFile(hFile, Line, len, &written, NULL); + + + for (i=1; i <= NumberofWPrecs; i++) + { + WP = WPRecPtr[i]; + + len = sprintf(Line, "%-7s,%c,%s,%s,%s,%s,%s,%s,%s,%d,%s,%s\r\n", + WP->callsign, WP->Type, WP->first_homebbs, WP->first_qth, WP->first_zip, + WP->secnd_homebbs, WP->secnd_qth, WP->secnd_zip, WP->name, WP->changed, + FormatWPDate((time_t)WP->last_modif), + FormatWPDate((time_t)WP->last_seen)); + + fwrite(Line, 1, len, hFile); + } + fclose(hFile); +} + + + + + + diff --git a/.svn/pristine/71/71537c76a883df8b69e1305d32be8ec239af0906.svn-base b/.svn/pristine/71/71537c76a883df8b69e1305d32be8ec239af0906.svn-base new file mode 100644 index 0000000..8a3071a --- /dev/null +++ b/.svn/pristine/71/71537c76a883df8b69e1305d32be8ec239af0906.svn-base @@ -0,0 +1,6436 @@ +/* +Copyright 2001-2022 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +LinBPQ/BPQ32 is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + +// +// Interface to allow G8BPQ switch to use ARDOP Virtual TNC + + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#ifdef WIN32 +//#include +#else + +// For serial over i2c support + +#ifdef MACBPQ +#define NOI2C +#endif + +#ifdef FREEBSD +#define NOI2C +#endif + +#ifndef NOI2C +#include "i2c-dev.h" +#endif +#endif + +#include "cheaders.h" + + +int (WINAPI FAR *GetModuleFileNameExPtr)(); +int (WINAPI FAR *EnumProcessesPtr)(); + +#define SD_RECEIVE 0x00 +#define SD_SEND 0x01 +#define SD_BOTH 0x02 + +#define APMaxStreams 10 // First is used for ARDOP, even though ARDOP uses channel 31 + +#include "bpq32.h" + +#include "tncinfo.h" + + +#define WSA_ACCEPT WM_USER + 1 +#define WSA_DATA WM_USER + 2 +#define WSA_CONNECT WM_USER + 3 + +static int Socket_Data(int sock, int error, int eventcode); + +int KillTNC(struct TNCINFO * TNC); +int RestartTNC(struct TNCINFO * TNC); +int KillPopups(struct TNCINFO * TNC); +VOID MoveWindows(struct TNCINFO * TNC); +int SendReporttoWL2K(struct TNCINFO * TNC); +char * CheckAppl(struct TNCINFO * TNC, char * Appl); +int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); +BOOL KillOldTNC(char * Path); +int ARDOPSendData(struct TNCINFO * TNC, char * Buff, int Len); +VOID ARDOPSendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue); +VOID ARDOPSendPktCommand(struct TNCINFO * TNC, int Stream, char * Buff); +VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen); +VOID ARDOPProcessDataPacket(struct TNCINFO * TNC, UCHAR * Type, UCHAR * Data, int Length); +void ARDOPSCSCheckRX(struct TNCINFO * TNC); +VOID ARDOPSCSPoll(struct TNCINFO * TNC); +VOID ARDOPDoTNCReinit(struct TNCINFO * TNC); +int SerialGetTCPMessage(struct TNCINFO * TNC, unsigned char * Buffer, int Len); +int SerialConnecttoTCP(struct TNCINFO * TNC); +int ARDOPWriteCommBlock(struct TNCINFO * TNC); +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); +int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len); +BOOL WriteCommBlock(struct TNCINFO * TNC); +VOID AddVirtualKISSPort(struct TNCINFO * TNC, int Port, char * buf); +int ConfigVirtualKISSPort(struct TNCINFO * TNC, char * Cmd); +void ProcessKISSBytes(struct TNCINFO * TNC, UCHAR * Data, int Len); +void ProcessKISSPacket(struct TNCINFO * TNC, UCHAR * KISSBuffer, int Len); +int ARDOPProcessDEDFrame(struct TNCINFO * TNC, UCHAR * Msg, int framelen); +int ConnecttoARDOP(struct TNCINFO * TNC); +int standardParams(struct TNCINFO * TNC, char * buf); + +#ifndef LINBPQ +BOOL CALLBACK EnumARDOPWindowsProc(HWND hwnd, LPARAM lParam); +#endif + +static char ClassName[]="ARDOPSTATUS"; +static char WindowTitle[] = "ARDOP"; +static int RigControlRow = 165; + +#define WINMOR +#define NARROWMODE 21 +#define WIDEMODE 22 + +#ifndef LINBPQ +#include +#endif + +extern int SemHeldByAPI; + +static RECT Rect; + +static int ProcessLine(char * buf, int Port); + + +BOOL ARDOPStopPort(struct PORTCONTROL * PORT) +{ + // Disable Port - close TCP Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + TNC->Streams[0].Connecting = 0; + TNC->Streams[0].Connected = 0; + TNC->Streams[0].Attached = 0; + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + if (TNC->hDevice) + { + CloseCOMPort(TNC->hDevice); + TNC->hDevice = 0; + } + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Stopped"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return TRUE; +} + +BOOL ARDOPStartPort(struct PORTCONTROL * PORT) +{ + // Restart Port - Open Sockets or Serial Port + + struct TNCINFO * TNC = PORT->TNC; + + if (TNC->ARDOPCommsMode == 'T') + { + ConnecttoARDOP(TNC); + TNC->lasttime = time(NULL);; + } + + sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Restarted"); + MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); + + return TRUE; +} + + +int GenCRC16(unsigned char * Data, unsigned short length) +{ + // For CRC-16-CCITT = x^16 + x^12 +x^5 + 1 intPoly = 1021 Init FFFF + // intSeed is the seed value for the shift register and must be in the range 0-&HFFFF + + int intRegister = 0xffff; //intSeed + int i,j; + int Bit; + int intPoly = 0x8810; // This implements the CRC polynomial x^16 + x^12 +x^5 + 1 + + for (j = 0; j < (length); j++) + { + int Mask = 0x80; // Top bit first + + for (i = 0; i < 8; i++) // for each bit processing MS bit first + { + Bit = Data[j] & Mask; + Mask >>= 1; + + if (intRegister & 0x8000) // Then ' the MSB of the register is set + { + // Shift left, place data bit as LSB, then divide + // Register := shiftRegister left shift 1 + // Register := shiftRegister xor polynomial + + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + + intRegister = intRegister ^ intPoly; + } + else + { + // the MSB is not set + // Register is not divisible by polynomial yet. + // Just shift left and bring current data bit onto LSB of shiftRegister + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + } + } + } + + return intRegister; +} + +BOOL checkcrc16(unsigned char * Data, unsigned short length) +{ + int intRegister = 0xffff; //intSeed + int i,j; + int Bit; + int intPoly = 0x8810; // This implements the CRC polynomial x^16 + x^12 +x^5 + 1 + + for (j = 0; j < (length - 2); j++) // ' 2 bytes short of data length + { + int Mask = 0x80; // Top bit first + + for (i = 0; i < 8; i++) // for each bit processing MS bit first + { + Bit = Data[j] & Mask; + Mask >>= 1; + + if (intRegister & 0x8000) // Then ' the MSB of the register is set + { + // Shift left, place data bit as LSB, then divide + // Register := shiftRegister left shift 1 + // Register := shiftRegister xor polynomial + + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + + intRegister = intRegister ^ intPoly; + } + else + { + // the MSB is not set + // Register is not divisible by polynomial yet. + // Just shift left and bring current data bit onto LSB of shiftRegister + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + } + } + } + + if (Data[length - 2] == intRegister >> 8) + if (Data[length - 1] == (intRegister & 0xFF)) + return TRUE; + + return FALSE; +} + + +// Logging Interface. Used for Log over Serial Mode + +BOOL ARDOPOpenLogFiles(struct TNCINFO * TNC) +{ + UCHAR FN[MAX_PATH]; + + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + strlop(TNC->LogPath, 13); + strlop(TNC->LogPath, 32); + + sprintf(FN,"%s/ARDOPDebugLog_%02d%02d_%d.txt", TNC->LogPath, tm->tm_mon + 1, tm->tm_mday, TNC->Port); + TNC->DebugHandle = fopen(FN, "ab"); + sprintf(FN,"%s/ARDOPLog_%02d%02d_%d.txt", TNC->LogPath, tm->tm_mon + 1, tm->tm_mday, TNC->Port); + TNC->LogHandle = fopen(FN, "ab"); + + return (TNC->LogHandle != NULL); +} + +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + +void SendARDOPorPacketData(struct TNCINFO * TNC, int Stream, UCHAR * Buff, int txlen) +{ + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + + if (Stream == 0) + { + ARDOPSendData(TNC, Buff, txlen); + STREAM->bytesTXed += txlen; + WritetoTrace(TNC, Buff, txlen); + } + else + { + // Packet. Only works over Serial + + PMSGWITHLEN buffptr; + UCHAR * buffp; + + if (TNC->ARDOPCommsMode == 'T') + return; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = txlen + 1; + + buffp = &buffptr->Data[0]; + buffp[0] = 0; // CMD/Data Flag on front + + memcpy(buffp +1, Buff, txlen); + + C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); + STREAM->FramesQueued++; + + if (TNC->Timeout == 0) // if link idle can send now + ARDOPSCSPoll(TNC); + } +} + + +static int ProcessLine(char * buf, int Port) +{ + UCHAR * ptr,* p_cmd; + char * p_ipad = 0; + char * p_port = 0; + char * PktPort = 0; + unsigned short WINMORport = 0; + int BPQport; + int len=510; + struct TNCINFO * TNC; + char errbuf[256]; + + strcpy(errbuf, buf); + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + // Must start ADDR or SERIAL + + ptr = strtok(NULL, " \t\n\r"); + BPQport = Port; + p_ipad = ptr; + + if (_stricmp(buf, "ADDR") == 0 || _stricmp(buf, "TCPSERIAL") == 0) + { + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + PktPort = strlop(p_port, '/'); + + if (PktPort) + TNC->PacketPort = atoi(PktPort); + + WINMORport = atoi(p_port); + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(WINMORport); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(WINMORport+1); + + TNC->HostName = malloc(strlen(p_ipad)+1); + + if (TNC->HostName == NULL) return TRUE; + + strcpy(TNC->HostName,p_ipad); + + if (buf[0] == 'A') + TNC->ARDOPCommsMode = 'T'; // TCP + else + TNC->ARDOPCommsMode = 'E'; // TCPSERIAL (ESP01) + } + else if (_stricmp(buf, "SERIAL") == 0) + { + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->ARDOPSerialPort = _strdup(p_ipad); + TNC->ARDOPSerialSpeed = atoi(p_port); + + TNC->ARDOPCommsMode = 'S'; + } + else if (_stricmp(buf, "I2C") == 0) + { + TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); + memset(TNC, 0, sizeof(struct TNCINFO)); + + if (p_ipad == NULL) + p_ipad = strtok(NULL, " \t\n\r"); + + if (p_ipad == NULL) return (FALSE); + + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + TNC->ARDOPSerialPort = _strdup(p_ipad); + TNC->ARDOPSerialSpeed = atoi(p_port); + + TNC->ARDOPCommsMode = 'I'; + } + else + return FALSE; // Must start with ADDR + + TNC->InitScript = malloc(1000); + TNC->InitScript[0] = 0; + + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + if (_stricmp(ptr, "PTT") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr) + { + DecodePTTString(TNC, ptr); + ptr = strtok(NULL, " \t\n\r"); + } + } + } + + if (ptr) + { + if (_memicmp(ptr, "PATH", 4) == 0) + { + p_cmd = strtok(NULL, "\n\r"); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + } + + TNC->MaxConReq = 10; // Default + TNC->OldMode = FALSE; // Default + + // Read Initialisation lines + + while(TRUE) + { + if (GetLine(buf) == 0) + return TRUE; + + strcpy(errbuf, buf); + + if (memcmp(buf, "****", 4) == 0) + return TRUE; + + ptr = strchr(buf, ';'); + if (ptr) + { + *ptr++ = 13; + *ptr = 0; + } + + if ((_memicmp(buf, "CAPTURE", 7) == 0) || (_memicmp(buf, "PLAYBACK", 8) == 0)) + {} // Ignore + else +/* + if (_memicmp(buf, "PATH", 4) == 0) + { + char * Context; + p_cmd = strtok_s(&buf[5], "\n\r", &Context); + if (p_cmd) TNC->ProgramPath = _strdup(p_cmd); + } + else +*/ + + if (_memicmp(buf, "PACKETCHANNELS", 14) == 0) // Packet Channels + TNC->PacketChannels = atoi(&buf[14]); + else + if (_memicmp(buf, "MAXCONREQ", 9) == 0) // Hold Time for Busy Detect + TNC->MaxConReq = atoi(&buf[9]); + + else + if (_memicmp(buf, "STARTINROBUST", 13) == 0) + TNC->StartInRobust = TRUE; + + else + if (_memicmp(buf, "ROBUST", 6) == 0) + { + if (_memicmp(&buf[7], "TRUE", 4) == 0) + TNC->Robust = TRUE; + + strcat (TNC->InitScript, buf); + } + else + if (_memicmp(buf, "LOGDIR ", 7) == 0) + TNC->LogPath = _strdup(&buf[7]); + else + if (_memicmp(buf, "ENABLEPACKET", 12) == 0) + { + if (TNC->PacketChannels == 0) + TNC->PacketChannels = 5; + // AddVirtualKISSPort(TNC, Port, buf); + } + +// else if (_memicmp(buf, "PAC ", 4) == 0 && _memicmp(buf, "PAC MODE", 8) != 0) +// { + // PAC MODE goes to TNC, others are parsed locally +// +// ConfigVirtualKISSPort(TNC, buf); +// } + else if (standardParams(TNC, buf) == FALSE) + strcat(TNC->InitScript, buf); + } + + + return (TRUE); +} + + +void ARDOPThread(struct TNCINFO * TNC); +VOID ARDOPProcessDataSocketData(int port); +int ConnecttoARDOP(struct TNCINFO * TNC); +static VOID ARDOPProcessReceivedData(struct TNCINFO * TNC); +static VOID ARDOPProcessReceivedControl(struct TNCINFO * TNC); +int V4ProcessReceivedData(struct TNCINFO * TNC); +VOID ARDOPReleaseTNC(struct TNCINFO * TNC); +VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); +VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); +VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); + + +static time_t ltime; + + +static SOCKADDR_IN sinx; +static SOCKADDR_IN rxaddr; + +static int addrlen=sizeof(sinx); + +unsigned short int compute_crc(unsigned char *buf,int len); + +VOID ARDOPSendPktCommand(struct TNCINFO * TNC, int Stream, char * Buff) +{ + // Encode and send to TNC. May be TCP or Serial + + int EncLen; + UCHAR Encoded[256]; + + if (Stream == 0) + { + if (Buff[0] == 0) // Terminal Keepalive? + return; + } + else + { + if (Buff[1] == 0) // Terminal Keepalive? + return; + } + + if (TNC->PacketSock) // Packet Data over separate TCP Connectoion? + { + // Chan, Cmd/Data, Len-1 + + int SentLen; + + EncLen = sprintf(Encoded, "%c%c%c%s\r", Buff[0], 1, (int)strlen(Buff) -2, &Buff[1]); + SentLen = send(TNC->PacketSock, Encoded, EncLen, 0); + + if (SentLen != EncLen) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Pkt Write Failed for port %d - error code = %d\r\n", TNC->PacketPort, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->PacketSock); + TNC->PacketSock = 0; + } + return; + + } + + EncLen = sprintf(Encoded, "%s\r", Buff); + SendToTNC(TNC, Stream, Encoded, EncLen); + + return; +} + + +VOID ARDOPSendCommand(struct TNCINFO * TNC, char * Buff, BOOL Queue) +{ + // Encode and send to TNC. May be TCP or Serial + + // Command Formst is C:TEXT + + int i, EncLen; + UCHAR Encoded[260]; // could get 256 byte packet + + if (Buff[0] == 0) // Terminal Keepalive? + return; + + if (memcmp(Buff, "LISTEN ", 7) == 0) + { + strcpy(TNC->WEB_MODE, &Buff[7]); + MySetWindowText(TNC->xIDC_MODE, &Buff[7]); + } + + EncLen = sprintf(Encoded, "%s\r", Buff); + + // it is possible for binary data to be dumped into the command + // handler if a session disconnects in middle of transfer + + for (i = 0; i < EncLen; i++) + { + if (Encoded[i] > 0x7f) + return; + } + + SendToTNC(TNC, 12, Encoded, EncLen); // Use streams 12 aad 13 for Host Mode Schannels 32 and 33 + return; +} + +VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen) +{ + int SentLen; + + if (TNC->hDevice || (TNC->ARDOPCommsMode == 'E' && TNC->TCPSock)) + { + // Serial mode. Queue to Hostmode driver + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + + if (TNC->Timeout == 0) // if link idle can send now + ARDOPSCSPoll(TNC); + + return; + } + + if(TNC->ARDOPCommsMode == 'T' && TNC->TCPSock) + { + SentLen = send(TNC->TCPSock, Encoded, EncLen, 0); + + if (SentLen != EncLen) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->CONNECTED = FALSE; + return; + } + } +} + +VOID SendDataToTNC(struct TNCINFO * TNC, int Streem , UCHAR * Encoded, int EncLen) +{ + int SentLen; + + if (TNC->hDevice || (TNC->ARDOPCommsMode == 'E' && TNC->TCPSock)) + { + // Serial mode. Queue to Hostmode driver + + int Stream = 13; // use 12 and 13 for scs channels 32 and 33 + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return; // No buffers, so ignore + + buffptr->Len = EncLen; + memcpy(&buffptr->Data[0], Encoded, EncLen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + TNC->Streams[Stream].FramesQueued++; + + if (TNC->Timeout == 0) // if link idle can send now + ARDOPSCSPoll(TNC); + + return; + } + + if(TNC->TCPDataSock) + { + SentLen = send(TNC->TCPDataSock, Encoded, EncLen, 0); + + if (SentLen != EncLen) + { + // WINMOR doesn't seem to recover from a blocked write. For now just reset + +// if (bytes == SOCKET_ERROR) +// { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + +// if (winerr != WSAEWOULDBLOCK) +// { + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + TNC->CONNECTED = FALSE; + return; + } + } +} + +int ARDOPSenPktdData(struct TNCINFO * TNC, int Stream, char * Buff, int Len) +{ + // Encode and send to TNC. May be TCP or Serial + + int EncLen; + + UCHAR Msg[400]; + UCHAR * Encoded = Msg; + + *(Encoded++) = Len >> 8; + *(Encoded++) = Len & 0xff; + + memcpy(Encoded, Buff, Len); + EncLen = Len + 2; + + SendDataToTNC(TNC, Stream, Msg, EncLen); + return Len; +} + + + +int ARDOPSendData(struct TNCINFO * TNC, char * Buff, int Len) +{ + // Encode and send to TNC. May be TCP or Serial + + int EncLen; + + UCHAR Msg[400]; + UCHAR * Encoded = Msg; + + *(Encoded++) = Len >> 8; + *(Encoded++) = Len & 0xff; + + memcpy(Encoded, Buff, Len); + + EncLen = Len + 2; + + SendDataToTNC(TNC, 13, Msg, EncLen); + return Len; +} + + + +VOID ARDOPChangeMYC(struct TNCINFO * TNC, char * Call) +{ + UCHAR TXMsg[100]; + int datalen; + + if (strcmp(Call, TNC->CurrentMYC) == 0) + return; // No Change + + strcpy(TNC->CurrentMYC, Call); + +// ARDOPSendCommand(TNC, "CODEC FALSE"); + + datalen = sprintf(TXMsg, "MYCALL %s", Call); + ARDOPSendCommand(TNC, TXMsg, TRUE); + +// ARDOPSendCommand(TNC, "CODEC TRUE"); +// TNC->StartSent = TRUE; + +// ARDOPSendCommand(TNC, "MYCALL", TRUE); +} + +static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) +{ + int datalen; + PMSGWITHLEN buffptr; +// char txbuff[500]; + unsigned int bytes,txlen=0; + size_t Param; + int Stream = 0; + HKEY hKey=0; + + struct TNCINFO * TNC = TNCInfo[port]; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + struct ScanEntry * Scan; + + if (TNC == NULL) + return 0; // Port not defined + + if (TNC->CONNECTED == 0) + { + // clear Q if not connected + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = (PMSGWITHLEN)Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + } + + + switch (fn) + { + case 7: + + // approx 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances + + // G7TAJ's code to record activity for stats display + + if ( TNC->BusyFlags && CDBusy ) + TNC->PortRecord->PORTCONTROL.ACTIVE += 2; + + if ( TNC->PTTState ) + TNC->PortRecord->PORTCONTROL.SENDING += 2; + + // Check session limit timer + + if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) + { + if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) + { + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); + STREAM->ReportDISC = 1; + STREAM->AttachTime = 0; + } + } + + // Check ATTACH time limit + + if (STREAM->Attached) + { + if (STREAM->AttachTime && TNC->AttachTimeLimit && time(NULL) > (TNC->AttachTimeLimit + STREAM->AttachTime)) + { + STREAM->ReportDISC = 1; + STREAM->AttachTime = 0; + } + } + + if (TNC->ARDOPCommsMode != 'T') // S I or E + { + ARDOPSCSCheckRX(TNC); + ARDOPSCSPoll(TNC); + } + + return 0; + + case 1: // poll + + // If not using serial interface, Rig Contol Frames are sent as + // ARDOP COmmand Frames. These are hex encoded + + if (TNC->ARDOPCommsMode == 'T' && TNC->BPQtoRadio_Q) + { + PMSGWITHLEN buffptr; + + buffptr = (PMSGWITHLEN)Q_REM(&TNC->BPQtoRadio_Q); + + if (TNC->CONNECTED) + { + int len = (int)buffptr->Len; + UCHAR * ptr = &buffptr->Data[0]; + char RigCommand[256] = "RADIOHEX "; + char * ptr2 = &RigCommand[9] ; + int i, j; + + if (len < 120) + { + while (len--) + { + i = *(ptr++); + j = i >>4; + j += '0'; // ascii + if (j > '9') + j += 7; + *(ptr2++) = j; + + j = i & 0xf; + j += '0'; // ascii + if (j > '9') + j += 7; + *(ptr2++) = j; + } + ARDOPSendCommand(TNC, RigCommand, FALSE); + } + } + ReleaseBuffer(buffptr); + + } + + while (TNC->PortRecord->UI_Q) + { + int datalen; + char * Buffer; + char FECMsg[512]; + char Call[12] = " "; + struct _MESSAGE * buffptr; + int CallLen; + char * ptr = FECMsg; + + buffptr = Q_REM(&TNC->PortRecord->UI_Q); + + if (TNC->CONNECTED == 0 || + TNC->Streams[0].Connecting || + TNC->Streams[0].Connected) + { + // discard if TNC not connected or session active + + ReleaseBuffer(buffptr); + continue; + } + + datalen = buffptr->LENGTH - MSGHDDRLEN; + Buffer = &buffptr->DEST[0]; // Raw Frame + Buffer[datalen] = 0; + + *ptr++ = '^'; // delimit frame with ^ + + // Frame has ax.25 format header. Convert to Text + + CallLen = ConvFromAX25(Buffer + 7, Call); // Origin + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + *ptr++ = '>'; + + CallLen = ConvFromAX25(Buffer, Call); // Dest + memcpy(ptr, Call, CallLen); + ptr += CallLen; + + Buffer += 14; // TO Digis + datalen -= 14; + + while ((Buffer[-1] & 1) == 0) + { + *ptr++ = ','; + CallLen = ConvFromAX25(Buffer, Call); + memcpy(ptr, Call, CallLen); + ptr += CallLen; + Buffer += 7; // End of addr + datalen -= 7; + } + + *ptr++ = '|'; // delimit calls + + if (Buffer[0] == 3) // UI + { + Buffer += 2; + datalen -= 2; + } + + memcpy(ptr, Buffer, datalen); + ptr += datalen; + *ptr++ = '^'; // delimit frame with ^ + + ARDOPSendData(TNC, FECMsg, (int)(ptr - FECMsg)); + TNC->FECPending = 1; + + ReleaseBuffer((UINT *)buffptr); + } + + if (TNC->Busy) // Count down to clear + { + if ((TNC->BusyFlags & CDBusy) == 0) // TNC Has reported not busy + { + TNC->Busy--; + if (TNC->Busy == 0) + MySetWindowText(TNC->xIDC_CHANSTATE, "Clear"); + strcpy(TNC->WEB_CHANSTATE, "Clear"); + } + } + + if (TNC->BusyDelay) + { + // Still Busy? + + if (InterlockedCheckBusy(TNC) == FALSE) + { + // No, so send + +// ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); // !!!! Temp bug workaround !!!! + + ARDOPSendCommand(TNC, TNC->ConnectCmd, TRUE); + TNC->Streams[0].Connecting = TRUE; + + memset(TNC->Streams[0].RemoteCall, 0, 10); + memcpy(TNC->Streams[0].RemoteCall, &TNC->ConnectCmd[8], (int)strlen(TNC->ConnectCmd)-10); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + free(TNC->ConnectCmd); + TNC->BusyDelay = 0; + } + else + { + // Wait Longer + + TNC->BusyDelay--; + + if (TNC->BusyDelay == 0) + { + // Timed out - Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = 39; + memcpy(&buffptr->Data[0], "Sorry, Can't Connect - Channel is busy\r", 39); + + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + free(TNC->ConnectCmd); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + } + } + } + + if (TNC->HeartBeat++ > 600 || (TNC->Streams[0].Connected && TNC->HeartBeat > 50)) // Every Minute unless connected + { + TNC->HeartBeat = 0; + + if (TNC->CONNECTED) + { + // Probe link + + if (TNC->Streams[0].Connecting || TNC->Streams[0].Connected) + fn =fn; //ARDOPSendCommand(TNC, "MODE", TRUE); + else + { +// if (time(NULL) - TNC->WinmorRestartCodecTimer > 300) // 5 mins +// { +// ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); +// ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); +// } +// else + ARDOPSendCommand(TNC, "STATE", TRUE); + } + } + } + + if (TNC->FECMode) + { + if (TNC->FECIDTimer++ > 6000) // ID every 10 Mins + { + if (!TNC->Busy) + { + TNC->FECIDTimer = 0; + ARDOPSendCommand(TNC, "SENDID", TRUE); + } + } + } + + // FECPending can be set if not in FEC Mode (eg beacon) + + if (TNC->FECPending) // Check if FEC Send needed + { + if (TNC->Streams[0].BytesOutstanding) //Wait for data to be queued (async data session) + { + if (TNC->Busy == 0 && TNC->GavePermission == 0) + { + TNC->FECPending = 0; + ARDOPSendCommand(TNC,"FECSEND TRUE", TRUE); + } + } + } + + if (TNC->DiscPending) + { + TNC->DiscPending--; + + if (TNC->DiscPending == 0) + { + // Too long in Disc Pending - Kill and Restart TNC + + if (TNC->PID) + KillTNC(TNC); + + RestartTNC(TNC); + } + } + + if (TNC->TimeSinceLast++ > 800) // Allow 10 secs for Keepalive + { + // Restart TNC + + if (TNC->ProgramPath && TNC->CONNECTED && 0) + { + struct tm * tm; + char Time[80]; + + TNC->Restarts++; + TNC->LastRestart = time(NULL); + + tm = gmtime(&TNC->LastRestart); + + sprintf_s(Time, sizeof(Time),"%04d/%02d/%02d %02d:%02dZ", + tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); + + MySetWindowText(TNC->xIDC_RESTARTTIME, Time); + strcpy(TNC->WEB_RESTARTTIME, Time); + + sprintf_s(Time, sizeof(Time),"%d", TNC->Restarts); + MySetWindowText(TNC->xIDC_RESTARTS, Time); + strcpy(TNC->WEB_RESTARTS, Time); + + KillTNC(TNC); + RestartTNC(TNC); + + TNC->TimeSinceLast = 0; + } + } + + for (Stream = 0; Stream <= APMaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (STREAM->NeedDisc) + { + STREAM->NeedDisc--; + + if (STREAM->NeedDisc == 0) + { + // Send the DISCONNECT + + if (Stream == 0) + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); + else + { + char Cmd[32]; + sprintf(Cmd, "%cDISCONNECT", Stream); + ARDOPSendPktCommand(TNC, Stream, Cmd); + } + } + } + + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) + { + // New Attach + + int calllen; + char Msg[80]; + + Debugprintf("ARDOP New Attach Stream %d DEDStream %d", Stream, STREAM->DEDStream); + + STREAM->Attached = TRUE; + STREAM->AttachTime = time(NULL); + + calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall); + TNC->Streams[Stream].MyCall[calllen] = 0; + + if (Stream == 0) + { + // If Pactor, stop scanning and take out of listen mode. + // if (Stream == 0) + // STREAM->DEDStream = 31; // Pactor + + // Stop Listening, and set MYCALL to user's call + + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + ARDOPChangeMYC(TNC, TNC->Streams[0].MyCall); + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Stop Scanning + + sprintf(Msg, "%d SCANSTOP", TNC->Port); + + Rig_Command( (TRANSPORTENTRY *) -1, Msg); + } + else + { + // Packet Connect + + } + } + + + if (STREAM->Attached) + CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); + + } + + if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE) + { + // See if time to reconnect + + time(<ime); + if (ltime - TNC->lasttime > 9 ) + { + if (TNC->ARDOPCommsMode == 'T' && TNC->PortRecord->PORTCONTROL.PortStopped == 0) + ConnecttoARDOP(TNC); + TNC->lasttime = ltime; + } + } + + // See if any frames for this port + + for (Stream = 0; Stream <= APMaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + if (TNC->ARDOPCommsMode == 'T') + { + // For serial mode packets are taken from the queue by ARDOPSCSPoll + + if (STREAM->BPQtoPACTOR_Q) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + STREAM->FramesQueued--; + txlen = (int)buffptr->Len; + STREAM->bytesTXed += txlen; + + if (Stream == 0) + { + bytes=ARDOPSendData(TNC, data, txlen); + WritetoTrace(TNC, data, txlen); + } + else + { + if (TNC->PacketSock) + { + // Using Packet over TCP) + // Chan, Cmd/Data, Len-1 + + UCHAR Encoded[280]; + int EncLen; + int SentLen; + + EncLen = sprintf(Encoded, "%c%c%c%s\r", Stream, 0, txlen - 1, data); + SentLen = send(TNC->PacketSock, Encoded, EncLen, 0); + + if (SentLen != EncLen) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Pkt Write Failed for port %d - error code = %d\r\n", TNC->PacketPort, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->PacketSock); + TNC->PacketSock = 0; + } + } + } + ReleaseBuffer(buffptr); + } + } + + if (STREAM->PACTORtoBPQ_Q != 0) + { + buffptr = Q_REM(&STREAM->PACTORtoBPQ_Q); + + datalen = (int)buffptr->Len; + + buff->PORT = Stream; // Compatibility with Kam Driver + buff->PID = 0xf0; + memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte + datalen += sizeof(void *) + 4; + + PutLengthinBuffer(buff, datalen); + + ReleaseBuffer(buffptr); + + return (1); + } + + if (STREAM->ReportDISC) // May need a delay so treat as a counter + { + STREAM->ReportDISC--; + if (STREAM->ReportDISC == 0) + { + buff->PORT = Stream; +// STREAM->Connected = 0; +// STREAM->Attached = 0; + return -1; + } + } + } + return (0); + + case 2: // send + + Stream = buff->PORT; + + if (!TNC->CONNECTED) + { + // Send Error Response + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return (0); // No buffers, so ignore + + buffptr->Len = sprintf(&buffptr->Data[0], "No Connection to ARDOP TNC\r"); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 0; // Don't try if not connected + } + + STREAM = &TNC->Streams[Stream]; + + if (TNC->SwallowSignon) + { + TNC->SwallowSignon = FALSE; // Discard *** connected + return 0; + } + + txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4); + + if (STREAM->Connected) + { + STREAM->PacketsSent++; + + if (Stream == 0) + { + bytes=ARDOPSendData(TNC, &buff->L2DATA[0], txlen); + TNC->Streams[Stream].BytesOutstanding += bytes; // So flow control works - will be updated by BUFFER response + STREAM->bytesTXed += bytes; + WritetoTrace(TNC, &buff->L2DATA[0], txlen); + } + else + { + // Packet. Only works over Serial + + PMSGWITHLEN buffptr; + UCHAR * buffp; + + if (TNC->PacketSock) + { + // Using Packet over TCP) + // Chan, Cmd/Data, Len-1 + + UCHAR Encoded[280]; + int EncLen; + int SentLen; + + Encoded[0] = Stream; + Encoded[1] = 0; // Data + Encoded[2] = txlen - 1; + + memcpy(&Encoded[3], &buff->L2DATA[0], txlen); + + EncLen = txlen + 3; + SentLen = send(TNC->PacketSock, Encoded, EncLen, 0); + + // We should increment outstanding here as TCP interface can fill buffer + // very quickly + + TNC->Streams[Stream].BytesOutstanding += txlen; + + if (SentLen != EncLen) + { + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Pkt Write Failed for port %d - error code = %d\r\n", TNC->PacketPort, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->PacketSock); + TNC->PacketSock = 0; + } + return 0; + } + + if (TNC->ARDOPCommsMode == 'T') + return 0; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 0; // No buffers, so ignore + + buffptr->Len = txlen + 1; + buffp = &buffptr->Data[0]; + + buffp[0] = 0; // CMD/Data Flag on front + + memcpy(buffp + 1, &buff->L2DATA[0], txlen); + + C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); + STREAM->FramesQueued++; + + if (TNC->Timeout == 0) // if link idle can send now + ARDOPSCSPoll(TNC); + + return 0; + } + } + else + { + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0 || _memicmp(&buff->L2DATA[0], "BYE\r", 4) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + if (TNC->FECMode) + { + char Buffer[300]; + int len; + + // Send FEC Data + + buff->L2DATA[txlen] = 0; + len = sprintf(Buffer, "%-9s: %s", TNC->Streams[0].MyCall, &buff->L2DATA[0]); + + ARDOPSendData(TNC, Buffer, len); + TNC->FECPending = 1; + + return 0; + } + + + // See if Local command (eg RADIO) + + if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0) + { + char cmd[56]; + + strcpy(cmd, &buff->L2DATA[6]); + sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, cmd); + + if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0])) + { + } + else + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) return 1; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 1; + } + +// if (_memicmp(&buff[8], "PAC ", 4) == 0 && _memicmp(&buff[8], "PAC MODE", 8) != 0) +// { + // PAC MODE goes to TNC, others are parsed locally + +// buff[7 + txlen] = 0; +// ConfigVirtualKISSPort(TNC, &buff[8]); +// return 1; +// } + + if (_memicmp(&buff->L2DATA[0], "OVERRIDEBUSY", 12) == 0) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->OverrideBusy = TRUE; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + + return 0; + + } + + + if (_memicmp(&buff->L2DATA[0], "MAXCONREQ", 9) == 0) + { + if (buff->L2DATA[9] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + // Limit connects + + int tries = atoi(&buff->L2DATA[10]); + if (tries > 10) tries = 10; + + TNC->MaxConReq = tries; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + } + + if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0) + { + if (buff->L2DATA[16] != 13) + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60; + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} OK\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + } + + if (_memicmp(&buff->L2DATA[0], "ARQBW ", 6) == 0) + TNC->WinmorCurrentMode = 0; // So scanner will set next value + + if (_memicmp(&buff->L2DATA[0], "CODEC TRUE", 9) == 0) + TNC->StartSent = TRUE; + + if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0) + { + STREAM->ReportDISC = TRUE; // Tell Node + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "FEC\r", 4) == 0 || _memicmp(&buff->L2DATA[0], "FEC ", 4) == 0) + { + TNC->FECMode = TRUE; + TNC->FECIDTimer = 0; +// ARDOPSendCommand(TNC,"FECRCV TRUE"); + + return 0; + } + + if (_memicmp(&buff->L2DATA[0], "PING ", 5) == 0) + { + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, reject + + if (TNC->OverrideBusy == 0) + { + // Reject + + PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Ping blocked by Busy\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + } + return 0; + } + } + TNC->OverrideBusy = FALSE; + } + + // See if a Connect Command. If so, start codec and set Connecting + + if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect + { + char Connect[80]; + char * ptr = strchr(&buff->L2DATA[2], 13); + + if (ptr) + *ptr = 0; + + _strupr(&buff->L2DATA[2]); + + if (strlen(&buff->L2DATA[2]) > 9) + buff->L2DATA[11] = 0; + + if (Stream == 0) + { + sprintf(Connect, "ARQCALL %s %d", &buff->L2DATA[2], TNC->MaxConReq); + + ARDOPChangeMYC(TNC, TNC->Streams[0].MyCall); + + hookL4SessionAttempt(STREAM, &buff->L2DATA[2], TNC->Streams[0].MyCall); + + // See if Busy + + if (InterlockedCheckBusy(TNC)) + { + // Channel Busy. Unless override set, wait + + if (TNC->OverrideBusy == 0) + { + // Save Command, and wait up to 10 secs + + sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + TNC->ConnectCmd = _strdup(Connect); + TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs + return 0; + } + } + + TNC->OverrideBusy = FALSE; + +// ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); // !!!! Temp bug workaround !!!! + + memset(TNC->Streams[0].RemoteCall, 0, 10); + strcpy(TNC->Streams[0].RemoteCall, &buff->L2DATA[2]); + + sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall); + ARDOPSendCommand(TNC, Connect, TRUE); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + } + else + { + // Packet Connect + + sprintf(Connect, "%cPKTCALL %s %s", Stream, &buff->L2DATA[2], STREAM->MyCall); + ARDOPSendPktCommand(TNC, Stream, Connect); + } + + STREAM->Connecting = TRUE; + STREAM->ConnectTime = time(NULL); + return 0; + + } + buff->L2DATA[txlen - 1] = 0; // Remove CR + ARDOPSendCommand(TNC, &buff->L2DATA[0], TRUE); + } + return 0; + + case 3: + + // CHECK IF OK TO SEND (And check TNC Status) + + Stream = (int)(size_t)buff; + + // I think we should check buffer space for all comms modes + + //if (!(TNC->ARDOPCommsMode == 'T')) + //{ + // // if serial mode must check buffer space + + { + int Queued; + int Outstanding = TNC->Streams[Stream].BytesOutstanding; + + if (Stream == 0) + Queued = TNC->Streams[13].FramesQueued; // ARDOP Native Mode Send Queue + else + Queued = TNC->Streams[Stream].FramesQueued; + + if (TNC->Mode == 'O') // OFDM version has more buffer space + { + if (Queued > 4 || Outstanding > 8500) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + else if (TNC->Mode == '3') // ARDOP3 has a bit more buffer space + { + if (Queued > 4 || Outstanding > 5000) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + else + { + if (Queued > 4 || Outstanding > 2000) + return (1 | (TNC->HostMode | TNC->CONNECTED) << 8 | STREAM->Disconnecting << 15); + } + + } + if (TNC->Streams[Stream].Attached == 0) + return TNC->CONNECTED << 8 | 1; + + return (TNC->CONNECTED << 8 | TNC->Streams[Stream].Disconnecting << 15); // OK + + + case 4: // reinit7 + + return 0; + + case 5: // Close + + if (TNC->CONNECTED) + { + if (TNC->WeStartedTNC) + { + GetSemaphore(&Semaphore, 52); + ARDOPSendCommand(TNC, "CLOSE", FALSE); + FreeSemaphore(&Semaphore); + Sleep(100); + } + } + + if (TNC->TCPSock) + { + shutdown(TNC->TCPSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + } + + if (TNC->TCPDataSock) + { + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPDataSock); + } + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + return 0; + + case 6: // Scan Stop Interface + + Param = (size_t)buff; + + if (Param == 2) // Check Permission (Shouldn't happen) + { + Debugprintf("Scan Check Permission called on ARDOP"); + return 1; // OK to change + } + + if (Param == 1) // Request Permission + { + if (TNC->ARDOPCommsMode == 'T') // TCP Mode + { + if (!TNC->CONNECTED) + return 0; // No connection so no interlock + } + else + { + // Serial Modes + + if (!TNC->HostMode) + return 0; // No connection so no interlock + } + + + if (TNC->ConnectPending == 0 && TNC->PTTState == 0) + { + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + TNC->GavePermission = TRUE; + return 0; // OK to Change + } + + if (TNC->ConnectPending) + TNC->ConnectPending--; // Time out if set too long + + return TRUE; + } + + if (Param == 3) // Release Permission + { + if (TNC->GavePermission) + { + TNC->GavePermission = FALSE; + if (TNC->ARDOPCurrentMode[0] != 'S') // Skip + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + } + return 0; + } + + // Param is Address of a struct ScanEntry + + Scan = (struct ScanEntry *)buff; + + if (Scan->ARDOPMode[0] == 0) + { + // Not specified, so no change from previous + + return 0; + } + + if (strcmp(Scan->ARDOPMode, TNC->ARDOPCurrentMode) != 0) + { + // Mode changed + + char CMD[32]; + + if (TNC->ARDOPCurrentMode[0] == 'S') // Skip + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + +// Debugprintf("ARDOPMODE %s", Scan->ARDOPMode); + + memcpy(TNC->ARDOPCurrentMode, Scan->ARDOPMode, 6); + + if (Scan->ARDOPMode[0] == 'S') // SKIP - Dont Allow Connects + { + if (TNC->ARDOPCurrentMode[0] != 'S') + { + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + TNC->ARDOPCurrentMode[0] = 'S'; + } + + TNC->WL2KMode = 0; + return 0; + } + + if (strchr(Scan->ARDOPMode, 'F')) + sprintf(CMD, "ARQBW %sORCED", Scan->ARDOPMode); + else if (strchr(Scan->ARDOPMode, 'M')) + sprintf(CMD, "ARQBW %sAX", Scan->ARDOPMode); + else + sprintf(CMD, "ARQBW %s", Scan->ARDOPMode); // ARDOPOFDM doesn't use MAX/FORCED + + ARDOPSendCommand(TNC, CMD, TRUE); + + return 0; + } + return 0; + } + return 0; +} + +VOID ARDOPReleaseTNC(struct TNCINFO * TNC) +{ + // Set mycall back to Node or Port Call, and Start Scanner + + UCHAR TXMsg[1000]; + + ARDOPChangeMYC(TNC, TNC->NodeCall); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + + // Start Scanner + + if (TNC->DefaultRadioCmd) + { + sprintf(TXMsg, "%d %s", TNC->Port, TNC->DefaultRadioCmd); + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + } + + sprintf(TXMsg, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, TXMsg); + + ReleaseOtherPorts(TNC); + +} + +VOID ARDOPSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) +{ + TNC->PortRecord->PORTCONTROL.PortSuspended = TRUE; + ARDOPSendCommand(TNC, "LISTEN FALSE", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Interlocked"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + +} + +VOID ARDOPReleasePort(struct TNCINFO * TNC) +{ + TNC->PortRecord->PORTCONTROL.PortSuspended = FALSE; + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); +} + +extern char WebProcTemplate[]; +extern char sliderBit[]; + +static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) +{ + int Len = sprintf(Buff, WebProcTemplate, TNC->Port, TNC->Port, "ARDOP Status", "ARDOP Status"); + + if (TNC->TXFreq) + Len += sprintf(&Buff[Len], sliderBit, TNC->TXOffset, TNC->TXOffset); + + Len += sprintf(&Buff[Len], ""); + + Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); + Len += sprintf(&Buff[Len], "", TNC->WEB_CHANSTATE, TNC->WEB_LEVELS); + Len += sprintf(&Buff[Len], "", TNC->WEB_PROTOSTATE); + Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); +// Len += sprintf(&Buff[Len], "", TNC->WEB_RESTARTS); + Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Listen%s
Channel State%s   %s
Proto State%s
Traffic%s
TNC Restarts
"); + + Len += sprintf(&Buff[Len], "", TNC->WebBuffer); + Len = DoScanLine(TNC, Buff, Len); + + return Len; +} + + +VOID * ARDOPExtInit(EXTPORTDATA * PortEntry) +{ + int i, port; + char Msg[255]; + char * ptr; + APPLCALLS * APPL; + struct TNCINFO * TNC; + char Aux[100] = "MYAUX "; + char Appl[11]; + char * TempScript; + int Stream; + + // + // Will be called once for each WINMOR port + // + // The Socket to connect to is in IOBASE + // + + port = PortEntry->PORTCONTROL.PORTNUMBER; + + ReadConfigFile(port, ProcessLine); + + TNC = TNCInfo[port]; + + if (TNC == NULL) + { + // Not defined in Config file + + sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n"); + WritetoConsole(Msg); + + return ExtProc; + } + + TNC->Port = port; + TNC->PortRecord = PortEntry; + + if (TNC->LogPath) + ARDOPOpenLogFiles(TNC); + + TNC->ARDOPBuffer = malloc(8192); + TNC->ARDOPDataBuffer = malloc(16384); + TNC->ARDOPAPRS = zalloc(512); + TNC->ARDOPAPRSLen = 0; + + if (TNC->ProgramPath) + TNC->WeStartedTNC = RestartTNC(TNC); + + TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_ARDOP; + + if (TNC->BusyWait == 0) + TNC->BusyWait = 10; + + if (TNC->BusyHold == 0) + TNC->BusyHold = 1; + + + if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) + memcpy(TNC->NodeCall, MYNODECALL, 10); + else + ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); + + if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) + TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; + + PortEntry->PORTCONTROL.PROTOCOL = 10; + PortEntry->PORTCONTROL.PORTQUALITY = 0; + + for (Stream = 1; Stream <= APMaxStreams; Stream++) + { + TNC->Streams[Stream].DEDStream = Stream; + } + + if (TNC->PacketChannels > APMaxStreams) + TNC->PacketChannels = APMaxStreams; + + PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; + + PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only + PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream + + PortEntry->PORTCONTROL.UICAPABLE = TRUE; + + if (PortEntry->PORTCONTROL.PORTPACLEN == 0) + PortEntry->PORTCONTROL.PORTPACLEN = 236; + + TNC->SuspendPortProc = ARDOPSuspendPort; + TNC->ReleasePortProc = ARDOPReleasePort; + + PortEntry->PORTCONTROL.PORTSTARTCODE = ARDOPStartPort; + PortEntry->PORTCONTROL.PORTSTOPCODE = ARDOPStopPort; + + TNC->ModemCentre = 1500; // ARDOP is always 1500 Offset + + ptr=strchr(TNC->NodeCall, ' '); + if (ptr) *(ptr) = 0; // Null Terminate + + // Set Essential Params and MYCALL + + // Put overridable ones on front, essential ones on end + + TempScript = malloc(1000); + + strcpy(TempScript, "INITIALIZE\r"); + strcat(TempScript, "VERSION\r"); + strcat(TempScript, "CWID False\r"); + strcat(TempScript, "PROTOCOLMODE ARQ\r"); + strcat(TempScript, "ARQTIMEOUT 90\r"); +// strcat(TempScript, "ROBUST False\r"); + + strcat(TempScript, TNC->InitScript); + + free(TNC->InitScript); + TNC->InitScript = TempScript; + + // Set MYCALL + +// strcat(TNC->InitScript,"FECRCV True\r"); +// strcat(TNC->InitScript,"AUTOBREAK True\r"); + + sprintf(Msg, "MYCALL %s\r", TNC->NodeCall); + strcat(TNC->InitScript, Msg); +// strcat(TNC->InitScript,"PROCESSID\r"); +// strcat(TNC->InitScript,"CODEC TRUE\r"); +// strcat(TNC->InitScript,"LISTEN TRUE\r"); + + for (i = 0; i < 32; i++) + { + APPL=&APPLCALLTABLE[i]; + + if (APPL->APPLCALL_TEXT[0] > ' ') + { + char * ptr; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + { + *ptr++ = ','; + *ptr = 0; + } + strcat(Aux, Appl); + } + } + + if (strlen(Aux) > 8) + { + Aux[strlen(Aux) - 1] = '\r'; + strcat(TNC->InitScript, Aux); + } + + strcpy(TNC->CurrentMYC, TNC->NodeCall); + + if (TNC->WL2K == NULL) + if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded + TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; + + if (TNC->destaddr.sin_family == 0) + { + // not defined in config file, so use localhost and port from IOBASE + + TNC->destaddr.sin_family = AF_INET; + TNC->destaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE); + TNC->Datadestaddr.sin_family = AF_INET; + TNC->Datadestaddr.sin_port = htons(PortEntry->PORTCONTROL.IOBASE+1); + + TNC->HostName=malloc(10); + + if (TNC->HostName != NULL) + strcpy(TNC->HostName,"127.0.0.1"); + + } + + PortEntry->PORTCONTROL.TNC = TNC; + + TNC->WebWindowProc = WebProc; + TNC->WebWinX = 520; + TNC->WebWinY = 500; + TNC->WebBuffer = zalloc(5000); + + TNC->WEB_COMMSSTATE = zalloc(100); + TNC->WEB_TNCSTATE = zalloc(100); + TNC->WEB_CHANSTATE = zalloc(100); + TNC->WEB_BUFFERS = zalloc(100); + TNC->WEB_PROTOSTATE = zalloc(100); + TNC->WEB_RESTARTTIME = zalloc(100); + TNC->WEB_RESTARTS = zalloc(100); + + TNC->WEB_MODE = zalloc(20); + TNC->WEB_TRAFFIC = zalloc(100); + TNC->WEB_LEVELS =zalloc(32); + +#ifndef LINBPQ + + CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, ForcedClose); + + CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,6,386,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,28,520,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Listen", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,50,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 120,72,82,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_LEVELS = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 200,72,200,20, TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,120,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,120,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); + + CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,120,138,40,20 , TNC->hDlg, NULL, hInstance, NULL); + CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL); + TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL); + + TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL, + 0,170,250,300, TNC->hDlg, NULL, hInstance, NULL); + + TNC->ClientHeight = 450; + TNC->ClientWidth = 500; + + TNC->hMenu = CreatePopupMenu(); + + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill ARDOP TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart ARDOP TNC"); + AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection"); + AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session"); + + CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED); + + MoveWindows(TNC); +#endif + + if (TNC->ARDOPCommsMode == 'T') + { + Consoleprintf("ARDOP Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port)); + ConnecttoARDOP(TNC); + } + + else if (TNC->ARDOPCommsMode == 'E') + { + Consoleprintf("ARDOP TCPSerial %s:%d", TNC->HostName, htons(TNC->destaddr.sin_port)); + SerialConnecttoTCP(TNC); + } + + else if (TNC->ARDOPCommsMode == 'S') + { + TNC->PortRecord->PORTCONTROL.SerialPortName = _strdup(TNC->ARDOPSerialPort); // for common routines + Consoleprintf("ARDOP Serial %s", TNC->ARDOPSerialPort); + OpenCOMMPort(TNC, TNC->ARDOPSerialPort, TNC->ARDOPSerialSpeed, FALSE); + } + else if (TNC->ARDOPCommsMode == 'I') + { +#ifdef WIN32 + sprintf(Msg,"ARDOP I2C is not supported on WIN32 systems\n"); + WritetoConsoleLocal(Msg); +#else +#ifdef NOI2C + sprintf(Msg,"I2C is not supported on this systems\n"); + WritetoConsoleLocal(Msg); +#else + char i2cname[30]; + int fd; + int retval; + + if (strlen(TNC->ARDOPSerialPort) < 3) + { + sprintf(i2cname, "/dev/i2c-%s", TNC->ARDOPSerialPort); + TNC->ARDOPSerialPort = _strdup(i2cname); + } + else + strcpy(i2cname, TNC->ARDOPSerialPort); + + TNC->PortRecord->PORTCONTROL.SerialPortName = _strdup(i2cname); // for common routines + + Consoleprintf("ARDOP I2C Bus %s Addr %d ", i2cname, TNC->ARDOPSerialSpeed); + + // Open and configure the i2c interface + + fd = TNC->hDevice = open(i2cname, O_RDWR); + + if (fd < 0) + printf("Cannot find i2c bus %s \n", i2cname); + else + { + retval = ioctl(TNC->hDevice, I2C_SLAVE, TNC->ARDOPSerialSpeed); + + if(retval == -1) + printf("Cannot open i2c device %x\n", TNC->ARDOPSerialSpeed); + + ioctl(TNC->hDevice, I2C_TIMEOUT, 10); //100 mS + } +#endif +#endif + } + + time(&TNC->lasttime); // Get initial time value + + return ExtProc; +} + +VOID TNCLost(struct TNCINFO * TNC) +{ + int Stream = 0; + struct STREAMINFO * STREAM; + + if (TNC->TCPSock) + closesocket(TNC->TCPSock); + if (TNC->TCPDataSock) + closesocket(TNC->TCPDataSock); + if (TNC->PacketSock) + closesocket(TNC->PacketSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->PacketSock = 0; + TNC->CONNECTED = FALSE; + + for (Stream = 0; Stream <= APMaxStreams; Stream++) + { + STREAM = &TNC->Streams[Stream]; + + STREAM->BytesOutstanding = 0; + + if (Stream == 0) + { + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->bytesTXed - STREAM->BytesOutstanding, STREAM->bytesRXed, STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + } + + if (STREAM->Attached) + STREAM->ReportDISC = TRUE; + + STREAM->Connected = FALSE; + STREAM->Connecting = FALSE; + + } +} + + +int ConnecttoARDOP(struct TNCINFO * TNC) +{ + _beginthread(ARDOPThread, 0, (void *)TNC); + + return 0; +} + +VOID ARDOPThread(struct TNCINFO * TNC) +{ + // Opens sockets and looks for data on control and data sockets. + + // Socket may be TCP/IP or Serial + + char Msg[255]; + int err, i, ret; + u_long param=1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + fd_set readfs; + fd_set errorfs; + struct timeval timeout; + char * ptr1, * ptr2; + PMSGWITHLEN buffptr; + char Cmd[64]; + + if (TNC->HostName == NULL) + return; + + TNC->BusyFlags = 0; + + TNC->CONNECTING = TRUE; + + Sleep(3000); // Allow init to complete + +#ifdef WIN32 + if (strcmp(TNC->HostName, "127.0.0.1") == 0) + { + // can only check if running on local host + + TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); + if (TNC->PID == 0) + { + TNC->CONNECTING = FALSE; + return; // Not listening so no point trying to connect + } + + // Get the File Name in case we want to restart it. + + if (TNC->ProgramPath == NULL) + { + if (GetModuleFileNameExPtr) + { + HANDLE hProc; + char ExeName[256] = ""; + + hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); + + if (hProc) + { + GetModuleFileNameExPtr(hProc, 0, ExeName, 255); + CloseHandle(hProc); + + TNC->ProgramPath = _strdup(ExeName); + } + } + } + + } +#endif + + +// // If we started the TNC make sure it is still running. + +// if (!IsProcess(TNC->PID)) +// { +// RestartTNC(TNC); +// Sleep(3000); +// } + + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + + } + + if (TNC->TCPSock) + { + Debugprintf("ARDOP Closing Sock %d", TNC->TCPSock); + closesocket(TNC->TCPSock); + } + if (TNC->TCPDataSock) + { + Debugprintf("ARDOP Closing Sock %d", TNC->TCPDataSock); + closesocket(TNC->TCPDataSock); + } + + if (TNC->PacketSock) + { + Debugprintf("ARDOP Closing Sock %d", TNC->PacketSock); + closesocket(TNC->PacketSock); + } + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->PacketSock = 0; + + TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPDataSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP Data socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + setsockopt(TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); +// setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + sprintf(Msg, "Connect Failed for ARDOP socket - error code = %d Port %d\n", + WSAGetLastError(), htons(TNC->destaddr.sin_port)); + + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + // Connect Data Port + + if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0) + { + // + // Connected successful + // + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for ARDOP Data socket - error code = %d\r\n", err); + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + TNC->CONNECTING = FALSE; + return; + } + + if (TNC->PacketPort) + { + struct sockaddr_in destaddr; + + TNC->PacketSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->PacketSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP Packet socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + TNC->PacketSock = 0; + } + else + { + setsockopt(TNC->PacketSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); + // setsockopt(TNC->PacketSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + + destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + destaddr.sin_family = AF_INET; + destaddr.sin_port = htons(TNC->PacketPort); + + if (connect(TNC->PacketSock,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0) + { + // Connected successful + } + else + { + if (TNC->Alerted == FALSE) + { + err=WSAGetLastError(); + i=sprintf(Msg, "Connect Failed for ARDOP Packet socket - error code = %d\r\n", err); + WritetoConsole(Msg); + TNC->Alerted = TRUE; + } + closesocket(TNC->PacketSock); + TNC->PacketSock = 0; + } + } + } + + +#ifndef LINBPQ +// FreeSemaphore(&Semaphore); + EnumWindows(EnumARDOPWindowsProc, (LPARAM)TNC); +// GetSemaphore(&Semaphore, 52); +#endif + Sleep(1000); + + TNC->LastFreq = 0; // so V4 display will be updated + + TNC->CONNECTING = FALSE; + TNC->CONNECTED = TRUE; + TNC->BusyFlags = 0; + TNC->InputLen = 0; + + // Send INIT script + + // ARDOP needs each command in a separate send + + ptr1 = &TNC->InitScript[0]; + + // We should wait for first RDY. Cheat by queueing a null command + + GetSemaphore(&Semaphore, 52); + + while(TNC->BPQtoWINMOR_Q) + { + buffptr = (PMSGWITHLEN)Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + + buffptr = (PMSGWITHLEN)GetBuff(); + buffptr->Len = 0; + C_Q_ADD(&TNC->BPQtoWINMOR_Q, buffptr); + + while (ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, 13); + if (ptr2) + *(ptr2) = 0; + + // if Date or Time command add current time + + if (_memicmp(ptr1, "DATETIME", 4) == 0) + { + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + sprintf(Cmd, "DATETIME %02d %02d %02d %02d %02d %02d", + tm->tm_mday, tm->tm_mon + 1, tm->tm_year - 100, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + ptr1 = Cmd; + } + + ARDOPSendCommand(TNC, ptr1, TRUE); + + if (ptr2) + *(ptr2++) = 13; // Put CR back for next time + + ptr1 = ptr2; + } + + TNC->Alerted = TRUE; + + ARDOPSendCommand(TNC, "LISTEN TRUE", TRUE); + + sprintf(TNC->WEB_COMMSSTATE, "Connected to ARDOP TNC"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + FreeSemaphore(&Semaphore); + + sprintf(Msg, "Connected to ARDOP TNC Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + while (TNC->CONNECTED) + { + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + + FD_SET(TNC->TCPSock,&readfs); + FD_SET(TNC->TCPSock,&errorfs); + + if (TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&readfs); + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + if (TNC->PacketSock) + { + FD_SET(TNC->PacketSock,&errorfs); + FD_SET(TNC->PacketSock,&readfs); + } + +// FD_ZERO(&writefs); + +// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing + + if (TNC->CONNECTING || TNC->CONNECTED) FD_SET(TNC->TCPDataSock,&errorfs); + timeout.tv_sec = 600; + timeout.tv_usec = 0; // We should get messages more frequently that this + + if (TNC->PacketSock) + ret = select((int)TNC->PacketSock + 1, &readfs, NULL, &errorfs, &timeout); + else + ret = select((int)TNC->TCPDataSock + 1, &readfs, NULL, &errorfs, &timeout); + + if (ret == SOCKET_ERROR) + { + Debugprintf("ARDOP Select failed %d ", WSAGetLastError()); + goto Lost; + } + if (ret > 0) + { + // See what happened + + if (FD_ISSET(TNC->TCPSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + ARDOPProcessReceivedControl(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->TCPDataSock, &readfs)) + { + GetSemaphore(&Semaphore, 52); + ARDOPProcessReceivedData(TNC); + FreeSemaphore(&Semaphore); + } + + if (FD_ISSET(TNC->PacketSock, &readfs)) + { + int InputLen, Used; + UCHAR Buffer[4096]; + + InputLen = recv(TNC->PacketSock, Buffer, 4096, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + sprintf(Msg, "ARDOP Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + strcpy(TNC->WEB_TNCSTATE, "Free"); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + TNCLost(TNC); + return; + } + + // Could be more than one frame in buffer + + while (InputLen > 0) + { + GetSemaphore(&Semaphore, 52); + Used = ARDOPProcessDEDFrame(TNC, Buffer, InputLen); + FreeSemaphore(&Semaphore); + + if (Used == 0) + break; // need to check + + InputLen -= Used; + + if (InputLen > 0) + memmove(Buffer, &Buffer[Used], InputLen); + + } + + } + + if (FD_ISSET(TNC->TCPSock, &errorfs)) + { +Lost: + sprintf(Msg, "ARDOP Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + return; + } + + if (FD_ISSET(TNC->TCPDataSock, &errorfs)) + { + sprintf(Msg, "ARDOP Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + return; + } + + if (FD_ISSET(TNC->PacketSock, &errorfs)) + { + sprintf(Msg, "ARDOP Packet Connection lost for Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + return; + } + + + continue; + } + else + { + // 60 secs without data. Shouldn't happen + + sprintf(Msg, "ARDOP No Data Timeout Port %d\r\n", TNC->Port); + WritetoConsole(Msg); + +// sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); +// GetSemaphore(&Semaphore, 52); +// MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); +// FreeSemaphore(&Semaphore); + + + TNC->CONNECTED = FALSE; + TNC->Alerted = FALSE; + + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); // Make sure PTT is down + + if (TNC->Streams[0].Attached) + TNC->Streams[0].ReportDISC = TRUE; + + GetSemaphore(&Semaphore, 52); + ARDOPSendCommand(TNC, "CODEC FALSE", FALSE); + FreeSemaphore(&Semaphore); + + shutdown(TNC->TCPSock, SD_BOTH); + shutdown(TNC->TCPDataSock, SD_BOTH); + Sleep(100); + closesocket(TNC->TCPSock); + closesocket(TNC->TCPDataSock); + + TNC->TCPSock = 0; + TNC->TCPDataSock = 0; + + if (TNC->PID && TNC->WeStartedTNC) + { +// KillTNC(TNC); + } + return; + } + } + sprintf(Msg, "ARDOP Thread Terminated Port %d\r\n", TNC->Port); + WritetoConsole(Msg); +} + +#ifndef LINBPQ + +BOOL CALLBACK EnumARDOPWindowsProc(HWND hwnd, LPARAM lParam) +{ + char wtext[100]; + struct TNCINFO * TNC = (struct TNCINFO *)lParam; + UINT ProcessId; + + GetWindowText(hwnd,wtext,99); + + if (memcmp(wtext,"ARDOP_Win ", 10) == 0) + { + GetWindowThreadProcessId(hwnd, &ProcessId); + + if (TNC->PID == ProcessId) + { + // Our Process + + sprintf (wtext, "ARDOP Virtual TNC - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); + SetWindowText(hwnd, wtext); + return FALSE; + } + } + + return (TRUE); +} +#endif + +VOID ARDOPProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen) +{ + PMSGWITHLEN buffptr; + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + Buffer[MsgLen - 1] = 0; // Remove CR + + if (_memicmp(Buffer, "RDY", 3) == 0) + return; // RDY not used now + + if (_memicmp(Buffer, "RADIOHEX ", 9) == 0) + { + // Parameter is block to send to radio, in hex + + char c; + int val; + char * ptr1 = &Buffer[9]; + UCHAR * ptr2 = Buffer; + PMSGWITHLEN buffptr; + int Len; + + // if not configured to use PTC Rig Control, Ignore + + if (TNC->RIG->PORT == NULL || TNC->RIG->PORT->PTC == NULL) + return; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == NULL) + return; + + while (c = *(ptr1++)) + { + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(ptr2++) = val; + } + + *(ptr2) = 0; + + Len = (int)(ptr2 - Buffer); + + buffptr->Len = Len; + memcpy(&buffptr->Data[0], Buffer, Len); + C_Q_ADD(&TNC->RadiotoBPQ_Q, buffptr); + +// WriteCOMBlock(hRIGDevice, ptrParams, ptr2 - ptrParams); + return; + + } + + + if (_memicmp(Buffer, "INPUTPEAKS", 10) == 0) + { + sscanf(&Buffer[10], "%i %i", &TNC->InputLevelMin, &TNC->InputLevelMax); + sprintf(TNC->WEB_LEVELS, "Input peaks %s", &Buffer[10]); + MySetWindowText(TNC->xIDC_LEVELS, TNC->WEB_LEVELS); + return; // Response shouldn't go to user + } + + if (_memicmp(Buffer, "LISTEN NOW", 10) == 0) + return; // Response shouldn't go to user + + if (_memicmp(Buffer, "ARQCALL ", 7) == 0) + return; // Response shouldn't go to user + + if (_memicmp(Buffer, "FAULT failure to Restart Sound card", 20) == 0) + { + Debugprintf(Buffer); + + // Force a restart + + ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); + ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); + } + else + { + TNC->TimeSinceLast = 0; + } + + + if (_memicmp(Buffer, "STATE ", 6) == 0) + { + if (_memicmp(&Buffer[6], "OFFLINE", 7) == 0) + { + // Force a restart + + ARDOPSendCommand(TNC, "CODEC FALSE", TRUE); + ARDOPSendCommand(TNC, "CODEC TRUE", TRUE); + } + return; + } + + if (_memicmp(Buffer, "PTT T", 5) == 0) + { + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + TNC->PTTState = TRUE; + + if (TNC->PTTMode) + Rig_PTT(TNC, TRUE); + + TNC->PTTonTime = GetTickCount(); + + // Cancel Busy timer (stats include ptt on time in port active + + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } + return; + } + if (_memicmp(Buffer, "PTT F", 5) == 0) + { + TNC->PTTState = FALSE; + if (TNC->PTTMode) + Rig_PTT(TNC, FALSE); + + if (TNC->PTTonTime) + { + TNC->PTTActivemS += (GetTickCount() - TNC->PTTonTime); + TNC->PTTonTime = 0; + } + + return; + } + + if (_memicmp(Buffer, "BUSY TRUE", 9) == 0) + { + TNC->BusyFlags |= CDBusy; + TNC->Busy = TNC->BusyHold * 10; // BusyHold delay + + TNC->BusyonTime = GetTickCount(); + + MySetWindowText(TNC->xIDC_CHANSTATE, "Busy"); + strcpy(TNC->WEB_CHANSTATE, "Busy"); + + TNC->WinmorRestartCodecTimer = time(NULL); + + return; + } + + if (_memicmp(Buffer, "BUSY FALSE", 10) == 0) + { + TNC->BusyFlags &= ~CDBusy; + if (TNC->Busy) + strcpy(TNC->WEB_CHANSTATE, "BusyHold"); + else + strcpy(TNC->WEB_CHANSTATE, "Clear"); + + if (TNC->BusyonTime) + { + TNC->BusyActivemS += (GetTickCount() - TNC->BusyonTime); + TNC->BusyonTime = 0; + } + + MySetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE); + TNC->WinmorRestartCodecTimer = time(NULL); + return; + } + + if (_memicmp(Buffer, "TARGET", 6) == 0) + { + TNC->ConnectPending = 6; // This comes before Pending + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 1); + memcpy(TNC->TargetCall, &Buffer[7], 10); + return; + } + + if (_memicmp(Buffer, "OFFSET", 6) == 0) + { +// WritetoTrace(TNC, Buffer, MsgLen - 5); + return; + } + + if (_memicmp(Buffer, "BUFFER", 6) == 0) + { + sscanf(&Buffer[7], "%d", &STREAM->BytesOutstanding); + + if (STREAM->BytesOutstanding == 0) + { + // all sent + + if (STREAM->Disconnecting) // Disconnect when all sent + { + if (STREAM->NeedDisc == 0) + STREAM->NeedDisc = 1; + } + } + else + { + // Make sure Node Keepalive doesn't kill session. + + TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + if (SESS) + { + SESS->L4KILLTIMER = 0; + SESS = SESS->L4CROSSLINK; + if (SESS) + SESS->L4KILLTIMER = 0; + } + } + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->bytesTXed - STREAM->BytesOutstanding, STREAM->bytesRXed, STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + return; + } + + if (_memicmp(Buffer, "CONNECTED ", 10) == 0) + { + char Call[11]; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + struct WL2KInfo * WL2K = TNC->WL2K; + int Speed = 0; + + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 1); + + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->PacketsSent = 0; + + memcpy(Call, &Buffer[10], 10); + + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + // Get Speed + + ptr = strchr(&Buffer[10], ' '); + if (ptr) + { + Speed = atoi(ptr); + + if (Speed == 200) + TNC->WL2KMode = 40; + else if (Speed == 500) + TNC->WL2KMode = 41; + else if (Speed == 1000) + TNC->WL2KMode = 42; + else if (Speed == 2000) + TNC->WL2KMode = 43; + } + + TNC->HadConnect = TRUE; + + if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0) + { + TRANSPORTENTRY * SESS; + + // Incoming Connect + + TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit + + // Stop other ports in same group + + SuspendOtherPorts(TNC); + + ProcessIncommingConnectEx(TNC, Call, 0, TRUE, TRUE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[0]; + + SESS->Mode = TNC->WL2KMode; + + TNC->ConnectPending = FALSE; + + if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT")) + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->TargetCall, TNC->RIG->Valchar); + SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq + } + else + { + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->TargetCall); + if (WL2K) + { + SESS->Frequency = WL2K->Freq; + } + } + + if (WL2K) + strcpy(SESS->RMSCall, WL2K->RMSCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Status); + Debugprintf("ARDOP Call from %s rejected", Call); + return; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(SESS->L4USER, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + char Status[64]; + + TidyClose(TNC, 0); + sprintf(Status, "%d SCANSTART 15", TNC->Port); + Rig_Command( (TRANSPORTENTRY *) -1, Status); + Debugprintf("ARDOP Call from %s not in ValidCalls - rejected", Call); + return; + } + } + } + + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TNC->TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12); + AppName[12] = 0; + + if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 + && (strstr(Call, "-T" ) || strstr(Call, "-R"))) + strcpy(AppName, "RELAY "); + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + MsgLen = sprintf(Buffer, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = MsgLen; + memcpy(&buffptr->Data[0], Buffer, MsgLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + // Send CTEXT First + + if (TNC->Streams[0].BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&TNC->Streams[0].BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + int txlen = (int)(buffptr->Len); + SendARDOPorPacketData(TNC, 0, data, txlen); + } + + SendARDOPorPacketData(TNC, 0, Msg, (int)strlen(Msg)); + STREAM->NeedDisc = 100; // 10 secs + } + } + strcpy(STREAM->MyCall, TNC->TargetCall); + return; + } + else + { + // Connect Complete + + char Reply[80]; + int ReplyLen; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + ReplyLen = sprintf(Reply, "*** Connected to %s\r", Call); + + buffptr->Len = ReplyLen; + memcpy(&buffptr->Data[0], Reply, ReplyLen); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = TRUE; // Subsequent data to data channel + + if (TNC->RIG && TNC->RIG->Valchar[0]) + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); + else + sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); + + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + UpdateMH(TNC, Call, '+', 'O'); + return; + } + } + + if (_memicmp(Buffer, "DISCONNECTED", 12) == 0 + || _memicmp(Buffer, "STATUS CONNECT TO", 17) == 0 + || _memicmp(Buffer, "STATUS END ARQ CALL", 19) == 0 + || _memicmp(Buffer, "STATUS ARQ TIMEOUT FROM PROTOCOL STATE", 24) == 0 +// || _memicmp(Buffer, "NEWSTATE DISC", 13) == 0 + || _memicmp(Buffer, "ABORT", 5) == 0) + + { + TNC->ConnectPending = FALSE; // Cancel Scan Lock + + if (TNC->FECMode) + return; + + if (TNC->StartSent) + { + TNC->StartSent = FALSE; // Disconnect reported following start codec + return; + } + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "*** Failure with %s\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + if (TNC->RestartAfterFailure) + { + if (TNC->PID) + KillTNC(TNC); + + RestartTNC(TNC); + } + + sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); + MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); + + return; + } + + WritetoTrace(TNC, Buffer, MsgLen - 1); + + // Release Session + + if (TNC->Streams[0].Connected) + { + // Create a traffic record + + hookL4SessionDeleted(TNC, STREAM); + + } + + + TNC->Streams[0].Connecting = FALSE; + TNC->Streams[0].Connected = FALSE; // Back to Command Mode + TNC->Streams[0].ReportDISC = TRUE; // Tell Node + + if (TNC->Streams[0].Disconnecting) // + ARDOPReleaseTNC(TNC); + + TNC->Streams[0].Disconnecting = FALSE; + + return; + } + + if (_memicmp(Buffer, "MODE", 4) == 0) + { + strcpy(TNC->WEB_MODE, &Buffer[5]); + MySetWindowText(TNC->xIDC_MODE, &Buffer[5]); + return; + } + + if (_memicmp(Buffer, "STATUS ", 7) == 0) + { + return; + } + + if (_memicmp(Buffer, "RADIOMODELS", 11) == 0) + return; + + if (_memicmp(&Buffer[0], "PENDING", 7) == 0) // Save Pending state for scan control + { + TNC->ConnectPending = 6; // Time out after 6 Scanintervals + return; + } + + // REJECTEDBW and REJECTEDBUSY are sent to both calling and called + + if (_memicmp(&Buffer[0], "REJECTEDBUSY", 12) == 0) + { + TNC->ConnectPending = FALSE; + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Connection to %s Rejected - Channel Busy\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + } + } + + if (_memicmp(&Buffer[0], "REJECTEDBW", 10) == 0) + { + TNC->ConnectPending = FALSE; + + if (TNC->Streams[0].Connecting) + { + // Report Connect Failed, and drop back to command mode + + TNC->Streams[0].Connecting = FALSE; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Connection to %s Rejected - Incompatible Bandwidth\r", TNC->Streams[0].RemoteCall); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + return; + } + } + + + + if (_memicmp(&Buffer[0], "CANCELPENDING", 13) == 0 + || _memicmp(&Buffer[0], "REJECTEDB", 9) == 0) //REJECTEDBUSY or REJECTEDBW + { + TNC->ConnectPending = FALSE; + return; + } + + if (_memicmp(Buffer, "FAULT", 5) == 0) + { + Debugprintf(Buffer); + WritetoTrace(TNC, Buffer, MsgLen - 1); +// return; + } + + if (_memicmp(Buffer, "NEWSTATE", 8) == 0) + { + TNC->WinmorRestartCodecTimer = time(NULL); + + MySetWindowText(TNC->xIDC_PROTOSTATE, &Buffer[9]); + strcpy(TNC->WEB_PROTOSTATE, &Buffer[9]); + + if (_memicmp(&Buffer[9], "DISC", 4) == 0) + { + TNC->DiscPending = FALSE; + TNC->ConnectPending = FALSE; + + return; + } + + if (strcmp(&Buffer[9], "ISS") == 0) // Save Pending state for scan control + TNC->TXRXState = 'S'; + else if (strcmp(&Buffer[9], "IRS") == 0) + TNC->TXRXState = 'R'; + + return; + } + + if ((_memicmp(Buffer, "FAULT Not from state FEC", 24) == 0) || (_memicmp(Buffer, "FAULT Blocked by Busy Lock", 24) == 0)) + { + if (TNC->FECMode) + { + Sleep(1000); + +// if (TNC->FEC1600) +// ARDOPSendCommand(TNC,"FECSEND 1600"); +// else +// ARDOPSendCommand(TNC,"FECSEND 500"); + return; + } + } + + if (_memicmp(Buffer, "PLAYBACKDEVICES", 15) == 0) + { + TNC->PlaybackDevices = _strdup(&Buffer[16]); + } + // Others should be responses to commands + + if (_memicmp(Buffer, "BLOCKED", 6) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 1); + return; + } + + if (_memicmp(Buffer, "OVER", 4) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 1); + return; + } + + if (_memicmp(Buffer, "PING ", 5) == 0) + { + char Call[32]; + + // Make sure not Echoed PING + + // c:ping gm8bpq-1 5 + // c:PING GM8BPQ>GM8BPQ-1 15 98 + + if (strchr(Buffer, '>') == 0) // Echoed + return; + + WritetoTrace(TNC, Buffer, MsgLen - 1); + + // Release scanlock after another interval (to allow time for response to be sent) + // ?? use cancelpending TNC->ConnectPending = 1; + + + memcpy(Call, &Buffer[5], 20); + strlop(Call, '>'); + UpdateMH(TNC, Call, '!', 'I'); + + return; + } + + if (_memicmp(Buffer, "VERSION ", 8) == 0) + { + // If contains "OFDM" or "ARDOP3" increase data session busy level + + if (strstr(&Buffer[8], "OFDM")) + TNC->Mode = 'O'; + else if (strstr(&Buffer[8], "TNC_3")) + TNC->Mode = '3'; + else + TNC->Mode = 0; + } + + if (_memicmp(Buffer, "PINGACK ", 8) == 0) + { + WritetoTrace(TNC, Buffer, MsgLen - 1); + // Drop through to return to user + } + + if (_memicmp(Buffer, "CQ ", 3) == 0 && MsgLen > 10) + { + char Call[32]; + char * Loc; + + WritetoTrace(TNC, Buffer, MsgLen - 1); + + // Update MH + { + memcpy(Call, &Buffer[3], 32); + Loc = strlop(Call, ' '); + strlop(Loc, ']'); + UpdateMHEx(TNC, Call, '!', 'I', &Loc[1], TRUE); + } + // Drop through to go to user if attached but not connected + + } + // Return others to user (if attached but not connected) + + if (TNC->Streams[0].Attached == 0) + return; + + if (TNC->Streams[0].Connected || TNC->Streams[0].Connecting) + return; + + if (MsgLen > 200) + MsgLen = 200; + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return; // No buffers, so ignore + } + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} %s\r", Buffer); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); +} + +static VOID ARDOPProcessReceivedData(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + + // May get several messages per packet + // May get message split over packets + + // Data has a length field + // ARQ|FEC|ERR|, 2 byte count (Hex 0001 – FFFF), binary data” + // New standard doesnt have d: + + if (TNC->DataInputLen > 16000) // Shouldnt have packets longer than this + TNC->DataInputLen=0; + + // OFDM can return large packets (up to 10160) + + // in serial mode, data has been put in input buffer by comms code + + if (TNC->ARDOPCommsMode == 'T') + { + InputLen=recv(TNC->TCPDataSock, &TNC->ARDOPDataBuffer[TNC->DataInputLen], 16384 - TNC->DataInputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + TNCLost(TNC); + return; + } + + TNC->DataInputLen += InputLen; + } +loop: + + if (TNC->OldMode) + goto OldRX; + + else + { // No D: + + // Data = check we have it all + + int DataLen = (TNC->ARDOPDataBuffer[0] << 8) + TNC->ARDOPDataBuffer[1]; // HI First + UCHAR DataType[4]; + UCHAR * Data; + + if (TNC->DataInputLen < DataLen + 2) + return; // Wait for more + + MsgLen = DataLen + 2; // Len + + memcpy(DataType, &TNC->ARDOPDataBuffer[2] , 3); + DataType[3] = 0; + + Data = &TNC->ARDOPDataBuffer[5]; + DataLen -= 3; + + ARDOPProcessDataPacket(TNC, DataType, Data, DataLen); + + // See if anything else in buffer + + TNC->DataInputLen -= MsgLen; + + if (TNC->DataInputLen == 0) + return; + + memmove(TNC->ARDOPDataBuffer, &TNC->ARDOPDataBuffer[MsgLen], TNC->DataInputLen); + goto loop; + } + +OldRX: + + if (TNC->DataInputLen < 8) + return; // Wait for more to arrive (?? timeout??) + + if (TNC->ARDOPDataBuffer[1] = ':') // At least message looks reasonable + { + if (TNC->ARDOPDataBuffer[0] == 'd') + { + // Data = check we have it all + + int DataLen = (TNC->ARDOPDataBuffer[2] << 8) + TNC->ARDOPDataBuffer[3]; // HI First +// unsigned short CRC; + UCHAR DataType[4]; + UCHAR * Data; + + if (TNC->DataInputLen < DataLen + 4) + return; // Wait for more + + MsgLen = DataLen + 4; // d: Len CRC + + memcpy(DataType, &TNC->ARDOPDataBuffer[4] , 3); + DataType[3] = 0; + Data = &TNC->ARDOPDataBuffer[7]; + DataLen -= 3; + + ARDOPProcessDataPacket(TNC, DataType, Data, DataLen); + + // See if anything else in buffer + + TNC->DataInputLen -= MsgLen; + + if (TNC->DataInputLen == 0) + return; + + memmove(TNC->ARDOPDataBuffer, &TNC->ARDOPDataBuffer[MsgLen], TNC->DataInputLen); + goto loop; + } + else + // Duff - clear input buffer + TNC->DataInputLen = 0; + + } + return; +} + + + +static VOID ARDOPProcessReceivedControl(struct TNCINFO * TNC) +{ + int InputLen, MsgLen; + char * ptr, * ptr2; + char Buffer[4096]; + + // May get several messages per packet + // May get message split over packets + + // Commands end with CR. + + if (TNC->InputLen > 8000) // Shouldnt have packets longer than this + TNC->InputLen=0; + + // in serial mode, data has been put in input buffer by comms code + + if (TNC->ARDOPCommsMode == 'T') + { + // I don't think it likely we will get packets this long, but be aware... + + InputLen=recv(TNC->TCPSock, &TNC->ARDOPBuffer[TNC->InputLen], 8192 - TNC->InputLen, 0); + + if (InputLen == 0 || InputLen == SOCKET_ERROR) + { + TNCLost(TNC); + return; + } + + TNC->InputLen += InputLen; + } + +loop: + + ptr = memchr(TNC->ARDOPBuffer, '\r', TNC->InputLen); + + if (ptr == 0) // CR in buffer + return; // Wait for it + + ptr2 = &TNC->ARDOPBuffer[TNC->InputLen]; + + if ((ptr2 - ptr) == 1) // CR (no CRC in new version) + { + // Usual Case - single meg in buffer + + ARDOPProcessResponse(TNC, TNC->ARDOPBuffer, TNC->InputLen); + TNC->InputLen=0; + return; + } + else + { + // buffer contains more that 1 message + + + MsgLen = TNC->InputLen - (int)(ptr2-ptr) + 1; // Include CR + + memcpy(Buffer, TNC->ARDOPBuffer, MsgLen); + + ARDOPProcessResponse(TNC, Buffer, MsgLen); + + if (TNC->InputLen < MsgLen) + { + TNC->InputLen = 0; + return; + } + memmove(TNC->ARDOPBuffer, ptr + 1, TNC->InputLen-MsgLen); + + TNC->InputLen -= MsgLen; + goto loop; + } + return; +} + + + +VOID ARDOPProcessDataPacket(struct TNCINFO * TNC, UCHAR * Type, UCHAR * Data, int Length) +{ + // Info on Data Socket - just packetize and send on + + struct STREAMINFO * STREAM = &TNC->Streams[0]; + + int PacLen = 236; + PMSGWITHLEN buffptr; + + TNC->TimeSinceLast = 0; + + if (strcmp(Type, "IDF") == 0) + { + // Place ID frames in Monitor Window and MH + + char Call[20]; + char * Loc; + +// GM8BPQ-2:[IO68VL] +//ID:GM8BPQ-2 IO68VL : +// GM8BPQ-2:[IO68VL] +//ID:GM8BPQ-2 [IO68vl]: +//ID:HB9AVK JN47HG : + +// TX BPQ IDF GM8BPQ-2:[IO68VL] +// RX Rick IDF ID:GM8BPQ-2 [IO68vl]: + +// TX Rick IDF GM8BPQ-2:[IO68VL] +// RX BPQ IDF ID:GM8BPQ-2 IO68VL : + +//ID:GM8BPQ-2 [IO68vl] : + + Data[Length] = 0; + WritetoTrace(TNC, Data, Length); + + Debugprintf("ARDOP IDF %s", Data); + + // Loos like transmitted ID doesnt have ID: + + if (memcmp(Data, "ID:", 3) == 0) // These seem to be received ID's + { + memcpy(Call, &Data[3], 20); + Loc = strlop(Call, ' '); + strlop(Loc, ']'); + UpdateMHEx(TNC, Call, '!', 'I', &Loc[1], TRUE); + } + return; + } + + STREAM->bytesRXed += Length; + + Data[Length] = 0; + + sprintf(TNC->WEB_TRAFFIC, "Sent %d RXed %d Queued %d", + STREAM->bytesTXed - STREAM->BytesOutstanding, STREAM->bytesRXed, STREAM->BytesOutstanding); + MySetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); + + + if (TNC->FECMode) + { + Length = (int)strlen(Data); + if (Data[Length - 1] == 10) + Data[Length - 1] = 13; + + } + + if (strcmp(Type, "FEC") == 0) + { + // May be an APRS Message + // These are delimired with ^ characters + // As they are likely to be split across + // FEC blocks they need to be recombined + + char * ptr = Data; + char * ptr1; + char * ptr2; + char c; + int Len = Length; + char Call[10] = ""; + + Debugprintf(Data); + + if (*ptr == '^' || TNC->ARDOPAPRSLen) + { + // New Packet or continuation + + while (Len--) + { + c = *(ptr++); + if (c == '^') + { + // may be start or end + + Debugprintf("Start/end of beacon Len = %d", TNC->ARDOPAPRSLen); + + if (TNC->ARDOPAPRSLen == 0) + continue; // Start + + // Validate and Process Block + + Debugprintf("beacon %s", TNC->ARDOPAPRS); + + ptr1 = TNC->ARDOPAPRS; + ptr2 = strchr(ptr1, '>'); + + if (ptr2 && (ptr2 - ptr1) < 10) + { + // Could be APRS + +// if ((memcmp(ptr2 + 1, "AP", 2) == 0) || (memcmp(ptr2 + 1, "BE", 2) == 0)) + if (1) // People using other dests + { + int APLen; + + // assume it is + + char * ptr3 = strchr(ptr2, '|'); + struct _MESSAGE * buffptr; + + if (ptr3 == 0) + { + TNC->ARDOPAPRSLen = 0; + Debugprintf("no |"); + continue; + } + + buffptr = GetBuff(); + *(ptr3++) = 0; // Terminate TO call + + APLen = TNC->ARDOPAPRSLen - (int)(ptr3 - ptr1); + + TNC->ARDOPAPRSLen = 0; + + Debugprintf("Good APRS %d Left", Len); + + // Convert to ax.25 format + + if (buffptr == 0) + continue; // No buffers, so ignore + + buffptr->PORT = TNC->Port; + + ConvToAX25(ptr1, buffptr->ORIGIN); + ConvToAX25(ptr2 + 1, buffptr->DEST); + buffptr->ORIGIN[6] |= 1; // Set end of address + buffptr->CTL = 3; + buffptr->PID = 0xF0; + memcpy(buffptr->L2DATA, ptr3, APLen); + buffptr->LENGTH = 16 + MSGHDDRLEN + APLen; + time(&buffptr->Timestamp); + + memcpy(Call,ptr1, 9); + strlop(Call, '>'); + UpdateMH(TNC, Call, '!', 'I'); + + BPQTRACE((MESSAGE *)buffptr, TRUE); + + ReleaseBuffer(buffptr); + + } + else + { + Debugprintf("Not APRS"); + TNC->ARDOPAPRSLen = 0; // in case not aprs + } + } + else + { + Debugprintf("cant be APRS"); + TNC->ARDOPAPRSLen = 0; // in case not aprs + } + continue; + } + + // Normal Char + + TNC->ARDOPAPRS[TNC->ARDOPAPRSLen++] = c; + if (TNC->ARDOPAPRSLen == 512) + TNC->ARDOPAPRSLen = 0; + } + // End of packet. + + Debugprintf("End of Packet Len %d", TNC->ARDOPAPRSLen); + } + + // FEC but not APRS. Discard if connected + + if (TNC->Streams[0].Connected) + return; + } + + WritetoTrace(TNC, Data, Length); + + // We can get messages of form ARQ [ConReq2000M: GM8BPQ-2 > OE3FQU] + // Noe (V2) [ConReq2500 > G8XXX] + + // when not connected. + + if (TNC->Streams[0].Connected == FALSE) + { + if (strcmp(Type, "ARQ") == 0) + { + if (Data[1] == '[') + { + // Log to MH + + char Call[20]; + char * ptr; + + // Add a Newline for monitoring + + Data[Length++] = 13; + Data[Length] = 0; + + ptr = strchr(Data, ':'); + + if (ptr) + { + memcpy(Call, &ptr[2], 20); + strlop(Call, ' '); + UpdateMH(TNC, Call, '!', 'I'); + } + } + } + } + + if (TNC->Streams[0].Attached == 0) + return; + + // May need to fragment + + while (Length) + { + int Fraglen = Length; + + if (Length > PACLEN) + Fraglen = PACLEN; + + Length -= Fraglen; + + buffptr = GetBuff(); + + if (buffptr == 0) + return; // No buffers, so ignore + + memcpy(&buffptr->Data[0], Data, Fraglen); + + Data += Fraglen; + + buffptr->Len = Fraglen; + + C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); + } + + return; +} + +/* +INT_PTR CALLBACK ConfigDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int Cmd = LOWORD(wParam); + + switch (message) + { + case WM_INITDIALOG: + { + struct TNCINFO * TNC = (struct TNCINFO * )lParam; + char * ptr1, *ptr2; + int ptr3 = 0; + char Line[1000]; + int len; + + ptr1 = TNC->CaptureDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_CAPTURE, Line); + + ptr3 = 0; + + ptr1 = TNC->PlaybackDevices; + + if (!ptr1) + return 0; // No Devices + + + while (ptr2 = strchr(ptr1, ',')) + { + len = ptr2 - ptr1; + memcpy(&Line[ptr3], ptr1, len); + ptr3 += len; + Line[ptr3++] = '\r'; + Line[ptr3++] = '\n'; + + ptr1 = ++ptr2; + } + Line[ptr3] = 0; + strcat(Line, ptr1); + + SetDlgItemText(hDlg, IDC_PLAYBACK, Line); + + SendDlgItemMessage(hDlg, IDC_PLAYBACK, EM_SETSEL, -1, 0); + +// KillTNC(TNC); + + return TRUE; + } + + case WM_SIZING: + { + return TRUE; + } + + case WM_ACTIVATE: + +// SendDlgItemMessage(hDlg, IDC_MESSAGE, EM_SETSEL, -1, 0); + + break; + + + case WM_COMMAND: + + + if (Cmd == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + + return (INT_PTR)TRUE; + + break; + } + return (INT_PTR)FALSE; +} +*/ + + +VOID TidyClose(struct TNCINFO * TNC, int Stream) +{ + // If all acked, send disc + + if (TNC->Streams[Stream].BytesOutstanding == 0) + { + if (Stream == 0) + ARDOPSendCommand(TNC, "DISCONNECT", TRUE); + else + { + char Cmd[32]; + sprintf(Cmd, "%cDISCONNECT", 1); + ARDOPSendPktCommand(TNC, Stream, Cmd); + } + } +} + +VOID ForcedClose(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + ARDOPSendCommand(TNC, "ABORT", TRUE); + else + { + char Cmd[32]; + sprintf(Cmd, "%cDISCONNECT", Stream); + ARDOPSendPktCommand(TNC, Stream, Cmd); + } +} + + + +VOID CloseComplete(struct TNCINFO * TNC, int Stream) +{ + if (Stream == 0) + { + ARDOPReleaseTNC(TNC); + + if (TNC->FECMode) + { + TNC->FECMode = FALSE; + ARDOPSendCommand(TNC, "SENDID", TRUE); + } + } +} + +VOID ARDOPAbort(struct TNCINFO * TNC) +{ + ARDOPSendCommand(TNC, "ABORT", TRUE); +} + +// Host Mode Stuff (we reuse some routines in SCSPactor) + +VOID ARDOPCRCStuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len) +{ + unsigned short int crc; + UCHAR StuffedMsg[500]; + int i, j; + + Msg[3] |= TNC->Toggle; + +// Debugprintf("ARDOP TX Toggle %x", TNC->Toggle); + + crc = compute_crc(&Msg[2], Len-2); + crc ^= 0xffff; + + Msg[Len++] = (crc&0xff); + Msg[Len++] = (crc>>8); + + for (i = j = 2; i < Len; i++) + { + StuffedMsg[j++] = Msg[i]; + if (Msg[i] == 170) + { + StuffedMsg[j++] = 0; + } + } + + if (j != i) + { + Len = j; + memcpy(Msg, StuffedMsg, j); + } + + TNC->TXLen = Len; + + Msg[0] = 170; + Msg[1] = 170; + + ARDOPWriteCommBlock(TNC); + + TNC->Retries = 5; +} + +VOID ARDOPExitHost(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + // Try to exit Host Mode + + TNC->TXBuffer[2] = 31; + TNC->TXBuffer[3] = 0x41; + TNC->TXBuffer[4] = 0x5; + memcpy(&TNC->TXBuffer[5], "JHOST0", 6); + + ARDOPCRCStuffAndSend(TNC, Poll, 11); + return; +} + + +VOID ARDOPDoTermModeTimeout(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + //Checking if in Terminal Mode - Try to set back to Term Mode + + TNC->ReinitState = 1; + ARDOPExitHost(TNC); + TNC->Retries = 1; + + return; + } + + if (TNC->ReinitState == 1) + { + // Forcing back to Term Mode + + TNC->ReinitState = 0; + ARDOPDoTNCReinit(TNC); // See if worked + return; + } + + if (TNC->ReinitState == 3) + { + // Entering Host Mode + + // Assume ok + + TNC->HostMode = TRUE; + TNC->IntCmdDelay = 10; + + return; + } +} + + +VOID ARDOPDoTNCReinit(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->ReinitState == 0) + { + // Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode + + Poll[0] = 13; + Poll[1] = 0x1B; + TNC->TXLen = 2; + +// Debugprintf("Sending CR ESC, Mode %c", TNC->ARDOPCommsMode); + + if (TNC->ARDOPCommsMode == 'E') + { + if (TNC->TCPCONNECTED) + { + int SentLen = send(TNC->TCPSock, TNC->TXBuffer, TNC->TXLen, 0); + + if (SentLen != TNC->TXLen) + { + // ARDOP doesn't seem to recover from a blocked write. For now just reset + + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->TCPCONNECTED = FALSE; + } + } + + TNC->TNCOK = FALSE; + sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->ARDOPSerialPort); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Timeout = 20; // 2 secs + TNC->Retries = 1; + return; + } + + if (TNC->hDevice == 0) // Dont try to init if device not open + { + if (TNC->PortRecord->PORTCONTROL.PortStopped == 0) + OpenCOMMPort(TNC, TNC->ARDOPSerialPort, TNC->ARDOPSerialSpeed, TRUE); + + TNC->Timeout = 100; // 10 secs + TNC->Retries = 1; + return; + } + + TNC->TNCOK = FALSE; + sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->ARDOPSerialPort); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + + if (ARDOPWriteCommBlock(TNC) == FALSE) + { + if (TNC->hDevice) + { + Debugprintf("ARDOPWriteCommBlock Failed Mode %c", TNC->ARDOPCommsMode); + CloseCOMPort(TNC->hDevice); + } + if (TNC->ARDOPCommsMode == 'S') + { + OpenCOMMPort(TNC, TNC->ARDOPSerialPort, TNC->ARDOPSerialSpeed, TRUE); + } + else + { +#ifdef WIN32 +#else +#ifdef NOI2C +#else + char i2cname[30]; + int fd; + int retval; + + // Open and configure the i2c interface + + if (strlen(TNC->ARDOPSerialPort) < 3) + sprintf(i2cname, "/dev/i2c-%s", TNC->ARDOPSerialPort); + else + strcpy(i2cname, TNC->ARDOPSerialPort); + + fd = TNC->hDevice = open(i2cname, O_RDWR); + + if (fd < 0) + printf("Cannot find i2c bus %s\n", i2cname); + else + { + retval = ioctl(fd, I2C_SLAVE, TNC->ARDOPSerialSpeed); + + if(retval == -1) + printf("Cannot open i2c device %x\n", TNC->ARDOPSerialSpeed); + + ioctl(fd, I2C_TIMEOUT, 10); + } +#endif +#endif + } + } + TNC->Retries = 1; + } + + if (TNC->ReinitState == 1) // Forcing back to Term + TNC->ReinitState = 0; + + if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands + { + Debugprintf("DOTNCReinit %d Complete - Entering Hostmode", TNC->Port); + + TNC->TXBuffer[2] = 0; + TNC->Toggle = 0; + + memcpy(Poll, "JHOST4\r", 7); + + TNC->TXLen = 7; + ARDOPWriteCommBlock(TNC); + + // Timeout will enter host mode + + TNC->Timeout = 1; + TNC->Retries = 1; + TNC->Toggle = 0; + TNC->ReinitState = 3; // Set toggle force bit + TNC->OKToChangeFreq = 1; // In case failed whilst waiting for permission + TNC->CONNECTED = TRUE; + + return; + } +} + +VOID ARDOPProcessTermModeResponse(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + char * ptr1, * ptr2; + int len; + + if (TNC->ReinitState == 0) + { + // Testing if in Term Mode. It is, so can now send Init Commands + + TNC->InitPtr = TNC->InitScript; + TNC->ReinitState = 2; + + // Send ARDOP to make sure TNC is in a known state + + strcpy(Poll, "ARDOP\r"); + +// OpenLogFile(TNC->Port); +// WriteLogLine(TNC->Port, Poll, 7); +// CloseLogFile(TNC->Port); + + TNC->TXLen = 6; + ARDOPWriteCommBlock(TNC); + + TNC->Timeout = 60; // 6 secs + + return; + } + if (TNC->ReinitState == 2) + { + // Sending Init Commands + + // Send INIT script + + ptr1 = &TNC->InitScript[0]; + +/* GetSemaphore(&Semaphore, 52); + + while(TNC->BPQtoWINMOR_Q) + { + void ** buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); + + if (buffptr) + ReleaseBuffer(buffptr); + } + + FreeSemaphore(&Semaphore, 52); +*/ + while (ptr1 && ptr1[0]) + { + ptr2 = strchr(ptr1, 13); + + if (ptr2 == 0) + break; + + len = (int)(ptr2 - ptr1) + 1; + + memcpy(Poll, ptr1, len); + + // if Date or Time command add current time + + if (_memicmp(ptr1, "DATETIME", 4) == 0) + { + time_t T; + struct tm * tm; + + T = time(NULL); + tm = gmtime(&T); + + len = sprintf(Poll, "DATETIME %02d %02d %02d %02d %02d %02d\r", + tm->tm_mday, tm->tm_mon + 1, tm->tm_year - 100, + tm->tm_hour, tm->tm_min, tm->tm_sec); + } + + // if RADIOPTTON ? or RADIOPTTOFF ? replace ? + // with correct string + + if (_memicmp(ptr1, "RADIOPTTOFF ?", 13) == 0) + { + int Len = TNC->RIG->PTTOffLen; + UCHAR * Cmd = TNC->RIG->PTTOff; + char Hex[256]; + char * hexptr = Hex; + int i, j; + while(Len--) + { + i = *(Cmd++); + j = i >>4; + j += '0'; // ascii + if (j > '9') + j += 7; + *(hexptr++) = j; + + j = i & 0xf; + j += '0'; // ascii + if (j > '9') + j += 7; + *(hexptr++) = j; + } + *(hexptr++) = 0; + + len = sprintf(Poll, "RADIOPTTOFF %s\r", Hex); + } + + if (_memicmp(ptr1, "RADIOPTTON ?", 12) == 0) + { + int Len = TNC->RIG->PTTOnLen; + UCHAR * Cmd = TNC->RIG->PTTOn; + char Hex[256]; + char * hexptr = Hex; + int i, j; + while(Len--) + { + i = *(Cmd++); + j = i >>4; + j += '0'; // ascii + if (j > '9') + j += 7; + *(hexptr++) = j; + + j = i & 0xf; + j += '0'; // ascii + if (j > '9') + j += 7; + *(hexptr++) = j; + } + *(hexptr++) = 0; + + len = sprintf(Poll, "RADIOPTTON %s\r", Hex); + } + + TNC->TXLen = len; + ARDOPWriteCommBlock(TNC); + + Sleep(50); + + TNC->Timeout = 60; // 6 secs + + if (ptr2) + *(ptr2++) = 13; // Put CR back for next time + + ptr1 = ptr2; + } + + + + // All Sent - enter Host Mode + + ARDOPDoTNCReinit(TNC); // Send Next Command + return; + } +} + +int ARDOPProcessDEDFrame(struct TNCINFO * TNC, UCHAR * Msg, int framelen) +{ + PMSGWITHLEN buffptr; + UCHAR * Buffer; // Data portion of frame + unsigned int Stream = 0, RealStream; + + if (Msg[0] == 255 && Msg[1] == 255) + { + goto tcpHostFrame; + } + + if (TNC->HostMode == 0) + return framelen; + + // Check toggle + +// Debugprintf("ARDOP RX Toggle = %x MSG[3] = %x", TNC->Toggle, Msg[3]); + + if (TNC->Toggle != (Msg[3] & 0x80)) + { + Debugprintf("ARDOP PTC Seq Error"); + return framelen; // should check if retrying + } + + // Any valid frame is an ACK + + TNC->Toggle ^= 0x80; // update toggle + + TNC->Timeout = 0; + + Msg[3] &= 0x7f; // remove toggle + + if (TNC->TNCOK == FALSE) + { + // Just come up + + TNC->TNCOK = TRUE; + sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->ARDOPSerialPort); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + } + +tcpHostFrame: + + Stream = RealStream = Msg[2]; + + // See if Poll Reply or Data + + if (Msg[3] == 1 && Stream > 0 && Stream <= APMaxStreams) + { + // Ardop Packet Data. Probably Buffer Status + + int Len = (int)strlen(&Msg[4]); + + if (memcmp(&Msg[4], "Queued ", 7) == 0) + { + int Count = atoi(&Msg[11]); + TNC->Streams[Stream].BytesOutstanding = Count; + } + + return Len + 5; + } + + if (Stream == 32) // Native Mode Command Response + { + if (Msg[3] == 1) // Null terminated response + { + int Len = (int)strlen(&Msg[4]) + 1; + ARDOPProcessResponse(TNC, &Msg[4], Len); + return Len + 5; + } + if (Msg[3] == 0) // Success, no response + return 5; + + if (Msg[3] == 7) // Status Reports + { + int Len = Msg[4] + 1; + + // may have more than one message in buffer, + // so pass to unpack + + memcpy(&TNC->ARDOPBuffer[TNC->InputLen],&Msg[5], Len); + TNC->InputLen += Len; + + ARDOPProcessReceivedControl(TNC); + return Len + 5; + } + return 0; + } + if (Stream == 33) // Native Mode Data + { + // May be connected, FEC or ID + + if (Msg[3] == 1) // Null terminated response + return 0; + + if (Msg[3] == 0) // Success, no response + return 0; + + if (Msg[3] == 7) // Data + { + int Len = Msg[4] + 1; + + // may have more than one message in buffer, + // so pass to unpack + + memcpy(&TNC->ARDOPDataBuffer[TNC->DataInputLen],&Msg[5], Len); + TNC->DataInputLen += Len; + + ARDOPProcessReceivedData(TNC); + return 0; + } + return 0; + + } + + if (Stream == 34) // Native Mode Log + { + int Len = Msg[4] + 1; + char timebuf[32]; + char Line[256]; + char * ptr, * ptr2; +#ifdef WIN32 + SYSTEMTIME st; +#else + struct timespec tp; + int hh; + int mm; + int ss; +#endif + if (TNC->LogHandle == 0 && TNC->DebugHandle == 0) + return 0; + +#ifdef WIN32 + GetSystemTime(&st); + sprintf(timebuf, "%02d:%02d:%02d.%03d ", + st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); +#else + clock_gettime(CLOCK_REALTIME, &tp); + ss = tp.tv_sec % 86400; // Secs int day + hh = ss / 3600; + mm = (ss - (hh * 3600)) / 60; + ss = ss % 60; + + sprintf(timebuf, "%02d:%02d:%02d.%03d ", + hh, mm, ss, (int)tp.tv_nsec/1000000); +#endif + // Messages may be blocked with top bit of first byte set + + ptr = &Msg[5]; + ptr2 = Line; + + while(Len--) + { + int c = (*ptr++); + + if (c & 0x80) + { + // New Message + + c &= 0x7f; + + *ptr2 = 0; + fputs(Line, TNC->DebugHandle); // rest of last line + if (TNC->LastLogType < '7') + fputs(Line, TNC->LogHandle); + + TNC->LastLogType = c; + + // Timestamp new message and add type + + fputs(timebuf, TNC->DebugHandle); + fputc(c, TNC->DebugHandle); + fputc(' ', TNC->DebugHandle); + if (TNC->LastLogType < '7') + { + fputs(timebuf, TNC->LogHandle); + fputc(c, TNC->LogHandle); + fputc(' ', TNC->LogHandle); + } + ptr2 = Line; + } + else + *(ptr2++) = c; + } + *ptr2 = 0; + + fputs(Line, TNC->DebugHandle); // rest of last line + fflush(TNC->DebugHandle); + + if (TNC->LastLogType < '7') + { + fputs(Line, TNC->LogHandle); + fflush(TNC->LogHandle); + } + + return 0; + } + + if (Msg[3] == 4 || Msg[3] == 5) + { + MESSAGE Monframe; + + // Packet Monitor Data. + // DED Host uses 4 and 5 as Null Terminated ascii encoded header + // and 6 byte count format info. + + // In ARDOP Native mode I pass both header and data + // in byte count raw format, as there is no point + // in ascii coding then converting back to pass to + // monitor code + + // The First byte is TX/RX Flag + + int Len = Msg[4]; // Would be +1 but first is Flag + + memset(&Monframe, 0, sizeof(Monframe)); + + memcpy(Monframe.DEST, &Msg[6], Len); + Monframe.LENGTH = Len + MSGHDDRLEN; + Monframe.PORT = TNC->Port | Msg[5]; // or in TX Flag + + time(&Monframe.Timestamp); + + if (Msg[3] == 5) // More to come + { + // Save the header till the data arrives + + if (TNC->Monframe) + free(TNC->Monframe); + + TNC->Monframe = malloc(sizeof(MESSAGE)); + + if (TNC->Monframe) + memcpy(TNC->Monframe, &Monframe, sizeof(MESSAGE)); + + return Len + 6; + } + + BPQTRACE((MESSAGE *)&Monframe, TRUE); + return Len + 6; + } + + if (Msg[3] == 6) + { + // Second part of I or UI + + int Len = Msg[4] + 1; + + MESSAGE Monframe; + UCHAR * ptr = (UCHAR *)&Monframe; + + memset(&Monframe, 0, sizeof(Monframe)); + + if (TNC->Monframe) + { + memcpy(&Monframe, TNC->Monframe, TNC->Monframe->LENGTH); + memcpy(&ptr[TNC->Monframe->LENGTH], &Msg[5], Len); + + Monframe.LENGTH += Len; + + time(&Monframe.Timestamp); + BPQTRACE((MESSAGE *)&Monframe, TRUE); + + free(TNC->Monframe); + TNC->Monframe = NULL; + } + return Len + 6; + } + + if (Msg[3] == 0) + { + // Success - Nothing Follows + + if (Stream < 32) + if (TNC->Streams[Stream].CmdSet) + return 4; // Response to Command Set + + if ((TNC->TXBuffer[3] & 1) == 0) // Data + return 4; + + if (Stream > 0 && Stream <= APMaxStreams) + { + // Packet Response + + return 4; + } + + // If the response to a Command, then we should convert to a text OK" for forward scripts, etc + + if (TNC->TXBuffer[5] == 'G') // Poll + return 4; + + if (TNC->TXBuffer[5] == 'C') // Connect - reply we need is async + return 4; + + if (TNC->TXBuffer[5] == 'L') // Shouldnt happen! + return 4; + + if (TNC->TXBuffer[5] == '#') // Shouldnt happen! + return 4; + + if (TNC->TXBuffer[5] == '%' && TNC->TXBuffer[6] == 'W') // Scan Control - Response to W1 + if (TNC->InternalCmd) + return 4; // Just Ignore + + if (TNC->TXBuffer[5] == 'J') // JHOST + { + if (TNC->TXBuffer[10] == '0') // JHOST0 + { + TNC->Timeout = 1; // + return 4; + } + } + + if (TNC->Streams[Stream].Connected) + return 4; + + buffptr = GetBuff(); + + if (buffptr == NULL) return 4; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0],"ARDOP} Ok\r"); + + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); +// C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + + return 4; + } + + if (Msg[3] > 0 && Msg[3] < 6) + { + // Success with message - null terminated + + UCHAR * ptr; + int len; + + if (Msg[2] == 0xff) // General Poll Response + { + UCHAR * Poll = TNC->TXBuffer; + UCHAR Chan = Msg[4] - 1; + + if (Chan == 255) // Nothing doing + return 0; + + if (Msg[5] != 0) + { + // More than one to poll - save the list of channels to poll + + strcpy(TNC->NexttoPoll, &Msg[5]); + } + + // Poll the channel that had data + + Poll[2] = Chan; // Channel + Poll[3] = 0x1; // Command + + if (Chan == 254) // Status - Send Extended Status (G3) + { + Poll[4] = 1; // Len-1 + Poll[5] = 'G'; // Extended Status Poll + Poll[6] = '3'; + } + else + { + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + } + + ARDOPCRCStuffAndSend(TNC, Poll, Poll[4] + 6); + TNC->InternalCmd = FALSE; + + return 0; + } + + Buffer = &Msg[4]; + + ptr = strchr(Buffer, 0); + + if (ptr == 0) + return 0; + + *(ptr++) = 13; + *(ptr) = 0; + + len = (int)(ptr - Buffer); + + if (len > 256) + return 0; + + if (Stream > 0 && Stream <= APMaxStreams) + { + // Packet Mode Response. Could be command response or status. + + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + PMSGWITHLEN buffptr; + + if (strstr(Buffer, "Incoming")) + { + // incoming call. Check which application it is for + + char Call[11]; + char TargetCall[11] = ""; + char * ptr; + APPLCALLS * APPL; + char * ApplPtr = APPLS; + int App; + char Appl[10]; + TRANSPORTENTRY * SESS; + + Buffer[len-1] = 0; + WritetoTrace(TNC, Buffer, len); + + STREAM->ConnectTime = time(NULL); + STREAM->bytesRXed = STREAM->bytesTXed = STREAM->PacketsSent = 0; + + memcpy(Call, &Buffer[19], 10); + ptr = strchr(Call, ' '); + if (ptr) *ptr = 0; + + ptr = strstr(&Buffer[19], " to "); + if (ptr) + { + memcpy(TargetCall, ptr + 4, 10); + ptr = strchr(TargetCall, 13); + if (ptr) + *ptr = 0; + } + + ProcessIncommingConnectEx(TNC, Call, Stream, TRUE, FALSE); + + SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; + + // Check for ExcludeList + + if (ExcludeList[0]) + { + if (CheckExcludeList(SESS->L4USER) == FALSE) + { + TidyClose(TNC, 0); + Debugprintf("ARDOP Packet Call from %s rejected", Call); + return 0; + } + } + + // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT + + if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) + { + UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; + + while (TRUE) + { + if (memcmp(SESS->L4USER, ptr, 6) == 0) // Ignore SSID + break; + + ptr += 7; + + if ((*ptr) == 0) // Not in list + { + TidyClose(TNC, 0); + Debugprintf("ARDOP Packet Call from %s not in ValidCalls - rejected", Call); + return 0; + } + } + } + + + // See which application the connect is for + + for (App = 0; App < 32; App++) + { + APPL=&APPLCALLTABLE[App]; + memcpy(Appl, APPL->APPLCALL_TEXT, 10); + ptr=strchr(Appl, ' '); + + if (ptr) + *ptr = 0; + + if (_stricmp(TargetCall, Appl) == 0) + break; + } + + if (App < 32) + { + char AppName[13]; + + memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12); + AppName[12] = 0; + + // Make sure app is available + + if (CheckAppl(TNC, AppName)) + { + char ApplCmd[80]; + int Len = sprintf(ApplCmd, "%s\r", AppName); + + buffptr = GetBuff(); + + if (buffptr == 0) + { + return len + 5; // No buffers, so ignore + } + + buffptr->Len = Len; + memcpy(&buffptr->Data[0], ApplCmd, Len); + + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + TNC->SwallowSignon = TRUE; + + // Save Appl Call in case needed for + + } + else + { + char Msg[] = "Application not available\r\n"; + + // Send a Message, then a disconenct + + // Send CTEXT First + + if (STREAM->BPQtoPACTOR_Q) //Used for CTEXT + { + PMSGWITHLEN buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); + UCHAR * data = &buffptr->Data[0]; + int txlen = (int)buffptr->Len; + SendARDOPorPacketData(TNC, Stream, data, txlen); + ReleaseBuffer(buffptr); + } + + SendARDOPorPacketData(TNC, Stream, Msg, (int)strlen(Msg)); + STREAM->NeedDisc = 100; // 10 secs + } + } + + STREAM->Connected = TRUE; + return len + 5; + } + + // Send to host + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr == 0) + return len + 5; // No buffers, so ignore + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", Buffer); + C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); + + // Unless Connected response close session + + STREAM->Connecting = FALSE; + + if (strstr(Buffer, "Connected")) + STREAM->Connected = TRUE; + else + if (strstr(Buffer, "Failure with")) + STREAM->ReportDISC = 10; // Gives time for failure message to display + else + if (strstr(Buffer, "Busy from")) + STREAM->ReportDISC = 10; // Gives time for failure message to display + else + if (strstr(Buffer, "Disconnected from")) + { + if (STREAM->Disconnecting) // We requested disconnect + { + STREAM->Connecting = FALSE; + STREAM->Connected = FALSE; // Back to Command Mode + STREAM->Disconnecting = FALSE; + } + else + { + STREAM->Connected = 0; + STREAM->ReportDISC = 10; + } + } + else + STREAM->NeedDisc = 10; + + return len + 5; + } + + // See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc + + if (Msg[3] < 3) // 1 or 2 - Success or Fail + { + return 0; + } + + if (Msg[3] == 3) // Status + { + struct STREAMINFO * STREAM = &TNC->Streams[Stream]; + return 0; + } + + // 1, 2, 4, 5 - pass to Appl + + return 0; + } + + if (Msg[3] == 6) + { + return 0; + } + + if (Msg[3] == 7) // Data + { + if (Stream > 0 && Stream <= APMaxStreams) + { + // Packet Response + + int len = Msg[4] + 1; + + if (TNC->Streams[Stream].Connected == 0) + return len + 5; + + buffptr = GetBuff(); + + if (buffptr == NULL) return 0; // No buffers, so ignore + + buffptr->Len = len; + memcpy((UCHAR *)&buffptr->Data[0], &Msg[5], buffptr->Len); + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return len + 5; + } + + if (Stream == 32) // Command string + { + int Len = Msg[4] + 1; + + ARDOPProcessResponse(TNC, &Msg[5], Len); + + return 0; + } + + if (Msg[2] == 0xfe) // Status Poll Response + { + return 0; + } + + if (Msg[2] == 248) // Log Message + { + // Monitor Data - Length format + // first 4 bytes contain a 32 bits long timestamp. + // That timestamp holds the number of seconds that elapsed since date 01.01.2000 at 00:00:00. + // The MS byte is sent first. The timestamp can be corrected to the usual C timestamp (seconds + //since 01.01.1970, 00:00:00) simply by adding 946684800 (seconds) to it. + // Teminated with LF + + int datalen = Msg[4] + 1; + time_t timestamp = (Msg[5] << 24) + (Msg[6] << 16) + + (Msg[7] << 8) + Msg[8] + 946684800; + char c; + char timebuf[32] = "HH:MM:SS.MMM"; + struct tm * tm; + + if (TNC->LogHandle == 0 || TNC->DebugHandle == 0) + return 0; + + tm = gmtime(×tamp); + + sprintf(timebuf, "%02d:%02d:%02d. ", + tm->tm_hour, tm->tm_min, tm->tm_sec); + + // ARDOP Messages have a millisec time in first 3 bytes + // and a log type in 4th + + c = Msg[12]; // Type + + memcpy(&timebuf[9], &Msg[9], 3); // copy millisecs + fputs(timebuf, TNC->DebugHandle); + fputc(c, TNC->DebugHandle); + fputc(' ', TNC->DebugHandle); + fwrite(&Msg[13], 1, datalen - 8, TNC->DebugHandle); + fflush(TNC->DebugHandle); + + if (c < '7') + { + // All types below debug go to log file + + fputs(timebuf, TNC->LogHandle); + fputc(c, TNC->LogHandle); + fputc(' ', TNC->LogHandle); + fwrite(&Msg[13], 1, datalen - 8, TNC->LogHandle); + fflush(TNC->LogHandle); + } + return 0; + } + + if (Msg[2] == 253) // Rig Port Response + { + // Queue for Rig Control Driver + + int datalen = Msg[4] + 1; + PMSGWITHLEN buffptr; + + // if not configured to use PTC Rig Control, Ignore + + if (TNC->RIG->PORT == NULL || TNC->RIG->PORT->PTC == NULL) + return 0; + + buffptr = (PMSGWITHLEN)GetBuff(); + + if (buffptr) + { + buffptr->Len = datalen; + memcpy(&buffptr->Data[0], &Msg[5], datalen); + C_Q_ADD(&TNC->RadiotoBPQ_Q, buffptr); + } + return 0; + } + + if (Msg[2] == 250) // KISS + { + // Pass to KISS Code + + int datalen = Msg[4] + 1; + void ** buffptr = NULL; + + ProcessKISSBytes(TNC, &Msg[5], datalen); + return datalen + 5; + } + + return 0; + } + return 0; +} + +void ARDOPSCSCheckRX(struct TNCINFO * TNC) +{ + int Length, Len = 0; + unsigned short crc; + char UnstuffBuffer[500]; + + if (TNC->RXLen == 500) + TNC->RXLen = 0; + + if (TNC->ARDOPCommsMode == 'I') + { + unsigned char Buffer[33]; + BOOL Error; + int gotThisTime = 0, i2clen; + + // i2c mode always returns as much as requested or error + // First two bytes of block are length + +// if (TNC->hDevice < 0) +// return; + + while ((TNC->RXLen + Len) < 460) + { + i2clen = ReadCOMBlockEx(TNC->hDevice, Buffer, 33, &Error); + + if (i2clen < 33 || i2clen == 5) + return; + + if (Error) + { + Debugprintf("ARDOP i2c returned %d bytes Error %d", i2clen, Error); + return; + } + gotThisTime = Buffer[0]; + + + if (gotThisTime == 0) + { + if (Len) + break; // Something to process + + return; // No More + } + +// if (gotThisTime != 7) +// Debugprintf("ARDOP i2c Len %d RXL %d %x %x %x %x %x %x %x %x %x %x %x %x", +// gotThisTime, TNC->RXLen + Len, +// Buffer[0], Buffer[1], Buffer[2], Buffer[3], +// Buffer[4], Buffer[5], Buffer[6], Buffer[7], +// Buffer[8], Buffer[9], Buffer[10], Buffer[11]); + + memcpy(&TNC->RXBuffer[TNC->RXLen + Len], &Buffer[1], gotThisTime); + + Len += gotThisTime; + + if (Buffer[0] < 32) + break; // no more + } + } + + else if (TNC->ARDOPCommsMode =='E') //Serial over TCP + Len = SerialGetTCPMessage(TNC, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + else + if (TNC->hDevice) + Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); + + if (Len == 0) + return; + + TNC->RXLen += Len; + + Length = TNC->RXLen; + + // DED mode doesn't have an end of frame delimiter. We need to know if we have a full frame + + // Fortunately this is a polled protocol, so we only get one frame at a time + + // If first char != 170, then probably a Terminal Mode Frame. Wait for CR on end + + // If first char is 170, we could check rhe length field, but that could be corrupt, as + // we haen't checked CRC. All I can think of is to check the CRC and if it is ok, assume frame is + // complete. If CRC is duff, we will eventually time out and get a retry. The retry code + // can clear the RC buffer + + if (TNC->RXBuffer[0] != 170) + { + // Char Mode Frame I think we need to see cmd: on end + + // If we think we are in host mode, then to could be noise - just discard. + + if (TNC->HostMode) + { + TNC->RXLen = 0; // Ready for next frame + return; + } + + TNC->RXBuffer[TNC->RXLen] = 0; + +// if (TNC->Streams[Stream].RXBuffer[TNC->Streams[Stream].RXLen-2] != ':') + + if (strlen(TNC->RXBuffer) < TNC->RXLen) + TNC->RXLen = 0; + + if ((strstr(TNC->RXBuffer, "cmd: ") == 0) && (strstr(TNC->RXBuffer, "pac: ") == 0)) + + return; // Wait for rest of frame + + // Complete Char Mode Frame + +// OpenLogFile(TNC->Port); +// WriteLogLine(TNC->Port, TNC->RXBuffer, (int)strlen(TNC->RXBuffer)); +// CloseLogFile(TNC->Port); + + TNC->RXLen = 0; // Ready for next frame + + if (TNC->HostMode == 0) + { + // We think TNC is in Terminal Mode + ARDOPProcessTermModeResponse(TNC); + return; + } + // We thought it was in Host Mode, but are wrong. + + TNC->HostMode = FALSE; + return; + } + + if (TNC->HostMode == FALSE) + { + TNC->RXLen = 0; // clear input and wait for char mode response + return; + } + + + // Receiving a Host Mode frame + + if (Length < 6) // Minimum Frame Sise + return; + + if (TNC->RXBuffer[2] == 170) + { + // Retransmit Request + + TNC->RXLen = 0; + return; // Ignore for now + } + + // Can't unstuff into same buffer - fails if partial msg received, and we unstuff twice + + + Length = Unstuff(&TNC->RXBuffer[2], &UnstuffBuffer[2], Length - 2); + + if (Length == -1) + { + // Unstuff returned an errors (170 not followed by 0) + + TNC->RXLen = 0; + return; // Ignore for now + } + + crc = compute_crc(&UnstuffBuffer[2], Length); + + if (crc == 0xf0b8) // Good CRC + { + TNC->RXLen = 0; // Ready for next frame + UnstuffBuffer[0] = 0; // Make sure not seen as TCP Frame + ARDOPProcessDEDFrame(TNC, UnstuffBuffer, Length); + + // If there are more channels to poll (more than 1 entry in general poll response, + // and link is not active, poll the next one + + if (TNC->Timeout == 0) + { + UCHAR * Poll = TNC->TXBuffer; + + if (TNC->NexttoPoll[0]) + { + UCHAR Chan = TNC->NexttoPoll[0] - 1; + + memmove(&TNC->NexttoPoll[0], &TNC->NexttoPoll[1], 19); + + Poll[2] = Chan; // Channel + Poll[3] = 0x1; // Command + + if (Chan == 254) // Status - Send Extended Status (G3) + { + Poll[4] = 1; // Len-1 + Poll[5] = 'G'; // Extended Status Poll + Poll[6] = '3'; + } + else + { + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + } + + ARDOPCRCStuffAndSend(TNC, Poll, Poll[4] + 6); + TNC->InternalCmd = FALSE; + + return; + } + else + { + // if last message wasn't a general poll, send one now + + if (TNC->PollSent) + return; + + TNC->PollSent = TRUE; + + // Use General Poll (255) + + Poll[2] = 255 ; // Channel + Poll[3] = 0x1; // Command + + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + + ARDOPCRCStuffAndSend(TNC, Poll, 6); + TNC->InternalCmd = FALSE; + } + } + return; + } + + // Bad CRC - assume incomplete frame, and wait for rest. If it was a full bad frame, timeout and retry will recover link. + + return; +} + +VOID ARDOPSCSPoll(struct TNCINFO * TNC) +{ + UCHAR * Poll = TNC->TXBuffer; + int Stream = 0; + + if (TNC->Timeout) + { + TNC->Timeout--; + + if (TNC->Timeout) // Still waiting + return; + + TNC->Retries--; + + if(TNC->Retries >= 0) + { + if (TNC->HostMode) + Debugprintf("ARDOP Timeout - Retransmit PTC Block"); + ARDOPWriteCommBlock(TNC); // Retransmit Block + return; + } + + // Retried out. + + if (TNC->HostMode == 0) + { + ARDOPDoTermModeTimeout(TNC); + return; + } + + // Retried out in host mode - Clear any connection and reinit the TNC + + Debugprintf("ARDOP - Link to TNC Lost"); + TNC->TNCOK = FALSE; + + sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); + SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + // Clear anything from UI_Q + + while (TNC->PortRecord->UI_Q) + { + void ** buffptr = Q_REM(&TNC->PortRecord->UI_Q); + ReleaseBuffer(buffptr); + } + + + TNC->HostMode = 0; + TNC->ReinitState = 0; + TNC->CONNECTED = FALSE; + + // Disconenct any attached sessions + + + for (Stream = 0; Stream <= APMaxStreams; Stream++) + { + if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected + { + TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode + TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node + } + } + } + + if (TNC->Timeout) + return; // We've sent something + + + // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence + + if (!TNC->HostMode) + { + ARDOPDoTNCReinit(TNC); + return; + } + + TNC->PollSent = FALSE; + + if (TNC->TNCOK && TNC->BPQtoRadio_Q) + { + int datalen; + PMSGWITHLEN buffptr; + + buffptr = (PMSGWITHLEN)Q_REM(&TNC->BPQtoRadio_Q); + datalen = (int)buffptr->Len; + Poll[2] = 253; // Radio Channel + Poll[3] = 0; // Data? + Poll[4] = datalen - 1; + memcpy(&Poll[5], &buffptr->Data[0], datalen); + + ReleaseBuffer(buffptr); + ARDOPCRCStuffAndSend(TNC, Poll, datalen + 5); + return; + } + + for (Stream = 0; Stream < 14; Stream++) // Priority to commands + { + if (TNC->TNCOK && TNC->Streams[Stream].BPQtoPACTOR_Q) + { + int datalen; + PMSGWITHLEN buffptr; + char * Buffer; + + buffptr=Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); + TNC->Streams[Stream].FramesQueued--; + + datalen = (int)buffptr->Len; + Buffer = &buffptr->Data[0]; // Data portion of frame + + Poll[3]= 0; + + if (Stream > 11) + Poll[2] = Stream + 20; // 12 and 13 to Channels 32 and 33 + else + if (Stream == 0) + Poll[2] = 33; + else + { + // Packet Frame + + Poll[2] = Stream; + Poll[3] = Buffer[0]; // First Byte is CMD/Data FLag + datalen--; + Buffer++; + } + + Poll[4] = datalen - 1; + + memcpy(&Poll[5], Buffer, datalen); + + ReleaseBuffer(buffptr); + + ARDOPCRCStuffAndSend(TNC, Poll, datalen + 5); + return; + } + } + +//0x04421CB0 aa aa 21 00 07 00 06 48 65 6c 6c 6f 0d c8 3e 38 42 50 51 2d 32 20 35 0d 4a 8a 4d 38 42 50 51 ªª!....Hello.È>8BPQ-2 5.JŠM8BPQ +//0x04421CCF 2d 31 30 2c 47 4d 38 42 50 51 2d 35 2c 47 4d 38 42 50 51 2c 47 4d 38 42 50 51 2d 31 35 0d 00 -10,GM8BPQ-5,GM8BPQ,GM8BPQ-15.. +//0x04421CEE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ............................... + + + if (TNC->TNCOK && TNC->KISSTX_Q) + { + int datalen; + PMSGWITHLEN buffptr; + + buffptr = (PMSGWITHLEN)Q_REM(&TNC->KISSTX_Q); + datalen = (int)buffptr->Len; + Poll[2] = 250; // KISS Channel + Poll[3] = 0; // Data + Poll[4] = datalen - 1; + memcpy(&Poll[5], &buffptr->Data[0], datalen); + + ReleaseBuffer(buffptr); + ARDOPCRCStuffAndSend(TNC, Poll, datalen + 5); + return; + } + + TNC->PollSent = TRUE; + + // Use General Poll (255) + + Poll[2] = 255 ; // Channel + Poll[3] = 0x1; // Command + + if (TNC->ReinitState == 3) + { + TNC->ReinitState = 0; + Poll[3] = 0x41; + } + + Poll[4] = 0; // Len-1 + Poll[5] = 'G'; // Poll + + ARDOPCRCStuffAndSend(TNC, Poll, 6); + TNC->InternalCmd = FALSE; + + return; + +} + +// ARDOP Serial over TCP Routines + +// Probably only for Teensy with ESP01. Runs SCS Emulator over a TCP Link + + +VOID SerialConnecttoTCPThread(struct TNCINFO * TNC); + +int SerialConnecttoTCP(struct TNCINFO * TNC) +{ + _beginthread(SerialConnecttoTCPThread, 0, (void *)TNC); + + return 0; +} + +VOID SerialConnecttoTCPThread(struct TNCINFO * TNC) +{ + char Msg[255]; + int i; + u_long param = 1; + BOOL bcopt=TRUE; + struct hostent * HostEnt; + SOCKADDR_IN sinx; + int addrlen=sizeof(sinx); + + if (TNC->HostName == NULL) + return; + + Sleep(5000); // Allow init to complete + + while(1) + { + if (TNC->TCPCONNECTED) + { + Sleep(57000); + continue; + } + + + TNC->BusyFlags = 0; + + TNC->CONNECTING = TRUE; + + TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName); + + if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE) + { + // Resolve name to address + + HostEnt = gethostbyname (TNC->HostName); + + if (!HostEnt) + { + TNC->CONNECTING = FALSE; + Sleep (57000); + return; // Resolve failed + } + memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4); + memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4); + } + + if (TNC->TCPSock) + { + Debugprintf("ARDOP Closing Sock %d", TNC->TCPSock); + closesocket(TNC->TCPSock); + } + + TNC->TCPSock = 0; + TNC->TCPSock = socket(AF_INET,SOCK_STREAM,0); + + if (TNC->TCPSock == INVALID_SOCKET) + { + i=sprintf(Msg, "Socket Failed for ARDOP socket - error code = %d\r\n", WSAGetLastError()); + WritetoConsole(Msg); + + TNC->CONNECTING = FALSE; + return; + } + + setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4); +// setsockopt(TNC->TCPDataSock, IPPROTO_TCP, TCP_NODELAY, (const char FAR *)&bcopt, 4); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = 0; + + if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) + { + // + // Connected successful + + TNC->TCPCONNECTED = TRUE; + ioctl(TNC->TCPSock, FIONBIO, ¶m); + Debugprintf("ARDOP TCPSerial connected, Socket %d", TNC->TCPSock); + } + else + { + if (TNC->Alerted == FALSE) + { + sprintf(Msg, "Connect Failed for ARDOP socket - error code = %d Port %d\n", + WSAGetLastError(), htons(TNC->destaddr.sin_port)); + + WritetoConsole(Msg); + sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed"); + MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); + + TNC->Alerted = TRUE; + } + + closesocket(TNC->TCPSock); + + TNC->TCPSock = 0; + TNC->CONNECTING = FALSE; + Sleep (57000); // 1 Mins + } + } +} + +int SerialGetTCPMessage(struct TNCINFO * TNC, unsigned char * Buffer, int Len) +{ + int index=0; + ULONG param = 1; + + if (TNC->TCPCONNECTED) + { + int InputLen; + + // Poll TCP COnnection for data + + // May have several messages per packet, or message split over packets + + InputLen = recv(TNC->TCPSock, Buffer, Len, 0); + + if (InputLen < 0) + { + int err = WSAGetLastError(); + + if (err == 10035 || err == 11) + { + InputLen = 0; + return 0; + } + Debugprintf("ARDOP Serial TCP RX Error %d received for socket %d", err, TNC->TCPSock); + + TNC->TCPCONNECTED = 0; + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + return 0; + } + + if (InputLen > 0) + return InputLen; + else + { + Debugprintf("ARDOP Serial TCP Close received for socket %d", TNC->TCPSock); + + TNC->TCPCONNECTED = 0; + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + return 0; + } + } + + return 0; +} + +int ARDOPWriteCommBlock(struct TNCINFO * TNC) +{ + if (TNC->ARDOPCommsMode == 'E') + { + if (TNC->TCPCONNECTED) + { + int SentLen = send(TNC->TCPSock, TNC->TXBuffer, TNC->TXLen, 0); + + if (SentLen != TNC->TXLen) + { + // ARDOP doesn't seem to recover from a blocked write. For now just reset + + int winerr=WSAGetLastError(); + char ErrMsg[80]; + + sprintf(ErrMsg, "ARDOP Write Failed for port %d - error code = %d\r\n", TNC->Port, winerr); + WritetoConsole(ErrMsg); + + closesocket(TNC->TCPSock); + TNC->TCPSock = 0; + TNC->TCPCONNECTED = FALSE; + } + } + TNC->Timeout = 20; // 2 secs + return 1; + } + if (TNC->hDevice) + return (WriteCommBlock(TNC)); + else + { + TNC->Timeout = 20; // 2 secs + return 0; + } +} + +// Teensy Combined ARDOP/AX.25 Support + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD +/* + +VOID ARAXINIT(struct PORTCONTROL * PORT) +{ + char Msg[80] = ""; + + memcpy(Msg, PORT->PORTDESCRIPTION, 30); + strcat(Msg, "\n); + + WritetoConsoleLocal(Msg); +} + +VOID ARAXTX(struct PORTCONTROL * PORT, PMESSAGE Buffer) +{ + // KISS Encode and Queue to Host Mode KISS Queue + + UINT * TXMsg = NULL; // KISS Message to queue to Hostmode KISS Queue + UCHAR * TXPtr; + int TXLen = 0; + struct _LINKTABLE * ACKWORD = Buffer->Linkptr; + struct _MESSAGE * Message = (struct _MESSAGE *)Buffer; + UCHAR c; + + char * ptr1; + int Len; + + struct TNCINFO * TNC = PORT->TNC; + + if (TNC && TNC->CONNECTED) // Have a Host Session + TXMsg = GetBuff(); // KISS Message to queue to Hostmode KISS Queue + + if (TXMsg == NULL) // No Session or No buffers + { + // Reset any ACKMODE Timer and release buffer C_Q_ADD(&TRACE_Q, Buffer); + + struct _LINKTABLE * LINK = Buffer->Linkptr; + + if (LINK) + { + if (LINK->L2TIMER) + LINK->L2TIMER = LINK->L2TIME; + + Buffer->Linkptr = 0; // CLEAR FLAG FROM BUFFER + } + C_Q_ADD(&TRACE_Q, Buffer); + return; + } + + TXPtr = (UCHAR *)&TXMsg[2]; + + ptr1 = &Message->DEST[0]; + Len = Message->LENGTH - 7; + *(TXPtr++) = FEND; + + if (ACKWORD) // Frame Needs ACK + { + *TXPtr++ = 0x0c; // ACK OPCODE + ACKWORD -= (UINT)LINKS; // Con only send 16 bits, so use offset into LINKS + *TXPtr++ = ACKWORD & 0xff; + *TXPtr++ = (ACKWORD >> 8) &0xff; + + // have to reset flag so trace doesnt clear it + + Buffer->Linkptr = 0; + TXLen = 4;struct _LINKTABLE * + } + else + { + *TXPtr++ = 0; + TXLen = 2; + } + + while (Len--) + { + c = *(ptr1++); + + switch (c) + { + case FEND: + (*TXPtr++) = FESC; + (*TXPtr++) = TFEND; + TXLen += 2; + break; + + case FESC: + (*TXPtr++) = FESC; + (*TXPtr++) = TFESC; + TXLen += 2; + break; + + default: + (*TXPtr++) = c; + TXLen++; + } + if (TXLen > 250) + { + // Queue frame to KISS Channel and get another buffer + // can take up to 256, but sometimes add 2 at a time + TXMsg[1] = (int)(TXPtr - (UCHAR *)&TXMsg[2]); + } + } + + (*TXPtr++) = FEND; + TXLen++; + + TXMsg[1] = TXLen; + + C_Q_ADD(&TNC->KISSTX_Q, TXMsg); + + // Pass buffer to trace routines + + C_Q_ADD(&TRACE_Q, Buffer); + +} + + +VOID ARAXRX() +{ +} + + +VOID ARAXTIMER() +{ +} + +VOID ARAXCLOSE() +{ +} + +BOOL ARAXTXCHECK() +{ + return 0; +} + + +#define DATABYTES 400000 // WAS 320000 +extern UCHAR * NEXTFREEDATA; // ADDRESS OF NEXT FREE BYTE in shared memory +extern UCHAR DATAAREA[DATABYTES]; + + + +VOID AddVirtualKISSPort(struct TNCINFO * TNC, int ARDOPPort, char * buf) +{ + // Adds a Virtual KISS port for simultaneous ARDOP and Packet on Teensy TNC + // or ARDOP_PTC ovet a single host mode port. + + // Not needed if using TCP interface as that uses KISS over TCP + + struct PORTCONTROL * PORTVEC=PORTTABLE; + struct PORTCONTROL * PORT; + int pl = sizeof(struct PORTCONTROL); + int mh = MHENTRIES * sizeof(MHSTRUC); + int space = (int)(&DATAAREA[DATABYTES] - NEXTFREEDATA); + char Msg[64]; + unsigned char * ptr3; + unsigned int3; + int newPortNumber = 0; + + if (TNC->ARDOPCommsMode == 'T') // TCP + return; + + if (buf[12] == '=') + newPortNumber = atoi(&buf[13]); + + if (space < (pl + mh)) + { + WritetoConsoleLocal("Insufficient space to add ARDOP/Packet Port\n"); + return; + } + + + while (PORTVEC->PORTPOINTER) + { + PORTVEC=PORTVEC->PORTPOINTER; + } + + // PORTVEC is now last port in chain + + ptr3 = NEXTFREEDATA; + + PORT = (struct PORTCONTROL *)ptr3; + + ptr3 += sizeof (struct PORTCONTROL); + + // Round to word boundary (for ARM5 etc) + + int3 = (int)ptr3; + int3 += 3; + int3 &= 0xfffffffc; + ptr3 = (UCHAR *)int3; + + PORTVEC->PORTPOINTER = PORT; // Chain to previous last port + + if (newPortNumber == 0) + newPortNumber = 32;; + + if (GetPortTableEntryFromPortNum(newPortNumber)) + { + // Number in use + + // If user specified search up, if default search down + + if (newPortNumber == 32) + while(newPortNumber && GetPortTableEntryFromPortNum(newPortNumber)) // Try next lower + newPortNumber--; + else + while(newPortNumber < 32 && GetPortTableEntryFromPortNum(newPortNumber)) // Try next highest + newPortNumber++; + + } + + if (newPortNumber == 0 || newPortNumber > 32) + { + WritetoConsoleLocal("No free Port Number to add ARDOP/Packet Port\n"); + return; + } + + + + NUMBEROFPORTS++; + + PORT->PORTNUMBER = newPortNumber; + PORT->PortSlot = PORTVEC->PortSlot + 1; + + sprintf(Msg, "Packet Port for ARDOP Port %d ", ARDOPPort); + memcpy(PORT->PORTDESCRIPTION, Msg, 30); + + PORT->TNC = TNC; + TNC->VirtualPORT = PORT; // Link TNC and PORT both ways + PORT->PORTINITCODE = ARAXINIT; + PORT->PORTTIMERCODE = ARAXTIMER; + PORT->PORTRXROUTINE = ARAXRX; + PORT->PORTTXROUTINE = ARAXTX; + PORT->PORTCLOSECODE = ARAXCLOSE; + PORT->PORTTXCHECKCODE = ARAXTXCHECK; + + // Default L2 Params + + PORT->PORTN2 = 5; + PORT->PORTT1 = 5000/333; // FRACK 5 secs + + // As we use IPoll we can set RESPTIME very long and FRACK short + + PORT->PORTT2 = 20000/333; // RESPTIME + PORT->PORTPACLEN = 128; + PORT->PORTWINDOW = 1; + + // ADD MH AREA IF NEEDED + + NEEDMH = 1; // Include MH in Command List + + PORT->PORTMHEARD = (PMHSTRUC)ptr3; + + ptr3 += MHENTRIES * sizeof(MHSTRUC); + + // Round to word boundary (for ARM5 etc) + + int3 = (int)ptr3; + int3 += 3; + int3 &= 0xfffffffc; + ptr3 = (UCHAR *)int3; + + NEXTFREEDATA = ptr3; + + return; +} + +int ConfigVirtualKISSPort(struct TNCINFO * TNC, char * Cmd) +{ + struct PORTCONTROL * PORT = TNC->VirtualPORT; + void ** buffptr = GetBuff(); + char * Context; + char * Param; + char * Command; + int Value; + int Stream = 0; + if (buffptr == NULL) + return 0; + + if (PORT == NULL) + { + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Packet Mode nor Enabled\r"); + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + + _strupr(Cmd); + + Command = strtok_s(&Cmd[4], " \n\r", &Context); + Param = strtok_s(NULL, " \n\r", &Context); + + if (Param) + Value = atoi(Param); + + if (strcmp(Command, "PACLEN") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d\r", Command, PORT->PORTPACLEN); + else + { + if (Value > 0 && Value <= 256) + PORT->PORTPACLEN = Value; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d\r", Command, PORT->PORTPACLEN); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else if (strcmp(Command, "RETRIES") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d\r", Command, PORT->PORTN2); + else + { + if (Value > 0 && Value <= 16) + PORT->PORTN2 = Value; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d\r", Command, PORT->PORTN2); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else if (strcmp(Command, "WINDOW") == 0 || strcmp(Command, "MAXFRAME") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d\r", Command, PORT->PORTWINDOW); + else + { + if (Value > 0 && Value <= 7) + PORT->PORTWINDOW = Value; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d\r", Command, PORT->PORTWINDOW); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else if (strcmp(Command, "FRACK") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d mS\r", Command, PORT->PORTT1 * 333); + else + { + if (Value > 0 && Value <= 20000) + PORT->PORTT1 = Value / 333; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d mS\r", Command, PORT->PORTT1 * 333); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else if (strcmp(Command, "RESPTIME") == 0) + { + if (Param == NULL) + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s %d mS\r", Command, PORT->PORTT2 * 333); + else + { + if (Value > 0 && Value <= 20000) + PORT->PORTT2 = Value / 333; + + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} PAC %s now %d mS\r", Command, PORT->PORTT2 * 333); + } + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + } + else + buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "ARDOP} Invalid Command %s\r", Cmd); + + + C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); + return 0; + + +} +*/ +void ProcessKISSBytes(struct TNCINFO * TNC, UCHAR * Data, int Len) +{ + // Kiss data received from TNC but not necessarrily a full packet + // and could be multiple packets + + // The TNC record is for the ARDOP Port, but we need to queue data + // to the linked Virtual Packet Port + + struct PORTCONTROL * PORT = TNC->VirtualPORT; + UCHAR * KISSBuffer = TNC->KISSBuffer; + UCHAR c; + UCHAR * inptr = Data; + int outptr = TNC->KISSInputLen; + + if (PORT == NULL) + return; + + while(Len--) + { + c = *(inptr++); + + if (TNC->ESCFLAG) + { + // + // FESC received - next should be TFESC or TFEND + + TNC->ESCFLAG = FALSE; + + if (c == TFESC) + c = FESC; + + if (c == TFEND) + c = FEND; + } + else + { + switch (c) + { + case FEND: + + // + // Either start of message or message complete + // + + if (outptr == 0) + { + // Start of Message. If polling, extend timeout + + continue; + } + + ProcessKISSPacket(TNC, KISSBuffer, outptr); + outptr = 0; + return; + + case FESC: + + TNC->ESCFLAG = TRUE; + continue; + + } + } + + // + // Ok, a normal char + // + + KISSBuffer[outptr++] = c; + } + + if (outptr > 510) + outptr = 0; // Protect Buffer + + TNC->KISSInputLen = outptr; + + return; +} + +void ProcessKISSPacket(struct TNCINFO * TNC, UCHAR * KISSBuffer, int Len) +{ + if (KISSBuffer[0] == 0x0c) // ACK Frame + { + // ACK FRAME - reset link timer + + struct _LINKTABLE * LINK; + UINT ACKWORD = KISSBuffer[1] | KISSBuffer[2] << 8; + + LINK = LINKS + ACKWORD; + + if (LINK->L2TIMER) + LINK->L2TIMER = LINK->L2TIME; + + return; + } + if (KISSBuffer[0] == 0) // Data Frame + { + PDATAMESSAGE Buffer = (PDATAMESSAGE)GetBuff(); + + if (Buffer) + { + memcpy(&Buffer->PID, &KISSBuffer[1], --Len); + Len += sizeof(void *) + 3; + + PutLengthinBuffer(Buffer, Len); + + C_Q_ADD(&TNC->VirtualPORT->PORTRX_Q, (UINT *)Buffer); + } + } +} diff --git a/.svn/pristine/73/739942e106607242bc5698281e342ba20074e7c1.svn-base b/.svn/pristine/73/739942e106607242bc5698281e342ba20074e7c1.svn-base new file mode 100644 index 0000000..e11af04 --- /dev/null +++ b/.svn/pristine/73/739942e106607242bc5698281e342ba20074e7c1.svn-base @@ -0,0 +1,2383 @@ +/******************************************************************************* + * Copyright (c) 2009, 2023 IBM Corp., Ian Craggs and others + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * https://www.eclipse.org/legal/epl-2.0/ + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation + * Ian Craggs, Allan Stockdill-Mander - SSL connections + * Ian Craggs - multiple server connection support + * Ian Craggs - MQTT 3.1.1 support + * Ian Craggs - fix for bug 444103 - success/failure callbacks not invoked + * Ian Craggs - automatic reconnect and offline buffering (send while disconnected) + * Ian Craggs - binary will message + * Ian Craggs - binary password + * Ian Craggs - remove const on eyecatchers #168 + * Ian Craggs - MQTT 5.0 + *******************************************************************************/ + +/********************************************************************/ + +/** + * @cond MQTTAsync_main + * @mainpage Asynchronous MQTT client library for C (MQTTAsync) + * + * © Copyright 2009, 2023 IBM Corp., Ian Craggs and others + * + * @brief An Asynchronous MQTT client library for C. + * + * An MQTT client application connects to MQTT-capable servers. + * A typical client is responsible for collecting information from a telemetry + * device and publishing the information to the server. It can also subscribe + * to topics, receive messages, and use this information to control the + * telemetry device. + * + * MQTT clients implement the published MQTT v3 protocol. You can write your own + * API to the MQTT protocol using the programming language and platform of your + * choice. This can be time-consuming and error-prone. + * + * To simplify writing MQTT client applications, this library encapsulates + * the MQTT v3 protocol for you. Using this library enables a fully functional + * MQTT client application to be written in a few lines of code. + * The information presented here documents the API provided + * by the Asynchronous MQTT Client library for C. + * + * Using the client
+ * Applications that use the client library typically use a similar structure: + *
    + *
  • Create a client object
  • + *
  • Set the options to connect to an MQTT server
  • + *
  • Set up callback functions
  • + *
  • Connect the client to an MQTT server
  • + *
  • Subscribe to any topics the client needs to receive
  • + *
  • Repeat until finished:
  • + *
      + *
    • Publish any messages the client needs to
    • + *
    • Handle any incoming messages
    • + *
    + *
  • Disconnect the client
  • + *
  • Free any memory being used by the client
  • + *
+ * Some simple examples are shown here: + *
    + *
  • @ref publish
  • + *
  • @ref subscribe
  • + *
+ * Additional information about important concepts is provided here: + *
    + *
  • @ref async
  • + *
  • @ref wildcard
  • + *
  • @ref qos
  • + *
  • @ref tracing
  • + *
  • @ref auto_reconnect
  • + *
  • @ref offline_publish
  • + *
+ * @endcond + */ + +/* +/// @cond EXCLUDE +*/ +#if !defined(MQTTASYNC_H) +#define MQTTASYNC_H + +#if defined(__cplusplus) + extern "C" { +#endif + +#include +/* +/// @endcond +*/ + +#include "MQTTExportDeclarations.h" + +#include "MQTTProperties.h" +#include "MQTTReasonCodes.h" +#include "MQTTSubscribeOpts.h" +#if !defined(NO_PERSISTENCE) +#include "MQTTClientPersistence.h" +#endif + +/** + * Return code: No error. Indicates successful completion of an MQTT client + * operation. + */ +#define MQTTASYNC_SUCCESS 0 +/** + * Return code: A generic error code indicating the failure of an MQTT client + * operation. + */ +#define MQTTASYNC_FAILURE -1 + +/* error code -2 is MQTTAsync_PERSISTENCE_ERROR */ + +#define MQTTASYNC_PERSISTENCE_ERROR -2 + +/** + * Return code: The client is disconnected. + */ +#define MQTTASYNC_DISCONNECTED -3 +/** + * Return code: The maximum number of messages allowed to be simultaneously + * in-flight has been reached. + */ +#define MQTTASYNC_MAX_MESSAGES_INFLIGHT -4 +/** + * Return code: An invalid UTF-8 string has been detected. + */ +#define MQTTASYNC_BAD_UTF8_STRING -5 +/** + * Return code: A NULL parameter has been supplied when this is invalid. + */ +#define MQTTASYNC_NULL_PARAMETER -6 +/** + * Return code: The topic has been truncated (the topic string includes + * embedded NULL characters). String functions will not access the full topic. + * Use the topic length value to access the full topic. + */ +#define MQTTASYNC_TOPICNAME_TRUNCATED -7 +/** + * Return code: A structure parameter does not have the correct eyecatcher + * and version number. + */ +#define MQTTASYNC_BAD_STRUCTURE -8 +/** + * Return code: A qos parameter is not 0, 1 or 2 + */ +#define MQTTASYNC_BAD_QOS -9 +/** + * Return code: All 65535 MQTT msgids are being used + */ +#define MQTTASYNC_NO_MORE_MSGIDS -10 +/** + * Return code: the request is being discarded when not complete + */ +#define MQTTASYNC_OPERATION_INCOMPLETE -11 +/** + * Return code: no more messages can be buffered + */ +#define MQTTASYNC_MAX_BUFFERED_MESSAGES -12 +/** + * Return code: Attempting SSL connection using non-SSL version of library + */ +#define MQTTASYNC_SSL_NOT_SUPPORTED -13 +/** + * Return code: protocol prefix in serverURI should be: + * @li @em tcp:// or @em mqtt:// - Insecure TCP + * @li @em ssl:// or @em mqtts:// - Encrypted SSL/TLS + * @li @em ws:// - Insecure websockets + * @li @em wss:// - Secure web sockets + * + * The TLS enabled prefixes (ssl, mqtts, wss) are only valid if the TLS + * version of the library is linked with. + */ +#define MQTTASYNC_BAD_PROTOCOL -14 +/** + * Return code: don't use options for another version of MQTT + */ +#define MQTTASYNC_BAD_MQTT_OPTION -15 +/** + * Return code: call not applicable to the client's version of MQTT + */ +#define MQTTASYNC_WRONG_MQTT_VERSION -16 +/** + * Return code: 0 length will topic + */ +#define MQTTASYNC_0_LEN_WILL_TOPIC -17 +/* + * Return code: connect or disconnect command ignored because there is already a connect or disconnect + * command at the head of the list waiting to be processed. Use the onSuccess/onFailure callbacks to wait + * for the previous connect or disconnect command to be complete. + */ +#define MQTTASYNC_COMMAND_IGNORED -18 + /* + * Return code: maxBufferedMessages in the connect options must be >= 0 + */ + #define MQTTASYNC_MAX_BUFFERED -19 + +/** + * Default MQTT version to connect with. Use 3.1.1 then fall back to 3.1 + */ +#define MQTTVERSION_DEFAULT 0 +/** + * MQTT version to connect with: 3.1 + */ +#define MQTTVERSION_3_1 3 +/** + * MQTT version to connect with: 3.1.1 + */ +#define MQTTVERSION_3_1_1 4 +/** + * MQTT version to connect with: 5 + */ +#define MQTTVERSION_5 5 +/** + * Bad return code from subscribe, as defined in the 3.1.1 specification + */ +#define MQTT_BAD_SUBSCRIBE 0x80 + + +/** + * Initialization options + */ +typedef struct +{ + /** The eyecatcher for this structure. Must be MQTG. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 */ + int struct_version; + /** 1 = we do openssl init, 0 = leave it to the application */ + int do_openssl_init; +} MQTTAsync_init_options; + +#define MQTTAsync_init_options_initializer { {'M', 'Q', 'T', 'G'}, 0, 0 } + +/** + * Global init of mqtt library. Call once on program start to set global behaviour. + * handle_openssl_init - if mqtt library should handle openssl init (1) or rely on the caller to init it before using mqtt (0) + */ +LIBMQTT_API void MQTTAsync_global_init(MQTTAsync_init_options* inits); + +/** + * A handle representing an MQTT client. A valid client handle is available + * following a successful call to MQTTAsync_create(). + */ +typedef void* MQTTAsync; +/** + * A value representing an MQTT message. A token is returned to the + * client application when a message is published. The token can then be used to + * check that the message was successfully delivered to its destination (see + * MQTTAsync_publish(), + * MQTTAsync_publishMessage(), + * MQTTAsync_deliveryComplete(), and + * MQTTAsync_getPendingTokens()). + */ +typedef int MQTTAsync_token; + +/** + * A structure representing the payload and attributes of an MQTT message. The + * message topic is not part of this structure (see MQTTAsync_publishMessage(), + * MQTTAsync_publish(), MQTTAsync_receive(), MQTTAsync_freeMessage() + * and MQTTAsync_messageArrived()). + */ +typedef struct +{ + /** The eyecatcher for this structure. must be MQTM. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 or 1. + * 0 indicates no message properties */ + int struct_version; + /** The length of the MQTT message payload in bytes. */ + int payloadlen; + /** A pointer to the payload of the MQTT message. */ + void* payload; + /** + * The quality of service (QoS) assigned to the message. + * There are three levels of QoS: + *
+ *
QoS0
+ *
Fire and forget - the message may not be delivered
+ *
QoS1
+ *
At least once - the message will be delivered, but may be + * delivered more than once in some circumstances.
+ *
QoS2
+ *
Once and one only - the message will be delivered exactly once.
+ *
+ */ + int qos; + /** + * The retained flag serves two purposes depending on whether the message + * it is associated with is being published or received. + * + * retained = true
+ * For messages being published, a true setting indicates that the MQTT + * server should retain a copy of the message. The message will then be + * transmitted to new subscribers to a topic that matches the message topic. + * For subscribers registering a new subscription, the flag being true + * indicates that the received message is not a new one, but one that has + * been retained by the MQTT server. + * + * retained = false
+ * For publishers, this indicates that this message should not be retained + * by the MQTT server. For subscribers, a false setting indicates this is + * a normal message, received as a result of it being published to the + * server. + */ + int retained; + /** + * The dup flag indicates whether or not this message is a duplicate. + * It is only meaningful when receiving QoS1 messages. When true, the + * client application should take appropriate action to deal with the + * duplicate message. This is an output parameter only. + */ + int dup; + /** The message identifier is reserved for internal use by the + * MQTT client and server. It is an output parameter only - writing + * to it will serve no purpose. It contains the MQTT message id of + * an incoming publish message. + */ + int msgid; + /** + * The MQTT V5 properties associated with the message. + */ + MQTTProperties properties; +} MQTTAsync_message; + +#define MQTTAsync_message_initializer { {'M', 'Q', 'T', 'M'}, 1, 0, NULL, 0, 0, 0, 0, MQTTProperties_initializer } + +/** + * This is a callback function. The client application + * must provide an implementation of this function to enable asynchronous + * receipt of messages. The function is registered with the client library by + * passing it as an argument to MQTTAsync_setCallbacks(). It is + * called by the client library when a new message that matches a client + * subscription has been received from the server. This function is executed on + * a separate thread to the one on which the client application is running. + * + * Note: Neither MQTTAsync_create() nor MQTTAsync_destroy() should be + * called within this callback. + * @param context A pointer to the context value originally passed to + * MQTTAsync_setCallbacks(), which contains any application-specific context. + * @param topicName The topic associated with the received message. + * @param topicLen The length of the topic if there are one + * more NULL characters embedded in topicName, otherwise topicLen + * is 0. If topicLen is 0, the value returned by strlen(topicName) + * can be trusted. If topicLen is greater than 0, the full topic name + * can be retrieved by accessing topicName as a byte array of length + * topicLen. + * @param message The MQTTAsync_message structure for the received message. + * This structure contains the message payload and attributes. + * @return This function must return 0 or 1 indicating whether or not + * the message has been safely received by the client application.
+ * Returning 1 indicates that the message has been successfully handled. + * To free the message storage, ::MQTTAsync_freeMessage must be called. + * To free the topic name storage, ::MQTTAsync_free must be called.
+ * Returning 0 indicates that there was a problem. In this + * case, the client library will reinvoke MQTTAsync_messageArrived() to + * attempt to deliver the message to the application again. + * Do not free the message and topic storage when returning 0, otherwise + * the redelivery will fail. + */ +typedef int MQTTAsync_messageArrived(void* context, char* topicName, int topicLen, MQTTAsync_message* message); + +/** + * This is a callback function. The client application + * must provide an implementation of this function to enable asynchronous + * notification of delivery of messages to the server. The function is + * registered with the client library by passing it as an argument to MQTTAsync_setCallbacks(). + * It is called by the client library after the client application has + * published a message to the server. It indicates that the necessary + * handshaking and acknowledgements for the requested quality of service (see + * MQTTAsync_message.qos) have been completed. This function is executed on a + * separate thread to the one on which the client application is running. + * + * Note: Neither MQTTAsync_create() nor MQTTAsync_destroy() should be + * called within this callback. + * @param context A pointer to the context value originally passed to + * MQTTAsync_setCallbacks(), which contains any application-specific context. + * @param token The ::MQTTAsync_token associated with + * the published message. Applications can check that all messages have been + * correctly published by matching the tokens returned from calls to + * MQTTAsync_send() and MQTTAsync_sendMessage() with the tokens passed + * to this callback. + */ +typedef void MQTTAsync_deliveryComplete(void* context, MQTTAsync_token token); + +/** + * This is a callback function. The client application + * must provide an implementation of this function to enable asynchronous + * notification of the loss of connection to the server. The function is + * registered with the client library by passing it as an argument to + * MQTTAsync_setCallbacks(). It is called by the client library if the client + * loses its connection to the server. The client application must take + * appropriate action, such as trying to reconnect or reporting the problem. + * This function is executed on a separate thread to the one on which the + * client application is running. + * + * Note: Neither MQTTAsync_create() nor MQTTAsync_destroy() should be + * called within this callback. + * @param context A pointer to the context value originally passed to + * MQTTAsync_setCallbacks(), which contains any application-specific context. + * @param cause The reason for the disconnection. + * Currently, cause is always set to NULL. + */ +typedef void MQTTAsync_connectionLost(void* context, char* cause); + + +/** + * This is a callback function, which will be called when the client + * library successfully connects. This is superfluous when the connection + * is made in response to a MQTTAsync_connect call, because the onSuccess + * callback can be used. It is intended for use when automatic reconnect + * is enabled, so that when a reconnection attempt succeeds in the background, + * the application is notified and can take any required actions. + * + * Note: Neither MQTTAsync_create() nor MQTTAsync_destroy() should be + * called within this callback. + * @param context A pointer to the context value originally passed to + * MQTTAsync_setCallbacks(), which contains any application-specific context. + * @param cause The reason for the disconnection. + * Currently, cause is always set to NULL. + */ +typedef void MQTTAsync_connected(void* context, char* cause); + +/** + * This is a callback function, which will be called when the client + * library receives a disconnect packet from the server. This applies to MQTT V5 and above only. + * + * Note: Neither MQTTAsync_create() nor MQTTAsync_destroy() should be + * called within this callback. + * @param context A pointer to the context value originally passed to + * MQTTAsync_setCallbacks(), which contains any application-specific context. + * @param properties the properties in the disconnect packet. + * @param properties the reason code from the disconnect packet + * Currently, cause is always set to NULL. + */ +typedef void MQTTAsync_disconnected(void* context, MQTTProperties* properties, + enum MQTTReasonCodes reasonCode); + +/** + * Sets the MQTTAsync_disconnected() callback function for a client. + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * + * Note: Neither MQTTAsync_create() nor MQTTAsync_destroy() should be + * called within this callback. + * @param context A pointer to any application-specific context. The + * the context pointer is passed to each of the callback functions to + * provide access to the context information in the callback. + * @param co A pointer to an MQTTAsync_connected() callback + * function. NULL removes the callback setting. + * @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set, + * ::MQTTASYNC_FAILURE if an error occurred. + */ +LIBMQTT_API int MQTTAsync_setDisconnected(MQTTAsync handle, void* context, MQTTAsync_disconnected* co); + +/** The connect options that can be updated before an automatic reconnect. */ +typedef struct +{ + /** The eyecatcher for this structure. Will be MQCD. */ + char struct_id[4]; + /** The version number of this structure. Will be 0 */ + int struct_version; + /** + * MQTT servers that support the MQTT v3.1 protocol provide authentication + * and authorisation by user name and password. This is the user name parameter. + * Set data to NULL to remove. To change, allocate new + * storage with ::MQTTAsync_allocate - this will then be free later by the library. + */ + const char* username; + /** + * The password parameter of the MQTT authentication. + * Set data to NULL to remove. To change, allocate new + * storage with ::MQTTAsync_allocate - this will then be free later by the library. + */ + struct { + int len; /**< binary password length */ + const void* data; /**< binary password data */ + } binarypwd; +} MQTTAsync_connectData; + +#define MQTTAsync_connectData_initializer {{'M', 'Q', 'C', 'D'}, 0, NULL, {0, NULL}} + +/** + * This is a callback function which will allow the client application to update the + * connection data. + * @param data The connection data which can be modified by the application. + * @return Return a non-zero value to update the connect data, zero to keep the same data. + */ +typedef int MQTTAsync_updateConnectOptions(void* context, MQTTAsync_connectData* data); + +/** + * Sets the MQTTAsync_updateConnectOptions() callback function for a client. + * @param handle A valid client handle from a successful call to MQTTAsync_create(). + * @param context A pointer to any application-specific context. The + * the context pointer is passed to each of the callback functions to + * provide access to the context information in the callback. + * @param co A pointer to an MQTTAsync_updateConnectOptions() callback + * function. NULL removes the callback setting. + */ +LIBMQTT_API int MQTTAsync_setUpdateConnectOptions(MQTTAsync handle, void* context, MQTTAsync_updateConnectOptions* co); + +/** + * Sets the MQTTPersistence_beforeWrite() callback function for a client. + * @param handle A valid client handle from a successful call to MQTTAsync_create(). + * @param context A pointer to any application-specific context. The + * the context pointer is passed to the callback function to + * provide access to the context information in the callback. + * @param co A pointer to an MQTTPersistence_beforeWrite() callback + * function. NULL removes the callback setting. + */ +LIBMQTT_API int MQTTAsync_setBeforePersistenceWrite(MQTTAsync handle, void* context, MQTTPersistence_beforeWrite* co); + + +/** + * Sets the MQTTPersistence_afterRead() callback function for a client. + * @param handle A valid client handle from a successful call to MQTTAsync_create(). + * @param context A pointer to any application-specific context. The + * the context pointer is passed to the callback function to + * provide access to the context information in the callback. + * @param co A pointer to an MQTTPersistence_beforeWrite() callback + * function. NULL removes the callback setting. + */ +LIBMQTT_API int MQTTAsync_setAfterPersistenceRead(MQTTAsync handle, void* context, MQTTPersistence_afterRead* co); + + +/** The data returned on completion of an unsuccessful API call in the response callback onFailure. */ +typedef struct +{ + /** A token identifying the failed request. */ + MQTTAsync_token token; + /** A numeric code identifying the error. */ + int code; + /** Optional text explaining the error. Can be NULL. */ + const char *message; +} MQTTAsync_failureData; + + +/** The data returned on completion of an unsuccessful API call in the response callback onFailure. */ +typedef struct +{ + /** The eyecatcher for this structure. Will be MQFD. */ + char struct_id[4]; + /** The version number of this structure. Will be 0 */ + int struct_version; + /** A token identifying the failed request. */ + MQTTAsync_token token; + /** The MQTT reason code returned. */ + enum MQTTReasonCodes reasonCode; + /** The MQTT properties on the ack, if any. */ + MQTTProperties properties; + /** A numeric code identifying the MQTT client library error. */ + int code; + /** Optional further text explaining the error. Can be NULL. */ + const char *message; + /** Packet type on which the failure occurred - used for publish QoS 1/2 exchanges*/ + int packet_type; +} MQTTAsync_failureData5; + +#define MQTTAsync_failureData5_initializer {{'M', 'Q', 'F', 'D'}, 0, 0, MQTTREASONCODE_SUCCESS, MQTTProperties_initializer, 0, NULL, 0} + +/** The data returned on completion of a successful API call in the response callback onSuccess. */ +typedef struct +{ + /** A token identifying the successful request. Can be used to refer to the request later. */ + MQTTAsync_token token; + /** A union of the different values that can be returned for subscribe, unsubscribe and publish. */ + union + { + /** For subscribe, the granted QoS of the subscription returned by the server. + * Also for subscribeMany, if only 1 subscription was requested. */ + int qos; + /** For subscribeMany, if more than one subscription was requested, + * the list of granted QoSs of the subscriptions returned by the server. */ + int* qosList; + /** For publish, the message being sent to the server. */ + struct + { + MQTTAsync_message message; /**< the message being sent to the server */ + char* destinationName; /**< the topic destination for the message */ + } pub; + /* For connect, the server connected to, MQTT version used, and sessionPresent flag */ + struct + { + char* serverURI; /**< the connection string of the server */ + int MQTTVersion; /**< the version of MQTT being used */ + int sessionPresent; /**< the session present flag returned from the server */ + } connect; + } alt; +} MQTTAsync_successData; + + +/** The data returned on completion of a successful API call in the response callback onSuccess. */ +typedef struct +{ + char struct_id[4]; /**< The eyecatcher for this structure. Will be MQSD. */ + int struct_version; /**< The version number of this structure. Will be 0 */ + /** A token identifying the successful request. Can be used to refer to the request later. */ + MQTTAsync_token token; + enum MQTTReasonCodes reasonCode; /**< MQTT V5 reason code returned */ + MQTTProperties properties; /**< MQTT V5 properties returned, if any */ + /** A union of the different values that can be returned for subscribe, unsubscribe and publish. */ + union + { + /** For subscribeMany, the list of reasonCodes returned by the server. */ + struct + { + int reasonCodeCount; /**< the number of reason codes in the reasonCodes array */ + enum MQTTReasonCodes* reasonCodes; /**< an array of reasonCodes */ + } sub; + /** For publish, the message being sent to the server. */ + struct + { + MQTTAsync_message message; /**< the message being sent to the server */ + char* destinationName; /**< the topic destination for the message */ + } pub; + /* For connect, the server connected to, MQTT version used, and sessionPresent flag */ + struct + { + char* serverURI; /**< the connection string of the server */ + int MQTTVersion; /**< the version of MQTT being used */ + int sessionPresent; /**< the session present flag returned from the server */ + } connect; + /** For unsubscribeMany, the list of reasonCodes returned by the server. */ + struct + { + int reasonCodeCount; /**< the number of reason codes in the reasonCodes array */ + enum MQTTReasonCodes* reasonCodes; /**< an array of reasonCodes */ + } unsub; + } alt; +} MQTTAsync_successData5; + +#define MQTTAsync_successData5_initializer {{'M', 'Q', 'S', 'D'}, 0, 0, MQTTREASONCODE_SUCCESS, MQTTProperties_initializer, {.sub={0,0}}} + +/** + * This is a callback function. The client application + * must provide an implementation of this function to enable asynchronous + * notification of the successful completion of an API call. The function is + * registered with the client library by passing it as an argument in + * ::MQTTAsync_responseOptions. + * + * Note: Neither MQTTAsync_create() nor MQTTAsync_destroy() should be + * called within this callback. + * @param context A pointer to the context value originally passed to + * ::MQTTAsync_responseOptions, which contains any application-specific context. + * @param response Any success data associated with the API completion. + */ +typedef void MQTTAsync_onSuccess(void* context, MQTTAsync_successData* response); + +/** + * This is a callback function, the MQTT V5 version of ::MQTTAsync_onSuccess. + * The client application + * must provide an implementation of this function to enable asynchronous + * notification of the successful completion of an API call. The function is + * registered with the client library by passing it as an argument in + * ::MQTTAsync_responseOptions. + * + * Note: Neither MQTTAsync_create() nor MQTTAsync_destroy() should be + * called within this callback. + * @param context A pointer to the context value originally passed to + * ::MQTTAsync_responseOptions, which contains any application-specific context. + * @param response Any success data associated with the API completion. + */ +typedef void MQTTAsync_onSuccess5(void* context, MQTTAsync_successData5* response); + +/** + * This is a callback function. The client application + * must provide an implementation of this function to enable asynchronous + * notification of the unsuccessful completion of an API call. The function is + * registered with the client library by passing it as an argument in + * ::MQTTAsync_responseOptions. + * + * Note: Neither MQTTAsync_create() nor MQTTAsync_destroy() should be + * called within this callback. + * @param context A pointer to the context value originally passed to + * ::MQTTAsync_responseOptions, which contains any application-specific context. + * @param response Failure data associated with the API completion. + */ +typedef void MQTTAsync_onFailure(void* context, MQTTAsync_failureData* response); + +/** + * This is a callback function, the MQTT V5 version of ::MQTTAsync_onFailure. + * The application must provide an implementation of this function to enable asynchronous + * notification of the unsuccessful completion of an API call. The function is + * registered with the client library by passing it as an argument in + * ::MQTTAsync_responseOptions. + * + * Note: Neither MQTTAsync_create() nor MQTTAsync_destroy() should be + * called within this callback. + * @param context A pointer to the context value originally passed to + * ::MQTTAsync_responseOptions, which contains any application-specific context. + * @param response Failure data associated with the API completion. + */ +typedef void MQTTAsync_onFailure5(void* context, MQTTAsync_failureData5* response); + +/** Structure to define call options. For MQTT 5.0 there is input data as well as that + * describing the response method. So there is now also a synonym ::MQTTAsync_callOptions + * to better reflect the use. This responseOptions name is kept for backward + * compatibility. + */ +typedef struct MQTTAsync_responseOptions +{ + /** The eyecatcher for this structure. Must be MQTR */ + char struct_id[4]; + /** The version number of this structure. Must be 0 or 1 + * if 0, no MQTTV5 options */ + int struct_version; + /** + * A pointer to a callback function to be called if the API call successfully + * completes. Can be set to NULL, in which case no indication of successful + * completion will be received. + */ + MQTTAsync_onSuccess* onSuccess; + /** + * A pointer to a callback function to be called if the API call fails. + * Can be set to NULL, in which case no indication of unsuccessful + * completion will be received. + */ + MQTTAsync_onFailure* onFailure; + /** + * A pointer to any application-specific context. The + * the context pointer is passed to success or failure callback functions to + * provide access to the context information in the callback. + */ + void* context; + /** + * A token is returned from the call. It can be used to track + * the state of this request, both in the callbacks and in future calls + * such as ::MQTTAsync_waitForCompletion. This is output only - any + * change by the application will be ignored. + */ + MQTTAsync_token token; + /** + * A pointer to a callback function to be called if the API call successfully + * completes. Can be set to NULL, in which case no indication of successful + * completion will be received. + */ + MQTTAsync_onSuccess5* onSuccess5; + /** + * A pointer to a callback function to be called if the API call successfully + * completes. Can be set to NULL, in which case no indication of successful + * completion will be received. + */ + MQTTAsync_onFailure5* onFailure5; + /** + * MQTT V5 input properties + */ + MQTTProperties properties; + /* + * MQTT V5 subscribe options, when used with subscribe only. + */ + MQTTSubscribe_options subscribeOptions; + /* + * MQTT V5 subscribe option count, when used with subscribeMany only. + * The number of entries in the subscribe_options_list array. + */ + int subscribeOptionsCount; + /* + * MQTT V5 subscribe option array, when used with subscribeMany only. + */ + MQTTSubscribe_options* subscribeOptionsList; +} MQTTAsync_responseOptions; + +#define MQTTAsync_responseOptions_initializer { {'M', 'Q', 'T', 'R'}, 1, NULL, NULL, 0, 0, NULL, NULL, MQTTProperties_initializer, MQTTSubscribe_options_initializer, 0, NULL} + +/** A synonym for responseOptions to better reflect its usage since MQTT 5.0 */ +typedef struct MQTTAsync_responseOptions MQTTAsync_callOptions; +#define MQTTAsync_callOptions_initializer MQTTAsync_responseOptions_initializer + +/** + * This function sets the global callback functions for a specific client. + * If your client application doesn't use a particular callback, set the + * relevant parameter to NULL. Any necessary message acknowledgements and + * status communications are handled in the background without any intervention + * from the client application. If you do not set a messageArrived callback + * function, you will not be notified of the receipt of any messages as a + * result of a subscription. + * + * Note: The MQTT client must be disconnected when this function is + * called. + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param context A pointer to any application-specific context. The + * the context pointer is passed to each of the callback functions to + * provide access to the context information in the callback. + * @param cl A pointer to an MQTTAsync_connectionLost() callback + * function. You can set this to NULL if your application doesn't handle + * disconnections. + * @param ma A pointer to an MQTTAsync_messageArrived() callback + * function. If this callback is not set, an error will be returned. + * You must set this callback because otherwise there would be + * no way to deliver any incoming messages. + * @param dc A pointer to an MQTTAsync_deliveryComplete() callback + * function. You can set this to NULL if you do not want to check + * for successful delivery. + * @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set, + * ::MQTTASYNC_FAILURE if an error occurred. + */ +LIBMQTT_API int MQTTAsync_setCallbacks(MQTTAsync handle, void* context, MQTTAsync_connectionLost* cl, + MQTTAsync_messageArrived* ma, MQTTAsync_deliveryComplete* dc); + +/** + * This function sets the callback function for a connection lost event for + * a specific client. Any necessary message acknowledgements and status + * communications are handled in the background without any intervention + * from the client application. + * + * Note: The MQTT client must be disconnected when this function is + * called. + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param context A pointer to any application-specific context. The + * the context pointer is passed the callback functions to provide + * access to the context information in the callback. + * @param cl A pointer to an MQTTAsync_connectionLost() callback + * function. You can set this to NULL if your application doesn't handle + * disconnections. + * @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set, + * ::MQTTASYNC_FAILURE if an error occurred. + */ + +LIBMQTT_API int MQTTAsync_setConnectionLostCallback(MQTTAsync handle, void* context, + MQTTAsync_connectionLost* cl); + +/** + * This function sets the callback function for a message arrived event for + * a specific client. Any necessary message acknowledgements and status + * communications are handled in the background without any intervention + * from the client application. If you do not set a messageArrived callback + * function, you will not be notified of the receipt of any messages as a + * result of a subscription. + * + * Note: The MQTT client must be disconnected when this function is + * called. + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param context A pointer to any application-specific context. The + * the context pointer is passed to the callback functions to provide + * access to the context information in the callback. + * @param ma A pointer to an MQTTAsync_messageArrived() callback + * function. You can set this to NULL if your application doesn't handle + * receipt of messages. + * @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set, + * ::MQTTASYNC_FAILURE if an error occurred. + */ +LIBMQTT_API int MQTTAsync_setMessageArrivedCallback(MQTTAsync handle, void* context, + MQTTAsync_messageArrived* ma); + +/** + * This function sets the callback function for a delivery complete event + * for a specific client. Any necessary message acknowledgements and status + * communications are handled in the background without any intervention + * from the client application. + * + * Note: The MQTT client must be disconnected when this function is + * called. + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param context A pointer to any application-specific context. The + * the context pointer is passed to the callback functions to provide + * access to the context information in the callback. + * @param dc A pointer to an MQTTAsync_deliveryComplete() callback + * function. You can set this to NULL if you do not want to check + * for successful delivery. + * @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set, + * ::MQTTASYNC_FAILURE if an error occurred. + */ +LIBMQTT_API int MQTTAsync_setDeliveryCompleteCallback(MQTTAsync handle, void* context, + MQTTAsync_deliveryComplete* dc); + +/** + * Sets the MQTTAsync_connected() callback function for a client. + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param context A pointer to any application-specific context. The + * the context pointer is passed to each of the callback functions to + * provide access to the context information in the callback. + * @param co A pointer to an MQTTAsync_connected() callback + * function. NULL removes the callback setting. + * @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set, + * ::MQTTASYNC_FAILURE if an error occurred. + */ +LIBMQTT_API int MQTTAsync_setConnected(MQTTAsync handle, void* context, MQTTAsync_connected* co); + + +/** + * Reconnects a client with the previously used connect options. Connect + * must have previously been called for this to work. + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @return ::MQTTASYNC_SUCCESS if the callbacks were correctly set, + * ::MQTTASYNC_FAILURE if an error occurred. + */ +LIBMQTT_API int MQTTAsync_reconnect(MQTTAsync handle); + + +/** + * This function creates an MQTT client ready for connection to the + * specified server and using the specified persistent storage (see + * MQTTAsync_persistence). See also MQTTAsync_destroy(). + * @param handle A pointer to an ::MQTTAsync handle. The handle is + * populated with a valid client reference following a successful return from + * this function. + * @param serverURI A null-terminated string specifying the server to + * which the client will connect. It takes the form + * protocol://host:port where protocol must be: + *
+ * @em tcp:// or @em mqtt:// - Insecure TCP + *
+ * @em ssl:// or @em mqtts:// - Encrypted SSL/TLS + *
+ * @em ws:// - Insecure websockets + *
+ * @em wss:// - Secure web sockets + *
+ * The TLS enabled prefixes (ssl, mqtts, wss) are only valid if a TLS + * version of the library is linked with. + * For host, you can specify either an IP address or a host name. For + * instance, to connect to a server running on the local machines with the + * default MQTT port, specify tcp://localhost:1883. + * @param clientId The client identifier passed to the server when the + * client connects to it. It is a null-terminated UTF-8 encoded string. + * @param persistence_type The type of persistence to be used by the client: + *
+ * ::MQTTCLIENT_PERSISTENCE_NONE: Use in-memory persistence. If the device or + * system on which the client is running fails or is switched off, the current + * state of any in-flight messages is lost and some messages may not be + * delivered even at QoS1 and QoS2. + *
+ * ::MQTTCLIENT_PERSISTENCE_DEFAULT: Use the default (file system-based) + * persistence mechanism. Status about in-flight messages is held in persistent + * storage and provides some protection against message loss in the case of + * unexpected failure. + *
+ * ::MQTTCLIENT_PERSISTENCE_USER: Use an application-specific persistence + * implementation. Using this type of persistence gives control of the + * persistence mechanism to the application. The application has to implement + * the MQTTClient_persistence interface. + * @param persistence_context If the application uses + * ::MQTTCLIENT_PERSISTENCE_NONE persistence, this argument is unused and should + * be set to NULL. For ::MQTTCLIENT_PERSISTENCE_DEFAULT persistence, it + * should be set to the location of the persistence directory (if set + * to NULL, the persistence directory used is the working directory). + * Applications that use ::MQTTCLIENT_PERSISTENCE_USER persistence set this + * argument to point to a valid MQTTClient_persistence structure. + * @return ::MQTTASYNC_SUCCESS if the client is successfully created, otherwise + * an error code is returned. + */ +LIBMQTT_API int MQTTAsync_create(MQTTAsync* handle, const char* serverURI, const char* clientId, + int persistence_type, void* persistence_context); + +/** Options for the ::MQTTAsync_createWithOptions call */ +typedef struct +{ + /** The eyecatcher for this structure. must be MQCO. */ + char struct_id[4]; + /** The version number of this structure. Must be 0, 1, 2 or 3 + * 0 means no MQTTVersion + * 1 means no allowDisconnectedSendAtAnyTime, deleteOldestMessages, restoreMessages + * 2 means no persistQoS0 + */ + int struct_version; + /** Whether to allow messages to be sent when the client library is not connected. */ + int sendWhileDisconnected; + /** The maximum number of messages allowed to be buffered. This is intended to be used to + * limit the number of messages queued while the client is not connected. It also applies + * when the client is connected, however, so has to be greater than 0. */ + int maxBufferedMessages; + /** Whether the MQTT version is 3.1, 3.1.1, or 5. To use V5, this must be set. + * MQTT V5 has to be chosen here, because during the create call the message persistence + * is initialized, and we want to know whether the format of any persisted messages + * is appropriate for the MQTT version we are going to connect with. Selecting 3.1 or + * 3.1.1 and attempting to read 5.0 persisted messages will result in an error on create. */ + int MQTTVersion; + /** + * Allow sending of messages while disconnected before a first successful connect. + */ + int allowDisconnectedSendAtAnyTime; + /* + * When the maximum number of buffered messages is reached, delete the oldest rather than the newest. + */ + int deleteOldestMessages; + /* + * Restore messages from persistence on create - or clear it. + */ + int restoreMessages; + /* + * Persist QoS0 publish commands - an option to not persist them. + */ + int persistQoS0; +} MQTTAsync_createOptions; + +#define MQTTAsync_createOptions_initializer { {'M', 'Q', 'C', 'O'}, 2, 0, 100, MQTTVERSION_DEFAULT, 0, 0, 1, 1} + +#define MQTTAsync_createOptions_initializer5 { {'M', 'Q', 'C', 'O'}, 2, 0, 100, MQTTVERSION_5, 0, 0, 1, 1} + + +LIBMQTT_API int MQTTAsync_createWithOptions(MQTTAsync* handle, const char* serverURI, const char* clientId, + int persistence_type, void* persistence_context, MQTTAsync_createOptions* options); + +/** + * MQTTAsync_willOptions defines the MQTT "Last Will and Testament" (LWT) settings for + * the client. In the event that a client unexpectedly loses its connection to + * the server, the server publishes the LWT message to the LWT topic on + * behalf of the client. This allows other clients (subscribed to the LWT topic) + * to be made aware that the client has disconnected. To enable the LWT + * function for a specific client, a valid pointer to an MQTTAsync_willOptions + * structure is passed in the MQTTAsync_connectOptions structure used in the + * MQTTAsync_connect() call that connects the client to the server. The pointer + * to MQTTAsync_willOptions can be set to NULL if the LWT function is not + * required. + */ +typedef struct +{ + /** The eyecatcher for this structure. must be MQTW. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 or 1 + 0 indicates no binary will message support + */ + int struct_version; + /** The LWT topic to which the LWT message will be published. */ + const char* topicName; + /** The LWT payload. */ + const char* message; + /** + * The retained flag for the LWT message (see MQTTAsync_message.retained). + */ + int retained; + /** + * The quality of service setting for the LWT message (see + * MQTTAsync_message.qos and @ref qos). + */ + int qos; + /** The LWT payload in binary form. This is only checked and used if the message option is NULL */ + struct + { + int len; /**< binary payload length */ + const void* data; /**< binary payload data */ + } payload; +} MQTTAsync_willOptions; + +#define MQTTAsync_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 1, NULL, NULL, 0, 0, { 0, NULL } } + +#define MQTT_SSL_VERSION_DEFAULT 0 +#define MQTT_SSL_VERSION_TLS_1_0 1 +#define MQTT_SSL_VERSION_TLS_1_1 2 +#define MQTT_SSL_VERSION_TLS_1_2 3 + +/** +* MQTTAsync_sslProperties defines the settings to establish an SSL/TLS connection using the +* OpenSSL library. It covers the following scenarios: +* - Server authentication: The client needs the digital certificate of the server. It is included +* in a store containting trusted material (also known as "trust store"). +* - Mutual authentication: Both client and server are authenticated during the SSL handshake. In +* addition to the digital certificate of the server in a trust store, the client will need its own +* digital certificate and the private key used to sign its digital certificate stored in a "key store". +* - Anonymous connection: Both client and server do not get authenticated and no credentials are needed +* to establish an SSL connection. Note that this scenario is not fully secure since it is subject to +* man-in-the-middle attacks. +*/ +typedef struct +{ + /** The eyecatcher for this structure. Must be MQTS */ + char struct_id[4]; + + /** The version number of this structure. Must be 0, 1, 2, 3, 4 or 5. + * 0 means no sslVersion + * 1 means no verify, CApath + * 2 means no ssl_error_context, ssl_error_cb + * 3 means no ssl_psk_cb, ssl_psk_context, disableDefaultTrustStore + * 4 means no protos, protos_len + */ + int struct_version; + + /** The file in PEM format containing the public digital certificates trusted by the client. */ + const char* trustStore; + + /** The file in PEM format containing the public certificate chain of the client. It may also include + * the client's private key. + */ + const char* keyStore; + + /** If not included in the sslKeyStore, this setting points to the file in PEM format containing + * the client's private key. + */ + const char* privateKey; + + /** The password to load the client's privateKey if encrypted. */ + const char* privateKeyPassword; + + /** + * The list of cipher suites that the client will present to the server during the SSL handshake. For a + * full explanation of the cipher list format, please see the OpenSSL on-line documentation: + * http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT + * If this setting is ommitted, its default value will be "ALL", that is, all the cipher suites -excluding + * those offering no encryption- will be considered. + * This setting can be used to set an SSL anonymous connection ("aNULL" string value, for instance). + */ + const char* enabledCipherSuites; + + /** True/False option to enable verification of the server certificate **/ + int enableServerCertAuth; + + /** The SSL/TLS version to use. Specify one of MQTT_SSL_VERSION_DEFAULT (0), + * MQTT_SSL_VERSION_TLS_1_0 (1), MQTT_SSL_VERSION_TLS_1_1 (2) or MQTT_SSL_VERSION_TLS_1_2 (3). + * Only used if struct_version is >= 1. + */ + int sslVersion; + + /** + * Whether to carry out post-connect checks, including that a certificate + * matches the given host name. + * Exists only if struct_version >= 2 + */ + int verify; + + /** + * From the OpenSSL documentation: + * If CApath is not NULL, it points to a directory containing CA certificates in PEM format. + * Exists only if struct_version >= 2 + */ + const char* CApath; + + /** + * Callback function for OpenSSL error handler ERR_print_errors_cb + * Exists only if struct_version >= 3 + */ + int (*ssl_error_cb) (const char *str, size_t len, void *u); + + /** + * Application-specific contex for OpenSSL error handler ERR_print_errors_cb + * Exists only if struct_version >= 3 + */ + void* ssl_error_context; + + /** + * Callback function for setting TLS-PSK options. Parameters correspond to that of + * SSL_CTX_set_psk_client_callback, except for u which is the pointer ssl_psk_context. + * Exists only if struct_version >= 4 + */ + unsigned int (*ssl_psk_cb) (const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len, void *u); + + /** + * Application-specific contex for ssl_psk_cb + * Exists only if struct_version >= 4 + */ + void* ssl_psk_context; + + /** + * Don't load default SSL CA. Should be used together with PSK to make sure + * regular servers with certificate in place is not accepted. + * Exists only if struct_version >= 4 + */ + int disableDefaultTrustStore; + + /** + * The protocol-lists must be in wire-format, which is defined as a vector of non-empty, 8-bit length-prefixed, byte strings. + * The length-prefix byte is not included in the length. Each string is limited to 255 bytes. A byte-string length of 0 is invalid. + * A truncated byte-string is invalid. + * Check documentation for SSL_CTX_set_alpn_protos + * Exists only if struct_version >= 5 + */ + const unsigned char *protos; + + /** + * The length of the vector protos vector + * Exists only if struct_version >= 5 + */ + unsigned int protos_len; +} MQTTAsync_SSLOptions; + +#define MQTTAsync_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 5, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 } + +/** Utility structure where name/value pairs are needed */ +typedef struct +{ + const char* name; /**< name string */ + const char* value; /**< value string */ +} MQTTAsync_nameValue; + +/** + * MQTTAsync_connectOptions defines several settings that control the way the + * client connects to an MQTT server. + * + * Suitable default values are set in the following initializers: + * - MQTTAsync_connectOptions_initializer: for MQTT 3.1.1 non-WebSockets + * - MQTTAsync_connectOptions_initializer5: for MQTT 5.0 non-WebSockets + * - MQTTAsync_connectOptions_initializer_ws: for MQTT 3.1.1 WebSockets + * - MQTTAsync_connectOptions_initializer5_ws: for MQTT 5.0 WebSockets + */ +typedef struct +{ + /** The eyecatcher for this structure. must be MQTC. */ + char struct_id[4]; + /** The version number of this structure. Must be 0, 1, 2, 3 4 5 6, 7 or 8. + * 0 signifies no SSL options and no serverURIs + * 1 signifies no serverURIs + * 2 signifies no MQTTVersion + * 3 signifies no automatic reconnect options + * 4 signifies no binary password option (just string) + * 5 signifies no MQTTV5 properties + * 6 signifies no HTTP headers option + * 7 signifies no HTTP proxy and HTTPS proxy options + */ + int struct_version; + /** The "keep alive" interval, measured in seconds, defines the maximum time + * that should pass without communication between the client and the server + * The client will ensure that at least one message travels across the + * network within each keep alive period. In the absence of a data-related + * message during the time period, the client sends a very small MQTT + * "ping" message, which the server will acknowledge. The keep alive + * interval enables the client to detect when the server is no longer + * available without having to wait for the long TCP/IP timeout. + * Set to 0 if you do not want any keep alive processing. + */ + int keepAliveInterval; + /** + * This is a boolean value. The cleansession setting controls the behaviour + * of both the client and the server at connection and disconnection time. + * The client and server both maintain session state information. This + * information is used to ensure "at least once" and "exactly once" + * delivery, and "exactly once" receipt of messages. Session state also + * includes subscriptions created by an MQTT client. You can choose to + * maintain or discard state information between sessions. + * + * When cleansession is true, the state information is discarded at + * connect and disconnect. Setting cleansession to false keeps the state + * information. When you connect an MQTT client application with + * MQTTAsync_connect(), the client identifies the connection using the + * client identifier and the address of the server. The server checks + * whether session information for this client + * has been saved from a previous connection to the server. If a previous + * session still exists, and cleansession=true, then the previous session + * information at the client and server is cleared. If cleansession=false, + * the previous session is resumed. If no previous session exists, a new + * session is started. + */ + int cleansession; + /** + * This controls how many messages can be in-flight simultaneously. + */ + int maxInflight; + /** + * This is a pointer to an MQTTAsync_willOptions structure. If your + * application does not make use of the Last Will and Testament feature, + * set this pointer to NULL. + */ + MQTTAsync_willOptions* will; + /** + * MQTT servers that support the MQTT v3.1 protocol provide authentication + * and authorisation by user name and password. This is the user name + * parameter. + */ + const char* username; + /** + * MQTT servers that support the MQTT v3.1 protocol provide authentication + * and authorisation by user name and password. This is the password + * parameter. + */ + const char* password; + /** + * The time interval in seconds to allow a connect to complete. + */ + int connectTimeout; + /** + * The time interval in seconds after which unacknowledged publish requests are + * retried during a TCP session. With MQTT 3.1.1 and later, retries are + * not required except on reconnect. 0 turns off in-session retries, and is the + * recommended setting. Adding retries to an already overloaded network only + * exacerbates the problem. + */ + int retryInterval; + /** + * This is a pointer to an MQTTAsync_SSLOptions structure. If your + * application does not make use of SSL, set this pointer to NULL. + */ + MQTTAsync_SSLOptions* ssl; + /** + * A pointer to a callback function to be called if the connect successfully + * completes. Can be set to NULL, in which case no indication of successful + * completion will be received. + */ + MQTTAsync_onSuccess* onSuccess; + /** + * A pointer to a callback function to be called if the connect fails. + * Can be set to NULL, in which case no indication of unsuccessful + * completion will be received. + */ + MQTTAsync_onFailure* onFailure; + /** + * A pointer to any application-specific context. The + * the context pointer is passed to success or failure callback functions to + * provide access to the context information in the callback. + */ + void* context; + /** + * The number of entries in the serverURIs array. + */ + int serverURIcount; + /** + * An array of null-terminated strings specifying the servers to + * which the client will connect. Each string takes the form protocol://host:port. + * protocol must be tcp, ssl, ws or wss. + * The TLS enabled prefixes (ssl, wss) are only valid if a TLS version of the library + * is linked with. + * For host, you can + * specify either an IP address or a domain name. For instance, to connect to + * a server running on the local machines with the default MQTT port, specify + * tcp://localhost:1883. + */ + char* const* serverURIs; + /** + * Sets the version of MQTT to be used on the connect. + * MQTTVERSION_DEFAULT (0) = default: start with 3.1.1, and if that fails, fall back to 3.1 + * MQTTVERSION_3_1 (3) = only try version 3.1 + * MQTTVERSION_3_1_1 (4) = only try version 3.1.1 + */ + int MQTTVersion; + /** + * Reconnect automatically in the case of a connection being lost. 0=false, 1=true + */ + int automaticReconnect; + /** + * The minimum automatic reconnect retry interval in seconds. Doubled on each failed retry. + */ + int minRetryInterval; + /** + * The maximum automatic reconnect retry interval in seconds. The doubling stops here on failed retries. + */ + int maxRetryInterval; + /** + * Optional binary password. Only checked and used if the password option is NULL + */ + struct { + int len; /**< binary password length */ + const void* data; /**< binary password data */ + } binarypwd; + /* + * MQTT V5 clean start flag. Only clears state at the beginning of the session. + */ + int cleanstart; + /** + * MQTT V5 properties for connect + */ + MQTTProperties *connectProperties; + /** + * MQTT V5 properties for the will message in the connect + */ + MQTTProperties *willProperties; + /** + * A pointer to a callback function to be called if the connect successfully + * completes. Can be set to NULL, in which case no indication of successful + * completion will be received. + */ + MQTTAsync_onSuccess5* onSuccess5; + /** + * A pointer to a callback function to be called if the connect fails. + * Can be set to NULL, in which case no indication of unsuccessful + * completion will be received. + */ + MQTTAsync_onFailure5* onFailure5; + /** + * HTTP headers for websockets + */ + const MQTTAsync_nameValue* httpHeaders; + /** + * HTTP proxy + */ + const char* httpProxy; + /** + * HTTPS proxy + */ + const char* httpsProxy; +} MQTTAsync_connectOptions; + +/** Initializer for connect options for MQTT 3.1.1 non-WebSocket connections */ +#define MQTTAsync_connectOptions_initializer { {'M', 'Q', 'T', 'C'}, 8, 60, 1, 65535, NULL, NULL, NULL, 30, 0,\ +NULL, NULL, NULL, NULL, 0, NULL, MQTTVERSION_DEFAULT, 0, 1, 60, {0, NULL}, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL} + +/** Initializer for connect options for MQTT 5.0 non-WebSocket connections */ +#define MQTTAsync_connectOptions_initializer5 { {'M', 'Q', 'T', 'C'}, 8, 60, 0, 65535, NULL, NULL, NULL, 30, 0,\ +NULL, NULL, NULL, NULL, 0, NULL, MQTTVERSION_5, 0, 1, 60, {0, NULL}, 1, NULL, NULL, NULL, NULL, NULL, NULL, NULL} + +/** Initializer for connect options for MQTT 3.1.1 WebSockets connections. + * The keepalive interval is set to 45 seconds to avoid webserver 60 second inactivity timeouts. + */ +#define MQTTAsync_connectOptions_initializer_ws { {'M', 'Q', 'T', 'C'}, 8, 45, 1, 65535, NULL, NULL, NULL, 30, 0,\ +NULL, NULL, NULL, NULL, 0, NULL, MQTTVERSION_DEFAULT, 0, 1, 60, {0, NULL}, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL} + +/** Initializer for connect options for MQTT 5.0 WebSockets connections. + * The keepalive interval is set to 45 seconds to avoid webserver 60 second inactivity timeouts. + */ +#define MQTTAsync_connectOptions_initializer5_ws { {'M', 'Q', 'T', 'C'}, 8, 45, 0, 65535, NULL, NULL, NULL, 30, 0,\ +NULL, NULL, NULL, NULL, 0, NULL, MQTTVERSION_5, 0, 1, 60, {0, NULL}, 1, NULL, NULL, NULL, NULL, NULL, NULL, NULL} + + +/** + * This function attempts to connect a previously-created client (see + * MQTTAsync_create()) to an MQTT server using the specified options. If you + * want to enable asynchronous message and status notifications, you must call + * MQTTAsync_setCallbacks() prior to MQTTAsync_connect(). + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param options A pointer to a valid MQTTAsync_connectOptions + * structure. + * @return ::MQTTASYNC_SUCCESS if the client connect request was accepted. + * If the client was unable to connect to the server, an error code is + * returned via the onFailure callback, if set. + * Error codes greater than 0 are returned by the MQTT protocol:

+ * 1: Connection refused: Unacceptable protocol version
+ * 2: Connection refused: Identifier rejected
+ * 3: Connection refused: Server unavailable
+ * 4: Connection refused: Bad user name or password
+ * 5: Connection refused: Not authorized
+ * 6-255: Reserved for future use
+ */ +LIBMQTT_API int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options); + +/** Options for the ::MQTTAsync_disconnect call */ +typedef struct +{ + /** The eyecatcher for this structure. Must be MQTD. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 or 1. 0 signifies no V5 properties */ + int struct_version; + /** + * The client delays disconnection for up to this time (in + * milliseconds) in order to allow in-flight message transfers to complete. + */ + int timeout; + /** + * A pointer to a callback function to be called if the disconnect successfully + * completes. Can be set to NULL, in which case no indication of successful + * completion will be received. + */ + MQTTAsync_onSuccess* onSuccess; + /** + * A pointer to a callback function to be called if the disconnect fails. + * Can be set to NULL, in which case no indication of unsuccessful + * completion will be received. + */ + MQTTAsync_onFailure* onFailure; + /** + * A pointer to any application-specific context. The + * the context pointer is passed to success or failure callback functions to + * provide access to the context information in the callback. + */ + void* context; + /** + * MQTT V5 input properties + */ + MQTTProperties properties; + /** + * Reason code for MQTTV5 disconnect + */ + enum MQTTReasonCodes reasonCode; + /** + * A pointer to a callback function to be called if the disconnect successfully + * completes. Can be set to NULL, in which case no indication of successful + * completion will be received. + */ + MQTTAsync_onSuccess5* onSuccess5; + /** + * A pointer to a callback function to be called if the disconnect fails. + * Can be set to NULL, in which case no indication of unsuccessful + * completion will be received. + */ + MQTTAsync_onFailure5* onFailure5; +} MQTTAsync_disconnectOptions; + +#define MQTTAsync_disconnectOptions_initializer { {'M', 'Q', 'T', 'D'}, 0, 0, NULL, NULL, NULL,\ + MQTTProperties_initializer, MQTTREASONCODE_SUCCESS, NULL, NULL } + +#define MQTTAsync_disconnectOptions_initializer5 { {'M', 'Q', 'T', 'D'}, 1, 0, NULL, NULL, NULL,\ + MQTTProperties_initializer, MQTTREASONCODE_SUCCESS, NULL, NULL } + +/** + * This function attempts to disconnect the client from the MQTT + * server. In order to allow the client time to complete handling of messages + * that are in-flight when this function is called, a timeout period is + * specified. When the timeout period has expired, the client disconnects even + * if there are still outstanding message acknowledgements. + * The next time the client connects to the same server, any QoS 1 or 2 + * messages which have not completed will be retried depending on the + * cleansession settings for both the previous and the new connection (see + * MQTTAsync_connectOptions.cleansession and MQTTAsync_connect()). + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param options The client delays disconnection for up to this time (in + * milliseconds) in order to allow in-flight message transfers to complete. + * @return ::MQTTASYNC_SUCCESS if the client successfully disconnects from + * the server. An error code is returned if the client was unable to disconnect + * from the server + */ +LIBMQTT_API int MQTTAsync_disconnect(MQTTAsync handle, const MQTTAsync_disconnectOptions* options); + + +/** + * This function allows the client application to test whether or not a + * client is currently connected to the MQTT server. + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @return Boolean true if the client is connected, otherwise false. + */ +LIBMQTT_API int MQTTAsync_isConnected(MQTTAsync handle); + + +/** + * This function attempts to subscribe a client to a single topic, which may + * contain wildcards (see @ref wildcard). This call also specifies the + * @ref qos requested for the subscription + * (see also MQTTAsync_subscribeMany()). + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param topic The subscription topic, which may include wildcards. + * @param qos The requested quality of service for the subscription. + * @param response A pointer to a response options structure. Used to set callback functions. + * @return ::MQTTASYNC_SUCCESS if the subscription request is successful. + * An error code is returned if there was a problem registering the + * subscription. + */ +LIBMQTT_API int MQTTAsync_subscribe(MQTTAsync handle, const char* topic, int qos, MQTTAsync_responseOptions* response); + + +/** + * This function attempts to subscribe a client to a list of topics, which may + * contain wildcards (see @ref wildcard). This call also specifies the + * @ref qos requested for each topic (see also MQTTAsync_subscribe()). + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param count The number of topics for which the client is requesting + * subscriptions. + * @param topic An array (of length count) of pointers to + * topics, each of which may include wildcards. + * @param qos An array (of length count) of @ref qos + * values. qos[n] is the requested QoS for topic[n]. + * @param response A pointer to a response options structure. Used to set callback functions. + * @return ::MQTTASYNC_SUCCESS if the subscription request is successful. + * An error code is returned if there was a problem registering the + * subscriptions. + */ +LIBMQTT_API int MQTTAsync_subscribeMany(MQTTAsync handle, int count, char* const* topic, const int* qos, MQTTAsync_responseOptions* response); + +/** + * This function attempts to remove an existing subscription made by the + * specified client. + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param topic The topic for the subscription to be removed, which may + * include wildcards (see @ref wildcard). + * @param response A pointer to a response options structure. Used to set callback functions. + * @return ::MQTTASYNC_SUCCESS if the subscription is removed. + * An error code is returned if there was a problem removing the + * subscription. + */ +LIBMQTT_API int MQTTAsync_unsubscribe(MQTTAsync handle, const char* topic, MQTTAsync_responseOptions* response); + +/** + * This function attempts to remove existing subscriptions to a list of topics + * made by the specified client. + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param count The number subscriptions to be removed. + * @param topic An array (of length count) of pointers to the topics of + * the subscriptions to be removed, each of which may include wildcards. + * @param response A pointer to a response options structure. Used to set callback functions. + * @return ::MQTTASYNC_SUCCESS if the subscriptions are removed. + * An error code is returned if there was a problem removing the subscriptions. + */ +LIBMQTT_API int MQTTAsync_unsubscribeMany(MQTTAsync handle, int count, char* const* topic, MQTTAsync_responseOptions* response); + + +/** + * This function attempts to publish a message to a given topic (see also + * ::MQTTAsync_sendMessage()). An ::MQTTAsync_token is issued when + * this function returns successfully if the QoS is greater than 0. + * If the client application needs to + * test for successful delivery of messages, a callback should be set + * (see ::MQTTAsync_onSuccess() and ::MQTTAsync_deliveryComplete()). + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param destinationName The topic associated with this message. + * @param payloadlen The length of the payload in bytes. + * @param payload A pointer to the byte array payload of the message. + * @param qos The @ref qos of the message. + * @param retained The retained flag for the message. + * @param response A pointer to an ::MQTTAsync_responseOptions structure. Used to set callback functions. + * This is optional and can be set to NULL. + * @return ::MQTTASYNC_SUCCESS if the message is accepted for publication. + * An error code is returned if there was a problem accepting the message. + */ +LIBMQTT_API int MQTTAsync_send(MQTTAsync handle, const char* destinationName, int payloadlen, const void* payload, int qos, + int retained, MQTTAsync_responseOptions* response); + +/** + * This function attempts to publish a message to a given topic (see also + * MQTTAsync_publish()). An ::MQTTAsync_token is issued when + * this function returns successfully if the QoS is greater than 0. + * If the client application needs to + * test for successful delivery of messages, a callback should be set + * (see ::MQTTAsync_onSuccess() and ::MQTTAsync_deliveryComplete()). + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param destinationName The topic associated with this message. + * @param msg A pointer to a valid MQTTAsync_message structure containing + * the payload and attributes of the message to be published. + * @param response A pointer to an ::MQTTAsync_responseOptions structure. Used to set callback functions. + * @return ::MQTTASYNC_SUCCESS if the message is accepted for publication. + * An error code is returned if there was a problem accepting the message. + */ +LIBMQTT_API int MQTTAsync_sendMessage(MQTTAsync handle, const char* destinationName, const MQTTAsync_message* msg, MQTTAsync_responseOptions* response); + + +/** + * This function sets a pointer to an array of tokens for + * messages that are currently in-flight (pending completion). + * + * Important note: The memory used to hold the array of tokens is + * malloc()'d in this function. The client application is responsible for + * freeing this memory when it is no longer required. + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param tokens The address of a pointer to an ::MQTTAsync_token. + * When the function returns successfully, the pointer is set to point to an + * array of tokens representing messages pending completion. The last member of + * the array is set to -1 to indicate there are no more tokens. If no tokens + * are pending, the pointer is set to NULL. + * @return ::MQTTASYNC_SUCCESS if the function returns successfully. + * An error code is returned if there was a problem obtaining the list of + * pending tokens. + */ +LIBMQTT_API int MQTTAsync_getPendingTokens(MQTTAsync handle, MQTTAsync_token **tokens); + +/** + * Tests whether a request corresponding to a token is complete. + * + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param token An ::MQTTAsync_token associated with a request. + * @return 1 if the request has been completed, 0 if not. + */ +#define MQTTASYNC_TRUE 1 +LIBMQTT_API int MQTTAsync_isComplete(MQTTAsync handle, MQTTAsync_token token); + + +/** + * Waits for a request corresponding to a token to complete. This only works for + * messages with QoS greater than 0. A QoS 0 message has no MQTT token. + * This function will always return ::MQTTASYNC_SUCCESS for a QoS 0 message. + * + * @param handle A valid client handle from a successful call to + * MQTTAsync_create(). + * @param token An ::MQTTAsync_token associated with a request. + * @param timeout the maximum time to wait for completion, in milliseconds + * @return ::MQTTASYNC_SUCCESS if the request has been completed in the time allocated, + * ::MQTTASYNC_FAILURE or ::MQTTASYNC_DISCONNECTED if not. + */ +LIBMQTT_API int MQTTAsync_waitForCompletion(MQTTAsync handle, MQTTAsync_token token, unsigned long timeout); + + +/** + * This function frees memory allocated to an MQTT message, including the + * additional memory allocated to the message payload. The client application + * calls this function when the message has been fully processed. Important + * note: This function does not free the memory allocated to a message + * topic string. It is the responsibility of the client application to free + * this memory using the MQTTAsync_free() library function. + * @param msg The address of a pointer to the ::MQTTAsync_message structure + * to be freed. + */ +LIBMQTT_API void MQTTAsync_freeMessage(MQTTAsync_message** msg); + +/** + * This function frees memory allocated by the MQTT C client library, especially the + * topic name. This is needed on Windows when the client library and application + * program have been compiled with different versions of the C compiler. It is + * thus good policy to always use this function when freeing any MQTT C client- + * allocated memory. + * @param ptr The pointer to the client library storage to be freed. + */ +LIBMQTT_API void MQTTAsync_free(void* ptr); + +/** + * This function is used to allocate memory to be used or freed by the MQTT C client library, + * especially the data in the ::MQTTPersistence_afterRead and ::MQTTPersistence_beforeWrite + * callbacks. This is needed on Windows when the client library and application + * program have been compiled with different versions of the C compiler. + * @param size The size of the memory to be allocated. + */ +LIBMQTT_API void* MQTTAsync_malloc(size_t size); + +/** + * This function frees the memory allocated to an MQTT client (see + * MQTTAsync_create()). It should be called when the client is no longer + * required. + * @param handle A pointer to the handle referring to the ::MQTTAsync + * structure to be freed. + */ +LIBMQTT_API void MQTTAsync_destroy(MQTTAsync* handle); + + + +enum MQTTASYNC_TRACE_LEVELS +{ + MQTTASYNC_TRACE_MAXIMUM = 1, + MQTTASYNC_TRACE_MEDIUM, + MQTTASYNC_TRACE_MINIMUM, + MQTTASYNC_TRACE_PROTOCOL, + MQTTASYNC_TRACE_ERROR, + MQTTASYNC_TRACE_SEVERE, + MQTTASYNC_TRACE_FATAL, +}; + + +/** + * This function sets the level of trace information which will be + * returned in the trace callback. + * @param level the trace level required + */ +LIBMQTT_API void MQTTAsync_setTraceLevel(enum MQTTASYNC_TRACE_LEVELS level); + + +/** + * This is a callback function prototype which must be implemented if you want + * to receive trace information. Do not invoke any other Paho API calls in this + * callback function - unpredictable behavior may result. + * @param level the trace level of the message returned + * @param message the trace message. This is a pointer to a static buffer which + * will be overwritten on each call. You must copy the data if you want to keep + * it for later. + */ +typedef void MQTTAsync_traceCallback(enum MQTTASYNC_TRACE_LEVELS level, char* message); + +/** + * This function sets the trace callback if needed. If set to NULL, + * no trace information will be returned. The default trace level is + * MQTTASYNC_TRACE_MINIMUM. + * @param callback a pointer to the function which will handle the trace information + */ +LIBMQTT_API void MQTTAsync_setTraceCallback(MQTTAsync_traceCallback* callback); + +/** + * This function returns version information about the library. + * no trace information will be returned. The default trace level is + * MQTTASYNC_TRACE_MINIMUM + * @return an array of strings describing the library. The last entry is a NULL pointer. + */ +LIBMQTT_API MQTTAsync_nameValue* MQTTAsync_getVersionInfo(void); + +/** + * Returns a pointer to a string representation of the error code, or NULL. + * Do not free after use. Returns NULL if the error code is unknown. + * @param code the MQTTASYNC_ return code. + * @return a static string representation of the error code. + */ +LIBMQTT_API const char* MQTTAsync_strerror(int code); + + +/*! + * @cond MQTTAsync_main + * @page async Threading + * The client application runs on several threads. + * Processing of handshaking and maintaining + * the network connection is performed in the background. + * This API is thread safe: functions may be called by multiple application + * threads. + * Notifications of status and message reception are provided to the client + * application using callbacks registered with the library by the call to + * MQTTAsync_setCallbacks() (see MQTTAsync_messageArrived(), + * MQTTAsync_connectionLost() and MQTTAsync_deliveryComplete()). + * In addition, some functions allow success and failure callbacks to be set + * for individual requests, in the ::MQTTAsync_responseOptions structure. Applications + * can be written as a chain of callback functions. + * + * @page callbacks Callbacks + * Any function from this API may be used within a callback. It is not advisable to + * use ::MQTTAsync_waitForCompletion within a callback, however, as it is the only + * API call that may take some time to complete, which may cause unpredictable + * behaviour. All the other API calls are intended to complete quickly, starting + * a request in the background, with success or failure notified by other callbacks. + * + * If no callbacks are assigned, this will include the message arrived callback. + * This could be done if the application is a pure publisher, and does + * not subscribe to any topics. If however messages are received, and no message + * arrived callback is set, then those messages will accumulate + * and take up memory, as there is no place for them to be delivered. + * A log message will be written to highlight the issue, but it is up + * to the application to protect against this situation. + * + * @page auto_reconnect Automatic Reconnect + * The ability for the client library to reconnect automatically in the event + * of a connection failure was added in 1.1. The connection lost callback + * allows a flexible response to the loss of a connection, so almost any + * behaviour can be implemented in that way. Automatic reconnect does have the + * advantage of being a little simpler to use. + * + * To switch on automatic reconnect, the connect options field + * automaticReconnect should be set to non-zero. The minimum and maximum times + * before the next connection attempt can also be set, the defaults being 1 and + * 60 seconds. At each failure to reconnect, the retry interval is doubled until + * the maximum value is reached, and there it stays until the connection is + * successfully re-established whereupon it is reset. + * + * When a reconnection attempt is successful, the ::MQTTAsync_connected callback + * function is invoked, if set by calling ::MQTTAsync_setConnected. This allows + * the application to take any actions needed, such as amending subscriptions. + * + * @page offline_publish Publish While Disconnected + * This feature was not originally available because with persistence enabled, + * messages could be stored locally without ever knowing if they could be sent. + * The client application could have created the client with an erroneous broker + * address or port for instance. + * + * To enable messages to be published when the application is disconnected + * ::MQTTAsync_createWithOptions must be used instead of ::MQTTAsync_create to + * create the client object. The ::MQTTAsync_createOptions field sendWhileDisconnected + * must be set to non-zero, and the maxBufferedMessages field set as required - + * the default being 100. + * + * ::MQTTAsync_getPendingTokens can be called to return the ids of the messages + * waiting to be sent, or for which the sending process has not completed. + * + * @page wildcard Subscription wildcards + * Every MQTT message includes a topic that classifies it. MQTT servers use + * topics to determine which subscribers should receive messages published to + * the server. + * + * Consider the server receiving messages from several environmental sensors. + * Each sensor publishes its measurement data as a message with an associated + * topic. Subscribing applications need to know which sensor originally + * published each received message. A unique topic is thus used to identify + * each sensor and measurement type. Topics such as SENSOR1TEMP, + * SENSOR1HUMIDITY, SENSOR2TEMP and so on achieve this but are not very + * flexible. If additional sensors are added to the system at a later date, + * subscribing applications must be modified to receive them. + * + * To provide more flexibility, MQTT supports a hierarchical topic namespace. + * This allows application designers to organize topics to simplify their + * management. Levels in the hierarchy are delimited by the '/' character, + * such as SENSOR/1/HUMIDITY. Publishers and subscribers use these + * hierarchical topics as already described. + * + * For subscriptions, two wildcard characters are supported: + *
    + *
  • A '#' character represents a complete sub-tree of the hierarchy and + * thus must be the last character in a subscription topic string, such as + * SENSOR/#. This will match any topic starting with SENSOR/, such as + * SENSOR/1/TEMP and SENSOR/2/HUMIDITY.
  • + *
  • A '+' character represents a single level of the hierarchy and is + * used between delimiters. For example, SENSOR/+/TEMP will match + * SENSOR/1/TEMP and SENSOR/2/TEMP.
  • + *
+ * Publishers are not allowed to use the wildcard characters in their topic + * names. + * + * Deciding on your topic hierarchy is an important step in your system design. + * + * @page qos Quality of service + * The MQTT protocol provides three qualities of service for delivering + * messages between clients and servers: "at most once", "at least once" and + * "exactly once". + * + * Quality of service (QoS) is an attribute of an individual message being + * published. An application sets the QoS for a specific message by setting the + * MQTTAsync_message.qos field to the required value. + * + * A subscribing client can set the maximum quality of service a server uses + * to send messages that match the client subscriptions. The + * MQTTAsync_subscribe() and MQTTAsync_subscribeMany() functions set this + * maximum. The QoS of a message forwarded to a subscriber thus might be + * different to the QoS given to the message by the original publisher. + * The lower of the two values is used to forward a message. + * + * The three levels are: + * + * QoS0, At most once: The message is delivered at most once, or it + * may not be delivered at all. Its delivery across the network is not + * acknowledged. The message is not stored. The message could be lost if the + * client is disconnected, or if the server fails. QoS0 is the fastest mode of + * transfer. It is sometimes called "fire and forget". + * + * The MQTT protocol does not require servers to forward publications at QoS0 + * to a client. If the client is disconnected at the time the server receives + * the publication, the publication might be discarded, depending on the + * server implementation. + * + * QoS1, At least once: The message is always delivered at least once. + * It might be delivered multiple times if there is a failure before an + * acknowledgment is received by the sender. The message must be stored + * locally at the sender, until the sender receives confirmation that the + * message has been published by the receiver. The message is stored in case + * the message must be sent again. + * + * QoS2, Exactly once: The message is always delivered exactly once. + * The message must be stored locally at the sender, until the sender receives + * confirmation that the message has been published by the receiver. The + * message is stored in case the message must be sent again. QoS2 is the + * safest, but slowest mode of transfer. A more sophisticated handshaking + * and acknowledgement sequence is used than for QoS1 to ensure no duplication + * of messages occurs. + * @page publish Publication example +@code +#include +#include +#include +#include "MQTTAsync.h" + +#if !defined(_WIN32) +#include +#else +#include +#endif + +#if defined(_WRS_KERNEL) +#include +#endif + +#define ADDRESS "tcp://mqtt.eclipseprojects.io:1883" +#define CLIENTID "ExampleClientPub" +#define TOPIC "MQTT Examples" +#define PAYLOAD "Hello World!" +#define QOS 1 +#define TIMEOUT 10000L + +int finished = 0; + +void connlost(void *context, char *cause) +{ + MQTTAsync client = (MQTTAsync)context; + MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer; + int rc; + + printf("\nConnection lost\n"); + printf(" cause: %s\n", cause); + + printf("Reconnecting\n"); + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS) + { + printf("Failed to start connect, return code %d\n", rc); + finished = 1; + } +} + +void onDisconnectFailure(void* context, MQTTAsync_failureData* response) +{ + printf("Disconnect failed\n"); + finished = 1; +} + +void onDisconnect(void* context, MQTTAsync_successData* response) +{ + printf("Successful disconnection\n"); + finished = 1; +} + +void onSendFailure(void* context, MQTTAsync_failureData* response) +{ + MQTTAsync client = (MQTTAsync)context; + MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer; + int rc; + + printf("Message send failed token %d error code %d\n", response->token, response->code); + opts.onSuccess = onDisconnect; + opts.onFailure = onDisconnectFailure; + opts.context = client; + if ((rc = MQTTAsync_disconnect(client, &opts)) != MQTTASYNC_SUCCESS) + { + printf("Failed to start disconnect, return code %d\n", rc); + exit(EXIT_FAILURE); + } +} + +void onSend(void* context, MQTTAsync_successData* response) +{ + MQTTAsync client = (MQTTAsync)context; + MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer; + int rc; + + printf("Message with token value %d delivery confirmed\n", response->token); + opts.onSuccess = onDisconnect; + opts.onFailure = onDisconnectFailure; + opts.context = client; + if ((rc = MQTTAsync_disconnect(client, &opts)) != MQTTASYNC_SUCCESS) + { + printf("Failed to start disconnect, return code %d\n", rc); + exit(EXIT_FAILURE); + } +} + + +void onConnectFailure(void* context, MQTTAsync_failureData* response) +{ + printf("Connect failed, rc %d\n", response ? response->code : 0); + finished = 1; +} + + +void onConnect(void* context, MQTTAsync_successData* response) +{ + MQTTAsync client = (MQTTAsync)context; + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + MQTTAsync_message pubmsg = MQTTAsync_message_initializer; + int rc; + + printf("Successful connection\n"); + opts.onSuccess = onSend; + opts.onFailure = onSendFailure; + opts.context = client; + pubmsg.payload = PAYLOAD; + pubmsg.payloadlen = (int)strlen(PAYLOAD); + pubmsg.qos = QOS; + pubmsg.retained = 0; + if ((rc = MQTTAsync_sendMessage(client, TOPIC, &pubmsg, &opts)) != MQTTASYNC_SUCCESS) + { + printf("Failed to start sendMessage, return code %d\n", rc); + exit(EXIT_FAILURE); + } +} + +int messageArrived(void* context, char* topicName, int topicLen, MQTTAsync_message* m) +{ + // not expecting any messages + return 1; +} + +int main(int argc, char* argv[]) +{ + MQTTAsync client; + MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer; + int rc; + + if ((rc = MQTTAsync_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTASYNC_SUCCESS) + { + printf("Failed to create client object, return code %d\n", rc); + exit(EXIT_FAILURE); + } + + if ((rc = MQTTAsync_setCallbacks(client, NULL, connlost, messageArrived, NULL)) != MQTTASYNC_SUCCESS) + { + printf("Failed to set callback, return code %d\n", rc); + exit(EXIT_FAILURE); + } + + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + conn_opts.onSuccess = onConnect; + conn_opts.onFailure = onConnectFailure; + conn_opts.context = client; + if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS) + { + printf("Failed to start connect, return code %d\n", rc); + exit(EXIT_FAILURE); + } + + printf("Waiting for publication of %s\n" + "on topic %s for client with ClientID: %s\n", + PAYLOAD, TOPIC, CLIENTID); + while (!finished) + #if defined(_WIN32) + Sleep(100); + #else + usleep(10000L); + #endif + + MQTTAsync_destroy(&client); + return rc; +} + + * @endcode + * @page subscribe Subscription example +@code +#include +#include +#include +#include "MQTTAsync.h" + +#if !defined(_WIN32) +#include +#else +#include +#endif + +#if defined(_WRS_KERNEL) +#include +#endif + +#define ADDRESS "tcp://mqtt.eclipseprojects.io:1883" +#define CLIENTID "ExampleClientSub" +#define TOPIC "MQTT Examples" +#define PAYLOAD "Hello World!" +#define QOS 1 +#define TIMEOUT 10000L + +int disc_finished = 0; +int subscribed = 0; +int finished = 0; + +void connlost(void *context, char *cause) +{ + MQTTAsync client = (MQTTAsync)context; + MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer; + int rc; + + printf("\nConnection lost\n"); + if (cause) + printf(" cause: %s\n", cause); + + printf("Reconnecting\n"); + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS) + { + printf("Failed to start connect, return code %d\n", rc); + finished = 1; + } +} + + +int msgarrvd(void *context, char *topicName, int topicLen, MQTTAsync_message *message) +{ + printf("Message arrived\n"); + printf(" topic: %s\n", topicName); + printf(" message: %.*s\n", message->payloadlen, (char*)message->payload); + MQTTAsync_freeMessage(&message); + MQTTAsync_free(topicName); + return 1; +} + +void onDisconnectFailure(void* context, MQTTAsync_failureData* response) +{ + printf("Disconnect failed, rc %d\n", response->code); + disc_finished = 1; +} + +void onDisconnect(void* context, MQTTAsync_successData* response) +{ + printf("Successful disconnection\n"); + disc_finished = 1; +} + +void onSubscribe(void* context, MQTTAsync_successData* response) +{ + printf("Subscribe succeeded\n"); + subscribed = 1; +} + +void onSubscribeFailure(void* context, MQTTAsync_failureData* response) +{ + printf("Subscribe failed, rc %d\n", response->code); + finished = 1; +} + + +void onConnectFailure(void* context, MQTTAsync_failureData* response) +{ + printf("Connect failed, rc %d\n", response->code); + finished = 1; +} + + +void onConnect(void* context, MQTTAsync_successData* response) +{ + MQTTAsync client = (MQTTAsync)context; + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + int rc; + + printf("Successful connection\n"); + + printf("Subscribing to topic %s\nfor client %s using QoS%d\n\n" + "Press Q to quit\n\n", TOPIC, CLIENTID, QOS); + opts.onSuccess = onSubscribe; + opts.onFailure = onSubscribeFailure; + opts.context = client; + if ((rc = MQTTAsync_subscribe(client, TOPIC, QOS, &opts)) != MQTTASYNC_SUCCESS) + { + printf("Failed to start subscribe, return code %d\n", rc); + finished = 1; + } +} + + +int main(int argc, char* argv[]) +{ + MQTTAsync client; + MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer; + MQTTAsync_disconnectOptions disc_opts = MQTTAsync_disconnectOptions_initializer; + int rc; + int ch; + + if ((rc = MQTTAsync_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL)) + != MQTTASYNC_SUCCESS) + { + printf("Failed to create client, return code %d\n", rc); + rc = EXIT_FAILURE; + goto exit; + } + + if ((rc = MQTTAsync_setCallbacks(client, client, connlost, msgarrvd, NULL)) != MQTTASYNC_SUCCESS) + { + printf("Failed to set callbacks, return code %d\n", rc); + rc = EXIT_FAILURE; + goto destroy_exit; + } + + conn_opts.keepAliveInterval = 20; + conn_opts.cleansession = 1; + conn_opts.onSuccess = onConnect; + conn_opts.onFailure = onConnectFailure; + conn_opts.context = client; + if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS) + { + printf("Failed to start connect, return code %d\n", rc); + rc = EXIT_FAILURE; + goto destroy_exit; + } + + while (!subscribed && !finished) + #if defined(_WIN32) + Sleep(100); + #else + usleep(10000L); + #endif + + if (finished) + goto exit; + + do + { + ch = getchar(); + } while (ch!='Q' && ch != 'q'); + + disc_opts.onSuccess = onDisconnect; + disc_opts.onFailure = onDisconnectFailure; + if ((rc = MQTTAsync_disconnect(client, &disc_opts)) != MQTTASYNC_SUCCESS) + { + printf("Failed to start disconnect, return code %d\n", rc); + rc = EXIT_FAILURE; + goto destroy_exit; + } + while (!disc_finished) + { + #if defined(_WIN32) + Sleep(100); + #else + usleep(10000L); + #endif + } + +destroy_exit: + MQTTAsync_destroy(&client); +exit: + return rc; +} + + * @endcode +* @page tracing Tracing + * + * Runtime tracing can be controlled by environment variables or API calls. + * + * #### Environment variables + * + * Tracing is switched on by setting the MQTT_C_CLIENT_TRACE environment variable. + * A value of ON, or stdout, prints to stdout, any other value is interpreted as a file name to use. + * + * The amount of trace detail is controlled with the MQTT_C_CLIENT_TRACE_LEVEL environment + * variable - valid values are ERROR, PROTOCOL, MINIMUM, MEDIUM and MAXIMUM + * (from least to most verbose). + * + * The variable MQTT_C_CLIENT_TRACE_MAX_LINES limits the number of lines of trace that are output + * to a file. Two files are used at most, when they are full, the last one is overwritten with the + * new trace entries. The default size is 1000 lines. + * + * #### Trace API calls + * + * MQTTAsync_traceCallback() is used to set a callback function which is called whenever trace + * information is available. This will be the same information as that printed if the + * environment variables were used to control the trace. + * + * The MQTTAsync_setTraceLevel() calls is used to set the maximum level of trace entries that will be + * passed to the callback function. The levels are: + * 1. ::MQTTASYNC_TRACE_MAXIMUM + * 2. ::MQTTASYNC_TRACE_MEDIUM + * 3. ::MQTTASYNC_TRACE_MINIMUM + * 4. ::MQTTASYNC_TRACE_PROTOCOL + * 5. ::MQTTASYNC_TRACE_ERROR + * 6. ::MQTTASYNC_TRACE_SEVERE + * 7. ::MQTTASYNC_TRACE_FATAL + * + * Selecting ::MQTTASYNC_TRACE_MAXIMUM will cause all trace entries at all levels to be returned. + * Choosing ::MQTTASYNC_TRACE_ERROR will cause ERROR, SEVERE and FATAL trace entries to be returned + * to the callback function. + * + * ### MQTT Packet Tracing + * + * A feature that can be very useful is printing the MQTT packets that are sent and received. To + * achieve this, use the following environment variable settings: + * @code + MQTT_C_CLIENT_TRACE=ON + MQTT_C_CLIENT_TRACE_LEVEL=PROTOCOL + * @endcode + * The output you should see looks like this: + * @code + 20130528 155936.813 3 stdout-subscriber -> CONNECT cleansession: 1 (0) + 20130528 155936.813 3 stdout-subscriber <- CONNACK rc: 0 + 20130528 155936.813 3 stdout-subscriber -> SUBSCRIBE msgid: 1 (0) + 20130528 155936.813 3 stdout-subscriber <- SUBACK msgid: 1 + 20130528 155941.818 3 stdout-subscriber -> DISCONNECT (0) + * @endcode + * where the fields are: + * 1. date + * 2. time + * 3. socket number + * 4. client id + * 5. direction (-> from client to server, <- from server to client) + * 6. packet details + * + * ### Default Level Tracing + * + * This is an extract of a default level trace of a call to connect: + * @code + 19700101 010000.000 (1152206656) (0)> MQTTClient_connect:893 + 19700101 010000.000 (1152206656) (1)> MQTTClient_connectURI:716 + 20130528 160447.479 Connecting to serverURI localhost:1883 + 20130528 160447.479 (1152206656) (2)> MQTTProtocol_connect:98 + 20130528 160447.479 (1152206656) (3)> MQTTProtocol_addressPort:48 + 20130528 160447.479 (1152206656) (3)< MQTTProtocol_addressPort:73 + 20130528 160447.479 (1152206656) (3)> Socket_new:599 + 20130528 160447.479 New socket 4 for localhost, port 1883 + 20130528 160447.479 (1152206656) (4)> Socket_addSocket:163 + 20130528 160447.479 (1152206656) (5)> Socket_setnonblocking:73 + 20130528 160447.479 (1152206656) (5)< Socket_setnonblocking:78 (0) + 20130528 160447.479 (1152206656) (4)< Socket_addSocket:176 (0) + 20130528 160447.479 (1152206656) (4)> Socket_error:95 + 20130528 160447.479 (1152206656) (4)< Socket_error:104 (115) + 20130528 160447.479 Connect pending + 20130528 160447.479 (1152206656) (3)< Socket_new:683 (115) + 20130528 160447.479 (1152206656) (2)< MQTTProtocol_connect:131 (115) + * @endcode + * where the fields are: + * 1. date + * 2. time + * 3. thread id + * 4. function nesting level + * 5. function entry (>) or exit (<) + * 6. function name : line of source code file + * 7. return value (if there is one) + * + * ### Memory Allocation Tracing + * + * Setting the trace level to maximum causes memory allocations and frees to be traced along with + * the default trace entries, with messages like the following: + * @code + 20130528 161819.657 Allocating 16 bytes in heap at file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c line 177 ptr 0x179f930 + + 20130528 161819.657 Freeing 16 bytes in heap at file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c line 201, heap use now 896 bytes + * @endcode + * When the last MQTT client object is destroyed, if the trace is being recorded + * and all memory allocated by the client library has not been freed, an error message will be + * written to the trace. This can help with fixing memory leaks. The message will look like this: + * @code + 20130528 163909.208 Some memory not freed at shutdown, possible memory leak + 20130528 163909.208 Heap scan start, total 880 bytes + 20130528 163909.208 Heap element size 32, line 354, file /home/icraggs/workspaces/mqrtc/mqttv3c/src/MQTTPacket.c, ptr 0x260cb00 + 20130528 163909.208 Content + 20130528 163909.209 Heap scan end + * @endcode + * @endcond + */ + +#if defined(__cplusplus) + } +#endif + +#endif diff --git a/.svn/pristine/74/744c8d7f8ff6238683e574f1e7ab69e9cb62b75d.svn-base b/.svn/pristine/74/744c8d7f8ff6238683e574f1e7ab69e9cb62b75d.svn-base new file mode 100644 index 0000000..502d0ec --- /dev/null +++ b/.svn/pristine/74/744c8d7f8ff6238683e574f1e7ab69e9cb62b75d.svn-base @@ -0,0 +1,6335 @@ +/* +Copyright 2001-2018 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 +*/ + +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" +#include "bpqmail.h" + +#define MAIL +#include "httpconnectioninfo.h" + +#ifdef WIN32 +//#include "C:\Program Files (x86)\GnuWin32\include\iconv.h" +#else +#include +#include +#endif + +static struct HTTPConnectionInfo * FindSession(char * Key); +int APIENTRY SessionControl(int stream, int command, int param); +int SetupNodeMenu(char * Buff); +VOID SetMultiStringValue(char ** values, char * Multi); +char * GetTemplateFromFile(int Version, char * FN); +VOID FormatTime(char * Time, time_t cTime); +struct MsgInfo * GetMsgFromNumber(int msgno); +BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP); +BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg); +int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * FileName, char * XML, char * Reply, char * RawMessage, int RawLen); +struct HTTPConnectionInfo * AllocateWebMailSession(); +VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen); +void ConvertTitletoUTF8(WebMailInfo * WebMail, char * Title, char * UTF8Title, int Len); +char *stristr (char *ch1, char *ch2); +char * ReadTemplate(char * FormSet, char * DirName, char * FileName); +VOID DoStandardTemplateSubsitutions(struct HTTPConnectionInfo * Session, char * txtFile); +BOOL CheckifPacket(char * Via); +int GetHTMLFormSet(char * FormSet); +void ProcessFormInput(struct HTTPConnectionInfo * Session, char * input, char * Reply, int * RLen, int InputLen); +char * WebFindPart(char ** Msg, char * Boundary, int * PartLen, char * End); +struct HTTPConnectionInfo * FindWMSession(char * Key); +int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Session, char * Alert); +char * BuildFormMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Keys[1000], char * Values[1000], int NumKeys); +char * FindXMLVariable(WebMailInfo * WebMail, char * Var); +int ReplyToFormsMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Reply, BOOL Reenter); +BOOL ParsetxtTemplate(struct HTTPConnectionInfo * Session, struct HtmlFormDir * Dir, char * FN, BOOL isReply); +VOID UpdateFormAction(char * Template, char * Key); +BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon); +BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon); +void FreeWebMailFields(WebMailInfo * WebMail); +VOID BuildXMLAttachment(struct HTTPConnectionInfo * Session, char * Keys[1000], char * Values[1000], int NumKeys); +VOID SaveTemplateMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); +VOID DownloadAttachments(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest); +VOID getAttachmentList(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest); +char * BuildB2Header(WebMailInfo * WebMail, struct MsgInfo * Msg, char ** ToCalls, int Calls); +VOID FormatTime2(char * Time, time_t cTime); +VOID ProcessSelectResponse(struct HTTPConnectionInfo * Session, char * URLParams); +VOID ProcessAskResponse(struct HTTPConnectionInfo * Session, char * URLParams); +char * CheckFile(struct HtmlFormDir * Dir, char * FN); +VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL); +VOID SendTemplateSelectScreen(struct HTTPConnectionInfo * Session, char *URLParams, int InputLen); +BOOL isAMPRMsg(char * Addr); +char * doXMLTransparency(char * string); +Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall); +void SendMessageReadEvent(char * Call, struct MsgInfo * Msg); +void SendNewMessageEvent(char * call, struct MsgInfo * Msg); +void MQTTMessageEvent(void* message); + +extern char NodeTail[]; +extern char BBSName[10]; + +extern char LTFROMString[2048]; +extern char LTTOString[2048]; +extern char LTATString[2048]; + + UCHAR BPQDirectory[260]; + +int LineCount = 35; // Lines per page on message list + +// Forms + +struct HtmlFormDir ** HtmlFormDirs = NULL; +int FormDirCount = 0; + +struct HtmlForm +{ + char * FileName; + BOOL HasInitial; + BOOL HasViewer; + BOOL HasReply; + BOOL HasReplyViewer; +}; + +struct HtmlFormDir +{ + char * FormSet; + char * DirName; + struct HtmlForm ** Forms; + int FormCount; + struct HtmlFormDir ** Dirs; // Nested Directories + int DirCount; +}; + + +char FormDirList[4][MAX_PATH] = {"Standard_Templates", "Standard Templates", "Local_Templates"}; + +static char PassError[] = "

Sorry, User or Password is invalid - please try again

"; +static char BusyError[] = "

Sorry, No sessions available - please try later

"; + +extern char MailSignon[]; + +char WebMailSignon[] = "BPQ32 Mail Server Access" + "

BPQ32 Mail Server %s Access

" + "

Please enter Callsign and Password to access WebMail

" + "
" + "" + "" + "
User
Password
" + "

"; + +static char MsgInputPage[] = "" + "" + "" + "" + "" + "

Webmail Interface - Message Input Form

" + "
" + "
" + "To      %s
" + "Subject      " +// "" +// "
" + "" + "
Type    " + "" + " BID
" + "" + "" + "
" + "
" + "
"; + +static char CheckFormMsgPage[] = "" + "" + "" + "

Webmail Forms Interface - Check Message

" + "
" + + "
" + "To      
" + "CC      
" + "Subject " +//"" + "
Type    " + "" + " BID

" + "
" + + "
" + "
"; + + +extern char * WebMailTemplate; +extern char * WebMailMsgTemplate; +extern char * jsTemplate; + +static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +char *longday[] = {"Sunday", "Monday", "Tusday", "Wednesday", "Thusday", "Friday", "Saturday"}; + +static struct HTTPConnectionInfo * WebSessionList = NULL; // active WebMail sessions + +#ifdef LINBPQ +UCHAR * GetBPQDirectory(); +#endif + +void UndoTransparency(char * input); + +#ifndef LINBPQ + +void UndoTransparency(char * input) +{ + char * ptr1, * ptr2; + char c; + int hex; + + if (input == NULL) + return; + + ptr1 = ptr2 = input; + + // Convert any %xx constructs + + while (1) + { + c = *(ptr1++); + + if (c == 0) + break; + + if (c == '%') + { + c = *(ptr1++); + if(isdigit(c)) + hex = (c - '0') << 4; + else + hex = (tolower(c) - 'a' + 10) << 4; + + c = *(ptr1++); + if(isdigit(c)) + hex += (c - '0'); + else + hex += (tolower(c) - 'a' + 10); + + *(ptr2++) = hex; + } + else if (c == '+') + *(ptr2++) = 32; + else + *(ptr2++) = c; + } + *ptr2 = 0; +} +#endif + +void ReleaseWebMailStruct(WebMailInfo * WebMail) +{ + // release any malloc'ed resources + + if (WebMail == NULL) + return; + + FreeWebMailFields(WebMail); + free(WebMail); + return; +} + +VOID FreeWebMailMallocs() +{ + // called when closing. Not really needed, but simplifies tracking down real memory leaks + + struct HTTPConnectionInfo * Session, * SaveNext; + int i; + Session = WebSessionList; + + while (Session) + { + SaveNext = Session->Next; + + // Release amy malloc'ed resouces + + ReleaseWebMailStruct(Session->WebMail); + free(Session); + Session = SaveNext; + } + + for (i = 0; i < FormDirCount; i++) + { + struct HtmlFormDir * Dir = HtmlFormDirs[i]; + + int j; + + for (j = 0; j < Dir->FormCount; j++) + { + free(Dir->Forms[j]->FileName); + free(Dir->Forms[j]); + } + + if (Dir->DirCount) + { + struct HtmlFormDir * SubDir; + + int k, l; + + for (l = 0; l < Dir->DirCount; l++) + { + SubDir = Dir->Dirs[l]; + + for (k = 0; k < Dir->Dirs[l]->FormCount; k++) + { + free(SubDir->Forms[k]->FileName); + free(SubDir->Forms[k]); + } + free(SubDir->DirName); + free(SubDir->Forms); + free(SubDir->FormSet); + + free(Dir->Dirs[l]); + } + } + free(Dir->DirName); + free(Dir->Forms); + free(Dir->FormSet); + free(Dir); + } + + free(HtmlFormDirs); + return; +} + +char * initMultipartUnpack(char ** Input) +{ + // Check if Multipart and return Boundary. Update Input to first part + + // look through header for Content-Type line, and if multipart + // find boundary string. + + char * ptr, * ptr2; + char Boundary[128]; + BOOL Multipart = FALSE; + + ptr = *Input; + + while(*ptr != 13) + { + ptr2 = strchr(ptr, 10); // Find CR + + while(ptr2[1] == ' ' || ptr2[1] == 9) // Whitespace - continuation line + ptr2 = strchr(&ptr2[1], 10); // Find CR + + if (_memicmp(ptr, "Content-Type: ", 14) == 0) + { + char Line[256] = ""; + char * ptr3; + size_t len = ptr2-ptr-14; + + if (len >255) + return NULL; + + memcpy(Line, &ptr[14], len); + + if (_memicmp(Line, "Multipart/", 10) == 0) + { + ptr3 = stristr(Line, "boundary"); + if (ptr3) + { + ptr3+=9; + + if ((*ptr3) == '"') + ptr3++; + + strcpy(Boundary, ptr3); + ptr3 = strchr(Boundary, '"'); + if (ptr3) *ptr3 = 0; + ptr3 = strchr(Boundary, 13); // CR + if (ptr3) *ptr3 = 0; + break; + } + else + return NULL; // Can't do anything without a boundary ?? + } + } + ptr = ptr2; + ptr++; + } + + // Find First part - there is a boundary before it + + ptr = strstr(ptr2, Boundary); + + // Next should be crlf then part + + ptr = strstr(ptr, "\r\n"); + + if (ptr) + ptr += 2; // Over CRLF + + *Input = ptr; // Return first part or NULL + return _strdup(Boundary); +} + +BOOL unpackPart(char * Boundary, char ** Input, char ** Name, char ** Value, int * ValLen, char * End) +{ + // Format seems to be +/* + ------WebKitFormBoundaryABJaEbBWB5SuAHmq + Content-Disposition: form-data; name="Subj" + + subj + ------WebKitFormBoundaryABJaEbBWB5SuAHmq + Content-Disposition: form-data; name="myFile[]"; filename="exiftool.txt" + Content-Type: text/plain + + c:\exiftool "-filenameTo = _strdup(Value); + else if (strcmp(Name, "CC") == 0) + WebMail->CC = _strdup(Value); + else if (strcmp(Name, "Subj") == 0) + WebMail->Subject = _strdup(Value); + else if (strcmp(Name, "Type") == 0) + WebMail->Type = Value[0]; + else if (strcmp(Name, "BID") == 0) + WebMail->BID = _strdup(Value); + else if (strcmp(Name, "Msg") == 0) + WebMail->Body = _strdup(Value); + + else if (_memicmp(Name, "myFile[]", 8) == 0) + { + // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain + + char * fn = strstr(Name, "filename="); + char * endfn; + if (fn) + { + fn += 10; + + endfn = strchr(fn, '"'); + if (endfn) + { + *endfn = 0; + + if (strlen(fn)) + { + WebMail->FileName[WebMail->Files] = _strdup(fn); + WebMail->FileBody[WebMail->Files] = malloc(ValLength); + memcpy(WebMail->FileBody[WebMail->Files], Value, ValLength); + WebMail->FileLen[WebMail->Files++] = ValLength; + } + } + } + } + + else if (_memicmp(Name, "myFile2[]", 8) == 0) + { + // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain + + char * fn = strstr(Name, "filename="); + char * endfn; + if (fn) + { + fn += 10; + + endfn = strchr(fn, '"'); + if (endfn) + { + *endfn = 0; + + if (strlen(fn)) + { + WebMail->Header = malloc(ValLength + 1); + memcpy(WebMail->Header, Value, ValLength + 1); + WebMail->HeaderLen = RemoveLF(WebMail->Header, ValLength); + } + } + } + } + + else if (_memicmp(Name, "myFile3[]", 8) == 0) + { + // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain + + char * fn = strstr(Name, "filename="); + char * endfn; + if (fn) + { + fn += 10; + + endfn = strchr(fn, '"'); + if (endfn) + { + *endfn = 0; + + if (strlen(fn)) + { + WebMail->Footer = malloc(ValLength + 1); + memcpy(WebMail->Footer, Value, ValLength + 1); + WebMail->FooterLen = RemoveLF(WebMail->Footer, ValLength); + } + } + } + } + + return TRUE; +} + + +struct HTTPConnectionInfo * AllocateWebMailSession() +{ + int KeyVal; + struct HTTPConnectionInfo * Session, * SaveNext; + time_t NOW = time(NULL); + + // First see if any session records havent been used for a while + + Session = WebSessionList; + + while (Session) + { + if (NOW - Session->WebMailLastUsed > 1200) // 20 Mins + { + SaveNext = Session->Next; + + // Release amy malloc'ed resouces + + ReleaseWebMailStruct(Session->WebMail); + + memset(Session, 0, sizeof(struct HTTPConnectionInfo)); + + Session->Next = SaveNext; + goto UseThis; + } + Session = Session->Next; + } + + Session = zalloc(sizeof(struct HTTPConnectionInfo)); + + if (Session == NULL) + return NULL; + + if (WebSessionList) + Session->Next = WebSessionList; + + WebSessionList = Session; + +UseThis: + + Session->WebMail = zalloc(sizeof(WebMailInfo)); + + KeyVal = ((rand() % 100) + 1); + + KeyVal *= (int)time(NULL); + + sprintf(Session->Key, "%c%08X", 'W', KeyVal); + + return Session; +} + +struct HTTPConnectionInfo * FindWMSession(char * Key) +{ + struct HTTPConnectionInfo * Session = WebSessionList; + + while (Session) + { + if (strcmp(Session->Key, Key) == 0) + { + Session->WebMailLastUsed = time(NULL); + return Session; + } + Session = Session->Next; + } + + return NULL; +} + + +// Build list of available forms + +VOID ProcessFormDir(char * FormSet, char * DirName, struct HtmlFormDir *** xxx, int * DirCount) +{ + struct HtmlFormDir * FormDir; + struct HtmlFormDir ** FormDirs = *xxx; + struct HtmlForm * Form; + char Search[MAX_PATH]; + int count = *DirCount; + +#ifdef WIN32 + HANDLE hFind = INVALID_HANDLE_VALUE; + WIN32_FIND_DATA ffd; +#else + DIR *dir; + struct dirent *entry; + char name[256]; +#endif + + FormDir = zalloc(sizeof (struct HtmlFormDir)); + + FormDir->DirName = _strdup(DirName); + FormDir->FormSet = _strdup(FormSet); + FormDirs=realloc(FormDirs, (count + 1) * sizeof(void *)); + FormDirs[count++] = FormDir; + + *DirCount = count; + *xxx = FormDirs; + + + // Scan Directory for .txt files + + sprintf(Search, "%s/%s/%s/*", GetBPQDirectory(), FormSet, DirName); + + // Find the first file in the directory. + +#ifdef WIN32 + + hFind = FindFirstFile(Search, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + return; + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + char Dir[MAX_PATH]; + + if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) + continue; + + // Recurse in subdir + + sprintf(Dir, "%s/%s", DirName, ffd.cFileName); + + ProcessFormDir(FormSet, Dir, &FormDir->Dirs, &FormDir->DirCount); + + continue; + + } + + // Add to list + + Form = zalloc(sizeof (struct HtmlForm)); + + Form->FileName = _strdup(ffd.cFileName); + + FormDir->Forms=realloc(FormDir->Forms, (FormDir->FormCount + 1) * sizeof(void *)); + FormDir->Forms[FormDir->FormCount++] = Form; + } + + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + +#else + + sprintf(Search, "%s/%s/%s", GetBPQDirectory(), FormSet, DirName); + + if (!(dir = opendir(Search))) + { + Debugprintf("%s %d %d", "cant open forms dir", errno, dir); + return ; + } + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_type == DT_DIR) + { + char Dir[MAX_PATH]; + + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + // Recurse in subdir + + sprintf(Dir, "%s/%s", DirName, entry->d_name); + + ProcessFormDir(FormSet, Dir, &FormDir->Dirs, &FormDir->DirCount); + continue; + } + + // Add to list + + Form = zalloc(sizeof (struct HtmlForm)); + + Form->FileName = _strdup(entry->d_name); + + FormDir->Forms=realloc(FormDir->Forms, (FormDir->FormCount + 1) * sizeof(void *)); + FormDir->Forms[FormDir->FormCount++] = Form; + } + closedir(dir); +#endif + return; +} + +int GetHTMLForms() +{ + int n = 0; + + while (FormDirList[n][0]) + GetHTMLFormSet(FormDirList[n++]); + + return 0; +} + +int GetHTMLFormSet(char * FormSet) +{ + int i; + +#ifdef WIN32 + + WIN32_FIND_DATA ffd; + char szDir[MAX_PATH]; + HANDLE hFind = INVALID_HANDLE_VALUE; + DWORD dwError=0; + + sprintf(szDir, "%s/%s/*", BPQDirectory, FormSet); + + // Find the first file in the directory. + + hFind = FindFirstFile(szDir, &ffd); + + if (INVALID_HANDLE_VALUE == hFind) + { + // Accept either + return 0; + } + + // Scan all directories looking for file + + do + { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) + continue; + + // Add to Directory List + + ProcessFormDir(FormSet, ffd.cFileName, &HtmlFormDirs, &FormDirCount); + } + } + + while (FindNextFile(hFind, &ffd) != 0); + + FindClose(hFind); + +#else + + DIR *dir; + struct dirent *entry; + char name[256]; + + sprintf(name, "%s/%s", BPQDirectory, FormSet); + + if (!(dir = opendir(name))) + { + Debugprintf("cant open forms dir %s %d %d", name, errno, dir); + } + else + { + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_type == DT_DIR) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + // Add to Directory List + + ProcessFormDir(FormSet, entry->d_name, &HtmlFormDirs, &FormDirCount); + } + } + closedir(dir); + } +#endif + + // List for testing + + return 0; + + Debugprintf("%d form dirs", FormDirCount); + + for (i = 0; i < FormDirCount; i++) + { + struct HtmlFormDir * Dir = HtmlFormDirs[i]; + + int j; + Debugprintf("%3d %s", Dir->FormCount, Dir->DirName); + + for (j = 0; j < Dir->FormCount; j++) + Debugprintf(" %s", Dir->Forms[j]->FileName); + + if (Dir->DirCount) + { + int k, l; + + for (l = 0; l < Dir->DirCount; l++) + { + Debugprintf("Subdir %3d %s", Dir->Dirs[l]->DirCount, Dir->Dirs[l]->DirName); + for (k = 0; k < Dir->Dirs[l]->FormCount; k++) + Debugprintf(" %s", Dir->Dirs[l]->Forms[k]->FileName); + } + } + } + + + return 0; +} + + +static int compare(const void *arg1, const void *arg2) +{ + // Compare Calls. Fortunately call is at start of stuct + + return _stricmp(*(char**)arg1 , *(char**)arg2); +} + + +int SendWebMailHeader(char * Reply, char * Key, struct HTTPConnectionInfo * Session) +{ + return SendWebMailHeaderEx(Reply, Key, Session, NULL); +} + + +int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Session, char * Alert) +{ + // Ex includes an alert string to be sent before message + + struct UserInfo * User = Session->User; + char Messages[245000]; + int m; + struct MsgInfo * Msg; + char * ptr = Messages; + int n = NumberofMessages; //LineCount; + char Via[64]; + int Count = 0; + + Messages[0] = 0; + + if (Alert && Alert[0]) + ptr += sprintf(Messages, "", Alert, Key); + + ptr += sprintf(ptr, "%s", " # Date XX Len To @ From Subject\r\n\r\n"); + + for (m = LatestMsg; m >= 1; m--) + { + if (ptr > &Messages[244000]) + break; // protect buffer + + Msg = GetMsgFromNumber(m); + + if (Msg == 0 || Msg->type == 0 || Msg->status == 0) + continue; // Protect against corrupt messages + + if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) + { + char UTF8Title[4096]; + char * EncodedTitle; + + // List if it is the right type and in the page range we want + + if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) + continue; + + // All Types or right Type. Check Mine Flag + + if (Session->WebMailMine) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0) + continue; + } + + if (Session->WebMailMyTX) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->from) != 0) + continue; + } + + if (Session->WebMailMyRX) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to)!= 0) + continue; + } + + if (Count++ < Session->WebMailSkip) + continue; + + strcpy(Via, Msg->via); + strlop(Via, '.'); + + // make sure title is HTML safe (no < > etc) and UTF 8 encoded + + EncodedTitle = doXMLTransparency(Msg->title); + + memset(UTF8Title, 0, 4096); // In case convert fails part way through + ConvertTitletoUTF8(Session->WebMail, EncodedTitle, UTF8Title, 4095); + + free(EncodedTitle); + + ptr += sprintf(ptr, "%6d %s %c%c %5d %-8s%-8s%-8s%s\r\n", + Key, Msg->number, Msg->number, + FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, + Msg->status, Msg->length, Msg->to, Via, + Msg->from, UTF8Title); + + n--; + + if (n == 0) + break; + } + } + + if (WebMailTemplate == NULL) + WebMailTemplate = GetTemplateFromFile(6, "WebMailPage.txt"); + + return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Key, Key, Key, Messages); +} + +int ViewWebMailMessage(struct HTTPConnectionInfo * Session, char * Reply, int Number, BOOL DisplayHTML) +{ + char * Key = Session->Key; + struct UserInfo * User = Session->User; + WebMailInfo * WebMail = Session->WebMail; + char * DisplayStyle; + + char Message[200000] = ""; + struct MsgInfo * Msg; + char * ptr = Message; + char * MsgBytes, * Save; + int msgLen; + + char FullTo[100]; + char UTF8Title[4096]; + int Index; + char * crcrptr; + char DownLoad[256] = ""; + + DisplayStyle = "textarea"; // Prevents interpretation of html and xml + + Msg = GetMsgFromNumber(Number); + + if (Msg == NULL) + { + ptr += sprintf(ptr, "Message %d not found\r\n", Number); + return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Message); + } + + // New Display so free any old values + + FreeWebMailFields(WebMail); + + WebMail->CurrentMessageIndex = Number; + + + if (!CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) + { + ptr += sprintf(ptr, "Message %d not for you\r", Number); + return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Message); + } + + if (_stricmp(Msg->to, "RMS") == 0) + sprintf(FullTo, "RMS:%s", Msg->via); + else + if (Msg->to[0] == 0) + sprintf(FullTo, "smtp:%s", Msg->via); + else + strcpy(FullTo, Msg->to); + + // make sure title is UTF 8 encoded + + memset(UTF8Title, 0, 4096); // In case convert fails part way through + ConvertTitletoUTF8(Session->WebMail, Msg->title, UTF8Title, 4095); + + // if a B2 message diplay B2 Header instead of a locally generated one + + if ((Msg->B2Flags & B2Msg) == 0) + { + ptr += sprintf(ptr, "From: %s%s\nTo: %s\nType/Status: %c%c\nDate/Time: %s\nBid: %s\nTitle: %s\n\n", + Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, UTF8Title); + } + + MsgBytes = Save = ReadMessageFile(Number); + + msgLen = Msg->length; + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else + Index = TMSG; + + if (MsgBytes) + { + if (Msg->B2Flags & B2Msg) + { + char * ptr1; + + // if message has attachments, display them if plain text + + if (Msg->B2Flags & Attachments) + { + int BodyLen, NewLen; + int i; + char *ptr2, *attptr; + + sprintf(DownLoad, "Save Attachments", Key, Msg->number); + + WebMail->Files = 0; + + ptr1 = MsgBytes; + + // ptr += sprintf(ptr, "Message has Attachments\r\n\r\n"); + + while(*ptr1 != 13) + { + ptr2 = strchr(ptr1, 10); // Find CR + + if (memcmp(ptr1, "Body: ", 6) == 0) + { + BodyLen = atoi(&ptr1[6]); + } + + if (memcmp(ptr1, "File: ", 6) == 0) + { + char * ptr3 = strchr(&ptr1[6], ' '); // Find Space + *(ptr2 - 1) = 0; + + WebMail->FileLen[WebMail->Files] = atoi(&ptr1[6]); + WebMail->FileName[WebMail->Files++] = _strdup(&ptr3[1]); + *(ptr2 - 1) = ' '; // put space back + } + + ptr1 = ptr2; + ptr1++; + } + + ptr1 += 2; // Over Blank Line and Separator + + // ptr1 is pointing to body. Save for possible reply + + WebMail->Body = malloc(BodyLen + 2); + memcpy(WebMail->Body, ptr1, BodyLen); + WebMail->Body[BodyLen] = 0; + + *(ptr1 + BodyLen) = 0; + + ptr += sprintf(ptr, "%s", MsgBytes); // B2 Header and Body + + ptr1 += BodyLen + 2; // to first file + + // Save pointers to file + + attptr = ptr1; + + for (i = 0; i < WebMail->Files; i++) + { + WebMail->FileBody[i] = malloc(WebMail->FileLen[i]); + memcpy(WebMail->FileBody[i], attptr, WebMail->FileLen[i]); + attptr += (WebMail->FileLen[i] + 2); + } + + // if first (only??) attachment is XML and filename + // starts "RMS_Express_Form" process as HTML Form + + if (DisplayHTML && _memicmp(ptr1, "FileName[0], "RMS_Express_Form_", 16) == 0) + { + int Len = DisplayWebForm(Session, Msg, WebMail->FileName[0], ptr1, Reply, MsgBytes, BodyLen + 32); // 32 for added "has attachments" + free(MsgBytes); + + // Flag as read + + if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + SendMessageReadEvent(Session->Callsign, Msg); + } + } + } + + return Len; + } + + for (i = 0; i < WebMail->Files; i++) + { + int n; + char * p = ptr1; + char c; + + // Check if message is probably binary + + int BinCount = 0; + + NewLen = WebMail->FileLen[i]; + + for (n = 0; n < NewLen; n++) + { + c = *p; + + if (c==0 || (c & 128)) + BinCount++; + + p++; + + } + + if (BinCount > NewLen/10) + { + // File is probably Binary + + ptr += sprintf(ptr, "\rAttachment %s is a binary file\r", WebMail->FileName[i]); + } + else + { + *(ptr1 + NewLen) = 0; + ptr += sprintf(ptr, "\rAttachment %s\r\r", WebMail->FileName[i]); + RemoveLF(ptr1, NewLen + 1); // Removes LF after CR but not on its own + + ptr += sprintf(ptr, "%s\r\r", ptr1); + + User->Total.MsgsSent[Index] ++; + User->Total.BytesForwardedOut[Index] += NewLen; + } + + ptr1 += WebMail->FileLen[i]; + ptr1 +=2; // Over separator + } + + free(Save); + + ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from); + + RemoveLF(Message, (int)strlen(Message) + 1); // Removes LF after CR but not on its own + + if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + SendMessageReadEvent(Session->Callsign, Msg); + } + } + } + + if (DisplayHTML && stristr(Message, "")) + DisplayStyle = "div"; // Use div so HTML and XML are interpreted + + return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle); + } + + // Remove B2 Headers (up to the File: Line) + + // ptr1 = strstr(MsgBytes, "Body:"); + + // if (ptr1) + // MsgBytes = ptr1; + } + + // Body may have cr cr lf which causes double space + + crcrptr = strstr(MsgBytes, "\r\r\n"); + + while (crcrptr) + { + *crcrptr = ' '; + crcrptr = strstr(crcrptr, "\r\r\n"); + } + + // Remove lf chars + + msgLen = RemoveLF(MsgBytes, msgLen); + + User->Total.MsgsSent[Index] ++; + // User->Total.BytesForwardedOut[Index] += Length; + + // if body not UTF-8, convert it + + if (WebIsUTF8(MsgBytes, msgLen) == FALSE) + { + int code = TrytoGuessCode(MsgBytes, msgLen); + + UCHAR * UTF = malloc(msgLen * 3); + + if (code == 437) + msgLen = Convert437toUTF8(MsgBytes, msgLen, UTF); + else if (code == 1251) + msgLen = Convert1251toUTF8(MsgBytes, msgLen, UTF); + else + msgLen = Convert1252toUTF8(MsgBytes, msgLen, UTF); + + free(MsgBytes); + Save = MsgBytes = UTF; + + MsgBytes[msgLen] = 0; + } + + // ptr += sprintf(ptr, "%s", MsgBytes); + + memcpy(ptr, MsgBytes, msgLen); + ptr += msgLen; + ptr[0] = 0; + + free(Save); + + ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from); + + if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0))) + { + if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) + { + if (Msg->status != 'Y') + { + Msg->status = 'Y'; + Msg->datechanged=time(NULL); + SaveMessageDatabase(); + SendMessageReadEvent(Session->Callsign, Msg); + } + } + } + } + else + { + ptr += sprintf(ptr, "File for Message %d not found\r", Number); + } + + if (DisplayHTML && stristr(Message, "")) + DisplayStyle = "div"; // Use div so HTML and XML are interpreted + + + return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle); +} + +int KillWebMailMessage(char * Reply, char * Key, struct UserInfo * User, int Number) +{ + struct MsgInfo * Msg; + char Message[100] = ""; + + Msg = GetMsgFromNumber(Number); + + if (Msg == NULL) + { + sprintf(Message, "Message %d not found", Number); + goto returnit; + } + + if (OkToKillMessage(User->flags & F_SYSOP, User->Call, Msg)) + { + FlagAsKilled(Msg, TRUE); + sprintf(Message, "Message #%d Killed\r", Number); + goto returnit; + } + + sprintf(Message, "Not your message\r"); + +returnit: + return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, "", Key, Key, Key, "div", Message, "div"); +} + +void freeKeys(KeyValues * Keys) +{ + while (Keys->Key) + { + free(Keys->Key); + free(Keys->Value); + Keys++; + } +} + +void FreeWebMailFields(WebMailInfo * WebMail) +{ + // release any malloc'ed resources + + int i; + char * SaveReply; + int * SaveRlen; + + if (WebMail == NULL) + return; + + if (WebMail->txtFile) + free(WebMail->txtFile); + + if (WebMail->txtFileName) + free(WebMail->txtFileName); + + if (WebMail->InputHTMLName) + free(WebMail->InputHTMLName); + + if (WebMail->DisplayHTMLName) + free(WebMail->DisplayHTMLName); + + if (WebMail->ReplyHTMLName) + free(WebMail->ReplyHTMLName); + + if (WebMail->To) + free(WebMail->To); + if (WebMail->CC) + free(WebMail->CC); + if (WebMail->Subject) + free(WebMail->Subject); + if (WebMail->BID) + free(WebMail->BID); + if (WebMail->Body) + free(WebMail->Body); + if (WebMail->XML) + free(WebMail->XML); + if (WebMail->XMLName) + free(WebMail->XMLName); + + if (WebMail->OrigTo) + free(WebMail->OrigTo); + if (WebMail->OrigSubject) + free(WebMail->OrigSubject); + if (WebMail->OrigBID) + free(WebMail->OrigBID); + if (WebMail->OrigBody) + free(WebMail->OrigBody); + + freeKeys(WebMail->txtKeys); + freeKeys(WebMail->XMLKeys); + + for (i = 0; i < WebMail->Files; i++) + { + free(WebMail->FileBody[i]); + free(WebMail->FileName[i]); + } + + if (WebMail->Header) + free(WebMail->Header); + if (WebMail->Footer) + free(WebMail->Footer); + + SaveReply = WebMail->Reply; + SaveRlen = WebMail->RLen; + +#ifndef WIN32 + if (WebMail->iconv_toUTF8) + iconv_close(WebMail->iconv_toUTF8); +#endif + + memset(WebMail, 0, sizeof(WebMailInfo)); + + WebMail->Reply = SaveReply; + WebMail->RLen = SaveRlen; + + return; +} + + +void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL LOCAL, char * Method, char * NodeURL, char * input, char * Reply, int * RLen, int InputLen) +{ + char * URLParams = strlop(Key, '&'); + int ReplyLen; + char Appl = 'M'; + + // Webmail doesn't use the normal Mail Key. + + // webscript.js doesn't need a key + + if (_stricmp(NodeURL, "/WebMail/webscript.js") == 0) + { + if (jsTemplate) + free(jsTemplate); + + jsTemplate = GetTemplateFromFile(2, "webscript.js"); + + ReplyLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Cache-Control: max-age=60\r\nContent-Type: text/javascript\r\n\r\n%s", (int)strlen(jsTemplate), jsTemplate); + *RLen = ReplyLen; + return; + } + + // Neither do js or file downloads + + if (_memicmp(NodeURL, "/WebMail/WMFile/", 16) == 0) + { + int FileSize; + char * MsgBytes; + char MsgFile[512]; + FILE * hFile; + size_t ReadLen; + char TimeString[64]; + char FileTimeString[64]; + struct stat STAT; + char * FN = &NodeURL[16]; + char * fileBit = FN; + char * ext; + + UndoTransparency(FN); + ext = strchr(FN, '.'); + + sprintf(MsgFile, "%s/%s", BPQDirectory, FN); + + while (strchr(fileBit, '/')) + fileBit = strlop(fileBit, '/'); + + if (stat(MsgFile, &STAT) == -1) + { + *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + return; + } + + hFile = fopen(MsgFile, "rb"); + + if (hFile == 0) + { + *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); + return; + } + + FileSize = STAT.st_size; + MsgBytes = malloc(FileSize + 1); + ReadLen = fread(MsgBytes, 1, FileSize, hFile); + + fclose(hFile); + + FormatTime2(FileTimeString, STAT.st_ctime); + FormatTime2(TimeString, time(NULL)); + + if (_stricmp(ext, ".htm") == 0 || _stricmp(ext, ".html") == 0 || _stricmp(ext, ".css") == 0 || _stricmp(ext, ".js") == 0) + { + *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Content-Type: text/css\r\n" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "\r\n%s", FileSize, TimeString, FileTimeString, MsgBytes); + } + else + { + *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Disposition: attachment; filename=\"%s\"\r\n" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "\r\n%s", FileSize, fileBit, TimeString, FileTimeString, MsgBytes); + + } + free (MsgBytes); + return; + } + + Session = NULL; + + if (Key && Key[0]) + Session = FindWMSession(Key); + + if (Session == NULL) + { + // Lost Session + + if (LOCAL) + { + Session = AllocateWebMailSession(); + + Key = Session->Key; + + if (SYSOPCall[0]) + Session->User = LookupCall(SYSOPCall); + else + Session->User = LookupCall(BBSName); + + if (Session->User) + { + strcpy(NodeURL, "/WebMail/WebMail"); + Session->WebMailSkip = 0; + Session->WebMailLastUsed = time(NULL); + } + } + else + { + // Send Login Page unless Signon request + + if (_stricmp(NodeURL, "/WebMail/Signon") != 0 || strcmp(Method, "POST") != 0) + { + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + } + } + + if (strcmp(Method, "POST") == 0) + { + if (_stricmp(NodeURL, "/WebMail/Signon") == 0) + { + char * msg = strstr(input, "\r\n\r\n"); // End of headers + char * user, * password, * Key; + char Msg[128]; + int n; + + if (msg) + { + struct UserInfo * User; + + if (strstr(msg, "Cancel=Cancel")) + { + *RLen = sprintf(Reply, ""); + return; + } + // Webmail Gets Here with a dummy Session + + Session = AllocateWebMailSession(); + Session->WebMail->Reply = Reply; + Session->WebMail->RLen = RLen; + + + Key = Session->Key; + + user = strtok_s(&msg[9], "&", &Key); + password = strtok_s(NULL, "=", &Key); + password = Key; + + Session->User = User = LookupCall(user); + + if (User) + { + // Check Password + + if (password[0] && strcmp(User->pass, password) == 0) + { + // send Message Index + + Session->WebMailLastUsed = time(NULL); + Session->WebMailSkip = 0; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + Session->WebMailMine = FALSE; + + if (WebMailTemplate) + { + free(WebMailTemplate); + WebMailTemplate = NULL; + } + + if (User->flags & F_Excluded) + { + n = sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s Rejected by Exclude Flag", _strupr(user)); + WriteLogLine(NULL, '|',Msg, n, LOG_BBS); + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + n=sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s", _strupr(user)); + WriteLogLine(NULL, '|',Msg, n, LOG_BBS); + + return; + } + + } + + // Bad User or Pass + + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + } + + Session->WebMail->Reply = Reply; + Session->WebMail->RLen = RLen; + + if (_stricmp(NodeURL, "/WebMail/EMSave") == 0) + { + // Save New Message + + SaveNewMessage(Session, input, Reply, RLen, Key, InputLen); + return; + } + + if (_stricmp(NodeURL, "/WebMail/Submit") == 0) + { + // Get the POST data from the page and place in message + + char * param = strstr(input, "\r\n\r\n"); // End of headers + WebMailInfo * WebMail = Session->WebMail; + + if (WebMail == NULL) + return; // Can't proceed if we have no info on form + + ProcessFormInput(Session, input, Reply, RLen, InputLen); + return; + } + + if (_stricmp(NodeURL, "/WebMail/FormMsgSave") == 0) + { + // Save New Message + + SaveTemplateMessage(Session, input, Reply, RLen, Key); + return; + } + + if (_stricmp(NodeURL, "/WebMail/GetTemplates") == 0) + { + SendTemplateSelectScreen(Session, input, InputLen); + return; + } + + // End of POST section + } + + if (_stricmp(NodeURL, "/WebMail/WMLogout") == 0) + { + Session->Key[0] = 0; + Session->WebMailLastUsed = 0; + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + + if ((_stricmp(NodeURL, "/WebMail/MailEntry") == 0) || + (_stricmp(NodeURL, "/WebMail") == 0) || + (_stricmp(NodeURL, "/WebMail/") == 0)) + { + // Entry from Menu if signed in, continue. If not and Localhost + // signin as sysop. + + if (Session->User == NULL) + { + // Not yet signed in + + if (LOCAL) + { + // Webmail Gets Here with a dummy Session + + Session = AllocateWebMailSession(); + Session->WebMail->Reply = Reply; + Session->WebMail->RLen = RLen; + + Key = Session->Key; + + if (SYSOPCall[0]) + Session->User = LookupCall(SYSOPCall); + else + Session->User = LookupCall(BBSName); + + if (Session->User) + { + strcpy(NodeURL, "/WebMail/WebMail"); + Session->WebMailSkip = 0; + Session->WebMailLastUsed = time(NULL); + } + } + else + { + // Send Login Page + + ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); + *RLen = ReplyLen; + return; + } + } + } + + Session->WebMail->Reply = Reply; + Session->WebMail->RLen = RLen; + + if (_stricmp(NodeURL, "/WebMail/WebMail") == 0) + { + if (WebMailTemplate) + { + free(WebMailTemplate); + WebMailTemplate = NULL; + } + + Session->WebMailSkip = 0; + Session->WebMailMine = FALSE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMAll") == 0) + { + Session->WebMailSkip = 0; + Session->WebMailTypes[0] = 0; + Session->WebMailMine = FALSE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMB") == 0) + { + Session->WebMailSkip = 0; + strcpy(Session->WebMailTypes, "B"); + Session->WebMailMine = FALSE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMP") == 0) + { + Session->WebMailSkip = 0; + strcpy(Session->WebMailTypes, "P"); + Session->WebMailMine = FALSE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMT") == 0) + { + Session->WebMailSkip = 0; + strcpy(Session->WebMailTypes, "T"); + Session->WebMailMine = FALSE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMMine") == 0) + { + Session->WebMailSkip = 0; + Session->WebMailTypes[0] = 0; + Session->WebMailMine = TRUE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMtoMe") == 0) + { + Session->WebMailSkip = 0; + Session->WebMailTypes[0] = 0; + Session->WebMailMine = FALSE; + Session->WebMailMyTX = FALSE; + Session->WebMailMyRX = TRUE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMfromMe") == 0) + { + Session->WebMailSkip = 0; + Session->WebMailTypes[0] = 0; + Session->WebMailMine = TRUE; + Session->WebMailMyTX = TRUE; + Session->WebMailMyRX = FALSE; + + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + + if (_stricmp(NodeURL, "/WebMail/WMSame") == 0) + { + *RLen = SendWebMailHeader(Reply, Session->Key, Session); + return; + } + + if (_stricmp(NodeURL, "/WebMail/WMAuto") == 0) + { + // Auto Refresh Version of index page. Uses Web Sockets + + char Page[4096]; + + char WebSockPage[] = + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "\r\n" + "\r\n" + + "\r\n" + "WebMail \r\n" + "\r\n" + + "\r\n" + "

%s Webmail Interface - User %s - Message List

\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" + "
BullsPersonalNTSAll TypesMineMy SentMy RxedAuto RefreshSend MessageLogoutNode Menu
\r\n" + "
\r\n" + + "
Waiting for data...
\r\n" + "\r\n"; + + sprintf(Page, WebSockPage, Key, Key ,BBSName, Session->User->Call, Key, Key, Key, Key, Key, Key, Key, Key, Key, Key); + + *RLen = sprintf(Reply, "%s", Page); + return; + } + + + if (memcmp(NodeURL, "/WebMail/QuoteOriginal/", 15) == 0) + { + // Reply to Message + + int n, len; + struct MsgInfo * Msg; + char Message[100] = ""; + char Title[100]; + char * MsgBytes, * Save, * NewBytes; + char * ptr; + char * ptr1, * ptr2; + char * EncodedTitle; + + n = Session->WebMail->CurrentMessageIndex; + + Msg = GetMsgFromNumber(n); + + if (Msg == NULL) + { + sprintf(Message, "Message %d not found", n); + *RLen = sprintf(Reply, "%s", Message); + return; + } + + Session->WebMail->Msg = Msg; + + if (stristr(Msg->title, "Re:") == 0) + sprintf(Title, "Re:%s", Msg->title); + else + sprintf(Title, "%s", Msg->title); + + MsgBytes = Save = ReadMessageFile(n); + + + ptr = NewBytes = malloc((Msg->length * 2) + 256); + + // Copy a line at a time with "> " in front of each + + ptr += sprintf(ptr, "%s", "\r\n\r\n\r\n\r\n\r\nOriginal Message\r\n\r\n> "); + + ptr1 = ptr2 = MsgBytes; + len = (int)strlen(MsgBytes); + + while (len-- > 0) + { + *ptr++ = *ptr1; + + if (*(ptr1) == '\n') + { + *ptr++ = '>'; + *ptr++ = ' '; + } + + ptr1++; + } + + *ptr++ = 0; + + EncodedTitle = doXMLTransparency(Msg->title); + + *RLen = sprintf(Reply, MsgInputPage, Key, Msg->from, "", EncodedTitle , NewBytes); + + free(EncodedTitle); + + free(MsgBytes); + free(NewBytes); + + return; + } + + + + if (memcmp(NodeURL, "/WebMail/Reply/", 15) == 0) + { + // Reply to Message + + int n = atoi(&NodeURL[15]); + struct MsgInfo * Msg; + char Message[100] = ""; + char Title[100]; + char * EncodedTitle; + + // Quote Original + + char Button[] = + "      " + ""; + + char Temp[1024]; + char ReplyAddr[128]; + + Msg = GetMsgFromNumber(n); + + if (Msg == NULL) + { + sprintf(Message, "Message %d not found", n); + *RLen = sprintf(Reply, "%s", Message); + return; + } + + Session->WebMail->Msg = Msg; + + // See if the message was displayed in an HTML form with a reply template + + *RLen = ReplyToFormsMessage(Session, Msg, Reply, FALSE); + + // If couldn't build reply form use normal text reply + + if (*RLen) + return; + + + sprintf(Temp, Button, Key); + + if (stristr(Msg->title, "Re:") == 0) + sprintf(Title, "Re:%s", Msg->title); + else + sprintf(Title, "%s", Msg->title); + + strcpy(ReplyAddr, Msg->from); + strcat(ReplyAddr, Msg->emailfrom); + + EncodedTitle = doXMLTransparency(Msg->title); + + *RLen = sprintf(Reply, MsgInputPage, Key, Msg->from, Temp, EncodedTitle , ""); + + free(EncodedTitle); + return; + } + + if (strcmp(NodeURL, "/WebMail/WM") == 0) + { + // Read Message + + int n = 0; + + if (URLParams) + n = atoi(URLParams); + + if (WebMailMsgTemplate) + free(WebMailMsgTemplate); + + WebMailMsgTemplate = GetTemplateFromFile(5, "WebMailMsg.txt"); + + *RLen = ViewWebMailMessage(Session, Reply, n, TRUE); + + return; + } + + if (strcmp(NodeURL, "/WebMail/WMPrev") == 0) + { + // Read Previous Message + + int m; + struct MsgInfo * Msg; + struct UserInfo * User = Session->User; + + + for (m = Session->WebMail->CurrentMessageIndex - 1; m >= 1; m--) + { + + Msg = GetMsgFromNumber(m); + + if (Msg == 0 || Msg->type == 0 || Msg->status == 0) + continue; // Protect against corrupt messages + + if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) + { + // Display if it is the right type and in the page range we want + + if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) + continue; + + // All Types or right Type. Check Mine Flag + + if (Session->WebMailMine) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0) + continue; + } + + if (Session->WebMailMyTX) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->from) != 0) + continue; + } + + if (Session->WebMailMyRX) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to) != 0) + continue; + } + *RLen = ViewWebMailMessage(Session, Reply, m, TRUE); + + return; + } + } + + // No More + + *RLen = sprintf(Reply, "", Session->Key); + return; + + } + + if (strcmp(NodeURL, "/WebMail/WMNext") == 0) + { + // Read Previous Message + + int m; + struct MsgInfo * Msg; + struct UserInfo * User = Session->User; + + for (m = Session->WebMail->CurrentMessageIndex + 1; m <= LatestMsg; m++) + { + Msg = GetMsgFromNumber(m); + + if (Msg == 0 || Msg->type == 0 || Msg->status == 0) + continue; // Protect against corrupt messages + + if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) + { + // Display if it is the right type and in the page range we want + + if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) + continue; + + // All Types or right Type. Check Mine Flag + + if (Session->WebMailMine) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0) + continue; + } + + if (Session->WebMailMyTX) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->from) != 0) + continue; + } + + if (Session->WebMailMyRX) + { + // Only list if to or from me + + if (strcmp(User->Call, Msg->to) != 0) + continue; + } + *RLen = ViewWebMailMessage(Session, Reply, m, TRUE); + + return; + } + } + + // No More + + *RLen = sprintf(Reply, "", Session->Key); + return; + + } + + + if (strcmp(NodeURL, "/WebMail/DisplayText") == 0) + { + // Read Message + + int n = 0; + + if (URLParams) + n = atoi(URLParams); + + if (WebMailMsgTemplate) + free(WebMailMsgTemplate); + + WebMailMsgTemplate = GetTemplateFromFile(5, "WebMailMsg.txt"); + + *RLen = ViewWebMailMessage(Session, Reply, n, FALSE); + + return; + } + if (memcmp(NodeURL, "/WebMail/WMDel/", 15) == 0) + { + // Kill Message + + int n = atoi(&NodeURL[15]); + + *RLen = KillWebMailMessage(Reply, Session->Key, Session->User, n); + + return; + } + + if (_stricmp(NodeURL, "/WebMail/NewMsg") == 0) + { + // Add HTML Template Button if we have any HTML Form + + char Button[] = + "      " + ""; + + char Temp[1024]; + + FreeWebMailFields(Session->WebMail); // Tidy up for new message + + sprintf(Temp, Button, Key); + + if (FormDirCount == 0) + *RLen = sprintf(Reply, MsgInputPage, Key, "", "", "", ""); + else + *RLen = sprintf(Reply, MsgInputPage, Key, "", Temp, "", ""); + + return; + } + + if (_memicmp(NodeURL, "/WebMail/GetPage/", 17) == 0) + { + // Read and Parse Template File + + GetPage(Session, NodeURL); + return; + } + + if (_memicmp(NodeURL, "/WebMail/GetList/", 17) == 0) + { + // Send Select Template Popup + + char * SubDir; + int DirNo = 0; + int SubDirNo = 0; + char popup[10000]; + + char popuphddr[] = + + "" + "" + "

" + "Select Required Template from %s

" + "

"); + + *RLen = sprintf(Reply, "%s", popup); + return; + } + + if (_stricmp(NodeURL, "/WebMail/DL") == 0) + { + getAttachmentList(Session, Reply, RLen, URLParams); + return; + } + + if (_stricmp(NodeURL, "/WebMail/GetDownLoad") == 0) + { + DownloadAttachments(Session, Reply, RLen, URLParams); + return; + } + + if (_stricmp(NodeURL, "/WebMail/DoSelect") == 0) + { + // User has selected item from Template "; + + char NewGroup [] = + "" + "

"); + + *WebMail->RLen = sprintf(WebMail->Reply, "%s", popup); + + free(Boundary); + return; +} + +static char WinlinkAddr[] = "WINLINK.ORG"; + +VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen) +{ + int i, ReplyLen = 0; + struct MsgInfo * Msg; + FILE * hFile; + int Template=0; + char * via = NULL; + BIDRec * BIDRec; + char MsgFile[MAX_PATH]; + size_t WriteLen=0; + char * HDest; + char * HDestCopy; + char * HDestRest; + char * Vptr = NULL; + char * FileList = NULL; + char Prompt[256] = "Message Saved"; + char OrigTo[256]; + WebMailInfo * WebMail = Session->WebMail; + struct UserInfo * user; + CIRCUIT conn; + + // So we can have attachments input is now Content-Type: multipart/form-data; + + char * Input; + size_t MsgLen = 0; + char * Boundary; + + strcpy(conn.Callsign, Session->User->Call); + + Input = MsgPtr; + + Boundary = initMultipartUnpack(&Input); + + if (Boundary == NULL) + return; // Can't work without one + + // Input points to start of part. Normally preceeded by \r\n which is Boundary Terminator. If preceeded by -- we have used last part + + while(Input && Input[-1] != '-') + { + char * Name, * Value; + int ValLen; + + if (unpackPart(Boundary, &Input, &Name, &Value, &ValLen, MsgPtr + InputLen) == FALSE) + { + // ReportCorrupt(WebMail); + free(Boundary); + return; + } + if (SaveInputValue(WebMail, Name, Value, ValLen) == FALSE) + { + *RLen = sprintf(Reply, "", Session->Key); + return; + } + } + + + if (WebMail->txtFileName) + { + // Processing Form Input + + SaveTemplateMessage(Session, MsgPtr, Reply, RLen, Rest); + + // Prevent re-entry + + free(WebMail->txtFileName); + WebMail->txtFileName = NULL; + + return; + } + + // If we aren't using a template then all the information is in the WebMail fields, as we haven't been here before. + + strlop(WebMail->BID, ' '); + if (strlen(WebMail->BID) > 12) + WebMail->BID[12] = 0; + + UndoTransparency(WebMail->BID); + UndoTransparency(WebMail->To); + UndoTransparency(WebMail->Subject); + UndoTransparency(WebMail->Body); + + MsgLen = strlen(WebMail->Body); + + // We will need to mess about with To field. Create a copy so the original can go in B2 header if we use one + + if (WebMail->To[0] == 0) + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + + if (strlen(WebMail->To) > 255) + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + + HDest = _strdup(WebMail->To); + + if (strlen(WebMail->BID)) + { + if (LookupBID(WebMail->BID)) + { + // Duplicate bid + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + } + + if (WebMail->Type == 'B') + { + if (RefuseBulls) + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + } + } + + // ?? Can we just loop though the rest of the code to allow multiple dests ?? + + HDestCopy = HDest; + + while (HDest && HDest[0]) + { + HDestRest = strlop(HDest, ';'); + + Msg = AllocateMsgRecord(); + + // Set number here so they remain in sequence + + Msg->number = ++LatestMsg; + MsgnotoMsg[Msg->number] = Msg; + + strcpy(Msg->from, Session->User->Call); + + if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0) + { + Vptr=&HDest[4]; + strcpy(Msg->to, "RMS"); + } + else if (_memicmp(HDest, "smtp:", 5) == 0) + { + if (ISP_Gateway_Enabled) + { + Vptr=&HDest[5]; + Msg->to[0] = 0; + } + } + else if (strchr(HDest, '@')) + { + strcpy(OrigTo, HDest); + + Vptr = strlop(HDest, '@'); + + if (Vptr) + { + // If looks like a valid email address, treat as such + + if (strlen(HDest) > 6 || !CheckifPacket(Vptr)) + { + // Assume Email address + + Vptr = OrigTo; + + if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route + strcpy(Msg->to, "RMS"); + else if (ISP_Gateway_Enabled) + Msg->to[0] = 0; + else if (isAMPRMsg(OrigTo)) + strcpy(Msg->to, "RMS"); // Routing will redirect it + else + { + *RLen = sprintf(Reply, "%s", ""); + FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise + return; + + } + } + else + strcpy(Msg->to, _strupr(HDest)); + } + } + else + { + // No @ + + if (strlen(HDest) > 6) + HDest[6] = 0; + + strcpy(Msg->to, _strupr(HDest)); + } + + if (SendBBStoSYSOPCall) + if (_stricmp(HDest, BBSName) == 0) + strcpy(Msg->to, SYSOPCall); + + if (Vptr) + { + if (strlen(Vptr) > 40) + Vptr[40] = 0; + + strcpy(Msg->via, _strupr(Vptr)); + } + else + { + // No via. If not local user try to add BBS + + struct UserInfo * ToUser = LookupCall(Msg->to); + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->flags & F_RMSREDIRECT) + { + // sent to Winlink + + strcpy(Msg->via, WinlinkAddr); + sprintf(Prompt, "Redirecting to winlink.org\r"); + } + else if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + sprintf(Prompt, "%s added from HomeBBS. Message Saved", Msg->via); + } + } + else + { + // Not local user - Check WP + + WPRecP WP = LookupWP(Msg->to); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + sprintf(Prompt, "%s added from WP", Msg->via); + } + } + } + + if (strlen(WebMail->Subject) > 60) + WebMail->Subject[60] = 0; + + strcpy(Msg->title, WebMail->Subject); + Msg->type = WebMail->Type; + + if (Session->User->flags & F_HOLDMAIL) + { + int Length=0; + char * MailBuffer = malloc(100); + char Title[100]; + + Msg->status = 'H'; + + Length = sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); + sprintf(Title, "Message %d Held - %s", Msg->number, "User has Hold Messages flag set"); + SendMessageToSYSOP(Title, MailBuffer, Length); + } + else + Msg->status = 'N'; + + if (strlen(WebMail->BID) == 0) + sprintf(Msg->bid, "%d_%s", LatestMsg, BBSName); + else + strcpy(Msg->bid, WebMail->BID); + + Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); + + BIDRec = AllocateBIDRecord(); + + strcpy(BIDRec->BID, Msg->bid); + BIDRec->mode = Msg->type; + BIDRec->u.msgno = LOWORD(Msg->number); + BIDRec->u.timestamp = LOWORD(time(NULL)/86400); + + Msg->length = (int)MsgLen + WebMail->HeaderLen + WebMail->FooterLen; + + sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); + + // BuildFormMessage(Session, Msg); + + if (WebMail->Files) + { + // Send as B2 + + char * B2Header = BuildB2Header(WebMail, Msg, NULL, 0); + + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen = fwrite(B2Header, 1, strlen(B2Header), hFile); + WriteLen += fwrite(WebMail->Header, 1, WebMail->HeaderLen, hFile); + WriteLen += fwrite(WebMail->Body, 1, MsgLen, hFile); + WriteLen += fwrite(WebMail->Footer, 1, WebMail->FooterLen, hFile); + WriteLen += fwrite("\r\n", 1, 2, hFile); + + for (i = 0; i < WebMail->Files; i++) + { + WriteLen += fwrite(WebMail->FileBody[i], 1, WebMail->FileLen[i], hFile); + WriteLen += fwrite("\r\n", 1, 2, hFile); + } + fclose(hFile); + free(B2Header); + + Msg->length = (int)WriteLen; + } + } + else + { + hFile = fopen(MsgFile, "wb"); + + if (hFile) + { + WriteLen += fwrite(WebMail->Header, 1, WebMail->HeaderLen, hFile); + WriteLen += fwrite(WebMail->Body, 1, MsgLen, hFile); + WriteLen += fwrite(WebMail->Footer, 1, WebMail->FooterLen, hFile); + fclose(hFile); + } + } + MatchMessagetoBBSList(Msg, &conn); + + BuildNNTPList(Msg); // Build NNTP Groups list + + if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) + Msg->status = '$'; // Has forwarding + + + if (EnableUI) + SendMsgUI(Msg); + + user = LookupCall(Msg->to); + + // If Event Notifications enabled report a new message event + + SendNewMessageEvent(user->Call, Msg); + +#ifndef NOMQTT + if (MQTT) + MQTTMessageEvent(Msg); +#endif + + if (user && (user->flags & F_APRSMFOR)) + { + char APRS[128]; + char Call[16]; + int SSID = user->flags >> 28; + + if (SSID) + sprintf(Call, "%s-%d", Msg->to, SSID); + else + strcpy(Call, Msg->to); + + sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from); + APISendAPRSMessage(APRS, Call); + } + + HDest = HDestRest; + } + + *RLen = SendWebMailHeaderEx(Reply, Session->Key, Session, Prompt); + + SaveMessageDatabase(); + SaveBIDDatabase(); + FreeWebMailFields(WebMail); + free(HDestCopy); + + return; +} + + + + + + +// RMS Express Forms Support + +char * GetHTMLViewerTemplate(char * FN) +{ + int i, j, k, l; + + // Seach list of forms for base file (without .html) + + for (i = 0; i < FormDirCount; i++) + { + struct HtmlFormDir * Dir = HtmlFormDirs[i]; + + for (j = 0; j < Dir->FormCount; j++) + { + if (strcmp(FN, Dir->Forms[j]->FileName) == 0) + { + return CheckFile(Dir, FN); + } + } + + if (Dir->DirCount) + { + for (l = 0; l < Dir->DirCount; l++) + { + struct HtmlFormDir * SDir = Dir->Dirs[l]; + + if (SDir->DirCount) + { + struct HtmlFormDir * SSDir = SDir->Dirs[0]; + int x = 1; + } + + for (k = 0; k < SDir->FormCount; k++) + { + if (_stricmp(FN, SDir->Forms[k]->FileName) == 0) + { + return CheckFile(SDir, SDir->Forms[k]->FileName); + } + } + if (SDir->DirCount) + { + struct HtmlFormDir * SSDir = SDir->Dirs[0]; + int x = 1; + } + } + } + } + + return NULL; +} +VOID GetReply(struct HTTPConnectionInfo * Session, char * NodeURL) +{ +} + +VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL) +{ + // Read the HTML Template file and do any needed substitutions + + WebMailInfo * WebMail = Session->WebMail; + KeyValues * txtKey = WebMail->txtKeys; + + int DirNo; + char * ptr; + int FileNo = 0; + char * SubDir; + int SubDirNo; + int i; + struct HtmlFormDir * Dir; + char * Template; + char * inptr; + char FormDir[MAX_PATH] = ""; + char FN[MAX_PATH] = ""; + char * InputName = NULL; // HTML to input message + char * ReplyName = NULL; + char * To = NULL; + char * CC = NULL; + char * BID = NULL; + char Type = 0; + char * Subject = NULL; + char * MsgBody = NULL; + char * varptr; + char * endptr; + size_t varlen, vallen = 0; + char val[256]=""; // replacement text + char var[100] = "\""; + char * MsgBytes; + char Submit[64]; + + if (NodeURL == NULL) + { + //rentry after processing or substitutions + + // if Dir not specified search all for Filename + + if (Dir == NULL) + { + for (i = 0; i < FormDirCount; i++) + { + int n; + + Dir = HtmlFormDirs[i]; + + MsgBytes = CheckFile(Dir, WebMail->txtFileName); + if (MsgBytes) + goto gotFile; + + // Recurse any Subdirs + + n = 0; + while (n < Dir->DirCount) + { + MsgBytes = CheckFile(Dir->Dirs[n], FN); + if (MsgBytes) + { + Dir = Dir->Dirs[n]; + goto gotFile; + } + n++; + } + } + return; + } + else + MsgBytes = CheckFile(Dir, WebMail->txtFileName); + +gotFile: + + WebMail->Dir = Dir; + + if (WebMail->txtFile) + free(WebMail->txtFile); + + WebMail->txtFile = MsgBytes; + +reEnter: + + if (ParsetxtTemplate(Session, Dir, WebMail->txtFileName, FALSE) == FALSE) + { + // Template has or + + if (WebMail->InputHTMLName == NULL) + { + // This is a plain text template without HTML +/* + if (To == NULL) + To = ""; + + if (To[0] == 0 && WebMail->To && WebMail->To[0]) + To = WebMail->To; + + if (CC == NULL) + CC = ""; + + if (CC[0] == 0 && WebMail->CC && WebMail->CC[0]) + CC = WebMail->CC; + + if (Subject == NULL) + Subject = ""; + + if (Subject[0] == 0 && WebMail->Subject && WebMail->Subject[0]) + Subject = WebMail->Subject; + + if (MsgBody == NULL) + MsgBody = ""; + + if (MsgBody[0] == 0 && WebMail->Body && WebMail->Body[0]) + MsgBody = WebMail->Body; + + *WebMail->RLen = sprintf(WebMail->Reply, CheckFormMsgPage, Session->Key, To, CC, Subject, MsgBody); + */ + return *WebMail->RLen; + } + + Template = CheckFile(WebMail->Dir, WebMail->InputHTMLName); + + if (Template == NULL) + { + // Missing HTML + + *WebMail->RLen = sprintf(WebMail->Reply, "", WebMail->InputHTMLName); + return *WebMail->RLen; + } + + // I've going to update the template in situ, as I can't see a better way + // of making sure all occurances of variables in any order are substituted. + // The space allocated to Template is twice the size of the file + // to allow for insertions + + UpdateFormAction(Template, Session->Key); // Update "Submit" Action + + // Search for "{var }" strings in form and replace with + // corresponding variable from XML + + while (txtKey->Key) + { + char Key[256] = "{var "; + + strcpy(&Key[5], txtKey->Key); + strcat(Key, "}"); + + inptr = Template; + varptr = stristr(inptr, Key); + + while (varptr) + { + // Move the remaining message up/down the buffer to make space for substitution + + varlen = (int)strlen(Key); + if (txtKey->Value) + vallen = (int)strlen(txtKey->Value); + else vallen = 0; + + endptr = varptr + varlen; + + memmove(varptr + vallen, endptr, strlen(endptr) + 1); // copy null on end + memcpy(varptr, txtKey->Value, vallen); + + inptr = endptr + 1; + + varptr = stristr(inptr, Key); + } + txtKey++; + } + + // Remove from end as we add it on later + + ptr = stristr(Template, ""); + + if (ptr) + *ptr = 0; + + Len = sprintf(Reply, "%s", Template); + free(Template); + return Len; +} + +char * CheckFile(struct HtmlFormDir * Dir, char * FN) +{ + struct stat STAT; + FILE * hFile; + char MsgFile[MAX_PATH]; + char * MsgBytes; + int ReadLen; + int FileSize; + +#ifndef WIN32 + + // Need to do case insensitive file search + + DIR *dir; + struct dirent *entry; + char name[256]; + + sprintf(name, "%s/%s/%s", BPQDirectory, Dir->FormSet, Dir->DirName); + + if (!(dir = opendir(name))) + { + Debugprintf("cant open forms dir %s %s %d", Dir->DirName, name, errno); + return 0; + } + + while ((entry = readdir(dir)) != NULL) + { + if (entry->d_type == DT_DIR) + continue; + + if (stricmp(entry->d_name, FN) == 0) + { + sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), Dir->FormSet, Dir->DirName, entry->d_name); + break; + } + } + closedir(dir); + +#else + + sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), Dir->FormSet, Dir->DirName, FN); + +#endif + + printf("%s\n", MsgFile); + + if (stat(MsgFile, &STAT) != -1) + { + hFile = fopen(MsgFile, "rb"); + + if (hFile == 0) + { + MsgBytes = _strdup("File is missing"); + return MsgBytes; + } + + FileSize = STAT.st_size; + MsgBytes = malloc(FileSize * 10); // Allow plenty of room for template substitution + ReadLen = (int)fread(MsgBytes, 1, FileSize, hFile); + MsgBytes[FileSize] = 0; + fclose(hFile); + + printf("%d %s\n", strlen(MsgBytes), MsgBytes); + + return MsgBytes; + } + return NULL; +} + +BOOL DoSelectPrompt(struct HTTPConnectionInfo * Session, char * Select) +{ + // Send a Popup window to select value. Reply handling code will update template then reenter ParsetxtTemplate + + char popuphddr[] = + + "" + "" + "
%s

" + "" + "
'); + + if (ptr) + *ptr = 0; + + ptr = SelCopy; + + if (*ptr == '"') + { + // String has " " round it + + ptr++; + + ptr1 = strchr(ptr, '"'); + if (ptr1 == NULL) + goto returnDuff; + + *(ptr1++) = 0; + prompt = ptr; + } + else + { + // Normal comma terminated + + ptr1 = strchr(ptr, ','); + if (ptr1 == NULL) + goto returnDuff; + + *(ptr1++) = 0; + prompt = ptr; + } + + ptr = ptr1; + + while (ptr && ptr[0]) + { + if (*ptr == '"') + { + // String has " " round it + + ptr++; + + ptr1 = strchr(ptr, '"'); + if (ptr1 == NULL) + goto returnDuff; + + *(ptr1++) = 0; + while(ptr1 && *ptr1 == ',') + ptr1++; + } + else + { + // Normal comma terminated + + ptr1 = strchr(ptr, ','); + if (ptr1) + *(ptr1++) = 0; + } + + var[vars++] = ptr; + + ptr = ptr1; + } + + len = sprintf(popup, popuphddr, Session->Key, prompt, vars + 1); + + for (i = 0; i < vars; i++) + { + char * key = strlop(var[i], '='); + + if (key == NULL) + key = var[i]; + + len += sprintf(&popup[len], "

"); + + *WebMail->RLen = sprintf(WebMail->Reply, "%s", popup); + free(SelCopy); + return TRUE; + +returnDuff: + *WebMail->RLen = sprintf(WebMail->Reply, "", Session->Key); + free(SelCopy); + return TRUE; + +} + +BOOL DoAskPrompt(struct HTTPConnectionInfo * Session, char * Select) +{ + return TRUE; +} + +VOID ProcessSelectResponse(struct HTTPConnectionInfo * Session, char * URLParams) +{ + // User has entered a response for a Template RLen = sprintf(WebMail->Reply, "", Session->Key); + return; + } + + varptr = Select; + endptr = strchr(Select, '>'); + + if (endptr == NULL) + { + *WebMail->RLen = sprintf(WebMail->Reply, "", Session->Key); + return; + } + + *endptr = 0; + varlen = endptr - varptr; + + + valptr = URLParams; + + // Move the remaining message up/down the buffer to make space for substitution + + vallen = strlen(valptr); + + endptr = varptr + varlen; + + memcpy(varptr, valptr, vallen); + memmove(varptr + vallen, endptr + 1, strlen(endptr + 1) + 1); // copy null on end + + if (WebMail->isReply) + *WebMail->RLen = ReplyToFormsMessage(Session, Session->Msg, WebMail->Reply, TRUE); + else + GetPage(Session, NULL); + + return ; +} + +BOOL ParsetxtTemplate(struct HTTPConnectionInfo * Session, struct HtmlFormDir * Dir, char * FN, BOOL isReply) +{ + WebMailInfo * WebMail = Session->WebMail; + KeyValues * txtKey = WebMail->txtKeys; + + char * MsgBytes; + + char * txtFile; + char * ptr, *ptr1; + char * InputName = NULL; // HTML to input message + char * ReplyName = NULL; + char * To = NULL; + char * Subject = NULL; + char * MsgBody = NULL; + char * Select = NULL; + char * Ask = NULL; + + char Date[16]; + char UDate[16]; + char DateTime[32]; + char UDateTime[32]; + char Day[16]; + char UDay[16]; + char UDTG[32]; + char Seq[16]; + char FormDir[MAX_PATH]; + double Lat; + double Lon; + char LatString[32], LonString[32], GPSString[32]; + BOOL GPSOK; + + struct tm * tm; + time_t NOW; + + // Template is now read before entering here + + MsgBytes = WebMail->txtFile; + + // if Template uses tm_year + 1900,tm->tm_mon + 1, tm->tm_mday); + + sprintf(DateTime, "%04d-%02d-%02d %02d:%02d:%02d", + tm->tm_year + 1900,tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + strcpy(Day, longday[tm->tm_wday]); + + tm = gmtime(&NOW); + + sprintf(UDate, "%04d-%02d-%02dZ", + tm->tm_year + 1900,tm->tm_mon + 1, tm->tm_mday); + + sprintf(UDateTime, "%04d-%02d-%02d %02d:%02d:%02dZ", + tm->tm_year + 100,tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + sprintf(UDTG, "%02d%02d%02dZ %s %04d", + tm->tm_mday, tm->tm_hour, tm->tm_min, month[tm->tm_mon], tm->tm_year + 1900); + + strcpy(UDay, longday[tm->tm_wday]); + + sprintf(Seq, "%d", Session->User->WebSeqNo); + sprintf(FormDir, "/WebMail/WMFile/%s/%s/", WebMail->Dir->FormSet, WebMail->Dir->DirName); + + // Keep SeqNo at front + + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(Seq); + + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(DateTime); + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(UDateTime); + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(Date); + txtKey->Key = _strdup(""); + txtKey++->Value = _strdup(UDate); + txtKey->Key = _strdup("