linbpq/CommonCode.c

5625 lines
114 KiB
C
Raw Normal View History

2022-08-28 09:35:46 +01:00
/*
2022-11-14 14:02:28 +00:00
Copyright 2001-2022 John Wiseman G8BPQ
2022-08-28 09:35:46 +01:00
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
*/
// General C Routines common to bpq32 and linbpq. Mainly moved from BPQ32.c
#pragma data_seg("_BPQDATA")
#define _CRT_SECURE_NO_DEPRECATE
#include <stdlib.h>
#include <string.h>
#include <time.h>
2024-11-05 21:03:15 +00:00
#include "mqtt.h"
2022-08-28 09:35:46 +01:00
#pragma data_seg("_BPQDATA")
#include "CHeaders.h"
#include "tncinfo.h"
#include "configstructs.h"
extern struct CONFIGTABLE xxcfg;
#define LIBCONFIG_STATIC
#include "libconfig.h"
#ifndef LINBPQ
//#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
#include "commctrl.h"
#include "Commdlg.h"
#endif
2023-12-28 10:31:09 +00:00
struct TNCINFO * TNCInfo[71]; // Records are Malloc'd
2022-08-28 09:35:46 +01:00
extern int ReportTimer;
Dll VOID APIENTRY Send_AX(UCHAR * Block, DWORD Len, UCHAR Port);
TRANSPORTENTRY * SetupSessionFromHost(PBPQVECSTRUC HOST, UINT ApplMask);
int Check_Timer();
VOID SENDUIMESSAGE(struct DATAMESSAGE * Msg);
DllExport struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot);
VOID APIENTRY md5 (char *arg, unsigned char * checksum);
VOID COMSetDTR(HANDLE fd);
VOID COMClearDTR(HANDLE fd);
VOID COMSetRTS(HANDLE fd);
VOID COMClearRTS(HANDLE fd);
VOID WriteMiniDump();
void printStack(void);
char * FormatMH(PMHSTRUC MH, char Format);
void WriteConnectLog(char * fromCall, char * toCall, UCHAR * Mode);
2023-10-10 22:07:04 +01:00
void SendDataToPktMap(char *Msg);
2022-08-28 09:35:46 +01:00
extern BOOL LogAllConnects;
2023-10-10 22:07:04 +01:00
extern BOOL M0LTEMap;
2024-10-11 15:37:11 +01:00
char * stristr (char *ch1, char *ch2);
2022-08-28 09:35:46 +01:00
2022-10-03 15:55:07 +01:00
extern VOID * ENDBUFFERPOOL;
2023-05-16 16:40:12 +01:00
2022-08-28 09:35:46 +01:00
// Read/Write length field in a buffer header
// Needed for Big/LittleEndian and ARM5 (unaligned operation problem) portability
VOID PutLengthinBuffer(PDATAMESSAGE buff, USHORT datalen)
{
if (datalen <= sizeof(void *) + 4)
datalen = sizeof(void *) + 4; // Protect
memcpy(&buff->LENGTH, &datalen, 2);
}
int GetLengthfromBuffer(PDATAMESSAGE buff)
{
USHORT Length;
memcpy(&Length, &buff->LENGTH, 2);
return Length;
}
BOOL CheckQHeadder(UINT * Q)
{
#ifdef WIN32
UINT Test;
__try
{
Test = *Q;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
Debugprintf("Invalid Q Header %p", Q);
printStack();
return FALSE;
}
#endif
return TRUE;
}
// Get buffer from Queue
VOID * _Q_REM(VOID **PQ, char * File, int Line)
{
void ** Q;
void ** first;
VOID * next;
PMESSAGE Test;
// PQ may not be word aligned, so copy as bytes (for ARM5)
Q = PQ;
if (Semaphore.Flag == 0)
Debugprintf("Q_REM called without semaphore from %s Line %d", File, Line);
if (CheckQHeadder((UINT *) Q) == 0)
return(0);
first = Q[0];
if (first == 0)
return (0); // Empty
next = first[0]; // Address of next buffer
Q[0] = next;
// Make sure guard zone is zeros
Test = (PMESSAGE)first;
if (Test->GuardZone != 0)
{
Debugprintf("Q_REM %p GUARD ZONE CORRUPT %x Called from %s Line %d", first, Test->GuardZone, File, Line);
printStack();
}
return first;
}
// Non=pool version (for IPGateway)
VOID * _Q_REM_NP(VOID *PQ, char * File, int Line)
{
void ** Q;
void ** first;
void * next;
// PQ may not be word aligned, so copy as bytes (for ARM5)
Q = PQ;
if (CheckQHeadder((UINT *)Q) == 0)
return(0);
first = Q[0];
if (first == 0) return (0); // Empty
next = first[0]; // Address of next buffer
Q[0] = next;
return first;
}
// Return Buffer to Free Queue
extern VOID * BUFFERPOOL;
extern void ** Bufferlist[1000];
void printStack(void);
void _CheckGuardZone(char * File, int Line)
{
int n = 0, i, offset = 0;
PMESSAGE Test;
UINT CodeDump[8];
unsigned char * ptr;
n = NUMBEROFBUFFERS;
while (n--)
{
Test = (PMESSAGE)Bufferlist[n];
if (Test && Test->GuardZone)
{
Debugprintf("CheckGuardZone %p GUARD ZONE CORRUPT %d Called from %s Line %d", Test, Test->Process, File, Line);
offset = 0;
ptr = (unsigned char *)Test;
while (offset < 400)
{
memcpy(CodeDump, &ptr[offset], 32);
for (i = 0; i < 8; i++)
CodeDump[i] = htonl(CodeDump[i]);
Debugprintf("%08x %08x %08x %08x %08x %08x %08x %08x %08x ",
&ptr[offset], CodeDump[0], CodeDump[1], CodeDump[2], CodeDump[3], CodeDump[4], CodeDump[5], CodeDump[6], CodeDump[7]);
offset += 32;
}
WriteMiniDump();
#ifdef MDIKERNEL
CloseAllNeeded = 1;
#endif
}
}
}
UINT _ReleaseBuffer(VOID *pBUFF, char * File, int Line)
{
void ** pointer, ** BUFF = pBUFF;
int n = 0;
void ** debug;
PMESSAGE Test;
2022-10-03 15:55:07 +01:00
UINT CodeDump[16];
int i;
unsigned int rev;
2022-08-28 09:35:46 +01:00
if (Semaphore.Flag == 0)
Debugprintf("ReleaseBuffer called without semaphore from %s Line %d", File, Line);
// Make sure address is within pool
2022-10-03 15:55:07 +01:00
if ((uintptr_t)BUFF < (uintptr_t)BUFFERPOOL || (uintptr_t)BUFF > (uintptr_t)ENDBUFFERPOOL)
{
// Not pointing to a buffer . debug points to the buffer that this is chained from
// Dump first chunk and source tag
memcpy(CodeDump, BUFF, 64);
2022-10-18 10:09:06 +01:00
Debugprintf("Releasebuffer Buffer not in pool from %s Line %d, ptr %p prev %d", File, Line, BUFF, 0);
2022-10-03 15:55:07 +01:00
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",
CodeDump[8], CodeDump[9], CodeDump[10], CodeDump[11], CodeDump[12], CodeDump[13], CodeDump[14], CodeDump[15]);
return 0;
}
2022-08-28 09:35:46 +01:00
Test = (PMESSAGE)pBUFF;
if (Test->GuardZone != 0)
{
Debugprintf("_ReleaseBuffer %p GUARD ZONE CORRUPT %x Called from %s Line %d", pBUFF, Test->GuardZone, File, Line);
}
while (n <= NUMBEROFBUFFERS)
{
if (BUFF == Bufferlist[n++])
goto BOK1;
}
Debugprintf("ReleaseBuffer %X not in Pool called from %s Line %d", BUFF, File, Line);
printStack();
return 0;
BOK1:
n = 0;
2022-10-03 15:55:07 +01:00
// validate free Queue
2022-08-28 09:35:46 +01:00
pointer = FREE_Q;
2022-10-03 15:55:07 +01:00
debug = &FREE_Q;
2022-08-28 09:35:46 +01:00
while (pointer)
{
2022-10-03 15:55:07 +01:00
// Validate pointer to make sure it is in pool - it may be a duff address if Q is corrupt
Test = (PMESSAGE)pointer;
if (Test->GuardZone || (uintptr_t)pointer < (uintptr_t)BUFFERPOOL || (uintptr_t)pointer > (uintptr_t)ENDBUFFERPOOL)
{
// Not pointing to a buffer . debug points to the buffer that this is chained from
// Dump first chunk and source tag
memcpy(CodeDump, debug, 64);
Debugprintf("Releasebuffer Pool Corruption n = %d, ptr %p prev %p", n, pointer, debug);
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",
CodeDump[8], CodeDump[9], CodeDump[10], CodeDump[11], CodeDump[12], CodeDump[13], CodeDump[14], CodeDump[15]);
if (debug[400])
Debugprintf(" %s", &debug[400]);
}
// See if already on free Queue
2022-08-28 09:35:46 +01:00
if (pointer == BUFF)
{
2022-10-18 10:09:06 +01:00
Debugprintf("Trying to free buffer %p when already on FREE_Q called from %s Line %d", BUFF, File, Line);
2022-08-28 09:35:46 +01:00
// WriteMiniDump();
return 0;
}
// if (pointer[0] && pointer == pointer[0])
// {
// Debugprintf("Buffer chained to itself");
// return 0;
// }
debug = pointer;
pointer = pointer[0];
n++;
if (n > 1000)
{
Debugprintf("Loop searching free chain - pointer = %p %p", debug, pointer);
return 0;
}
}
pointer = FREE_Q;
*BUFF = pointer;
FREE_Q = BUFF;
QCOUNT++;
return 0;
}
int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line)
{
void ** Q;
void ** BUFF = PBUFF;
void ** next;
PMESSAGE Test;
int n = 0;
// PQ may not be word aligned, so copy as bytes (for ARM5)
Q = PQ;
if (Semaphore.Flag == 0)
Debugprintf("C_Q_ADD called without semaphore from %s Line %d", File, Line);
if (CheckQHeadder((UINT *)Q) == 0) // Make sure Q header is readable
return(0);
// Make sure guard zone is zeros
Test = (PMESSAGE)PBUFF;
if (Test->GuardZone != 0)
{
Debugprintf("C_Q_ADD %p GUARD ZONE CORRUPT %x Called from %s Line %d", PBUFF, Test->GuardZone, File, Line);
}
Test = (PMESSAGE)Q;
// Make sure address is within pool
while (n <= NUMBEROFBUFFERS)
{
if (BUFF == Bufferlist[n++])
goto BOK2;
}
Debugprintf("C_Q_ADD %X not in Pool called from %s Line %d", BUFF, File, Line);
printStack();
return 0;
BOK2:
BUFF[0] = 0; // Clear chain in new buffer
if (Q[0] == 0) // Empty
{
Q[0]=BUFF; // New one on front
return(0);
}
next = Q[0];
while (next[0] != 0)
{
next = next[0]; // Chain to end of queue
}
next[0] = BUFF; // New one on end
return(0);
}
// Non-pool version
int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF)
{
void ** Q;
void ** BUFF = PBUFF;
void ** next;
int n = 0;
// PQ may not be word aligned, so copy as bytes (for ARM5)
Q = PQ;
if (CheckQHeadder((UINT *)Q) == 0) // Make sure Q header is readable
return(0);
BUFF[0]=0; // Clear chain in new buffer
if (Q[0] == 0) // Empty
{
Q[0]=BUFF; // New one on front
// memcpy(PQ, &BUFF, 4);
return 0;
}
next = Q[0];
while (next[0] != 0)
next=next[0]; // Chain to end of queue
next[0] = BUFF; // New one on end
return(0);
}
int C_Q_COUNT(VOID *PQ)
{
void ** Q;
int count = 0;
// PQ may not be word aligned, so copy as bytes (for ARM5)
Q = PQ;
if (CheckQHeadder((UINT *)Q) == 0) // Make sure Q header is readable
return(0);
// SEE HOW MANY BUFFERS ATTACHED TO Q HEADER
while (*Q)
{
count++;
if ((count + QCOUNT) > MAXBUFFS)
{
Debugprintf("C_Q_COUNT Detected corrupt Q %p len %d", PQ, count);
return count;
}
Q = *Q;
}
return count;
}
VOID * _GetBuff(char * File, int Line)
{
UINT * Temp;
MESSAGE * Msg;
char * fptr = 0;
unsigned char * byteaddr;
Temp = Q_REM(&FREE_Q);
// FindLostBuffers();
if (Semaphore.Flag == 0)
Debugprintf("GetBuff called without semaphore from %s Line %d", File, Line);
if (Temp)
{
QCOUNT--;
if (QCOUNT < MINBUFFCOUNT)
MINBUFFCOUNT = QCOUNT;
Msg = (MESSAGE *)Temp;
fptr = File + (int)strlen(File);
while (*fptr != '\\' && *fptr != '/')
fptr--;
fptr++;
// Buffer Length is BUFFLEN, but buffers are allocated 512
// So add file info in gap between
byteaddr = (unsigned char *)Msg;
2022-10-23 09:28:34 +01:00
memset(&byteaddr[0], 0, 64); // simplify debugging lost buffers
2022-08-28 09:35:46 +01:00
memset(&byteaddr[400], 0, 64); // simplify debugging lost buffers
sprintf(&byteaddr[400], "%s %d", fptr, Line);
Msg->Process = (short)GetCurrentProcessId();
Msg->Linkptr = NULL;
2024-10-11 15:37:11 +01:00
Msg->Padding[0] = 0; // Used for modem status info
2022-08-28 09:35:46 +01:00
}
else
Debugprintf("Warning - Getbuff returned NULL");
return Temp;
}
void * zalloc(int len)
{
// malloc and clear
void * ptr;
ptr=malloc(len);
if (ptr)
memset(ptr, 0, len);
return ptr;
}
2024-12-16 17:54:16 +00:00
char * strlop(const char * buf, char delim)
2022-08-28 09:35:46 +01:00
{
// 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;
}
VOID DISPLAYCIRCUIT(TRANSPORTENTRY * L4, char * Buffer)
{
UCHAR Type = L4->L4CIRCUITTYPE;
struct PORTCONTROL * PORT;
struct _LINKTABLE * LINK;
BPQVECSTRUC * VEC;
struct DEST_LIST * DEST;
char Normcall[20] = ""; // Could be alias:call
char Normcall2[11] = "";
char Alias[11] = "";
Buffer[0] = 0;
switch (Type)
{
case PACTOR+UPLINK:
PORT = L4->L4TARGET.PORT;
ConvFromAX25(L4->L4USER, Normcall);
strlop(Normcall, ' ');
if (PORT)
sprintf(Buffer, "%s %d/%d(%s)", "TNC Uplink Port", PORT->PORTNUMBER, L4->KAMSESSION, Normcall);
return;
case PACTOR+DOWNLINK:
PORT = L4->L4TARGET.PORT;
if (PORT)
sprintf(Buffer, "%s %d/%d", "Attached to Port", PORT->PORTNUMBER, L4->KAMSESSION);
return;
case L2LINK+UPLINK:
LINK = L4->L4TARGET.LINK;
ConvFromAX25(L4->L4USER, Normcall);
strlop(Normcall, ' ');
if (LINK &&LINK->LINKPORT)
sprintf(Buffer, "%s %d(%s)", "Uplink", LINK->LINKPORT->PORTNUMBER, Normcall);
return;
case L2LINK+DOWNLINK:
LINK = L4->L4TARGET.LINK;
if (LINK == NULL)
return;
ConvFromAX25(LINK->OURCALL, Normcall);
strlop(Normcall, ' ');
ConvFromAX25(LINK->LINKCALL, Normcall2);
strlop(Normcall2, ' ');
sprintf(Buffer, "%s %d(%s %s)", "Downlink", LINK->LINKPORT->PORTNUMBER, Normcall, Normcall2);
return;
case BPQHOST + UPLINK:
case BPQHOST + DOWNLINK:
// if the call has a Level 4 address display ALIAS:CALL, else just Call
if (FindDestination(L4->L4USER, &DEST))
Normcall[DecodeNodeName(DEST->DEST_CALL, Normcall)] = 0; // null terminate
else
Normcall[ConvFromAX25(L4->L4USER, Normcall)] = 0;
VEC = L4->L4TARGET.HOST;
sprintf(Buffer, "%s%02d(%s)", "Host", (int)(VEC - BPQHOSTVECTOR) + 1, Normcall);
return;
case SESSION + DOWNLINK:
case SESSION + UPLINK:
ConvFromAX25(L4->L4USER, Normcall);
strlop(Normcall, ' ');
DEST = L4->L4TARGET.DEST;
if (DEST == NULL)
return;
ConvFromAX25(DEST->DEST_CALL, Normcall2);
strlop(Normcall2, ' ');
memcpy(Alias, DEST->DEST_ALIAS, 6);
strlop(Alias, ' ');
sprintf(Buffer, "Circuit(%s:%s %s)", Alias, Normcall2, Normcall);
return;
}
}
VOID CheckForDetach(struct TNCINFO * TNC, int Stream, struct STREAMINFO * STREAM,
2024-12-16 17:54:16 +00:00
VOID TidyCloseProc(struct TNCINFO * TNC, int Stream), VOID ForcedCloseProc(struct TNCINFO * TNC, int Stream), VOID CloseComplete(struct TNCINFO * TNC, int Stream))
2022-08-28 09:35:46 +01:00
{
void ** buffptr;
if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0)
{
// Node has disconnected - clear any connection
if (STREAM->Disconnecting)
{
// Already detected the detach, and have started to close
STREAM->DisconnectingTimeout--;
if (STREAM->DisconnectingTimeout)
return; // Give it a bit longer
// Close has timed out - force a disc, and clear
ForcedCloseProc(TNC, Stream); // Send Tidy Disconnect
goto NotConnected;
}
// New Disconnect
Debugprintf("New Disconnect Port %d Q %x", TNC->Port, STREAM->BPQtoPACTOR_Q);
if (STREAM->Connected || STREAM->Connecting)
{
char logmsg[120];
time_t Duration;
// Need to do a tidy close
STREAM->Connecting = FALSE;
STREAM->Disconnecting = TRUE;
STREAM->DisconnectingTimeout = 300; // 30 Secs
if (Stream == 0)
SetWindowText(TNC->xIDC_TNCSTATE, "Disconnecting");
// Create a traffic record
2024-11-05 21:03:15 +00:00
hookL4SessionDeleted(TNC, STREAM);
2022-08-28 09:35:46 +01:00
if (STREAM->BPQtoPACTOR_Q) // Still data to send?
return; // Will close when all acked
// if (STREAM->FramesOutstanding && TNC->Hardware == H_UZ7HO)
// return; // Will close when all acked
TidyCloseProc(TNC, Stream); // Send Tidy Disconnect
return;
}
// Not connected
NotConnected:
STREAM->Disconnecting = FALSE;
STREAM->Attached = FALSE;
STREAM->Connecting = FALSE;
STREAM->Connected = FALSE;
if (Stream == 0)
SetWindowText(TNC->xIDC_TNCSTATE, "Free");
STREAM->FramesQueued = 0;
STREAM->FramesOutstanding = 0;
CloseComplete(TNC, Stream);
2023-03-16 06:52:27 +00:00
if (TNC->DefaultRXFreq && TNC->RXRadio)
{
char Msg[128];
sprintf(Msg, "R%d %f", TNC->RXRadio, TNC->DefaultRXFreq);
2023-05-16 16:40:12 +01:00
Rig_Command( (TRANSPORTENTRY *) -1, Msg);
2023-03-16 06:52:27 +00:00
}
if (TNC->DefaultTXFreq && TNC->TXRadio && TNC->TXRadio != TNC->RXRadio)
{
char Msg[128];
sprintf(Msg, "R%d %f", TNC->TXRadio, TNC->DefaultTXFreq);
2023-05-16 16:40:12 +01:00
Rig_Command( (TRANSPORTENTRY *) -1, Msg);
2023-03-16 06:52:27 +00:00
}
2022-08-28 09:35:46 +01:00
while(STREAM->BPQtoPACTOR_Q)
{
buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q);
ReleaseBuffer(buffptr);
}
while(STREAM->PACTORtoBPQ_Q)
{
buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q);
ReleaseBuffer(buffptr);
}
}
}
char * CheckAppl(struct TNCINFO * TNC, char * Appl)
{
APPLCALLS * APPL;
BPQVECSTRUC * PORTVEC;
int Allocated = 0, Available = 0;
int App, Stream;
struct TNCINFO * APPLTNC;
// Debugprintf("Checking if %s is running", Appl);
for (App = 0; App < 32; App++)
{
APPL=&APPLCALLTABLE[App];
if (_memicmp(APPL->APPLCMD, Appl, 12) == 0)
{
int _APPLMASK = 1 << App;
// If App has an alias, assume it is running , unless a CMS alias - then check CMS
if (APPL->APPLHASALIAS)
{
if (_memicmp(APPL->APPLCMD, "RELAY ", 6) == 0)
return APPL->APPLCALL_TEXT; // Assume people using RELAY know what they are doing
if (APPL->APPLPORT && (_memicmp(APPL->APPLCMD, "RMS ", 4) == 0))
{
APPLTNC = TNCInfo[APPL->APPLPORT];
{
if (APPLTNC)
{
if (APPLTNC->TCPInfo && !APPLTNC->TCPInfo->CMSOK && !APPLTNC->TCPInfo->FallbacktoRelay)
return NULL;
}
}
}
return APPL->APPLCALL_TEXT;
}
// See if App is running
PORTVEC = &BPQHOSTVECTOR[0];
for (Stream = 0; Stream < 64; Stream++)
{
if (PORTVEC->HOSTAPPLMASK & _APPLMASK)
{
Allocated++;
if (PORTVEC->HOSTSESSION == 0 && (PORTVEC->HOSTFLAGS & 3) == 0)
{
// Free and no outstanding report
return APPL->APPLCALL_TEXT; // Running
}
}
PORTVEC++;
}
}
}
return NULL; // Not Running
}
VOID SetApplPorts()
{
// If any appl has an alias, get port number
struct APPLCONFIG * App;
APPLCALLS * APPL;
char C[80];
char Port[80];
char Call[80];
int i, n;
App = &xxcfg.C_APPL[0];
for (i=0; i < NumberofAppls; i++)
{
APPL=&APPLCALLTABLE[i];
if (APPL->APPLHASALIAS)
{
n = sscanf(App->CommandAlias, "%s %s %s", &C[0], &Port[0], &Call[0]);
if (n == 3)
APPL->APPLPORT = atoi(Port);
}
App++;
}
}
char Modenames[19][10] = {"WINMOR", "SCS", "KAM", "AEA", "HAL", "TELNET", "TRK",
"V4", "UZ7HO", "MPSK", "FLDIGI", "UIARQ", "ARDOP", "VARA",
"SERIAL", "KISSHF", "WINRPR", "HSMODEM", "FREEDATA"};
BOOL ProcessIncommingConnect(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT)
{
return ProcessIncommingConnectEx(TNC, Call, Stream, SENDCTEXT, FALSE);
}
BOOL ProcessIncommingConnectEx(struct TNCINFO * TNC, char * Call, int Stream, BOOL SENDCTEXT, BOOL AllowTR)
{
TRANSPORTENTRY * Session;
int Index = 0;
PMSGWITHLEN buffptr;
int Totallen = 0;
UCHAR * ptr;
2024-01-16 20:42:28 +00:00
struct PORTCONTROL * PORT = (struct PORTCONTROL *)TNC->PortRecord;
2024-11-05 21:03:15 +00:00
struct STREAMINFO * STREAM = &TNC->Streams[Stream];
2023-02-05 11:01:05 +00:00
2022-08-28 09:35:46 +01:00
// Stop Scanner
if (Stream == 0 || TNC->Hardware == H_UZ7HO)
{
char Msg[80];
sprintf(Msg, "%d SCANSTOP", TNC->Port);
2023-05-16 16:40:12 +01:00
Rig_Command( (TRANSPORTENTRY *) -1, Msg);
2023-02-05 11:01:05 +00:00
2022-08-28 09:35:46 +01:00
UpdateMH(TNC, Call, '+', 'I');
}
Session = L4TABLE;
// Find a free Circuit Entry
while (Index < MAXCIRCUITS)
{
if (Session->L4USER[0] == 0)
break;
Session++;
Index++;
}
if (Index == MAXCIRCUITS)
return FALSE; // Tables Full
memset(Session, 0, sizeof(TRANSPORTENTRY));
2024-11-05 21:03:15 +00:00
memcpy(STREAM->RemoteCall, Call, 9); // Save Text Callsign
// May be subsequently rejected but a good place to capture calls
hookL4SessionAccepted(STREAM, Call, TNC->TargetCall);
2022-08-28 09:35:46 +01:00
if (AllowTR)
ConvToAX25Ex(Call, Session->L4USER); // Allow -T and -R SSID's for MPS
else
ConvToAX25(Call, Session->L4USER);
ConvToAX25(MYNODECALL, Session->L4MYCALL);
Session->CIRCUITINDEX = Index;
Session->CIRCUITID = NEXTID;
NEXTID++;
if (NEXTID == 0) NEXTID++; // Keep non-zero
TNC->PortRecord->ATTACHEDSESSIONS[Stream] = Session;
2024-11-05 21:03:15 +00:00
STREAM->Attached = TRUE;
2022-08-28 09:35:46 +01:00
Session->L4TARGET.EXTPORT = TNC->PortRecord;
Session->L4CIRCUITTYPE = UPLINK+PACTOR;
Session->L4WINDOW = L4DEFAULTWINDOW;
Session->L4STATE = 5;
Session->SESSIONT1 = L4T1;
Session->SESSPACLEN = TNC->PortRecord->PORTCONTROL.PORTPACLEN;
Session->KAMSESSION = Stream;
2024-11-05 21:03:15 +00:00
STREAM->Connected = TRUE; // Subsequent data to data channel
2022-08-28 09:35:46 +01:00
if (LogAllConnects)
{
if (TNC->TargetCall[0])
WriteConnectLog(Call, TNC->TargetCall, Modenames[TNC->Hardware - 1]);
else
WriteConnectLog(Call, MYNODECALL, Modenames[TNC->Hardware - 1]);
}
if (SENDCTEXT == 0)
return TRUE;
// if Port CTEXT defined, use it
2023-02-05 11:01:05 +00:00
2022-08-28 09:35:46 +01:00
if (PORT->CTEXT)
{
Totallen = strlen(PORT->CTEXT);
ptr = PORT->CTEXT;
}
else if (HFCTEXTLEN > 0)
{
Totallen = HFCTEXTLEN;
ptr = HFCTEXT;
}
else
return TRUE;
while (Totallen > 0)
{
int sendLen = TNC->PortRecord->ATTACHEDSESSIONS[Stream]->SESSPACLEN;
if (sendLen == 0)
sendLen = 80;
if (Totallen < sendLen)
sendLen = Totallen;
buffptr = (PMSGWITHLEN)GetBuff();
if (buffptr == 0) return TRUE; // No buffers
buffptr->Len = sendLen;
memcpy(&buffptr->Data[0], ptr, sendLen);
C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr);
Totallen -= sendLen;
ptr += sendLen;
}
return TRUE;
}
char * Config;
static char * ptr1, * ptr2;
2024-12-16 17:54:16 +00:00
BOOL ReadConfigFile(int Port, int ProcLine(char * buf, int Port))
2022-08-28 09:35:46 +01:00
{
char buf[256],errbuf[256];
if (TNCInfo[Port]) // If restarting, free old config
free(TNCInfo[Port]);
TNCInfo[Port] = NULL;
Config = PortConfig[Port];
if (Config)
{
// Using config from bpq32.cfg
if (strlen(Config) == 0)
{
// Empty Config File - OK for most types
struct TNCINFO * TNC = TNCInfo[Port] = zalloc(sizeof(struct TNCINFO));
TNC->InitScript = malloc(2);
TNC->InitScript[0] = 0;
return TRUE;
}
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 error
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);
}
int GetLine(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 DigiToMultiplePorts(struct PORTCONTROL * PORTVEC, PMESSAGE Msg)
{
USHORT Mask=PORTVEC->DIGIMASK;
int i;
for (i=1; i<=NUMBEROFPORTS; i++)
{
if (Mask & 1)
{
// Block includes the Msg Header (7/11 bytes), Len Does not!
Msg->PORT = i;
Send_AX((UCHAR *)&Msg, Msg->LENGTH - MSGHDDRLEN, i);
Mask>>=1;
}
}
}
int CompareAlias(struct DEST_LIST ** a, struct DEST_LIST ** b)
{
return memcmp(a[0]->DEST_ALIAS, b[0]->DEST_ALIAS, 6);
/* strcmp functions works exactly as expected from comparison function */
}
int CompareNode(struct DEST_LIST ** a, struct DEST_LIST ** b)
{
return memcmp(a[0]->DEST_CALL, b[0]->DEST_CALL, 7);
}
DllExport int APIENTRY CountFramesQueuedOnStream(int Stream)
{
BPQVECSTRUC * PORTVEC = &BPQHOSTVECTOR[Stream-1]; // API counts from 1
TRANSPORTENTRY * L4 = PORTVEC->HOSTSESSION;
int Count = 0;
if (L4)
{
if (L4->L4CROSSLINK) // CONNECTED?
Count = CountFramesQueuedOnSession(L4->L4CROSSLINK);
else
Count = CountFramesQueuedOnSession(L4);
}
return Count;
}
DllExport int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall)
{
// Equivalent to "*** linked to" command
memcpy(BPQHOSTVECTOR[Stream-1].HOSTSESSION->L4USER, AXCall, 7);
return (0);
}
DllExport int APIENTRY ChangeSessionPaclen(int Stream, int Paclen)
{
BPQHOSTVECTOR[Stream-1].HOSTSESSION->SESSPACLEN = Paclen;
return (0);
}
DllExport int APIENTRY ChangeSessionIdletime(int Stream, int idletime)
{
if (BPQHOSTVECTOR[Stream-1].HOSTSESSION)
BPQHOSTVECTOR[Stream-1].HOSTSESSION->L4LIMIT = idletime;
return (0);
}
DllExport int APIENTRY Get_APPLMASK(int Stream)
{
return BPQHOSTVECTOR[Stream-1].HOSTAPPLMASK;
}
DllExport int APIENTRY GetStreamPID(int Stream)
{
return BPQHOSTVECTOR[Stream-1].STREAMOWNER;
}
DllExport int APIENTRY GetApplFlags(int Stream)
{
return BPQHOSTVECTOR[Stream-1].HOSTAPPLFLAGS;
}
DllExport int APIENTRY GetApplNum(int Stream)
{
return BPQHOSTVECTOR[Stream-1].HOSTAPPLNUM;
}
DllExport int APIENTRY GetApplMask(int Stream)
{
return BPQHOSTVECTOR[Stream-1].HOSTAPPLMASK;
}
DllExport BOOL APIENTRY GetAllocationState(int Stream)
{
return BPQHOSTVECTOR[Stream-1].HOSTFLAGS & 0x80;
}
VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port);
extern int InitDone;
extern int SemHeldByAPI;
extern char pgm[256]; // Uninitialised so per process
extern int BPQHOSTAPI();
VOID POSTSTATECHANGE(BPQVECSTRUC * SESS)
{
// Post a message if requested
#ifndef LINBPQ
if (SESS->HOSTHANDLE)
PostMessage(SESS->HOSTHANDLE, BPQMsg, SESS->HOSTSTREAM, 4);
#endif
return;
}
DllExport int APIENTRY SessionControl(int stream, int command, int Mask)
{
BPQVECSTRUC * SESS;
TRANSPORTENTRY * L4;
stream--; // API uses 1 - 64
if (stream < 0 || stream > 63)
return (0);
SESS = &BPQHOSTVECTOR[stream];
// Send Session Control command (BPQHOST function 6)
//; CL=0 CONNECT USING APPL MASK IN DL
//; CL=1, CONNECT. CL=2 - DISCONNECT. CL=3 RETURN TO NODE
if (command > 1)
{
// Disconnect
if (SESS->HOSTSESSION == 0)
{
SESS->HOSTFLAGS |= 1; // State Change
POSTSTATECHANGE(SESS);
return 0; // NOT CONNECTED
}
if (command == 3)
SESS->HOSTFLAGS |= 0x20; // Set Stay
SESS->HOSTFLAGS |= 0x40; // SET 'DISC REQ' FLAG
return 0;
}
// 0 or 1 - connect
if (SESS->HOSTSESSION) // ALREADY CONNECTED
{
SESS->HOSTFLAGS |= 1; // State Change
POSTSTATECHANGE(SESS);
return 0;
}
// SET UP A SESSION FOR THE CONSOLE
SESS->HOSTFLAGS |= 0x80; // SET ALLOCATED BIT
if (command == 1) // Zero is mask supplied by caller
Mask = SESS->HOSTAPPLMASK; // SO WE GET CORRECT CALLSIGN
L4 = SetupSessionFromHost(SESS, Mask);
if (L4 == 0) // tables Full
{
SESS->HOSTFLAGS |= 3; // State Change
POSTSTATECHANGE(SESS);
return 0;
}
SESS->HOSTSESSION = L4;
L4->L4CIRCUITTYPE = BPQHOST | UPLINK;
L4->Secure_Session = AuthorisedProgram; // Secure Host Session
SESS->HOSTFLAGS |= 1; // State Change
POSTSTATECHANGE(SESS);
return 0; // ALREADY CONNECTED
}
int FindFreeStreamEx(int GetSem);
int FindFreeStreamNoSem()
{
return FindFreeStreamEx(0);
}
DllExport int APIENTRY FindFreeStream()
{
return FindFreeStreamEx(1);
}
int FindFreeStreamEx(int GetSem)
{
int stream, n;
BPQVECSTRUC * PORTVEC;
// Returns number of first unused BPQHOST stream. If none available,
// returns 255. See API function 13.
// if init has not yet been run, wait.
while (InitDone == 0)
{
Debugprintf("Waiting for init to complete");
Sleep(1000);
}
if (InitDone == -1) // Init failed
exit(0);
if (GetSem)
GetSemaphore(&Semaphore, 9);
stream = 0;
n = 64;
while (n--)
{
PORTVEC = &BPQHOSTVECTOR[stream++];
if ((PORTVEC->HOSTFLAGS & 0x80) == 0)
{
PORTVEC->STREAMOWNER=GetCurrentProcessId();
PORTVEC->HOSTFLAGS = 128; // SET ALLOCATED BIT, clear others
memcpy(&PORTVEC->PgmName[0], pgm, 31);
if (GetSem)
FreeSemaphore(&Semaphore);
return stream;
}
}
if (GetSem)
FreeSemaphore(&Semaphore);
return 255;
}
DllExport int APIENTRY AllocateStream(int stream)
{
// Allocate stream. If stream is already allocated, return nonzero.
// Otherwise allocate stream, and return zero.
BPQVECSTRUC * PORTVEC = &BPQHOSTVECTOR[stream -1]; // API counts from 1
if ((PORTVEC->HOSTFLAGS & 0x80) == 0)
{
PORTVEC->STREAMOWNER=GetCurrentProcessId();
PORTVEC->HOSTFLAGS = 128; // SET ALLOCATED BIT, clear others
memcpy(&PORTVEC->PgmName[0], pgm, 31);
FreeSemaphore(&Semaphore);
return 0;
}
return 1; // Already allocated
}
DllExport int APIENTRY DeallocateStream(int stream)
{
BPQVECSTRUC * PORTVEC;
UINT * monbuff;
BOOL GotSem = Semaphore.Flag;
// Release stream.
stream--;
if (stream < 0 || stream > 63)
return (0);
PORTVEC=&BPQHOSTVECTOR[stream];
PORTVEC->STREAMOWNER=0;
PORTVEC->PgmName[0] = 0;
PORTVEC->HOSTAPPLFLAGS=0;
PORTVEC->HOSTAPPLMASK=0;
PORTVEC->HOSTHANDLE=0;
// Clear Trace Queue
if (PORTVEC->HOSTSESSION)
SessionControl(stream + 1, 2, 0);
if (GotSem == 0)
GetSemaphore(&Semaphore, 0);
while (PORTVEC->HOSTTRACEQ)
{
monbuff = Q_REM((void *)&PORTVEC->HOSTTRACEQ);
ReleaseBuffer(monbuff);
}
if (GotSem == 0)
FreeSemaphore(&Semaphore);
PORTVEC->HOSTFLAGS &= 0x60; // Clear Allocated. Must leave any DISC Pending bits
return(0);
}
DllExport int APIENTRY SessionState(int stream, int * state, int * change)
{
// Get current Session State. Any state changed is ACK'ed
// automatically. See BPQHOST functions 4 and 5.
BPQVECSTRUC * HOST = &BPQHOSTVECTOR[stream -1]; // API counts from 1
Check_Timer(); // In case Appl doesnt call it often ehough
GetSemaphore(&Semaphore, 20);
// 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).
// HOSTFLAGS = Bit 80 = Allocated
// Bit 40 = Disc Request
// Bit 20 = Stay Flag
// Bit 02 and 01 State Change Bits
if ((HOST->HOSTFLAGS & 3) == 0)
// No Chaange
*change = 0;
else
*change = 1;
if (HOST->HOSTSESSION) // LOCAL SESSION
// Connected
*state = 1;
else
*state = 0;
HOST->HOSTFLAGS &= 0xFC; // Clear Change Bitd
FreeSemaphore(&Semaphore);
return 0;
}
DllExport int APIENTRY SessionStateNoAck(int stream, int * state)
{
// Get current Session State. Dont ACK any change
// See BPQHOST function 4
BPQVECSTRUC * HOST = &BPQHOSTVECTOR[stream -1]; // API counts from 1
Check_Timer(); // In case Appl doesnt call it often ehough
if (HOST->HOSTSESSION) // LOCAL SESSION
// Connected
*state = 1;
else
*state = 0;
return 0;
}
DllExport int APIENTRY SendMsg(int stream, char * msg, int len)
{
// Send message to stream (BPQHOST Function 2)
BPQVECSTRUC * SESS;
TRANSPORTENTRY * L4;
TRANSPORTENTRY * Partner;
PDATAMESSAGE MSG;
Check_Timer();
if (len > 256)
return 0; // IGNORE
if (stream == 0)
{
// Send UNPROTO - SEND FRAME TO ALL RADIO PORTS
// COPY DATA TO A BUFFER IN OUR SEGMENTS - SIMPLFIES THINGS LATER
if (QCOUNT < 50)
return 0; // Dont want to run out
GetSemaphore(&Semaphore, 10);
if ((MSG = GetBuff()) == 0)
{
FreeSemaphore(&Semaphore);
return 0;
}
MSG->PID = 0xF0; // Normal Data PID
memcpy(&MSG->L2DATA[0], msg, len);
MSG->LENGTH = (len + MSGHDDRLEN + 1);
SENDUIMESSAGE(MSG);
ReleaseBuffer(MSG);
FreeSemaphore(&Semaphore);
return 0;
}
stream--; // API uses 1 - 64
if (stream < 0 || stream > 63)
return 0;
SESS = &BPQHOSTVECTOR[stream];
L4 = SESS->HOSTSESSION;
if (L4 == 0)
return 0;
GetSemaphore(&Semaphore, 22);
SESS->HOSTFLAGS |= 0x80; // SET ALLOCATED BIT
if (QCOUNT < 40) // PLENTY FREE?
{
FreeSemaphore(&Semaphore);
return 1;
}
// Dont allow massive queues to form
if (QCOUNT < 100)
{
int n = CountFramesQueuedOnStream(stream + 1);
if (n > 100)
{
Debugprintf("Stream %d QCOUNT %d Q Len %d - discarding", stream, QCOUNT, n);
FreeSemaphore(&Semaphore);
return 1;
}
}
if ((MSG = GetBuff()) == 0)
{
FreeSemaphore(&Semaphore);
return 1;
}
MSG->PID = 0xF0; // Normal Data PID
memcpy(&MSG->L2DATA[0], msg, len);
MSG->LENGTH = len + MSGHDDRLEN + 1;
// IF CONNECTED, PASS MESSAGE TO TARGET CIRCUIT - FLOW CONTROL AND
// DELAYED DISC ONLY WORK ON ONE SIDE
Partner = L4->L4CROSSLINK;
L4->L4KILLTIMER = 0; // RESET SESSION TIMEOUT
if (Partner && Partner->L4STATE > 4) // Partner and link up
{
// Connected
Partner->L4KILLTIMER = 0; // RESET SESSION TIMEOUT
C_Q_ADD(&Partner->L4TX_Q, MSG);
PostDataAvailable(Partner);
}
else
C_Q_ADD(&L4->L4RX_Q, MSG);
FreeSemaphore(&Semaphore);
return 0;
}
DllExport int APIENTRY SendRaw(int port, char * msg, int len)
{
struct PORTCONTROL * PORT;
MESSAGE * MSG;
Check_Timer();
// Send Raw (KISS mode) frame to port (BPQHOST function 10)
if (len > (MAXDATA - (MSGHDDRLEN + 8)))
return 0;
if (QCOUNT < 50)
return 1;
// GET A BUFFER
PORT = GetPortTableEntryFromSlot(port);
if (PORT == 0)
return 0;
GetSemaphore(&Semaphore, 24);
MSG = GetBuff();
if (MSG == 0)
{
FreeSemaphore(&Semaphore);
return 1;
}
memcpy(MSG->DEST, msg, len);
MSG->LENGTH = len + MSGHDDRLEN;
if (PORT->PROTOCOL == 10) // PACTOR/WINMOR Style
{
// 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, MSG);
FreeSemaphore(&Semaphore);
return 0;
}
MSG->PORT = PORT->PORTNUMBER;
PUT_ON_PORT_Q(PORT, MSG);
FreeSemaphore(&Semaphore);
return 0;
}
DllExport time_t APIENTRY GetRaw(int stream, char * msg, int * len, int * count)
{
time_t Stamp;
BPQVECSTRUC * SESS;
PMESSAGE MSG;
int Msglen;
Check_Timer();
*len = 0;
*count = 0;
stream--; // API uses 1 - 64
if (stream < 0 || stream > 63)
return 0;
SESS = &BPQHOSTVECTOR[stream];
GetSemaphore(&Semaphore, 26);
if (SESS->HOSTTRACEQ == 0)
{
FreeSemaphore(&Semaphore);
return 0;
}
MSG = Q_REM((void *)&SESS->HOSTTRACEQ);
Msglen = MSG->LENGTH;
if (Msglen < 0 || Msglen > 350)
{
FreeSemaphore(&Semaphore);
return 0;
}
Stamp = MSG->Timestamp;
2024-10-11 15:37:11 +01:00
memcpy(msg, MSG, BUFFLEN - sizeof(void *)); // To c
2022-08-28 09:35:46 +01:00
*len = Msglen;
ReleaseBuffer(MSG);
*count = C_Q_COUNT(&SESS->HOSTTRACEQ);
FreeSemaphore(&Semaphore);
return Stamp;
}
DllExport int APIENTRY GetMsg(int stream, char * msg, int * len, int * count )
{
// Get message from stream. Returns length, and count of frames
// still waiting to be collected. (BPQHOST function 3)
// 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).
//
BPQVECSTRUC * SESS;
TRANSPORTENTRY * L4;
PDATAMESSAGE MSG;
int Msglen;
Check_Timer();
*len = 0;
*count = 0;
stream--; // API uses 1 - 64
if (stream < 0 || stream > 63)
return 0;
SESS = &BPQHOSTVECTOR[stream];
L4 = SESS->HOSTSESSION;
GetSemaphore(&Semaphore, 25);
if (L4 == 0 || L4->L4TX_Q == 0)
{
FreeSemaphore(&Semaphore);
return 0;
}
L4->L4KILLTIMER = 0; // RESET SESSION TIMEOUT
if(L4->L4CROSSLINK)
L4->L4CROSSLINK->L4KILLTIMER = 0;
MSG = Q_REM((void *)&L4->L4TX_Q);
Msglen = MSG->LENGTH - (MSGHDDRLEN + 1); // Dont want PID
if (Msglen < 0)
{
FreeSemaphore(&Semaphore);
return 0;
}
if (Msglen > 256)
Msglen = 256;
memcpy(msg, &MSG->L2DATA[0], Msglen);
*len = Msglen;
ReleaseBuffer(MSG);
*count = C_Q_COUNT(&L4->L4TX_Q);
FreeSemaphore(&Semaphore);
return 0;
}
DllExport int APIENTRY RXCount(int stream)
{
// Returns count of packets waiting on stream
// (BPQHOST function 7 (part)).
BPQVECSTRUC * SESS;
TRANSPORTENTRY * L4;
Check_Timer();
stream--; // API uses 1 - 64
if (stream < 0 || stream > 63)
return 0;
SESS = &BPQHOSTVECTOR[stream];
L4 = SESS->HOSTSESSION;
if (L4 == 0)
return 0; // NOT CONNECTED
return C_Q_COUNT(&L4->L4TX_Q);
}
DllExport int APIENTRY TXCount(int stream)
{
// Returns number of packets on TX queue for stream
// (BPQHOST function 7 (part)).
BPQVECSTRUC * SESS;
TRANSPORTENTRY * L4;
Check_Timer();
stream--; // API uses 1 - 64
if (stream < 0 || stream > 63)
return 0;
SESS = &BPQHOSTVECTOR[stream];
L4 = SESS->HOSTSESSION;
if (L4 == 0)
return 0; // NOT CONNECTED
L4 = L4->L4CROSSLINK;
if (L4 == 0)
return 0; // NOTHING ro Q on
return (CountFramesQueuedOnSession(L4));
}
DllExport int APIENTRY MONCount(int stream)
{
// Returns number of monitor frames available
// (BPQHOST function 7 (part)).
BPQVECSTRUC * SESS;
Check_Timer();
stream--; // API uses 1 - 64
if (stream < 0 || stream > 63)
return 0;
SESS = &BPQHOSTVECTOR[stream];
return C_Q_COUNT(&SESS->HOSTTRACEQ);
}
DllExport int APIENTRY GetCallsign(int stream, char * callsign)
{
// Returns call connected on stream (BPQHOST function 8 (part)).
BPQVECSTRUC * SESS;
TRANSPORTENTRY * L4;
TRANSPORTENTRY * Partner;
UCHAR Call[11] = "SWITCH ";
UCHAR * AXCall = NULL;
Check_Timer();
stream--; // API uses 1 - 64
if (stream < 0 || stream > 63)
return 0;
SESS = &BPQHOSTVECTOR[stream];
L4 = SESS->HOSTSESSION;
GetSemaphore(&Semaphore, 26);
if (L4 == 0)
{
FreeSemaphore(&Semaphore);
return 0;
}
Partner = L4->L4CROSSLINK;
if (Partner)
{
// CONNECTED OUT - GET TARGET SESSION
if (Partner->L4CIRCUITTYPE & BPQHOST)
{
AXCall = &Partner->L4USER[0];
}
else if (Partner->L4CIRCUITTYPE & L2LINK)
{
struct _LINKTABLE * LINK = Partner->L4TARGET.LINK;
if (LINK)
AXCall = LINK->LINKCALL;
if (Partner->L4CIRCUITTYPE & UPLINK)
{
// IF UPLINK, SHOULD USE SESSION CALL, IN CASE *** LINKED HAS BEEN USED
AXCall = &Partner->L4USER[0];
}
}
else if (Partner->L4CIRCUITTYPE & PACTOR)
{
// PACTOR Type - Frames are queued on the Port Entry
EXTPORTDATA * EXTPORT = Partner->L4TARGET.EXTPORT;
if (EXTPORT)
AXCall = &EXTPORT->ATTACHEDSESSIONS[Partner->KAMSESSION]->L4USER[0];
}
else
{
// MUST BE NODE SESSION
// ANOTHER NODE
// IF THE HOST IS THE UPLINKING STATION, WE NEED THE TARGET CALL
if (L4->L4CIRCUITTYPE & UPLINK)
{
struct DEST_LIST *DEST = Partner->L4TARGET.DEST;
if (DEST)
AXCall = &DEST->DEST_CALL[0];
}
else
AXCall = Partner->L4USER;
}
if (AXCall)
ConvFromAX25(AXCall, Call);
}
memcpy(callsign, Call, 10);
FreeSemaphore(&Semaphore);
return 0;
}
DllExport int APIENTRY GetConnectionInfo(int stream, char * callsign,
int * port, int * sesstype, int * paclen,
int * maxframe, int * l4window)
{
// Return the Secure Session Flag rather than not connected
BPQVECSTRUC * SESS;
TRANSPORTENTRY * L4;
TRANSPORTENTRY * Partner;
UCHAR Call[11] = "SWITCH ";
UCHAR * AXCall;
Check_Timer();
stream--; // API uses 1 - 64
if (stream < 0 || stream > 63)
return 0;
SESS = &BPQHOSTVECTOR[stream];
L4 = SESS->HOSTSESSION;
GetSemaphore(&Semaphore, 27);
if (L4 == 0)
{
FreeSemaphore(&Semaphore);
return 0;
}
Partner = L4->L4CROSSLINK;
// Return the Secure Session Flag rather than not connected
// AL = Radio port on which channel is connected (or zero)
// AH = SESSION TYPE BITS
// EBX = L2 paclen for the radio port
// ECX = L2 maxframe for the radio port
// EDX = L4 window size (if L4 circuit, or zero) or -1 if not connected
// ES:DI = CALLSIGN
*port = 0;
*sesstype = 0;
*paclen = 0;
*maxframe = 0;
*l4window = 0;
if (L4->SESSPACLEN)
*paclen = L4->SESSPACLEN;
else
*paclen = 256;
if (Partner)
{
// CONNECTED OUT - GET TARGET SESSION
*l4window = Partner->L4WINDOW;
*sesstype = Partner->L4CIRCUITTYPE;
if (Partner->L4CIRCUITTYPE & BPQHOST)
{
AXCall = &Partner->L4USER[0];
}
else if (Partner->L4CIRCUITTYPE & L2LINK)
{
struct _LINKTABLE * LINK = Partner->L4TARGET.LINK;
// EXTRACT PORT AND MAXFRAME
*port = LINK->LINKPORT->PORTNUMBER;
*maxframe = LINK->LINKWINDOW;
*l4window = 0;
AXCall = LINK->LINKCALL;
if (Partner->L4CIRCUITTYPE & UPLINK)
{
// IF UPLINK, SHOULD USE SESSION CALL, IN CASE *** LINKED HAS BEEN USED
AXCall = &Partner->L4USER[0];
}
}
else if (Partner->L4CIRCUITTYPE & PACTOR)
{
// PACTOR Type - Frames are queued on the Port Entry
EXTPORTDATA * EXTPORT = Partner->L4TARGET.EXTPORT;
*port = EXTPORT->PORTCONTROL.PORTNUMBER;
AXCall = &EXTPORT->ATTACHEDSESSIONS[Partner->KAMSESSION]->L4USER[0];
}
else
{
// MUST BE NODE SESSION
// ANOTHER NODE
// IF THE HOST IS THE UPLINKING STATION, WE NEED THE TARGET CALL
if (L4->L4CIRCUITTYPE & UPLINK)
{
struct DEST_LIST *DEST = Partner->L4TARGET.DEST;
AXCall = &DEST->DEST_CALL[0];
}
else
AXCall = Partner->L4USER;
}
ConvFromAX25(AXCall, Call);
}
memcpy(callsign, Call, 10);
FreeSemaphore(&Semaphore);
if (Partner)
return Partner->Secure_Session;
return 0;
}
DllExport int APIENTRY SetAppl(int stream, int flags, int mask)
{
// Sets Application Flags and Mask for stream. (BPQHOST function 1)
// AH = 1 Set application mask to value in EDX (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.
BPQVECSTRUC * PORTVEC;
stream--;
if (stream < 0 || stream > 63)
return (0);
PORTVEC=&BPQHOSTVECTOR[stream];
PORTVEC->HOSTAPPLFLAGS = flags;
PORTVEC->HOSTAPPLMASK = mask;
// If either is non-zero, set allocated and Process. This gets round problem with
// stations that don't call allocate stream
if (flags || mask)
{
if ((PORTVEC->HOSTFLAGS & 128) == 0) // Not allocated
{
PORTVEC->STREAMOWNER=GetCurrentProcessId();
memcpy(&PORTVEC->PgmName[0], pgm, 31);
PORTVEC->HOSTFLAGS = 128; // SET ALLOCATED BIT, clear others
}
}
return (0);
}
DllExport struct PORTCONTROL * APIENTRY GetPortTableEntry(int portslot) // Kept for Legacy apps
{
struct PORTCONTROL * PORTVEC=PORTTABLE;
if (portslot>NUMBEROFPORTS)
portslot=NUMBEROFPORTS;
while (--portslot > 0)
PORTVEC=PORTVEC->PORTPOINTER;
return PORTVEC;
}
// Proc below renamed to avoid confusion with GetPortTableEntryFromPortNum
DllExport struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot)
{
struct PORTCONTROL * PORTVEC=PORTTABLE;
if (portslot>NUMBEROFPORTS)
portslot=NUMBEROFPORTS;
while (--portslot > 0)
PORTVEC=PORTVEC->PORTPOINTER;
return PORTVEC;
}
2023-10-10 22:07:04 +01:00
int CanPortDigi(int Port)
{
struct PORTCONTROL * PORTVEC = GetPortTableEntryFromPortNum(Port);
struct TNCINFO * TNC;
if (PORTVEC == NULL)
return FALSE;
TNC = PORTVEC->TNC;
if (TNC == NULL)
return TRUE;
if (TNC->Hardware == H_SCS || TNC->Hardware == H_TRK || TNC->Hardware == H_TRKM || TNC->Hardware == H_WINRPR)
return FALSE;
return TRUE;
}
2022-08-28 09:35:46 +01:00
struct PORTCONTROL * APIENTRY GetPortTableEntryFromPortNum(int portnum)
{
2023-10-10 22:07:04 +01:00
struct PORTCONTROL * PORTVEC = PORTTABLE;
2022-08-28 09:35:46 +01:00
do
{
if (PORTVEC->PORTNUMBER == portnum)
return PORTVEC;
PORTVEC=PORTVEC->PORTPOINTER;
}
while (PORTVEC);
return NULL;
}
DllExport UCHAR * APIENTRY GetPortDescription(int portslot, char * Desc)
{
struct PORTCONTROL * PORTVEC=PORTTABLE;
if (portslot>NUMBEROFPORTS)
portslot=NUMBEROFPORTS;
while (--portslot > 0)
PORTVEC=PORTVEC->PORTPOINTER;
memcpy(Desc, PORTVEC->PORTDESCRIPTION, 30);
Desc[30]=0;
return 0;
}
// Standard serial port handling routines, used by lots of modules.
int OpenCOMMPort(struct TNCINFO * conn, char * Port, int Speed, BOOL Quiet)
{
if (conn->WEB_COMMSSTATE == NULL)
conn->WEB_COMMSSTATE = zalloc(100);
if (Port == NULL)
return (FALSE);
conn->hDevice = OpenCOMPort(Port, Speed, TRUE, TRUE, Quiet, 0);
if (conn->hDevice == 0)
{
sprintf(conn->WEB_COMMSSTATE,"%s Open failed - Error %d", Port, GetLastError());
if (conn->xIDC_COMMSSTATE)
SetWindowText(conn->xIDC_COMMSSTATE, conn->WEB_COMMSSTATE);
return (FALSE);
}
sprintf(conn->WEB_COMMSSTATE,"%s Open", Port);
if (conn->xIDC_COMMSSTATE)
SetWindowText(conn->xIDC_COMMSSTATE, conn->WEB_COMMSSTATE);
return TRUE;
}
#ifdef WIN32
HANDLE OpenCOMPort(char * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits)
{
2024-06-09 21:48:39 +01:00
char szPort[256];
2022-08-28 09:35:46 +01:00
BOOL fRetVal ;
COMMTIMEOUTS CommTimeOuts ;
int Err;
char buf[100];
HANDLE fd;
DCB dcb;
// if Port Name starts COM, convert to \\.\COM or ports above 10 wont work
if (_memicmp(pPort, "COM", 3) == 0)
{
char * pp = (char *)pPort;
int p = atoi(&pp[3]);
sprintf( szPort, "\\\\.\\COM%d", p);
}
else
strcpy(szPort, pPort);
// open COMM device
fd = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if (fd == (HANDLE) -1)
{
if (Quiet == 0)
{
Debugprintf("%s could not be opened %d", pPort, GetLastError());
}
return (FALSE);
}
Err = GetFileType(fd);
// 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 = 0xFFFFFFFF ;
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());
WritetoConsoleLocal(buf);
OutputDebugString(buf);
CloseHandle(fd);
return 0;
}
return fd;
}
int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error);
int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength)
{
BOOL Error;
return ReadCOMBlockEx(fd, Block, MaxLength, &Error);
}
// version to pass read error back to caller
int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error)
{
BOOL fReadStat ;
COMSTAT ComStat ;
DWORD dwErrorFlags;
DWORD dwLength;
BOOL ret;
if (fd == NULL)
return 0;
// only try to read number of bytes in queue
ret = ClearCommError(fd, &dwErrorFlags, &ComStat);
if (ret == 0)
{
int Err = GetLastError();
*Error = TRUE;
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 ) ;
}
}
*Error = FALSE;
return dwLength;
}
BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite)
{
BOOL fWriteStat;
DWORD BytesWritten;
DWORD ErrorFlags;
COMSTAT ComStat;
2023-10-10 22:07:04 +01:00
DWORD Mask = 0;
int Err;
Err = GetCommModemStatus(fd, &Mask);
2024-01-16 20:42:28 +00:00
// if ((Mask & MS_CTS_ON) == 0) // trap com0com other end not open
// return TRUE;
2022-08-28 09:35:46 +01:00
fWriteStat = WriteFile(fd, Block, BytesToWrite,
&BytesWritten, NULL );
if ((!fWriteStat) || (BytesToWrite != BytesWritten))
{
int Err = GetLastError();
ClearCommError(fd, &ErrorFlags, &ComStat);
return FALSE;
}
return TRUE;
}
VOID CloseCOMPort(HANDLE fd)
{
if (fd == NULL)
return;
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);
fd = NULL;
}
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);
}
#else
static struct speed_struct
{
int user_speed;
speed_t termios_speed;
} speed_table[] = {
{300, B300},
{600, B600},
{1200, B1200},
{2400, B2400},
{4800, B4800},
{9600, B9600},
{19200, B19200},
{38400, B38400},
{57600, B57600},
{115200, B115200},
{-1, B0}
};
HANDLE OpenCOMPort(VOID * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits)
{
char Port[256];
2024-12-16 17:54:16 +00:00
char buf[512];
2022-08-28 09:35:46 +01:00
// Linux Version.
int fd;
int hwflag = 0;
u_long param=1;
struct termios term;
struct speed_struct *s;
2024-10-11 15:37:11 +01:00
if ((uintptr_t)pPort < 256)
sprintf(Port, "%s/com%d", BPQDirectory, (int)(uintptr_t)pPort);
2022-08-28 09:35:46 +01:00
else
strcpy(Port, pPort);
if ((fd = open(Port, O_RDWR | O_NDELAY)) == -1)
{
if (Quiet == 0)
{
perror("Com Open Failed");
sprintf(buf," %s could not be opened \n", Port);
WritetoConsoleLocal(buf);
Debugprintf(buf);
}
return 0;
}
// Validate Speed Param
for (s = speed_table; s->user_speed != -1; s++)
if (s->user_speed == speed)
break;
if (s->user_speed == -1)
{
fprintf(stderr, "tty_speed: invalid speed %d\n", speed);
return FALSE;
}
if (tcgetattr(fd, &term) == -1)
{
perror("tty_speed: tcgetattr");
return FALSE;
}
cfmakeraw(&term);
cfsetispeed(&term, s->termios_speed);
cfsetospeed(&term, s->termios_speed);
if (tcsetattr(fd, TCSANOW, &term) == -1)
{
perror("tty_speed: tcsetattr");
return FALSE;
}
ioctl(fd, FIONBIO, &param);
Debugprintf("LinBPQ Port %s fd %d", Port, fd);
if (SetDTR)
{
COMSetDTR(fd);
}
else
{
COMClearDTR(fd);
}
if (SetRTS)
{
COMSetRTS(fd);
}
else
{
COMClearRTS(fd);
}
return fd;
}
int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error);
int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength)
{
BOOL Error;
return ReadCOMBlockEx(fd, Block, MaxLength, &Error);
}
// version to pass read error back to caller
int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error)
{
int Length;
if (fd == 0)
{
*Error = 1;
return 0;
}
errno = 22222; // to catch zero read (?? file closed ??)
Length = read(fd, Block, MaxLength);
*Error = 0;
if (Length == 0 && errno == 22222) // seems to be result of unpluging USB
{
// printf("KISS read returned zero len and no errno\n");
*Error = 1;
return 0;
}
if (Length < 0)
{
if (errno != 11 && errno != 35) // Would Block
{
perror("read");
printf("Handle %d Errno %d Len %d\n", fd, errno, Length);
*Error = errno;
}
return 0;
}
return Length;
}
BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite)
{
// Some systems seem to have a very small max write size
int ToSend = BytesToWrite;
int Sent = 0, ret;
2024-06-09 21:48:39 +01:00
int loops = 100;
2022-08-28 09:35:46 +01:00
2024-06-09 21:48:39 +01:00
while (ToSend && loops-- > 0)
2022-08-28 09:35:46 +01:00
{
ret = write(fd, &Block[Sent], ToSend);
if (ret >= ToSend)
return TRUE;
if (ret == -1)
{
if (errno != 11 && errno != 35) // Would Block
return FALSE;
usleep(10000);
ret = 0;
}
Sent += ret;
ToSend -= ret;
}
2024-06-09 21:48:39 +01:00
// if (ToSend)
// {
// // Send timed out. Close and reopen device
//
// }
2022-08-28 09:35:46 +01:00
return TRUE;
}
VOID CloseCOMPort(HANDLE fd)
{
if (fd == 0)
return;
close(fd);
fd = 0;
}
VOID COMSetDTR(HANDLE fd)
{
int status;
ioctl(fd, TIOCMGET, &status);
status |= TIOCM_DTR;
ioctl(fd, TIOCMSET, &status);
}
VOID COMClearDTR(HANDLE fd)
{
int status;
ioctl(fd, TIOCMGET, &status);
status &= ~TIOCM_DTR;
ioctl(fd, TIOCMSET, &status);
}
VOID COMSetRTS(HANDLE fd)
{
int status;
ioctl(fd, TIOCMGET, &status);
status |= TIOCM_RTS;
ioctl(fd, TIOCMSET, &status);
}
VOID COMClearRTS(HANDLE fd)
{
int status;
ioctl(fd, TIOCMGET, &status);
status &= ~TIOCM_RTS;
ioctl(fd, TIOCMSET, &status);
}
#endif
int MaxNodes;
int MaxRoutes;
int NodeLen;
int RouteLen;
struct DEST_LIST * Dests;
struct ROUTE * Routes;
FILE *file;
int DoRoutes()
{
char digis[30] = "";
int count, len;
char Normcall[10], Portcall[10];
char line[80];
for (count=0; count<MaxRoutes; count++)
{
if (Routes->NEIGHBOUR_CALL[0] != 0)
{
len=ConvFromAX25(Routes->NEIGHBOUR_CALL,Normcall);
Normcall[len]=0;
if (Routes->NEIGHBOUR_DIGI1[0] != 0)
{
memcpy(digis," VIA ",5);
len=ConvFromAX25(Routes->NEIGHBOUR_DIGI1,Portcall);
Portcall[len]=0;
strcpy(&digis[5],Portcall);
if (Routes->NEIGHBOUR_DIGI2[0] != 0)
{
len=ConvFromAX25(Routes->NEIGHBOUR_DIGI2,Portcall);
Portcall[len]=0;
strcat(digis," ");
strcat(digis,Portcall);
}
}
else
digis[0] = 0;
len=sprintf(line,
"ROUTE ADD %s %d %d %s %d %d %d %d %d\n",
Normcall,
Routes->NEIGHBOUR_PORT,
Routes->NEIGHBOUR_QUAL, digis,
Routes->NBOUR_MAXFRAME,
Routes->NBOUR_FRACK,
Routes->NBOUR_PACLEN,
Routes->INP3Node | (Routes->NoKeepAlive << 2),
Routes->OtherendsRouteQual);
fputs(line, file);
}
Routes+=1;
}
return (0);
}
int DoNodes()
{
int count, len, cursor, i;
char Normcall[10], Portcall[10];
char line[80];
char Alias[7];
Dests-=1;
for (count=0; count<MaxNodes; count++)
{
Dests+=1;
if (Dests->NRROUTE[0].ROUT_NEIGHBOUR == 0)
continue;
{
len=ConvFromAX25(Dests->DEST_CALL,Normcall);
Normcall[len]=0;
memcpy(Alias,Dests->DEST_ALIAS,6);
Alias[6]=0;
for (i=0;i<6;i++)
{
if (Alias[i] == ' ')
Alias[i] = 0;
}
cursor=sprintf(line,"NODE ADD %s:%s ", Alias,Normcall);
if (Dests->NRROUTE[0].ROUT_NEIGHBOUR != 0 && Dests->NRROUTE[0].ROUT_NEIGHBOUR->INP3Node == 0)
{
len=ConvFromAX25(
Dests->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL,Portcall);
Portcall[len]=0;
len=sprintf(&line[cursor],"%s %d %d ",
Portcall,
Dests->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_PORT,
Dests->NRROUTE[0].ROUT_QUALITY);
cursor+=len;
if (Dests->NRROUTE[0].ROUT_OBSCOUNT > 127)
{
len=sprintf(&line[cursor],"! ");
cursor+=len;
}
}
if (Dests->NRROUTE[1].ROUT_NEIGHBOUR != 0 && Dests->NRROUTE[1].ROUT_NEIGHBOUR->INP3Node == 0)
{
len=ConvFromAX25(
Dests->NRROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL,Portcall);
Portcall[len]=0;
len=sprintf(&line[cursor],"%s %d %d ",
Portcall,
Dests->NRROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_PORT,
Dests->NRROUTE[1].ROUT_QUALITY);
cursor+=len;
if (Dests->NRROUTE[1].ROUT_OBSCOUNT > 127)
{
len=sprintf(&line[cursor],"! ");
cursor+=len;
}
}
if (Dests->NRROUTE[2].ROUT_NEIGHBOUR != 0 && Dests->NRROUTE[2].ROUT_NEIGHBOUR->INP3Node == 0)
{
len=ConvFromAX25(
Dests->NRROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL,Portcall);
Portcall[len]=0;
len=sprintf(&line[cursor],"%s %d %d ",
Portcall,
Dests->NRROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_PORT,
Dests->NRROUTE[2].ROUT_QUALITY);
cursor+=len;
if (Dests->NRROUTE[2].ROUT_OBSCOUNT > 127)
{
len=sprintf(&line[cursor],"! ");
cursor+=len;
}
}
if (cursor > 30)
{
line[cursor++]='\n';
line[cursor++]=0;
fputs(line, file);
}
}
}
return (0);
}
void SaveMH()
{
char FN[250];
struct PORTCONTROL * PORT = PORTTABLE;
FILE *file;
if (BPQDirectory[0] == 0)
{
strcpy(FN, "MHSave.txt");
}
else
{
strcpy(FN,BPQDirectory);
strcat(FN,"/");
strcat(FN,"MHSave.txt");
}
if ((file = fopen(FN, "w")) == NULL)
return;
while (PORT)
{
int Port = 0;
char * ptr;
MHSTRUC * MH = PORT->PORTMHEARD;
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
if (MH == NULL)
continue;
fprintf(file, "Port:%d\n", PORT->PORTNUMBER);
while (count--)
{
if (MH->MHCALL[0] == 0)
break;
Digi = 0;
len = ConvFromAX25(MH->MHCALL, Normcall);
Normcall[len] = 0;
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, 'U');
ptr[15] = 0;
if (MH->MHDIGI)
fprintf(file, "%d %6d %-10s%c %s %s|%s|%s\n", (int)MH->MHTIME, MH->MHCOUNT, Normcall, MH->MHDIGI, ptr, DigiList, MH->MHLocator, MH->MHFreq);
else
fprintf(file, "%d %6d %-10s%c %s %s|%s|%s\n", (int)MH->MHTIME, MH->MHCOUNT, Normcall, ' ', ptr, DigiList, MH->MHLocator, MH->MHFreq);
MH++;
}
PORT = PORT->PORTPOINTER;
}
fclose(file);
return;
}
int APIENTRY SaveNodes ()
{
char FN[250];
Routes = NEIGHBOURS;
RouteLen = ROUTE_LEN;
MaxRoutes = MAXNEIGHBOURS;
Dests = DESTS;
NodeLen = DEST_LIST_LEN;
MaxNodes = MAXDESTS;
// 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 ((file = fopen(FN, "w")) == NULL)
return FALSE;
DoRoutes();
DoNodes();
fclose(file);
return (0);
}
DllExport int APIENTRY ClearNodes ()
{
char FN[250];
// 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 ((file = fopen(FN, "w")) == NULL)
return FALSE;
fclose(file);
return (0);
}
char * FormatUptime(int Uptime)
{
struct tm * TM;
static char UPTime[50];
time_t szClock = Uptime * 60;
TM = gmtime(&szClock);
sprintf(UPTime, "Uptime (Days Hours Mins) %.2d:%.2d:%.2d\r",
TM->tm_yday, TM->tm_hour, TM->tm_min);
return UPTime;
}
static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
char * FormatMH(PMHSTRUC MH, char Format)
{
struct tm * TM;
static char MHTime[50];
time_t szClock;
char LOC[7];
memcpy(LOC, MH->MHLocator, 6);
LOC[6] = 0;
if (Format == 'U' || Format =='L')
szClock = MH->MHTIME;
else
szClock = time(NULL) - MH->MHTIME;
if (Format == 'L')
TM = localtime(&szClock);
else
TM = gmtime(&szClock);
if (Format == 'U' || Format =='L')
sprintf(MHTime, "%s %02d %.2d:%.2d:%.2d %s %s",
month[TM->tm_mon], TM->tm_mday, TM->tm_hour, TM->tm_min, TM->tm_sec, MH->MHFreq, LOC);
else
sprintf(MHTime, "%.2d:%.2d:%.2d:%.2d %s %s",
TM->tm_yday, TM->tm_hour, TM->tm_min, TM->tm_sec, MH->MHFreq, LOC);
return MHTime;
}
Dll VOID APIENTRY CreateOneTimePassword(char * Password, char * KeyPhrase, int TimeOffset)
{
// Create a time dependent One Time Password from the KeyPhrase
// TimeOffset is used when checking to allow for slight variation in clocks
time_t NOW = time(NULL);
UCHAR Hash[16];
char Key[1000];
int i, chr;
NOW = NOW/30 + TimeOffset; // Only Change every 30 secs
sprintf(Key, "%s%x", KeyPhrase, (int)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;
return;
}
Dll BOOL APIENTRY CheckOneTimePassword(char * Password, char * KeyPhrase)
{
char CheckPassword[17];
int Offsets[10] = {0, -1, 1, -2, 2, -3, 3, -4, 4};
int i, Pass;
if (strlen(Password) < 16)
Pass = atoi(Password);
for (i = 0; i < 9; i++)
{
CreateOneTimePassword(CheckPassword, KeyPhrase, Offsets[i]);
if (strlen(Password) < 16)
{
// Using a numeric extract
long long Val;
memcpy(&Val, CheckPassword, 8);
Val = Val %= 1000000;
if (Pass == Val)
return TRUE;
}
else
if (memcmp(Password, CheckPassword, 16) == 0)
return TRUE;
}
return FALSE;
}
DllExport BOOL ConvToAX25Ex(unsigned char * callsign, unsigned char * ax25call)
{
// Allows SSID's of 'T and 'R'
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
//
if (callsign[i+1] == 'T')
{
ax25call[6]=0x42;
return TRUE;
}
if (callsign[i+1] == 'R')
{
ax25call[6]=0x44;
return TRUE;
}
i = atoi(&callsign[i+1]);
if (i < 16)
{
ax25call[6] |= i<<1;
return (TRUE);
}
return (FALSE);
}
if (callsign[i] == 0 || callsign[i] == 13 || callsign[i] == ' ' || callsign[i] == ',')
{
//
// End of call - no ssid
//
return (TRUE);
}
ax25call[i] = callsign[i] << 1;
}
//
// Too many chars
//
return (FALSE);
}
DllExport BOOL ConvToAX25(unsigned char * callsign, unsigned char * ax25call)
{
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;
return (TRUE);
}
return (FALSE);
}
if (callsign[i] == 0 || callsign[i] == 13 || callsign[i] == ' ' || callsign[i] == ',')
{
//
// End of call - no ssid
//
return (TRUE);
}
ax25call[i] = callsign[i] << 1;
}
//
// Too many chars
//
return (FALSE);
}
DllExport int ConvFromAX25(unsigned char * incall,unsigned char * outcall)
{
int in,out=0;
unsigned char chr;
memset(outcall,0x20,10);
for (in=0;in<6;in++)
{
chr=incall[in];
if (chr == 0x40)
break;
chr >>= 1;
outcall[out++]=chr;
}
chr=incall[6]; // ssid
if (chr == 0x42)
{
outcall[out++]='-';
outcall[out++]='T';
return out;
}
if (chr == 0x44)
{
outcall[out++]='-';
outcall[out++]='R';
return out;
}
chr >>= 1;
chr &= 15;
if (chr > 0)
{
outcall[out++]='-';
if (chr > 9)
{
chr-=10;
outcall[out++]='1';
}
chr+=48;
outcall[out++]=chr;
}
return (out);
}
unsigned short int compute_crc(unsigned char *buf, int txlen);
SOCKADDR_IN reportdest = {0};
SOCKET ReportSocket = 0;
SOCKADDR_IN Chatreportdest = {0};
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];
VOID SendReportMsg(char * buff, int txlen)
{
unsigned short int crc = compute_crc(buff, txlen);
crc ^= 0xffff;
buff[txlen++] = (crc&0xff);
buff[txlen++] = (crc>>8);
2023-06-21 08:21:04 +01:00
sendto(ReportSocket, buff, txlen, 0, (struct sockaddr *)&reportdest, sizeof(reportdest));
2022-08-28 09:35:46 +01:00
}
VOID SendLocation()
{
MESSAGE AXMSG = {0};
PMESSAGE AXPTR = &AXMSG;
char Msg[512];
int Len;
Len = sprintf(Msg, "%s %s<br>%s", LOCATOR, VersionString, MAPCOMMENT);
#ifdef LINBPQ
Len = sprintf(Msg, "%s L%s<br>%s", LOCATOR, VersionString, MAPCOMMENT);
#endif
#ifdef MACBPQ
Len = sprintf(Msg, "%s M%s<br>%s", LOCATOR, VersionString, MAPCOMMENT);
#endif
#ifdef FREEBSD
Len = sprintf(Msg, "%s F%s<br>%s", LOCATOR, VersionString, MAPCOMMENT);
#endif
if (Len > 256)
Len = 256;
// 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);
2023-10-10 22:07:04 +01:00
if (M0LTEMap)
SendDataToPktMap("");
2022-08-28 09:35:46 +01:00
return;
}
VOID SendMH(struct TNCINFO * TNC, char * call, char * freq, char * LOC, char * Mode)
{
MESSAGE AXMSG;
PMESSAGE AXPTR = &AXMSG;
char Msg[100];
int Len;
if (ReportSocket == 0 || LOCATOR[0] == 0)
return;
Len = sprintf(Msg, "MH %s,%s,%s,%s", call, freq, LOC, Mode);
// Block includes the Msg Header (7 bytes), Len Does not!
memcpy(AXPTR->DEST, ReportDest, 7);
if (TNC->PortRecord->PORTCONTROL.PORTCALL[0])
memcpy(AXPTR->ORIGIN, TNC->PortRecord->PORTCONTROL.PORTCALL, 7);
else
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) ;
return;
}
time_t TimeLastNRRouteSent = 0;
char NRRouteMessage[256];
int NRRouteLen = 0;
VOID SendNETROMRoute(struct PORTCONTROL * PORT, unsigned char * axcall)
{
// Called to update Link Map when a NODES Broadcast is received
// Batch to reduce Load
MESSAGE AXMSG;
PMESSAGE AXPTR = &AXMSG;
char Msg[300];
int Len;
char Call[10];
char Report[16];
time_t Now = time(NULL);
int NeedSend = FALSE;
if (ReportSocket == 0 || LOCATOR[0] == 0)
return;
Call[ConvFromAX25(axcall, Call)] = 0;
sprintf(Report, "%s,%d,", Call, PORT->PORTTYPE);
if (Now - TimeLastNRRouteSent > 60)
NeedSend = TRUE;
if (strstr(NRRouteMessage, Report) == 0) // reported recently
strcat(NRRouteMessage, Report);
if (strlen(NRRouteMessage) > 230 || NeedSend)
{
Len = sprintf(Msg, "LINK %s", NRRouteMessage);
// 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) ;
TimeLastNRRouteSent = Now;
NRRouteMessage[0] = 0;
}
return;
}
DllExport char * APIENTRY GetApplCall(int Appl)
{
if (Appl < 1 || Appl > NumberofAppls ) return NULL;
return (UCHAR *)(&APPLCALLTABLE[Appl-1].APPLCALL_TEXT);
}
DllExport char * APIENTRY GetApplAlias(int Appl)
{
if (Appl < 1 || Appl > NumberofAppls ) return NULL;
return (UCHAR *)(&APPLCALLTABLE[Appl-1].APPLALIAS_TEXT);
}
DllExport int32_t APIENTRY GetApplQual(int Appl)
{
if (Appl < 1 || Appl > NumberofAppls ) return 0;
return (APPLCALLTABLE[Appl-1].APPLQUAL);
}
char * GetApplCallFromName(char * App)
{
int i;
char PaddedAppl[13] = " ";
memcpy(PaddedAppl, App, (int)strlen(App));
for (i = 0; i < NumberofAppls; i++)
{
if (memcmp(&APPLCALLTABLE[i].APPLCMD, PaddedAppl, 12) == 0)
return &APPLCALLTABLE[i].APPLCALL_TEXT[0];
}
return NULL;
}
DllExport char * APIENTRY GetApplName(int Appl)
{
if (Appl < 1 || Appl > NumberofAppls ) return NULL;
return (UCHAR *)(&APPLCALLTABLE[Appl-1].APPLCMD);
}
DllExport int APIENTRY GetNumberofPorts()
{
return (NUMBEROFPORTS);
}
DllExport int APIENTRY GetPortNumber(int portslot)
{
struct PORTCONTROL * PORTVEC=PORTTABLE;
if (portslot>NUMBEROFPORTS)
portslot=NUMBEROFPORTS;
while (--portslot > 0)
PORTVEC=PORTVEC->PORTPOINTER;
return PORTVEC->PORTNUMBER;
}
DllExport char * APIENTRY GetVersionString()
{
// return ((char *)&VersionStringWithBuild);
return ((char *)&VersionString);
}
#ifdef MACBPQ
//Fiddle till I find a better solution
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1060
int __sync_lock_test_and_set(int * ptr, int val)
{
*ptr = val;
return 0;
}
#endif // __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
#endif // MACBPQ
2024-01-16 20:42:28 +00:00
2022-08-28 09:35:46 +01:00
void GetSemaphore(struct SEM * Semaphore, int ID)
{
//
// Wait for it to be free
//
if (Semaphore->Flag != 0)
{
Semaphore->Clashes++;
}
loop1:
while (Semaphore->Flag != 0)
{
Sleep(10);
}
//
// try to get semaphore
//
#ifdef WIN32
{
if (InterlockedExchange(&Semaphore->Flag, 1) != 0) // Failed to get it
goto loop1; // try again;;
}
#else
if (__sync_lock_test_and_set(&Semaphore->Flag, 1) != 0)
// Failed to get it
goto loop1; // try again;
#endif
//Ok. got it
Semaphore->Gets++;
Semaphore->SemProcessID = GetCurrentProcessId();
Semaphore->SemThreadID = GetCurrentThreadId();
SemHeldByAPI = ID;
return;
}
void FreeSemaphore(struct SEM * Semaphore)
{
if (Semaphore->Flag == 0)
Debugprintf("Free Semaphore Called when Sem not held");
Semaphore->Rels++;
Semaphore->Flag = 0;
return;
}
#ifdef WIN32
#include "DbgHelp.h"
/*
USHORT WINAPI RtlCaptureStackBackTrace(
__in ULONG FramesToSkip,
__in ULONG FramesToCapture,
__out PVOID *BackTrace,
__out_opt PULONG BackTraceHash
);
*/
#endif
void printStack(void)
{
#ifdef WIN32
#ifdef _DEBUG // So we can use on 98/2K
unsigned int i;
void * stack[ 100 ];
unsigned short frames;
SYMBOL_INFO * symbol;
HANDLE process;
Debugprintf("Stack Backtrace");
process = GetCurrentProcess();
SymInitialize( process, NULL, TRUE );
frames = RtlCaptureStackBackTrace( 0, 60, stack, NULL );
symbol = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 );
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
for( i = 0; i < frames; i++ )
{
SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol );
Debugprintf( "%i: %s - %p", frames - i - 1, symbol->Name, symbol->Address );
}
free(symbol);
#endif
#endif
}
pthread_t ResolveUpdateThreadId = 0;
char NodeMapServer[80] = "update.g8bpq.net";
char ChatMapServer[80] = "chatupdate.g8bpq.net";
VOID ResolveUpdateThread(void * Unused)
{
struct hostent * HostEnt1;
struct hostent * HostEnt2;
ResolveUpdateThreadId = GetCurrentThreadId();
while (TRUE)
{
if (pthread_equal(ResolveUpdateThreadId, GetCurrentThreadId()) == FALSE)
{
Debugprintf("Resolve Update thread %x redundant - closing", GetCurrentThreadId());
return;
}
// Resolve name to address
Debugprintf("Resolving %s", NodeMapServer);
HostEnt1 = gethostbyname (NodeMapServer);
// HostEnt1 = gethostbyname ("192.168.1.64");
if (HostEnt1)
memcpy(&reportdest.sin_addr.s_addr,HostEnt1->h_addr,4);
Debugprintf("Resolving %s", ChatMapServer);
HostEnt2 = gethostbyname (ChatMapServer);
// HostEnt2 = gethostbyname ("192.168.1.64");
if (HostEnt2)
memcpy(&Chatreportdest.sin_addr.s_addr,HostEnt2->h_addr,4);
if (HostEnt1 && HostEnt2)
{
Sleep(1000 * 60 * 30);
continue;
}
Debugprintf("Resolve Failed for update.g8bpq.net or chatmap.g8bpq.net");
Sleep(1000 * 60 * 5);
}
}
VOID OpenReportingSockets()
{
u_long param=1;
BOOL bcopt=TRUE;
if (LOCATOR[0])
{
// Enable Node Map Reports
2024-10-11 15:37:11 +01:00
ReportTimer = 1200; // 2 mins - Give Rigcontrol time to start
2022-08-28 09:35:46 +01:00
ReportSocket = socket(AF_INET,SOCK_DGRAM,0);
if (ReportSocket == INVALID_SOCKET)
{
Debugprintf("Failed to create Reporting socket");
ReportSocket = 0;
return;
}
ioctlsocket (ReportSocket, FIONBIO, &param);
setsockopt (ReportSocket, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt,4);
reportdest.sin_family = AF_INET;
reportdest.sin_port = htons(81);
ConvToAX25("DUMMY-1", ReportDest);
}
// Set up Chat Report even if no LOCATOR reportdest.sin_family = AF_INET;
// Socket must be opened in MailChat Process
Chatreportdest.sin_family = AF_INET;
Chatreportdest.sin_port = htons(81);
_beginthread(ResolveUpdateThread, 0, NULL);
2024-11-05 21:03:15 +00:00
printf("MQTT Enabled %d\n", MQTT);
if (MQTT)
MQTTConnect(MQTT_HOST, MQTT_PORT, MQTT_USER, MQTT_PASS);
2022-08-28 09:35:46 +01:00
}
VOID WriteMiniDumpThread();
time_t lastMiniDump = 0;
void WriteMiniDump()
{
#ifdef WIN32
_beginthread(WriteMiniDumpThread, 0, 0);
Sleep(3000);
}
VOID WriteMiniDumpThread()
{
HANDLE hFile;
BOOL ret;
char FN[256];
struct tm * TM;
time_t Now = time(NULL);
if (lastMiniDump == Now) // Not more than one per second
{
Debugprintf("minidump suppressed");
return;
}
lastMiniDump = Now;
TM = gmtime(&Now);
sprintf(FN, "%s/Logs/MiniDump%d%02d%02d%02d%02d%02d.dmp", BPQDirectory,
TM->tm_year + 1900, TM->tm_mon +1, TM->tm_mday, TM->tm_hour, TM->tm_min, TM->tm_sec);
hFile = CreateFile(FN, GENERIC_READ | GENERIC_WRITE,
0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
{
// Create the minidump
ret = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
hFile, MiniDumpNormal, 0, 0, 0 );
if(!ret)
Debugprintf("MiniDumpWriteDump failed. Error: %u", GetLastError());
else
Debugprintf("Minidump %s created.", FN);
CloseHandle(hFile);
}
#endif
}
// UI Util Code
#pragma pack(1)
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 PADDING[56]; // In case he have Digis
}MESSAGEX, *PMESSAGEX;
#pragma pack()
2023-05-25 14:17:53 +01:00
int PortNum[MaxBPQPortNo + 1] = {0}; // Tab nunber to port
2022-08-28 09:35:46 +01:00
2023-05-25 14:17:53 +01:00
char * UIUIDigi[MaxBPQPortNo + 1]= {0};
char * UIUIDigiAX[MaxBPQPortNo + 1] = {0}; // ax.25 version of digistring
int UIUIDigiLen[MaxBPQPortNo + 1] = {0}; // Length of AX string
2022-08-28 09:35:46 +01:00
2023-05-25 14:17:53 +01:00
char UIUIDEST[MaxBPQPortNo + 1][11] = {0}; // Dest for Beacons
2022-08-28 09:35:46 +01:00
2023-05-25 14:17:53 +01:00
char UIAXDEST[MaxBPQPortNo + 1][7] = {0};
2022-08-28 09:35:46 +01:00
2023-05-25 14:17:53 +01:00
UCHAR FN[MaxBPQPortNo + 1][256]; // Filename
int Interval[MaxBPQPortNo + 1]; // Beacon Interval (Mins)
int MinCounter[MaxBPQPortNo + 1]; // Interval Countdown
2022-08-28 09:35:46 +01:00
2023-05-25 14:17:53 +01:00
BOOL SendFromFile[MaxBPQPortNo + 1];
char Message[MaxBPQPortNo + 1][1000]; // Beacon Text
2022-08-28 09:35:46 +01:00
VOID SendUIBeacon(int Port);
BOOL RunUI = TRUE;
VOID UIThread(void * Unused)
{
int Port, MaxPorts = GetNumberofPorts();
Sleep(60000);
while (RunUI)
{
int sleepInterval = 60000;
for (Port = 1; Port <= MaxPorts; Port++)
{
if (MinCounter[Port])
{
MinCounter[Port]--;
if (MinCounter[Port] == 0)
{
MinCounter[Port] = Interval[Port];
SendUIBeacon(Port);
// pause beteen beacons but adjust sleep interval to suit
Sleep(10000);
sleepInterval -= 10000;
}
}
}
while (sleepInterval <= 0) // just in case we have a crazy config
sleepInterval += 60000;
Sleep(sleepInterval);
}
}
int UIRemoveLF(char * Message, int len)
{
// Remove lf chars
char * ptr1, * ptr2;
ptr1 = ptr2 = Message;
while (len-- > 0)
{
*ptr2 = *ptr1;
if (*ptr1 == '\r')
if (*(ptr1+1) == '\n')
{
ptr1++;
len--;
}
ptr1++;
ptr2++;
}
return (int)(ptr2 - Message);
}
VOID UISend_AX_Datagram(UCHAR * Msg, DWORD Len, UCHAR Port, UCHAR * HWADDR, BOOL Queue)
{
MESSAGEX AXMSG;
PMESSAGEX AXPTR = &AXMSG;
int DataLen = Len;
struct PORTCONTROL * PORT = GetPortTableEntryFromSlot(Port);
// Block includes the Msg Header (7 or 11 bytes), Len Does not!
memcpy(AXPTR->DEST, HWADDR, 7);
// Get BCALL or PORTCALL if set
if (PORT && PORT->PORTBCALL[0])
memcpy(AXPTR->ORIGIN, PORT->PORTBCALL, 7);
else if (PORT && PORT->PORTCALL[0])
memcpy(AXPTR->ORIGIN, PORT->PORTCALL, 7);
else
memcpy(AXPTR->ORIGIN, MYCALL, 7);
AXPTR->DEST[6] &= 0x7e; // Clear End of Call
AXPTR->DEST[6] |= 0x80; // set Command Bit
if (UIUIDigi[Port])
{
// This port has a digi string
int DigiLen = UIUIDigiLen[Port];
UCHAR * ptr;
memcpy(&AXPTR->CTL, UIUIDigiAX[Port], DigiLen);
ptr = (UCHAR *)AXPTR;
ptr += DigiLen;
AXPTR = (PMESSAGEX)ptr;
Len += DigiLen;
}
AXPTR->ORIGIN[6] |= 1; // Set End of Call
AXPTR->CTL = 3; //UI
AXPTR->PID = 0xf0;
memcpy(AXPTR->DATA, Msg, DataLen);
// if (Queue)
// QueueRaw(Port, &AXMSG, Len + 16);
// else
SendRaw(Port, (char *)&AXMSG.DEST, Len + 16);
return;
}
VOID SendUIBeacon(int Port)
{
char UIMessage[1024];
int Len = (int)strlen(Message[Port]);
int Index = 0;
if (SendFromFile[Port])
{
FILE * hFile;
hFile = fopen(FN[Port], "rb");
if (hFile == 0)
return;
Len = (int)fread(UIMessage, 1, 1024, hFile);
fclose(hFile);
}
else
strcpy(UIMessage, Message[Port]);
Len = UIRemoveLF(UIMessage, Len);
while (Len > 256)
{
UISend_AX_Datagram(&UIMessage[Index], 256, Port, UIAXDEST[Port], TRUE);
Index += 256;
Len -= 256;
Sleep(2000);
}
UISend_AX_Datagram(&UIMessage[Index], Len, Port, UIAXDEST[Port], TRUE);
}
#ifndef LINBPQ
typedef struct tag_dlghdr
{
HWND hwndTab; // tab control
HWND hwndDisplay; // current child dialog box
RECT rcDisplay; // display rectangle for the tab control
2023-05-25 14:17:53 +01:00
DLGTEMPLATE *apRes[MaxBPQPortNo + 1];
2022-08-28 09:35:46 +01:00
} DLGHDR;
DLGTEMPLATE * WINAPI DoLockDlgRes(LPCSTR lpszResName);
#endif
HWND hwndDlg;
int PageCount;
int CurrentPage=0; // Page currently on show in tabbed Dialog
VOID WINAPI OnSelChanged(HWND hwndDlg);
VOID WINAPI OnChildDialogInit(HWND hwndDlg);
#define ICC_STANDARD_CLASSES 0x00004000
HWND hwndDisplay;
#define ID_TEST 102
#define IDD_DIAGLOG1 103
#define IDC_FROMFILE 1022
#define IDC_EDIT1 1054
#define IDC_FILENAME 1054
#define IDC_EDIT2 1055
#define IDC_MESSAGE 1055
#define IDC_EDIT3 1056
#define IDC_INTERVAL 1056
#define IDC_EDIT4 1057
#define IDC_UIDEST 1057
#define IDC_FILE 1058
#define IDC_TAB1 1059
#define IDC_UIDIGIS 1059
#define IDC_PORTNAME 1060
extern HKEY REGTREE;
HBRUSH bgBrush;
VOID SetupUI(int Port)
{
char DigiString[100], * DigiLeft;
ConvToAX25(UIUIDEST[Port], &UIAXDEST[Port][0]);
UIUIDigiLen[Port] = 0;
if (UIUIDigi[Port])
{
UIUIDigiAX[Port] = zalloc(100);
strcpy(DigiString, UIUIDigi[Port]);
DigiLeft = strlop(DigiString,',');
while(DigiString[0])
{
ConvToAX25(DigiString, &UIUIDigiAX[Port][UIUIDigiLen[Port]]);
UIUIDigiLen[Port] += 7;
if (DigiLeft)
{
memmove(DigiString, DigiLeft, (int)strlen(DigiLeft) + 1);
DigiLeft = strlop(DigiString,',');
}
else
DigiString[0] = 0;
}
}
}
#ifndef LINBPQ
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 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);
}
#endif
config_t cfg;
VOID SaveUIConfig()
{
config_setting_t *root, *group, *UIGroup;
int Port, MaxPort = GetNumberofPorts();
char ConfigName[256];
if (BPQDirectory[0] == 0)
{
strcpy(ConfigName,"UIUtil.cfg");
}
else
{
strcpy(ConfigName,BPQDirectory);
strcat(ConfigName,"/");
strcat(ConfigName,"UIUtil.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);
UIGroup = config_setting_add(group, "UIUtil", CONFIG_TYPE_GROUP);
for (Port = 1; Port <= MaxPort; Port++)
{
char Key[20];
sprintf(Key, "Port%d", Port);
group = config_setting_add(UIGroup, Key, CONFIG_TYPE_GROUP);
SaveStringValue(group, "UIDEST", &UIUIDEST[Port][0]);
SaveStringValue(group, "FileName", &FN[Port][0]);
SaveStringValue(group, "Message", &Message[Port][0]);
SaveStringValue(group, "Digis", UIUIDigi[Port]);
SaveIntValue(group, "Interval", Interval[Port]);
SaveIntValue(group, "SendFromFile", SendFromFile[Port]);
}
if(!config_write_file(&cfg, ConfigName))
{
fprintf(stderr, "Error while writing file.\n");
config_destroy(&cfg);
return;
}
config_destroy(&cfg);
}
2023-10-10 22:07:04 +01:00
int GetRegConfig();
2022-08-28 09:35:46 +01:00
VOID GetUIConfig()
{
char Key[100];
char CfgFN[256];
char Digis[100];
struct stat STAT;
config_t cfg;
config_setting_t *group;
int Port, MaxPort = GetNumberofPorts();
memset((void *)&cfg, 0, sizeof(config_t));
config_init(&cfg);
if (BPQDirectory[0] == 0)
{
strcpy(CfgFN,"UIUtil.cfg");
}
else
{
strcpy(CfgFN,BPQDirectory);
strcat(CfgFN,"/");
strcat(CfgFN,"UIUtil.cfg");
}
if (stat(CfgFN, &STAT) == -1)
{
2023-10-10 22:07:04 +01:00
// No file. If Windows try to read from registy
#ifndef LINBPQ
GetRegConfig();
#else
2022-08-28 09:35:46 +01:00
Debugprintf("UIUtil Config File not found\n");
2023-10-10 22:07:04 +01:00
#endif
2022-08-28 09:35:46 +01:00
return;
}
if(!config_read_file(&cfg, CfgFN))
{
fprintf(stderr, "UI Util Config Error Line %d - %s\n", config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
return;
}
group = config_lookup(&cfg, "main");
if (group)
{
for (Port = 1; Port <= MaxPort; Port++)
{
sprintf(Key, "main.UIUtil.Port%d", Port);
group = config_lookup (&cfg, Key);
if (group)
{
GetStringValue(group, "UIDEST", &UIUIDEST[Port][0]);
GetStringValue(group, "FileName", &FN[Port][0]);
GetStringValue(group, "Message", &Message[Port][0]);
GetStringValue(group, "Digis", Digis);
UIUIDigi[Port] = _strdup(Digis);
Interval[Port] = GetIntValue(group, "Interval");
MinCounter[Port] = Interval[Port];
SendFromFile[Port] = GetIntValue(group, "SendFromFile");
SetupUI(Port);
}
}
}
2023-06-21 08:21:04 +01:00
2023-10-10 22:07:04 +01:00
_beginthread(UIThread, 0, NULL);
}
#ifndef LINBPQ
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;
}
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;
}
value[0] = 0;
return FALSE;
}
int GetRegConfig()
{
2022-08-28 09:35:46 +01:00
int retCode, Vallen, Type, i;
char Key[80];
char Size[80];
HKEY hKey;
RECT Rect;
wsprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil");
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",&Rect.left,&Rect.right,&Rect.top,&Rect.bottom);
RegCloseKey(hKey);
}
for (i=1; i<=32; i++)
{
wsprintf(Key, "SOFTWARE\\G8BPQ\\BPQ32\\UIUtil\\UIPort%d", i);
retCode = RegOpenKeyEx (REGTREE,
Key,
0,
KEY_QUERY_VALUE,
&hKey);
if (retCode == ERROR_SUCCESS)
{
Vallen=0;
RegQueryValueEx(hKey,"Digis",0,
(ULONG *)&Type, NULL, (ULONG *)&Vallen);
if (Vallen)
{
UIUIDigi[i] = malloc(Vallen);
RegQueryValueEx(hKey,"Digis",0,
(ULONG *)&Type, UIUIDigi[i], (ULONG *)&Vallen);
}
Vallen=4;
retCode = RegQueryValueEx(hKey, "Interval", 0,
(ULONG *)&Type, (UCHAR *)&Interval[i], (ULONG *)&Vallen);
MinCounter[i] = Interval[i];
Vallen=4;
retCode = RegQueryValueEx(hKey, "SendFromFile", 0,
(ULONG *)&Type, (UCHAR *)&SendFromFile[i], (ULONG *)&Vallen);
Vallen=10;
retCode = RegQueryValueEx(hKey, "UIDEST", 0, &Type, &UIUIDEST[i][0], &Vallen);
Vallen=255;
retCode = RegQueryValueEx(hKey, "FileName", 0, &Type, &FN[i][0], &Vallen);
Vallen=999;
retCode = RegQueryValueEx(hKey, "Message", 0, &Type, &Message[i][0], &Vallen);
SetupUI(i);
RegCloseKey(hKey);
}
}
SaveUIConfig();
2023-10-10 22:07:04 +01:00
return TRUE;
2022-08-28 09:35:46 +01:00
}
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 = BPQDirectory;
if (GetOpenFileName(&ofn))
SetDlgItemText(hDlg, IDC_FILENAME, &FN[Port][0]);
break;
case IDOK:
GetDlgItemText(hDlg, IDC_UIDEST, &UIUIDEST[Port][0], 10);
if (UIUIDigi[Port])
{
free(UIUIDigi[Port]);
UIUIDigi[Port] = NULL;
}
if (UIUIDigiAX[Port])
{
free(UIUIDigiAX[Port]);
UIUIDigiAX[Port] = NULL;
}
GetDlgItemText(hDlg, IDC_UIDIGIS, Digis, 99);
UIUIDigi[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);
wsprintf(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 *)&UIUIDEST[Port][0], (int)strlen(&UIUIDEST[Port][0]));
retCode = RegSetValueEx(hKey, "FileName", 0, REG_SZ,(BYTE *)&FN[Port][0], (int)strlen(&FN[Port][0]));
retCode = RegSetValueEx(hKey, "Message", 0, REG_SZ,(BYTE *)&Message[Port][0], (int)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, Digis, (int)strlen(Digis));
RegCloseKey(hKey);
}
SetupUI(Port);
SaveUIConfig();
return (INT_PTR)TRUE;
case IDCANCEL:
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
case ID_TEST:
SendUIBeacon(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.
#define GWL_USERDATA (-21)
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, hInstance, 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 <= NUMBEROFPORTS; i++)
{
// Only allow UI on ax.25 ports
PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromSlot(i);
if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL
if (PORTVEC->PORTCONTROL.PROTOCOL == 10) // Pactor/WINMOR
if (PORTVEC->PORTCONTROL.UICAPABLE == 0)
continue;
wsprintf(PortNo, "Port %2d", GetPortNumber(i));
PortNum[tab] = 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(hInstance, lpszResName, RT_DIALOG);
HGLOBAL hglb = LoadResource(hInstance, 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(hInstance, 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, &UIUIDEST[Port][0]);
SetDlgItemText(hwndDisplay, IDC_UIDIGIS, UIUIDigi[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);
}
LRESULT CALLBACK UIWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
HKEY hKey=0;
switch (message) {
case WM_INITDIALOG:
OnTabbedDialogInit(hWnd);
return (INT_PTR)TRUE;
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code)
{
case TCN_SELCHANGE:
OnSelChanged(hWnd);
return TRUE;
// More cases on WM_NOTIFY switch.
case NM_CHAR:
return TRUE;
}
break;
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:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId) {
case IDOK:
return TRUE;
default:
return 0;
}
case WM_SYSCOMMAND:
wmId = LOWORD(wParam); // Remember, these are...
wmEvent = HIWORD(wParam); // ...different for Win32!
switch (wmId)
{
case SC_RESTORE:
return (DefWindowProc(hWnd, message, wParam, lParam));
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:
return(DestroyWindow(hWnd));
default:
return (DefWindowProc(hWnd, message, wParam, lParam));
}
return (0);
}
#endif
extern struct DATAMESSAGE * REPLYBUFFER;
char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...);
2024-04-06 02:10:08 +01:00
2024-12-16 17:54:16 +00:00
void GetPortCTEXT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
2022-08-28 09:35:46 +01:00
{
char FN[250];
FILE *hFile;
struct stat STAT;
struct PORTCONTROL * PORT = PORTTABLE;
char PortList[256] = "";
while (PORT)
{
if (PORT->CTEXT)
{
free(PORT->CTEXT);
PORT->CTEXT = 0;
}
if (BPQDirectory[0] == 0)
sprintf(FN, "Port%dCTEXT.txt", PORT->PORTNUMBER);
else
sprintf(FN, "%s/Port%dCTEXT.txt", BPQDirectory, PORT->PORTNUMBER);
if (stat(FN, &STAT) == -1)
{
PORT = PORT->PORTPOINTER;
continue;
}
hFile = fopen(FN, "rb");
if (hFile)
{
char * ptr;
PORT->CTEXT = zalloc(STAT.st_size + 1);
fread(PORT->CTEXT , 1, STAT.st_size, hFile);
fclose(hFile);
// convert CRLF or LF to CR
while (ptr = strstr(PORT->CTEXT, "\r\n"))
memmove(ptr, ptr + 1, strlen(ptr));
// Now has LF
while (ptr = strchr(PORT->CTEXT, '\n'))
*ptr = '\r';
sprintf(PortList, "%s,%d", PortList, PORT->PORTNUMBER);
}
PORT = PORT->PORTPOINTER;
}
if (Session)
{
Bufferptr = Cmdprintf(Session, Bufferptr, "CTEXT Read for ports %s\r", &PortList[1]);
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
}
else
Debugprintf("CTEXT Read for ports %s\r", &PortList[1]);
}
2024-04-06 02:10:08 +01:00
// Get the current frequency for a port. This can get a bit complicated, especially if looking for centre freq
// rather than dial freq (as this depends on mode).
//
// Used for various reporting functions - MH, Maps, BBS New User message,
// I think I'll try PORT "PortFreq" setting first then if that isn't available via rigcontrol.
//
// For now at least will report dial freq if using RIGCONTROL
DllExport uint64_t APIENTRY GetPortFrequency(int PortNo, char * FreqString)
{
struct PORTCONTROL * PORT = GetPortTableEntryFromPortNum(PortNo);
double freq = 0.0;
uint64_t freqint = 0;
char * ptr;
int n = 3;
FreqString[0] = 0;
if (PORT == 0)
return 0;
if (PORT->PortFreq)
{
freqint = PORT->PortFreq;
freq = freqint / 1000000.0;
}
else
{
// Try rigcontrol
struct TNCINFO * TNC;
struct RIGINFO * RIG = 0;
if (PORT->RIGPort)
TNC = TNCInfo[PORT->RIGPort];
else
TNC = TNCInfo[PortNo];
if (TNC)
RIG = TNC->RIG;
if (RIG == 0)
return 0;
// Frequency should be in valchar
if (RIG->Valchar[0] == 0)
return 0;
freq = atof(TNC->RIG->Valchar);
freqint = (int64_t)(freq * 1000000.0);
}
sprintf(FreqString, "%.6f", freq);
// Return 3 digits after . (KHz) unless more are significant
ptr = &FreqString[strlen(FreqString) - 1];
while (n-- && *(ptr) == '0')
*ptr-- = 0;
return freqint;
}
2023-10-10 22:07:04 +01:00
SOCKET OpenHTTPSock(char * Host)
{
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 (Host);
if (!HostEnt)
{
err = WSAGetLastError();
2024-12-16 17:54:16 +00:00
Debugprintf("Resolve Failed for %s %d %x", Host, err, err);
2023-10-10 22:07:04 +01:00
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;
}
static char HeaderTemplate[] = "POST %s HTTP/1.1\r\n"
2024-12-16 17:54:16 +00:00
"Accept: app N B lication/json\r\n"
2023-10-10 22:07:04 +01:00
// "Accept-Encoding: gzip,deflate,gzip, deflate\r\n"
"Content-Type: application/json\r\n"
"Host: %s:%d\r\n"
"Content-Length: %d\r\n"
2024-01-16 20:42:28 +00:00
"User-Agent: %s%s\r\n"
2023-10-10 22:07:04 +01:00
// "Expect: 100-continue\r\n"
"\r\n";
2024-12-16 17:54:16 +00:00
DllExport VOID WINAPI SendWebRequest(char * Host, char * Request, char * Params, char * Return)
2023-10-10 22:07:04 +01:00
{
2024-12-16 17:54:16 +00:00
SOCKET sock;
2023-10-10 22:07:04 +01:00
int InputLen = 0;
int inptr = 0;
char Buffer[4096];
char Header[256];
char * ptr, * ptr1;
int Sent;
2024-12-16 17:54:16 +00:00
int Len = strlen(Params);
if (M0LTEMap == 0)
return;
sock = OpenHTTPSock(Host);
if (sock == 0)
return;
2023-10-10 22:07:04 +01:00
2024-01-16 20:42:28 +00:00
#ifdef LINBPQ
sprintf(Header, HeaderTemplate, Request, Host, 80, Len, "linbpq/", VersionString, Params);
#else
sprintf(Header, HeaderTemplate, Request, Host, 80, Len, "bpq32/", VersionString, Params);
#endif
2023-10-10 22:07:04 +01:00
Sent = send(sock, Header, (int)strlen(Header), 0);
Sent = send(sock, Params, (int)strlen(Params), 0);
if (Sent == -1)
{
int Err = WSAGetLastError();
Debugprintf("Error %d from Web Update send()", Err);
2024-12-16 17:54:16 +00:00
closesocket(sock);
2023-10-10 22:07:04 +01:00
return;
}
while (InputLen != -1)
{
InputLen = recv(sock, &Buffer[inptr], 4096 - inptr, 0);
if (InputLen == -1 || InputLen == 0)
{
int Err = WSAGetLastError();
Debugprintf("Error %d from Web Update recv()", Err);
2024-12-16 17:54:16 +00:00
closesocket(sock);
2023-10-10 22:07:04 +01:00
return;
}
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("Map Database update ok");
}
else
{
strlop(Buffer, 13);
Debugprintf("Map Update failed - %s", Buffer);
}
2024-12-16 17:54:16 +00:00
closesocket(sock);
2023-10-10 22:07:04 +01:00
return;
}
}
else
{
ptr1 = strstr(_strlwr(Buffer), "transfer-encoding:");
if (ptr1)
{
// Just accept anything until I've sorted things with Lee
Debugprintf("%s", ptr1);
2024-12-16 17:54:16 +00:00
closesocket(sock);
2023-10-10 22:07:04 +01:00
Debugprintf("Web Database update ok");
return;
}
}
}
}
}
// https://packetnodes.spots.radio/api/NodeData/{callsign}
//SendHTTPRequest(sock, "/account/exists", Message, Len, Response);
#include "kiss.h"
extern char MYALIASLOPPED[10];
extern int MasterPort[MAXBPQPORTS+1];
2024-01-16 20:42:28 +00:00
// G7TAJ //
/*
{"mheard": [
{
"Callsign": "GB7CIP-7",
"Port": "VHF",
"Packets": 70369,
"LastHeard": "2024-12-29 20:26:32"
},
*/
void BuildPortMH(char * MHJSON, struct PORTCONTROL * PORT)
{
2024-03-26 00:52:55 +00:00
struct tm * TM;
static char MHTIME[50];
time_t szClock;
MHSTRUC * MH = PORT->PORTMHEARD;
int count = MHENTRIES;
char Normcall[20];
int len;
char * ptr;
char mhstr[400];
2024-08-30 10:14:56 +01:00
int i;
char c;
2024-03-26 00:52:55 +00:00
if (MH == NULL)
return;
2024-01-16 20:42:28 +00:00
2024-03-26 00:52:55 +00:00
while (count--)
2024-01-16 20:42:28 +00:00
{
if (MH->MHCALL[0] == 0)
break;
len = ConvFromAX25(MH->MHCALL, Normcall);
Normcall[len] = 0;
ptr = &MH->MHCALL[6]; // End of Address bit
if ((*ptr & 1) == 0)
{
2024-03-26 00:52:55 +00:00
// at least one digi - which we are not going to include
MH++;
continue;
2024-01-16 20:42:28 +00:00
}
2024-08-30 10:14:56 +01:00
// validate call to prevent corruption of json
for (i=0; i < len; i++)
{
c = Normcall[i];
if (!isalnum(c) && !(c == '#') && !(c == ' ') && !(c == '-'))
goto skipit;
}
2024-01-16 20:42:28 +00:00
//format TIME
2024-03-26 00:52:55 +00:00
2024-01-16 20:42:28 +00:00
szClock = MH->MHTIME;
TM = gmtime(&szClock);
sprintf(MHTIME, "%d-%d-%d %02d:%02d:%02d",
2024-03-26 00:52:55 +00:00
TM->tm_year+1900, TM->tm_mon + 1, TM->tm_mday, TM->tm_hour, TM->tm_min, TM->tm_sec);
2024-01-16 20:42:28 +00:00
sprintf(mhstr, "{\"callSign\": \"%s\", \"port\": \"%d\", \"packets\": %d, \"lastHeard\": \"%s\" },\r\n" ,
2024-03-26 00:52:55 +00:00
Normcall, PORT->PORTNUMBER, MH->MHCOUNT, MHTIME);
2024-01-16 20:42:28 +00:00
strcat( MHJSON, mhstr );
2024-08-30 10:14:56 +01:00
skipit:
2024-01-16 20:42:28 +00:00
MH++;
}
}
2023-10-10 22:07:04 +01:00
void SendDataToPktMap(char *Msg)
{
SOCKET sock;
char Return[256];
char Request[64];
char Params[50000];
2024-01-16 20:42:28 +00:00
2023-10-10 22:07:04 +01:00
struct PORTCONTROL * PORT = PORTTABLE;
struct PORTCONTROL * SAVEPORT;
struct ROUTE * Routes = NEIGHBOURS;
int MaxRoutes = MAXNEIGHBOURS;
2024-01-16 20:42:28 +00:00
2023-10-10 22:07:04 +01:00
int PortNo;
int Active;
uint64_t Freq;
int Baud;
int Bitrate;
char * Mode;
char * Use;
char * Type;
char * Modulation;
2024-10-11 15:37:11 +01:00
char * Usage;
2023-10-10 22:07:04 +01:00
char locked[] = " ! ";
int Percent = 0;
int Port = 0;
char Normcall[10];
char Copy[20];
2024-01-16 20:42:28 +00:00
char ID[33];
2023-10-10 22:07:04 +01:00
char * ptr = Params;
2024-01-16 20:42:28 +00:00
// G7TAJ //
char MHJSON[50000];
char * mhptr;
char * b4Routesptr;
MHJSON[0]=0;
// G7TAJ //
// printf("Sending to new map\n");
2023-10-10 22:07:04 +01:00
sprintf(Request, "/api/NodeData/%s", MYNODECALL);
// https://packetnodes.spots.radio/swagger/index.html
// This builds the request and sends it
// Minimum header seems to be
// "nodeAlias": "BPQ",
// "location": {"locator": "IO68VL"},
// "software": {"name": "BPQ32","version": "6.0.24.3"},
ptr += sprintf(ptr, "{\"nodeAlias\": \"%s\",\r\n", MYALIASLOPPED);
if (strlen(LOCATOR) == 6)
ptr += sprintf(ptr, "\"location\": {\"locator\": \"%s\"},\r\n", LOCATOR);
else
{
// Lat Lon
double myLat, myLon;
char LocCopy[80];
char * context;
strcpy(LocCopy, LOCATOR);
myLat = atof(strtok_s(LocCopy, ",:; ", &context));
myLon = atof(context);
ptr += sprintf(ptr, "\"location\": {\"coords\": {\"lat\": %f, \"lon\": %f}},\r\n",
myLat, myLon);
}
#ifdef LINBPQ
ptr += sprintf(ptr, "\"software\": {\"name\": \"LINBPQ\",\"version\": \"%s\"},\r\n", VersionString);
#else
ptr += sprintf(ptr, "\"software\": {\"name\": \"BPQ32\",\"version\": \"%s\"},\r\n", VersionString);
#endif
ptr += sprintf(ptr, "\"source\": \"ReportedByNode\",\r\n");
2024-01-16 20:42:28 +00:00
// G7TAJ //
sprintf(MHJSON, ",\"mheard\": [");
// G7TAJ //
2023-10-10 22:07:04 +01:00
//Ports
ptr += sprintf(ptr, "\"ports\": [");
// Get active ports
while (PORT)
{
PortNo = PORT->PORTNUMBER;
if (PORT->Hide)
{
PORT = PORT->PORTPOINTER;
continue;
}
if (PORT->SendtoM0LTEMap == 0)
{
PORT = PORT->PORTPOINTER;
continue;
}
// Try to get port status - may not be possible with some
if (PORT->PortStopped)
{
PORT = PORT->PORTPOINTER;
continue;
}
Active = 0;
Freq = 0;
Baud = 0;
Mode = "ax.25";
Use = "";
Type = "RF";
Bitrate = 0;
Modulation = "FSK";
2024-10-11 15:37:11 +01:00
Usage = "Access";
if (PORT->PortFreq)
Freq = PORT->PortFreq;
2023-10-10 22:07:04 +01:00
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)
Active = 1;
}
else
Active = 1; // UDP - Cant tell
}
else
if (Port->idComDev) // Serial port Open
Active = 1;
PORT = SAVEPORT;
}
}
else if (PORT->PORTTYPE == 14) // Loopback
Active = 0;
else if (PORT->PORTTYPE == 16) // External
{
if (PORT->PROTOCOL == 10) // 'HF' Port
{
struct TNCINFO * TNC = TNCInfo[PortNo];
struct AGWINFO * AGW;
if (TNC == NULL)
{
PORT = PORT->PORTPOINTER;
continue;
}
2024-10-11 15:37:11 +01:00
if (Freq == 0 && TNC->RIG)
2023-10-10 22:07:04 +01:00
Freq = TNC->RIG->RigFreq * 1000000;
switch (TNC->Hardware) // Hardware Type
{
case H_KAM:
case H_AEA:
case H_HAL:
case H_SERIAL:
// Serial
if (TNC->hDevice)
Active = 1;
break;
case H_SCS:
case H_TRK:
case H_WINRPR:
if (TNC->HostMode)
Active = 1;
break;
case H_UZ7HO:
if (TNCInfo[MasterPort[PortNo]]->CONNECTED)
Active = 1;
// Try to get mode and frequency
AGW = TNC->AGWInfo;
if (AGW && AGW->isQTSM)
{
if (AGW->ModemName[0])
{
char * ptr1, * ptr2, *Context;
strcpy(Copy, AGW->ModemName);
ptr1 = strtok_s(Copy, " ", & Context);
ptr2 = strtok_s(NULL, " ", & Context);
if (Context)
{
Modulation = Copy;
if (strstr(ptr1, "BPSK") || strstr(ptr1, "AFSK"))
{
Baud = Bitrate = atoi(Context);
}
else if (strstr(ptr1, "QPSK"))
{
Modulation = "QPSK";
Bitrate = atoi(Context);
Baud = Bitrate /2;
}
}
}
}
break;
2024-10-11 15:37:11 +01:00
case H_KISSHF:
// Try to get mode from ID then drop through
if (stristr(PORT->PORTDESCRIPTION, "BPSK"))
{
Modulation = "BPSK";
}
2023-10-10 22:07:04 +01:00
case H_WINMOR:
case H_V4:
case H_MPSK:
case H_FLDIGI:
case H_UIARQ:
case H_ARDOP:
case H_VARA:
2024-10-11 15:37:11 +01:00
2023-10-10 22:07:04 +01:00
case H_FREEDATA:
// TCP
2023-12-28 10:31:09 +00:00
Mode = Modenames[TNC->Hardware - 1];
2023-10-10 22:07:04 +01:00
if (TNC->CONNECTED)
Active = 1;
break;
case H_TELNET:
Active = 1;
Type = "Internet";
Mode = "";
}
}
else
{
// External but not HF - AXIP, BPQETHER VKISS, ??
struct _EXTPORTDATA * EXTPORT = (struct _EXTPORTDATA *)PORT;
Type = "Internet";
Active = 1;
}
}
if (Active)
{
2024-01-16 20:42:28 +00:00
char * ptr2 = &ID[29];
strcpy(ID, PORT->PORTDESCRIPTION);
while (*(ptr2) == ' ' && ptr2 != ID)
*(ptr2--) = 0;
2024-10-11 15:37:11 +01:00
if (PORT->M0LTEMapInfo)
{
// Override with user configured values - RF,7.045,BPSK,300,300,Access
char param[256];
char *p1, *p2, *p3, *p4, *p5;
strcpy(param, PORT->M0LTEMapInfo);
p1 = strlop(param, ',');
p2 = strlop(p1, ',');
p3 = strlop(p2, ',');
p4 = strlop(p3, ',');
p5 = strlop(p4, ',');
// int n = sscanf(PORT->M0LTEMapInfo, "%s,%s,%s,%s,%s,%s", &p1, &p2, &p3, &p4, &p5, &p6);
if (p5)
{
if (param[0]) Type = param;
if (p1[0])
{
// if set to DIAL+=n and frequency set from config or rigcontrol modify it
uint64_t offset = 0;
if (_memicmp(p1, "DIAL+", 5) == 0)
offset = atoi(&p1[5]);
else if (_memicmp(p1, "DIAL-", 5) == 0)
offset = -atoi(&p1[5]);
else
Freq = atof(p1) * 1000000;
if (Freq != 0)
Freq += offset;
}
if (p2[0]) Modulation = p2;
if (p3[0]) Baud = atoi(p3);
if (p4[0]) Bitrate = atoi(p4);
if (p5[0]) Usage = p5;
}
}
2023-10-10 22:07:04 +01:00
ptr += sprintf(ptr, "{\"id\": \"%d\",\"linkType\": \"%s\","
"\"freq\": \"%lld\",\"mode\": \"%s\",\"modulation\": \"%s\","
"\"baud\": \"%d\",\"bitrate\": \"%d\",\"usage\": \"%s\",\"comment\": \"%s\"},\r\n",
PortNo, Type,
Freq, Mode, Modulation,
2024-10-11 15:37:11 +01:00
Baud, Bitrate, Usage, ID);
2024-01-16 20:42:28 +00:00
// G7TAJ //
// make MH list to be added later
BuildPortMH(MHJSON, PORT);
// G7TAJ //
2023-10-10 22:07:04 +01:00
}
2024-01-16 20:42:28 +00:00
2023-10-10 22:07:04 +01:00
PORT = PORT->PORTPOINTER;
}
ptr -= 3;
ptr += sprintf(ptr, "],\r\n");
// Neighbours
2024-01-16 20:42:28 +00:00
// G7TAJ //
b4Routesptr = ptr-3;
// G7TAJ //
2023-10-10 22:07:04 +01:00
ptr += sprintf(ptr, "\"neighbours\": [\r\n");
while (MaxRoutes--)
{
if (Routes->NEIGHBOUR_CALL[0] != 0)
if (Routes->NEIGHBOUR_LINK && Routes->NEIGHBOUR_LINK->L2STATE >= 5)
{
ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall);
strlop(Normcall, ' ');
2024-01-16 20:42:28 +00:00
ptr += sprintf(ptr,
2023-10-10 22:07:04 +01:00
"{\"node\": \"%s\", \"port\": \"%d\", \"quality\": \"%d\"},\r\n",
Normcall, Routes->NEIGHBOUR_PORT, Routes->NEIGHBOUR_QUAL);
}
Routes++;
}
2024-01-16 20:42:28 +00:00
// G7TAJ //
// if !strstr quality, then there are none, so remove neighbours portion
if ( strstr(Params, "quality") == NULL ) {
ptr = b4Routesptr;
} else {
ptr -= 3;
ptr += sprintf(ptr, "]");
}
if ( strlen(MHJSON) > 15 ) {
mhptr = MHJSON + strlen(MHJSON);
mhptr -= 3;
sprintf(mhptr, "]\r\n");
ptr += sprintf(ptr, "\r\n%s", MHJSON);
}
ptr += sprintf(ptr, "}");
// G7TAJ //
2023-10-10 22:07:04 +01:00
/*
{
"nodeAlias": "BPQ",
"location": {"locator": "IO92KX"},
"software": {"name": "BPQ32","version": "6.0.24.11 Debug Build "},
"contact": "G8BPQ",
"sysopComment": "Testing",
"source": "ReportedByNode"
}
"ports": [
{
"id": "string",
"linkType": "RF",
"freq": 0,
"mode": "string",
"modulation": "string",
"baud": 0,
"bitrate": 0,
"usage": "Access",
"comment": "string"
}
],
*/
// "contact": "string",
// "neighbours": [{"node": "G7TAJ","port": "30"}]
2024-12-16 17:54:16 +00:00
SendWebRequest("packetnodes.spots.radio", Request, Params, Return);
2023-10-10 22:07:04 +01:00
}
// ="{\"neighbours\": [{\"node\": \"G7TAJ\",\"port\": \"30\"}]}";
//'POST' \
// 'https://packetnodes.spots.radio/api/NodeData/GM8BPQ' \
// -H 'accept: */*' \
// -H 'Content-Type: application/json' \
// -d '{
// "nodeAlias": "BPQ",
// "location": {"locator": "IO68VL"},
// "software": {"name": "BPQ32","version": "6.0.24.3"},
// "contact": "string",
// "neighbours": [{"node": "G7TAJ","port": "30"}]
//}'
2022-08-28 09:35:46 +01:00