linbpq/HFCommon.c

2201 lines
50 KiB
C

/*
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 <time.h>
#define SD_RECEIVE 0x00
#define SD_SEND 0x01
#define SD_BOTH 0x02
#include "kernelresource.h"
#include "CHeaders.h"
#include "tncinfo.h"
#ifndef LINBPQ
#include <commctrl.h>
#endif
//#include <stdlib.h>
#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())
{
#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":"&gt;",
"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, &param);
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'<callsign>', '<base callsign>', '<grid square>', <frequency>, <mode>, <baud>, <power>,
// <antenna height>, <antenna gain>, <antenna direction>, '<hours>', <group reference>, '<service code>'
// 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(&param[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) ;
}