linbpq/V4.c

1767 lines
40 KiB
C
Raw Permalink Normal View History

2022-08-28 09:35:46 +01:00
/*
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 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 <stdio.h>
#include <time.h>
#define SD_RECEIVE 0x00
#define SD_SEND 0x01
#define SD_BOTH 0x02
#include "CHeaders.h"
#include "tncinfo.h"
#include "bpq32.h"
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 <commctrl.h>
extern int SemHeldByAPI;
static RECT Rect;
extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd
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);
#define MAXBPQPORTS 32
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(-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(&ltime);
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)
{
sprintf(buff->L2DATA, "%d %s", TNC->Port, &buff->L2DATA[6]);
if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK->CIRCUITINDEX, 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, "<html><meta http-equiv=expires content=0><meta http-equiv=refresh content=15>"
"<script type=\"text/javascript\">\r\n"
"function ScrollOutput()\r\n"
"{var textarea = document.getElementById('textarea');"
"textarea.scrollTop = textarea.scrollHeight;}</script>"
"</head><title>V4 Status</title></head><body id=Text onload=\"ScrollOutput()\">"
"<h2>V4 Status</h2>");
Len += sprintf(&Buff[Len], "<table style=\"text-align: left; width: 500px; font-family: monospace; align=center \" border=1 cellpadding=2 cellspacing=2>");
Len += sprintf(&Buff[Len], "<tr><td width=110px>Comms State</td><td>%s</td></tr>", TNC->WEB_COMMSSTATE);
Len += sprintf(&Buff[Len], "<tr><td>TNC State</td><td>%s</td></tr>", TNC->WEB_TNCSTATE);
Len += sprintf(&Buff[Len], "<tr><td>Mode</td><td>%s</td></tr>", TNC->WEB_MODE);
Len += sprintf(&Buff[Len], "<tr><td>Channel State</td><td>%s</td></tr>", TNC->WEB_CHANSTATE);
Len += sprintf(&Buff[Len], "<tr><td>Proto State</td><td>%s</td></tr>", TNC->WEB_PROTOSTATE);
Len += sprintf(&Buff[Len], "<tr><td>Traffic</td><td>%s</td></tr>", TNC->WEB_TRAFFIC);
// Len += sprintf(&Buff[Len], "<tr><td>TNC Restarts</td><td></td></tr>", TNC->WEB_RESTARTS);
Len += sprintf(&Buff[Len], "</table>");
Len += sprintf(&Buff[Len], "<textarea rows=10 style=\"width:500px; height:250px;\" id=textarea >%s</textarea>", 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;
if (TNC->ProgramPath)
TNC->WeStartedTNC = RestartTNC(TNC);
TNC->Hardware = H_V4;
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->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(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);
}
}